Merge branch 'master' into feature/syndicate
diff --git a/apiary.apib b/apiary.apib
index ee28967..25915e9 100644
--- a/apiary.apib
+++ b/apiary.apib
@@ -13,6 +13,49 @@
+ Response 200 (application/json)
+ [
+ {
+ "humanReadableName": "MyDeployment",
+ "id": 1,
+ "created": "2016-04-29T16:19:03.549901Z",
+ "updated": "2016-04-29T16:19:05.624151Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "MyDeployment",
+ "accessControl": "allow all",
+ "images": [
+ "1"
+ ],
+ "sites": [
+ "1"
+ ],
+ "flavors": [
+ "1",
+ "2",
+ "3"
+ ],
+ "dashboardviews": [
+ "1"
+ ]
+ }
+ ]
+
+### Create a deployment [POST]
+
++ Request (application/json)
+
+ {
+ "humanReadableName": "MyDeployment",
+ }
+
++ Response 200 (application/json)
+
{
"humanReadableName": "MyDeployment",
"id": 1,
@@ -43,6 +86,49 @@
"1"
]
}
+
+### View a Deployment Detail [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Deployment in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyDeployment",
+ "id": 1,
+ "created": "2016-04-27T21:46:57.354544Z",
+ "updated": "2016-04-27T21:47:05.221720Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "MyDeployment",
+ "accessControl": "allow all",
+ "images": [],
+ "sites": [
+ "1"
+ ],
+ "flavors": [
+ "3",
+ "2",
+ "1"
+ ],
+ "dashboardviews": [
+ "3"
+ ]
+ }
+
+### Delete a Deployment [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Deployment in the form of an integer
+
++ Response 204
# Group Flavors
@@ -80,6 +166,39 @@
}
]
+### Create a Flavor [POST]
+
++ Request (application/json)
+
+ {
+ "humanReadableName": "mq.test",
+ }
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "m1.large",
+ "id": 1,
+ "created": "2016-04-29T16:19:01.979548Z",
+ "updated": "2016-04-29T16:19:03.568238Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "m1.large",
+ "description": null,
+ "flavor": "m1.large",
+ "order": 0,
+ "default": false,
+ "deployments": [
+ "1"
+ ]
+ }
+
### View a Flavors Detail [GET]
+ Parameters
@@ -109,13 +228,23 @@
"1"
]
}
+
+### Delete a Flavors Detail [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Flavors in the form of an integer
+
++ Response 204
# Group Instances
List of the XOS instances
-## Instances [/api/core/instances/{id}/]
+## Instances Collection [/api/core/instances/{?no_hyperlinks}]
+
+ + no_hyperlinks (number, optional) - Wheter to return relation with links or ids
+ + Default: `0`
### List all Instances [GET]
@@ -156,7 +285,107 @@
]
}
]
-
+
+### Create an Instance [POST]
+
++ Parameters
+ + no_hyperlinks: 1
+
++ Request (application/json)
+
+ {
+ "name": "test-instance",
+ "image": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 1
+ }
+
++ Response 200 (application/json)
+
+ {
+ "id": 1,
+ "humanReadableName": "uninstantiated-1",
+ "created": "2016-04-26T00:36:22.465259Z",
+ "updated": "2016-04-26T00:36:22.465288Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": null,
+ "instance_uuid": null,
+ "name": "test-instance",
+ "instance_name": null,
+ "ip": null,
+ "image": "1",
+ "creator": "1",
+ "slice": "1",
+ "deployment": "1",
+ "node": "1",
+ "numberCores": 0,
+ "flavor": "1",
+ "userData": null,
+ "isolation": "vm",
+ "volumes": "/etc/dnsmasq.d,/etc/ufw",
+ "parent": null,
+ "networks": [
+ "1"
+ ]
+ }
+
+## Instances Detail [/api/core/instances/{id}/]
+
+### Get instance details [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Instance in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "id": 1,
+ "humanReadableName": "uninstantiated-1",
+ "created": "2016-04-26T00:36:22.465259Z",
+ "updated": "2016-04-26T00:36:22.465288Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": null,
+ "instance_uuid": null,
+ "name": "mysite_vcpe",
+ "instance_name": null,
+ "ip": null,
+ "image": "1",
+ "creator": "1",
+ "slice": "1",
+ "deployment": "1",
+ "node": "1",
+ "numberCores": 0,
+ "flavor": "1",
+ "userData": null,
+ "isolation": "vm",
+ "volumes": "/etc/dnsmasq.d,/etc/ufw",
+ "parent": null,
+ "networks": [
+ "1"
+ ]
+ }
+
+### Delete instance [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Instance in the form of an integer
+
++ Response 204
# Group Nodes
@@ -323,40 +552,6 @@
-# Group Utility
-
-List of the XOS Utility API
-
-## Login [/api/utility/login/]
-
-### Log a user in the system [POST]
-
-+ Request (application/json)
-
- {
- "username": "padmin@vicci.org",
- "password": "letmein"
- }
-
-+ Response 200 (application/json)
-
- {
- "xoscsrftoken":"xuvsRC1jkXAsnrdRlgJvcXpmtthTAqqf",
- "xossessionid":"7ds5a3wzjlgbjqo4odkd25qsm0j2s6zg",
- "user": "{\"policed\": null, \"site\": 3, \"is_appuser\": false, \"is_staff\": true, \"backend_status\": \"Provisioning in progress\", \"id\": 3, \"is_registering\": false, \"last_login\": \"2016-04-30T22:51:04.788675+00:00\", \"email\": \"padmin@vicci.org\", \"no_sync\": false, \"username\": \"padmin@vicci.org\", \"dashboards\": [11], \"login_page\": null, \"firstname\": \"XOS\", \"user_url\": null, \"deleted\": false, \"lastname\": \"admin\", \"is_active\": true, \"lazy_blocked\": false, \"phone\": null, \"is_admin\": true, \"enacted\": null, \"public_key\": null, \"is_readonly\": false, \"no_policy\": false, \"write_protect\": false}"
- }
-
-## Logout [/api/utility/logout/]
-
-### Log a user out of the system [POST]
-
-+ Request (application/json)
- {xossessionid: "sessionId"}
-
-+ Response 200 (application/json)
-
-
-
# Group Example
## Example Services Collection [/api/service/exampleservice/]
@@ -415,6 +610,40 @@
]
+# Group Utility
+
+List of the XOS Utility API
+
+## Login [/api/utility/login/]
+
+### Log a user in the system [POST]
+
++ Request (application/json)
+
+ {
+ "username": "padmin@vicci.org",
+ "password": "letmein"
+ }
+
++ Response 200 (application/json)
+
+ {
+ "xoscsrftoken":"xuvsRC1jkXAsnrdRlgJvcXpmtthTAqqf",
+ "xossessionid":"7ds5a3wzjlgbjqo4odkd25qsm0j2s6zg",
+ "user": "{\"policed\": null, \"site\": 3, \"is_appuser\": false, \"is_staff\": true, \"backend_status\": \"Provisioning in progress\", \"id\": 3, \"is_registering\": false, \"last_login\": \"2016-04-30T22:51:04.788675+00:00\", \"email\": \"padmin@vicci.org\", \"no_sync\": false, \"username\": \"padmin@vicci.org\", \"dashboards\": [11], \"login_page\": null, \"firstname\": \"XOS\", \"user_url\": null, \"deleted\": false, \"lastname\": \"admin\", \"is_active\": true, \"lazy_blocked\": false, \"phone\": null, \"is_admin\": true, \"enacted\": null, \"public_key\": null, \"is_readonly\": false, \"no_policy\": false, \"write_protect\": false}"
+ }
+
+## Logout [/api/utility/logout/]
+
+### Log a user out of the system [POST]
+
++ Request (application/json)
+ {xossessionid: "sessionId"}
+
++ Response 200 (application/json)
+
+
+
# Group Subscribers
Resource related to the CORD Subscribers.
diff --git a/views/ngXosLib/README.md b/views/ngXosLib/README.md
index 2eb73cb..f4b9c8f 100644
--- a/views/ngXosLib/README.md
+++ b/views/ngXosLib/README.md
@@ -6,14 +6,6 @@
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 Apiary.
-
->You can generate api related documentation with: `npm run doc`. The output is locate in `api/docs`. You can have a list of available method also trough Apiary at `http://docs.xos.apiary.io/#`
-
### Vendors
Xos comes with a preset of common libraries, as listed in `bower.json`:
@@ -33,7 +25,7 @@
### Helpers
-XOS comes with an helper library that is automatically loaded in the Django template.
+XOS comes with an helper library that is automatically loaded in the Django template. It a set of Services and UI Components
To use it, add `xos.helpers` to your required modules:
@@ -45,14 +37,12 @@
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.
-
->_NOTE: for the API related service, check documentation in [Apigen](#apigen) section._
-
To develop components inside this folder there is a particular command: `npm run dev`, this will watch the helpers file and rebuild them with sourcemaps. For this reason remember to build them when done developing.
When some changes are applied to this common library it should be rebuilt with: `npm run build`
+To generate the relative documentation use: `npm run doc`
+
### Yo Xos
We have created a [yeoman](http://yeoman.io/) generator to help you scaffolding views.
diff --git a/views/ngXosLib/apigen/blueprintToNgResource.js b/views/ngXosLib/apigen/blueprintToNgResource.js
deleted file mode 100644
index 21d65e7..0000000
--- a/views/ngXosLib/apigen/blueprintToNgResource.js
+++ /dev/null
@@ -1,78 +0,0 @@
-'use strict';
-
-const protagonist = require('protagonist');
-const fs = require('fs');
-const P = require('bluebird');
-const _ = require('lodash');
-const chalk = require('chalk');
-const Handlebars = require('handlebars');
-const util = require('util');
-const path = require('path');
-
-P.promisifyAll(fs);
-P.promisifyAll(protagonist);
-
-const angualarModuleName = 'xos.helpers'
-
-// format href in angular format
-const formatHref = url => url.replace('{', ':').replace('}', '');
-
-const formatTitle = title => title.split(' ').join('-');
-
-const getParamName = url => url.match(/\{([^)]+)\}/) ? url.match(/\{([^)]+)\}/)[1] : '';
-
-// Get Group description
-const getGroupDescription = (group) => _.find(group, {element: 'copy'}) ? _.find(group, {element: 'copy'}).content.replace(/\n$/, '') : '';
-
-// Loop APIs endpoint
-const loopApiEndpoint = (group) => {
- // {name: 'ResourceName', attributes: {href: '/ahhsiiis'}}
- _.remove(group, {element: 'copy'})
- // console.log(group);
- // _.forEach(group, d => console.log(d));
-
- return _.map(group, g => {
- return {
- name: formatTitle(g.meta.title),
- param: {href: formatHref(g.attributes.href), name: getParamName(g.attributes.href)},
- }
- })
-};
-
-// Loop APIs groups
-const loopApiGroups = (defs) => {
- if (!Array.isArray(defs)) {
- return;
- }
- _.forEach(defs, d => {
- console.info(chalk.blue.bold(`Parsing Group: ${d.meta.title}`));
- var data = {
- description: getGroupDescription(d.content),
- ngModule: angualarModuleName,
- resources: loopApiEndpoint(d.content)
- };
- fs.writeFileSync(path.join(__dirname, `../xosHelpers/src/services/rest/${formatTitle(d.meta.title)}.js`), handlebarsTemplate(data));
- });
-
- console.info(chalk.green.bold(`Api Generated`));
-
-};
-
-// Loop the top level definitions
-const loopApiDefinitions = (defs) => {
- // console.log(util.inspect(defs, false, null));
- _.forEach(defs, d => loopApiGroups(d.content));
-};
-
-let handlebarsTemplate;
-
-// read blueprint docs and parse
-fs.readFileAsync(path.join(__dirname, './ngResourceTemplate.handlebars'), 'utf8')
-.then((template) => {
- handlebarsTemplate = Handlebars.compile(template);
- return fs.readFileAsync(path.join(__dirname, '../../../apiary.apib'), 'utf8')
-})
-.then(data => protagonist.parseAsync(data))
-.then(result => loopApiDefinitions(result.content))
-.catch(console.warn);
-
diff --git a/views/ngXosLib/apigen/ngResourceTemplate.handlebars b/views/ngXosLib/apigen/ngResourceTemplate.handlebars
deleted file mode 100644
index c1bc8f5..0000000
--- a/views/ngXosLib/apigen/ngResourceTemplate.handlebars
+++ /dev/null
@@ -1,17 +0,0 @@
-(function() {
- 'use strict';
-
- angular.module('{{ngModule}}')
- {{#each resources}}
- /**
- * @ngdoc service
- * @name {{../ngModule}}.{{name}}
- * @description Angular resource to fetch {{param.href}}
- **/
- .service('{{name}}', function($resource){
- return $resource('{{param.href}}'{{#if param.name}}, { {{param.name}}: '@id' }, {
- update: { method: 'PUT' }
- }{{/if}});
- })
- {{/each}}
-})();
\ No newline at end of file
diff --git a/views/ngXosLib/bower.json b/views/ngXosLib/bower.json
index 56fed7f..bc9c23a 100644
--- a/views/ngXosLib/bower.json
+++ b/views/ngXosLib/bower.json
@@ -20,7 +20,8 @@
"ng-lodash": "0.3.0",
"angular-cookies": "1.4.7",
"angular-animate": "1.4.7",
- "lodash": "~4.11.1"
+ "lodash": "~4.11.1",
+ "angular-chart.js": "~0.10.2"
},
"devDependencies": {
"angular-mocks": "1.4.7",
diff --git a/views/ngXosLib/generator-xos/app/templates/bower.json b/views/ngXosLib/generator-xos/app/templates/bower.json
index 9336a98..bbddc5b 100644
--- a/views/ngXosLib/generator-xos/app/templates/bower.json
+++ b/views/ngXosLib/generator-xos/app/templates/bower.json
@@ -25,6 +25,7 @@
"angular-animate": "1.4.7",
"angular-resource": "1.4.7",
"lodash": "~4.11.1",
- "bootstrap-css": "3.3.6"
+ "bootstrap-css": "3.3.6",
+ "angular-chart.js": "~0.10.2"
}
}
diff --git a/views/ngXosLib/gulp/ngXosHelpers.js b/views/ngXosLib/gulp/ngXosHelpers.js
index 435e239..f29a646 100644
--- a/views/ngXosLib/gulp/ngXosHelpers.js
+++ b/views/ngXosLib/gulp/ngXosHelpers.js
@@ -65,16 +65,13 @@
});
gulp.task('makeDocs', ['cleanDocs'], function(){
+
var ngOptions = {
- scripts: [
- 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js',
- 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-animate.min.js',
- 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-cookies.min.js',
- 'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-resource.js',
+ scripts: [].concat([
'https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular-mocks.js',
- 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js',
+ `./${options.ngXosVendor}ngXosVendor.js`,
`./${options.ngXosVendor}ngXosHelpers.js`,
- ],
+ ]),
styles: [
`./${options.ngXosStyles}xosNgLib.css`,
'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.css',
diff --git a/views/ngXosLib/gulp/ngXosVendor.js b/views/ngXosLib/gulp/ngXosVendor.js
index 7508fe4..c11ef9e 100644
--- a/views/ngXosLib/gulp/ngXosVendor.js
+++ b/views/ngXosLib/gulp/ngXosVendor.js
@@ -1,6 +1,6 @@
var gulp = require('gulp');
var uglify = require('gulp-uglify');
-var concat = require("gulp-concat");
+var concat = require('gulp-concat');
var wiredep = require('wiredep');
module.exports = function(options){
@@ -8,7 +8,7 @@
var bowerDeps = wiredep().js;
return gulp.src(bowerDeps)
.pipe(concat('ngXosVendor.js'))
- // .pipe(uglify())
+ .pipe(uglify())
.pipe(gulp.dest(options.ngXosVendor));
});
};
\ No newline at end of file
diff --git a/views/ngXosLib/karma.conf.js b/views/ngXosLib/karma.conf.js
index 49f72a1..36ddda1 100644
--- a/views/ngXosLib/karma.conf.js
+++ b/views/ngXosLib/karma.conf.js
@@ -16,7 +16,7 @@
'xosHelpers/src/**/*.module.js',
'xosHelpers/src/**/*.js',
'xosHelpers/spec/**/*.test.js'
- // 'xosHelpers/spec/ui/form.test.js'
+ // 'xosHelpers/spec/ui/smart-pie.test.js'
]);
module.exports = function(config) {
diff --git a/views/ngXosLib/xosHelpers/spec/ui/smart-pie.test.js b/views/ngXosLib/xosHelpers/spec/ui/smart-pie.test.js
new file mode 100644
index 0000000..8dce4f9
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/spec/ui/smart-pie.test.js
@@ -0,0 +1,134 @@
+/**
+ * © OpenCORD
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ let mockData;
+
+ describe('The xos.helper module', function(){
+ describe('The xos-smart-pie component', () => {
+
+ var spy, scope, isolatedScope, element;
+
+ beforeEach(module('xos.helpers'));
+
+ beforeEach(function() {
+
+ // set mockData
+ mockData = [
+ {
+ id: 1,
+ first_name: 'Jon',
+ last_name: 'Snow',
+ category: 1
+ },
+ {
+ id: 2,
+ first_name: 'Danaerys',
+ last_name: 'Targaryen',
+ category: 2
+ },
+ {
+ id: 3,
+ first_name: 'Aria',
+ last_name: 'Stark',
+ category: 1
+ }
+ ]
+
+ });
+
+ // mock the service
+ beforeEach(function(){
+ module(function($provide){
+ $provide.service('MockResource', function(){
+ return {
+ query: ''
+ }
+ });
+ });
+ })
+
+ beforeEach(inject(function ($compile, $rootScope, $q, MockResource) {
+ scope = $rootScope.$new();
+
+ scope.config = {
+ resource: 'MockResource',
+ groupBy: 'category',
+ classes: 'my-test-class'
+ };
+
+ spy = MockResource;
+
+ spyOn(MockResource, 'query').and.callFake(function() {
+ var deferred = $q.defer();
+ deferred.resolve(mockData);
+ return {$promise: deferred.promise};
+ });
+
+ element = angular.element('<xos-smart-pie config="config"></xos-smart-pie>');
+ $compile(element)(scope);
+ scope.$digest();
+ isolatedScope = element.isolateScope().vm;
+ }));
+
+ it('should attach provided classes', () => {
+ expect($(element).find('canvas')).toHaveClass('my-test-class');
+ });
+
+ it('should group elements', () => {
+ let groupedData = [2, 1];
+ expect(spy.query).toHaveBeenCalled();
+ expect(isolatedScope.data).toEqual(groupedData);
+ });
+
+ describe('when a labelFormatter function is provided', () => {
+ beforeEach(inject(function ($compile, $rootScope){
+ scope = $rootScope.$new();
+ scope.config = {
+ resource: 'MockResource',
+ groupBy: 'category',
+ classes: 'label-formatter-test',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'First' : 'Second');
+ }
+ };
+ element = angular.element('<xos-smart-pie config="config"></xos-smart-pie>');
+ $compile(element)(scope);
+ scope.$digest();
+ isolatedScope = element.isolateScope().vm;
+ }));
+
+ it('should format labels', () => {
+ expect(isolatedScope.labels).toEqual(['First', 'Second'])
+ });
+ });
+
+ describe('when polling is enabled', () => {
+ beforeEach(inject(function ($compile, $rootScope){
+ scope = $rootScope.$new();
+ scope.config = {
+ resource: 'MockResource',
+ groupBy: 'category',
+ classes: 'label-formatter-test',
+ poll: 2
+ };
+ element = angular.element('<xos-smart-pie config="config"></xos-smart-pie>');
+ $compile(element)(scope);
+ scope.$digest();
+ isolatedScope = element.isolateScope().vm;
+ }));
+
+ it('should call the backend every 2 second', () => {
+ expect(spy.query).toHaveBeenCalled();
+ // $interval
+ });
+ });
+
+ });
+ });
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Deployments.js b/views/ngXosLib/xosHelpers/src/services/rest/Deployments.js
new file mode 100644
index 0000000..4fecbc2
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Deployments.js
@@ -0,0 +1,15 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Deployments
+ * @description Angular resource to fetch /api/core/deployments/:id/
+ **/
+ .service('Deployments', function($resource){
+ return $resource('/api/core/deployments/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Flavors.js b/views/ngXosLib/xosHelpers/src/services/rest/Flavors.js
new file mode 100644
index 0000000..348b770
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Flavors.js
@@ -0,0 +1,15 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Flavors
+ * @description Angular resource to fetch /api/core/flavors/:id/
+ **/
+ .service('Flavors', function($resource){
+ return $resource('/api/core/flavors/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Nodes.js b/views/ngXosLib/xosHelpers/src/services/rest/Nodes.js
new file mode 100644
index 0000000..bf2e387
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Nodes.js
@@ -0,0 +1,15 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Nodes
+ * @description Angular resource to fetch /api/core/nodes/:id/
+ **/
+ .service('Nodes', function($resource){
+ return $resource('/api/core/nodes/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Sites.js b/views/ngXosLib/xosHelpers/src/services/rest/Sites.js
new file mode 100644
index 0000000..818d741
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Sites.js
@@ -0,0 +1,15 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Sites
+ * @description Angular resource to fetch /api/core/sites/:id/
+ **/
+ .service('Sites', function($resource){
+ return $resource('/api/core/sites/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Subscribers.js b/views/ngXosLib/xosHelpers/src/services/rest/Subscribers.js
index 342d856..945e1e8 100644
--- a/views/ngXosLib/xosHelpers/src/services/rest/Subscribers.js
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Subscribers.js
@@ -5,71 +5,143 @@
/**
* @ngdoc service
* @name xos.helpers.Subscribers
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/
+ * @description Angular resource to fetch Subscribers
**/
.service('Subscribers', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/
- **/
- .service('Subscriber-features', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-uplink_speed
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/uplink_speed/
- **/
- .service('Subscriber-features-uplink_speed', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/uplink_speed/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-downlink_speed
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/downlink_speed/
- **/
- .service('Subscriber-features-downlink_speed', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/downlink_speed/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-cdn
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/cdn/
- **/
- .service('Subscriber-features-cdn', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/cdn/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-uverse
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/uverse/
- **/
- .service('Subscriber-features-uverse', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/uverse/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- })
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-status
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/status/
- **/
- .service('Subscriber-features-status', function($resource){
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/status/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
+ return $resource('/api/tenant/cord/subscriber/:id/', { id: '@id' }, {
+ update: { method: 'PUT' },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#View-a-Subscriber-Features-Detail
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * View-a-Subscriber-Features-Detail
+ **/
+ 'View-a-Subscriber-Features-Detail': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uplink_speed
+ **/
+ 'Read-Subscriber-uplink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uplink_speed
+ **/
+ 'Update-Subscriber-uplink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-downlink_speed
+ **/
+ 'Read-Subscriber-downlink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-downlink_speed
+ **/
+ 'Update-Subscriber-downlink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-cdn
+ **/
+ 'Read-Subscriber-cdn': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-cdn
+ **/
+ 'Update-Subscriber-cdn': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uverse
+ **/
+ 'Read-Subscriber-uverse': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uverse
+ **/
+ 'Update-Subscriber-uverse': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-status
+ **/
+ 'Read-Subscriber-status': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-status
+ **/
+ 'Update-Subscriber-status': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ }
+ })
})
})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Utility.js b/views/ngXosLib/xosHelpers/src/services/rest/Utility.js
new file mode 100644
index 0000000..a735c46
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Utility.js
@@ -0,0 +1,21 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Login
+ * @description Angular resource to fetch /api/utility/login/
+ **/
+ .service('Login', function($resource){
+ return $resource('/api/utility/login/');
+ })
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Logout
+ * @description Angular resource to fetch /api/utility/logout/
+ **/
+ .service('Logout', function($resource){
+ return $resource('/api/utility/logout/');
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/styles/main.scss b/views/ngXosLib/xosHelpers/src/styles/main.scss
index 800466e..43c54f6 100644
--- a/views/ngXosLib/xosHelpers/src/styles/main.scss
+++ b/views/ngXosLib/xosHelpers/src/styles/main.scss
@@ -1,5 +1,5 @@
@import './animations.scss';
-@import '../../../../../../style/sass/bootstrap/bootstrap/_variables.scss';
+@import '../../../../../views/style/sass/bootstrap/bootstrap/_variables.scss';
@import '../ui_components/dumbComponents/table/table.scss';
@import '../ui_components/dumbComponents/alert/alert.scss';
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js
new file mode 100644
index 0000000..2290583
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js
@@ -0,0 +1,173 @@
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosSmartPie
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component,
+ * it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
+ * and a field name that is used to group the data.
+ * ```
+ * {
+ resource: 'Users',
+ groupBy: 'fieldName',
+ classes: 'my-custom-class',
+ labelFormatter: (labels) => {
+ // here you can format your label,
+ // you should return an array with the same order
+ return labels;
+ }
+ }
+ * ```
+ * @scope
+ * @example
+ <example module="sampleSmartPie">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPie', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ poll: 2,
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'Active' : 'Banned');
+ }
+ };
+ });
+ </file>
+ <file name="backend.js">
+ angular.module('sampleSmartPie')
+ .run(function($httpBackend, _){
+ let mock = [
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 1}
+ ],
+
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 2},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ],
+
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ]
+ ];
+ $httpBackend.whenGET('/test').respond(function(method, url, data, headers, params) {
+ return [200, mock[Math.round(Math.random() * 3)]];
+ });
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+
+ <example module="sampleSmartPiePoll">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPiePoll', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+ });
+ </file>
+ <file name="backendPoll.js">
+ angular.module('sampleSmartPiePoll')
+ .run(function($httpBackend, _){
+ let datas = [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1}
+ ];
+
+ $httpBackend.whenGET('/test').respond(200, datas)
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+ */
+ .directive('xosSmartPie', function(){
+ return {
+ restrict: 'E',
+ scope: {
+ config: '='
+ },
+ template: `
+ <canvas
+ class="chart chart-pie {{vm.config.classes}}"
+ chart-data="vm.data" chart-labels="vm.labels"
+ chart-legend="{{vm.config.legend}}">
+ </canvas>
+ `,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: function($injector, $timeout, $interval, _){
+
+ this.Resource = $injector.get(this.config.resource);
+
+ const getData = () => {
+ this.Resource.query().$promise
+ .then((res) => {
+
+ if(!res[0]){
+ return;
+ }
+
+ // group data
+ let grouped = _.groupBy(res, this.config.groupBy);
+ this.data = _.reduce(Object.keys(grouped), (data, group) => data.concat(grouped[group].length), []);
+ // create labels
+ this.labels = angular.isFunction(this.config.labelFormatter) ? this.config.labelFormatter(Object.keys(grouped)) : Object.keys(grouped);
+ });
+ }
+
+ getData();
+
+ if(this.config.poll){
+ $interval(() => {getData()}, this.config.poll * 1000)
+ }
+ }
+ };
+ });
+})();
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
index 8cbe0af..74226cc 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
@@ -140,7 +140,6 @@
label: 'delete',
icon: 'remove',
cb: (item) => {
- console.log(item);
this.Resource.delete({id: item.id}).$promise
.then(() => {
_.remove(this.data, (d) => d.id === item.id);
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js b/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
index 03d6308..ad3dc49 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/ui-components.module.js
@@ -24,5 +24,6 @@
**/
angular.module('xos.uiComponents', [
+ 'chart.js'
])
})();
diff --git a/views/ngXosViews/sampleView/bower.json b/views/ngXosViews/sampleView/bower.json
index a940676..5cc2226 100644
--- a/views/ngXosViews/sampleView/bower.json
+++ b/views/ngXosViews/sampleView/bower.json
@@ -25,6 +25,7 @@
"angular-animate": "1.4.7",
"angular-resource": "1.4.7",
"lodash": "~4.11.1",
- "bootstrap-css": "3.3.6"
+ "bootstrap-css": "3.3.6",
+ "angular-chart.js": "~0.10.2"
}
}
diff --git a/views/ngXosViews/sampleView/env/default.js b/views/ngXosViews/sampleView/env/default.js
index 6343727..465003d 100644
--- a/views/ngXosViews/sampleView/env/default.js
+++ b/views/ngXosViews/sampleView/env/default.js
@@ -8,6 +8,6 @@
module.exports = {
host: 'http://xos.dev:9999/',
- xoscsrftoken: '6OdNq1P7Aydyut3KjWBZoXLPJhb4DcuQ',
- xossessionid: 'fpyv5s71bj779wmguxtg8wori16kiyrz'
+ xoscsrftoken: 'p5xTzNM2gHDnCpZO4GphloROs0kXmTDX',
+ xossessionid: 'fw0fio7kuwi9z5sm100xtat5cm5jbp5j'
};
diff --git a/views/ngXosViews/sampleView/src/index.html b/views/ngXosViews/sampleView/src/index.html
index d9905a4..df9cb0e 100644
--- a/views/ngXosViews/sampleView/src/index.html
+++ b/views/ngXosViews/sampleView/src/index.html
@@ -1,6 +1,7 @@
<!-- browserSync -->
<!-- bower:css -->
<link rel="stylesheet" href="vendor/bootstrap-css/css/bootstrap.min.css" />
+<link rel="stylesheet" href="vendor/angular-chart.js/dist/angular-chart.css" />
<!-- endbower -->
<!-- endcss -->
<!-- inject:css -->
@@ -97,6 +98,8 @@
<script src="vendor/angular-resource/angular-resource.js"></script>
<script src="vendor/lodash/lodash.js"></script>
<script src="vendor/bootstrap-css/js/bootstrap.min.js"></script>
+<script src="vendor/Chart.js/Chart.js"></script>
+<script src="vendor/angular-chart.js/dist/angular-chart.js"></script>
<!-- endbower -->
<!-- endjs -->
<!-- inject:js -->
diff --git a/views/ngXosViews/sampleView/src/js/main.js b/views/ngXosViews/sampleView/src/js/main.js
index b07768a..f008e22 100644
--- a/views/ngXosViews/sampleView/src/js/main.js
+++ b/views/ngXosViews/sampleView/src/js/main.js
@@ -25,8 +25,15 @@
templateUrl: 'templates/users-list.tpl.html',
controller: function(Users){
- this.tableConfig = {
- resource: 'Users'
+ this.config = {
+ resource: 'Users',
+ groupBy: 'is_admin',
+ legend: true,
+ poll: 2,
+ labelFormatter: (labels) => {
+ console.log(labels);
+ return labels.map(l => l === 'true' ? 'Admin' : 'Non admin');
+ }
};
}
diff --git a/views/ngXosViews/sampleView/src/templates/users-list.tpl.html b/views/ngXosViews/sampleView/src/templates/users-list.tpl.html
index 0641dd9..214622c 100644
--- a/views/ngXosViews/sampleView/src/templates/users-list.tpl.html
+++ b/views/ngXosViews/sampleView/src/templates/users-list.tpl.html
@@ -1 +1,2 @@
-<xos-smart-table config="vm.tableConfig"></xos-smart-table>
\ No newline at end of file
+<!-- <xos-table config="vm.tableConfig" data="vm.users"></xos-table> -->
+<xos-smart-pie config="vm.config"></xos-smart-pie>
\ No newline at end of file
diff --git a/xos/api/examples/misc/get_sshkeys.sh b/xos/api/examples/misc/get_sshkeys.sh
new file mode 100755
index 0000000..0ac14c0
--- /dev/null
+++ b/xos/api/examples/misc/get_sshkeys.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+source ./config.sh
+source ./util.sh
+
+curl -H "Accept: application/json; indent=4" -u $AUTH -X GET $HOST/api/utility/sshkeys/
diff --git a/xos/api/utility/portforwarding.py b/xos/api/utility/portforwarding.py
new file mode 100644
index 0000000..0bb7a93
--- /dev/null
+++ b/xos/api/utility/portforwarding.py
@@ -0,0 +1,49 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework.views import APIView
+from core.models import *
+from django.forms import widgets
+from django.core.exceptions import PermissionDenied
+from xos.exceptions import XOSNotFound
+from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+from django.db.models import Q
+
+class PortForwardingSerializer(serializers.Serializer):
+ id = serializers.IntegerField(read_only=True)
+ ip = serializers.CharField(read_only=True)
+ ports = serializers.CharField(read_only=True, source="network.ports")
+ hostname = serializers.CharField(read_only=True, source="instance.node.name")
+
+ class Meta:
+ model = Port
+ fields = ('id', 'ip', 'ports', 'hostname')
+
+class PortForwardingViewSet(XOSViewSet):
+ base_name = "portforwarding"
+ method_name = "portforwarding"
+ method_kind = "viewset"
+ serializer_class = PortForwardingSerializer
+
+ def get_queryset(self):
+ queryset=Port.objects.exclude(Q(network__isnull=True) |
+ Q(instance__isnull=True) |
+ Q(instance__node__isnull=True) |
+ Q(network__ports__isnull=True) | Q(network__ports__exact='') |
+ Q(ip__isnull=True))
+
+ node_name = self.request.query_params.get('node_name', None)
+ if node_name is not None:
+ queryset = queryset.filter(instance__node__name = node_name)
+
+ if "" in [q.ip for q in list(queryset)]:
+ # Q(ip__exact=='') does not work right, so let's filter the hard way
+ queryset = [q for q in list(queryset) if q.ip!='']
+ queryset = [q.id for q in queryset]
+ queryset = Port.objects.filter(pk__in=queryset)
+
+ return queryset
+
+
diff --git a/xos/api/utility/sshkeys.py b/xos/api/utility/sshkeys.py
new file mode 100644
index 0000000..6ddd1ef
--- /dev/null
+++ b/xos/api/utility/sshkeys.py
@@ -0,0 +1,42 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework.views import APIView
+from core.models import *
+from django.forms import widgets
+from django.core.exceptions import PermissionDenied
+from xos.exceptions import XOSNotFound
+from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+from django.db.models import Q
+
+class SSHKeysSerializer(PlusModelSerializer):
+ id = serializers.CharField(read_only=True, source="instance_id")
+ public_keys = serializers.ListField(read_only=True, source="get_public_keys")
+ node_name = serializers.CharField(read_only=True, source="node.name")
+
+ class Meta:
+ model = Instance
+ fields = ('id', 'public_keys', 'node_name')
+
+class SSHKeysViewSet(XOSViewSet):
+ base_name = "sshkeys"
+ method_name = "sshkeys"
+ method_kind = "viewset"
+ serializer_class = SSHKeysSerializer
+ read_only = True
+
+ lookup_field = "instance_id"
+ lookup_url_kwarg = "pk"
+
+ def get_queryset(self):
+ queryset = queryset=Instance.objects.exclude(Q(instance_id__isnull=True) | Q(instance_id__exact=''))
+
+ node_name = self.request.query_params.get('node_name', None)
+ if node_name is not None:
+ queryset = queryset.filter(node__name = node_name)
+
+ return queryset
+
+
diff --git a/xos/api/xosapi_helpers.py b/xos/api/xosapi_helpers.py
index 4dccb2a..8c737cb 100644
--- a/xos/api/xosapi_helpers.py
+++ b/xos/api/xosapi_helpers.py
@@ -75,6 +75,7 @@
class XOSViewSet(viewsets.ModelViewSet):
api_path=""
+ read_only=False
@classmethod
def get_api_method_path(self):
@@ -85,7 +86,7 @@
@classmethod
def detail_url(self, pattern, viewdict, name):
- return url(self.get_api_method_path() + r'(?P<pk>[a-zA-Z0-9\-]+)/' + pattern,
+ return url(self.get_api_method_path() + r'(?P<pk>[a-zA-Z0-9\-_]+)/' + pattern,
self.as_view(viewdict),
name=self.base_name+"_"+name)
@@ -101,8 +102,12 @@
patterns = []
- patterns.append(url(self.get_api_method_path() + '$', self.as_view({'get': 'list', 'post': 'create'}), name=self.base_name+'_list'))
- patterns.append(url(self.get_api_method_path() + '(?P<pk>[a-zA-Z0-9\-]+)/$', self.as_view({'get': 'retrieve', 'put': 'update', 'post': 'update', 'delete': 'destroy', 'patch': 'partial_update'}), name=self.base_name+'_detail'))
+ if self.read_only:
+ patterns.append(url(self.get_api_method_path() + '$', self.as_view({'get': 'list'}), name=self.base_name+'_list'))
+ patterns.append(url(self.get_api_method_path() + '(?P<pk>[a-zA-Z0-9\-_]+)/$', self.as_view({'get': 'retrieve'}), name=self.base_name+'_detail'))
+ else:
+ patterns.append(url(self.get_api_method_path() + '$', self.as_view({'get': 'list', 'post': 'create'}), name=self.base_name+'_list'))
+ patterns.append(url(self.get_api_method_path() + '(?P<pk>[a-zA-Z0-9\-_]+)/$', self.as_view({'get': 'retrieve', 'put': 'update', 'post': 'update', 'delete': 'destroy', 'patch': 'partial_update'}), name=self.base_name+'_detail'))
return patterns
diff --git a/xos/configurations/cord-pod/cord-vtn-vsg.yaml b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
index e3f73c6..a315e41 100644
--- a/xos/configurations/cord-pod/cord-vtn-vsg.yaml
+++ b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
@@ -8,21 +8,23 @@
topology_template:
node_templates:
# CORD Services
- service_vtr:
+ service#vtr:
type: tosca.nodes.Service
properties:
view_url: /admin/vtr/vtrservice/$id$/
kind: vTR
+ replaces: service_vtr
- service_volt:
+ service#volt:
type: tosca.nodes.Service
requirements:
- vsg_tenant:
- node: service_vsg
+ node: service#vsg
relationship: tosca.relationships.TenantOfService
properties:
view_url: /admin/cord/voltservice/$id$/
kind: vOLT
+ replaces: service_volt
addresses_vsg:
type: tosca.nodes.AddressPool
@@ -38,11 +40,11 @@
gateway_ip: 10.168.1.1
gateway_mac: 02:42:0a:a8:00:01
- service_vsg:
+ service#vsg:
type: tosca.nodes.VSGService
requirements:
- vrouter_tenant:
- node: service_vrouter
+ node: service#vrouter
relationship: tosca.relationships.TenantOfService
properties:
view_url: /admin/cord/vsgservice/$id$/
@@ -50,13 +52,15 @@
public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
# node_label: label_vsg
+ replaces: service_vsg
artifacts:
pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
- service_vrouter:
+ service#vrouter:
type: tosca.nodes.VRouterService
properties:
view_url: /admin/vrouter/vrouterservice/$id$/
+ replaces: service_vrouter
requirements:
- addresses_vsg:
node: addresses_vsg
@@ -65,10 +69,11 @@
node: addresses_exampleservice-public
relationship: tosca.relationships.ProvidesAddresses
- service_fabric:
+ service#fabric:
type: tosca.nodes.FabricService
properties:
view_url: /admin/fabric/fabricservice/$id$/
+ replaces: service_fabric
Private:
type: tosca.nodes.NetworkTemplate
@@ -110,7 +115,7 @@
network: noauto
requirements:
- vsg_service:
- node: service_vsg
+ node: service#vsg
relationship: tosca.relationships.MemberOfService
- site:
node: mysite
@@ -193,7 +198,7 @@
c_tag: 111
requirements:
- provider_service:
- node: service_volt
+ node: service#volt
relationship: tosca.relationships.MemberOfService
- subscriber:
node: My House
diff --git a/xos/configurations/cord-pod/make-vtn-external-yaml.sh b/xos/configurations/cord-pod/make-vtn-external-yaml.sh
index 4c9e0ee..8d0885d 100644
--- a/xos/configurations/cord-pod/make-vtn-external-yaml.sh
+++ b/xos/configurations/cord-pod/make-vtn-external-yaml.sh
@@ -13,7 +13,7 @@
topology_template:
node_templates:
- service_ONOS_CORD:
+ service#ONOS_CORD:
type: tosca.nodes.ONOSService
requirements:
properties:
@@ -21,8 +21,9 @@
view_url: /admin/onos/onosservice/\$id$/
no_container: true
rest_hostname: onos-cord
+ replaces: service_ONOS_CORD
- service_vtn:
+ service#vtn:
type: tosca.nodes.VTNService
properties:
view_url: /admin/vtn/vtnservice/\$id$/
@@ -35,6 +36,7 @@
xosEndpoint: http://xos/
xosUser: padmin@vicci.org
xosPassword: letmein
+ replaces: service_vtn
EOF
@@ -57,7 +59,7 @@
node: $NODE
relationship: tosca.relationships.TagsObject
- service:
- node: service_ONOS_CORD
+ node: service#ONOS_CORD
relationship: tosca.relationships.MemberOfService
# VTN dataPlaneIntf field for node $NODE
@@ -71,7 +73,7 @@
node: $NODE
relationship: tosca.relationships.TagsObject
- service:
- node: service_ONOS_CORD
+ node: service#ONOS_CORD
relationship: tosca.relationships.MemberOfService
# VTN dataPlaneIp field for node $NODE
@@ -85,7 +87,7 @@
node: $NODE
relationship: tosca.relationships.TagsObject
- service:
- node: service_ONOS_CORD
+ node: service#ONOS_CORD
relationship: tosca.relationships.MemberOfService
EOF
@@ -96,10 +98,10 @@
type: tosca.nodes.ONOSVTNApp
requirements:
- onos_tenant:
- node: service_ONOS_CORD
+ node: service#ONOS_CORD
relationship: tosca.relationships.TenantOfService
- vtn_service:
- node: service_vtn
+ node: service#vtn
relationship: tosca.relationships.UsedByService
properties:
dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, org.onosproject.cordvtn, org.onosproject.olt, org.onosproject.igmp, org.onosproject.cordmcast
diff --git a/xos/configurations/cord-pod/pod-exampleservice.yaml b/xos/configurations/cord-pod/pod-exampleservice.yaml
index 4e4835c..ec45bb8 100644
--- a/xos/configurations/cord-pod/pod-exampleservice.yaml
+++ b/xos/configurations/cord-pod/pod-exampleservice.yaml
@@ -18,7 +18,7 @@
no-delete: true
no-update: true
- service_vrouter:
+ service#vrouter:
type: tosca.nodes.Service
properties:
no-create: true
@@ -40,7 +40,7 @@
node: mysite_exampleservice
relationship: tosca.relationships.ConnectsToSlice
- vrouter_tenant:
- node: service_vrouter
+ node: service#vrouter
relationship: tosca.relationships.TenantOfService
mysite:
@@ -59,10 +59,10 @@
node: management
relationship: tosca.relationships.ConnectsToNetwork
- exmapleserver:
- node: service_exampleservice
+ node: service#exampleservice
relationship: tosca.relationships.MemberOfService
- service_exampleservice:
+ service#exampleservice:
type: tosca.nodes.ExampleService
requirements:
- management:
diff --git a/xos/configurations/test-standalone/Makefile b/xos/configurations/test-standalone/Makefile
index efea0b5..64335d3 100644
--- a/xos/configurations/test-standalone/Makefile
+++ b/xos/configurations/test-standalone/Makefile
@@ -28,6 +28,8 @@
sudo docker exec teststandalone_xos_db_1 psql -U postgres -d xos -c "$$TRUNCATE_FN" >/dev/null 2>&1
sudo docker exec teststandalone_xos_db_1 psql -U postgres -d xos -c "SELECT truncate_tables('postgres');" >/dev/null 2>&1
sudo docker exec teststandalone_xos_db_1 psql -U postgres -d xos -c "SELECT setval('core_tenant_id_seq', 1)" >/dev/null 2>&1
+ sudo docker exec teststandalone_xos_db_1 psql -U postgres -d xos -c "SELECT setval('core_deployment_id_seq', 1)" >/dev/null 2>&1
+ sudo docker exec teststandalone_xos_db_1 psql -U postgres -d xos -c "SELECT setval('core_flavor_id_seq', 1)" >/dev/null 2>&1
sudo docker-compose run xos python /opt/xos/manage.py --noobserver --nomodelpolicy loaddata /opt/xos/core/fixtures/core_initial_data.json
sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/fixtures.yaml
sudo docker-compose run xos python /opt/xos/tosca/run.py none /opt/xos/configurations/common/mydeployment.yaml
@@ -39,7 +41,7 @@
sudo docker cp ../../../apiary.apib teststandalone_xos_1:/opt/xos/tests/api/apiary.apib
sudo docker exec -i teststandalone_xos_1 bash -c "cd /opt/xos/tests/api; npm test"
-test-tosca:
+test-tosca: restore-initial-db-status
sudo docker-compose run xos bash -c "cd /opt/xos/tosca/tests; python ./alltests.py"
base-container:
diff --git a/xos/core/static/xosNgLib.css b/xos/core/static/xosNgLib.css
index 22631da..b0efcf0 100644
--- a/xos/core/static/xosNgLib.css
+++ b/xos/core/static/xosNgLib.css
@@ -143,13 +143,11 @@
margin-top: 15px;
display: block; }
-xos-smart-table .row + xos-table {
- margin-top: 15px; }
-
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important; }
.row + .row {
- margin-top: 20px; }
+ /* TODO move in xos.scss*/
+ margin-top: 15px; }
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieG9zTmdMaWIuY3NzIiwic291cmNlcyI6WyJtYWluLnNjc3MiLCJhbmltYXRpb25zLnNjc3MiLCIuLi91aV9jb21wb25lbnRzL2R1bWJDb21wb25lbnRzL3RhYmxlL3RhYmxlLnNjc3MiLCIuLi91aV9jb21wb25lbnRzL2R1bWJDb21wb25lbnRzL2FsZXJ0L2FsZXJ0LnNjc3MiLCIuLi91aV9jb21wb25lbnRzL2R1bWJDb21wb25lbnRzL3ZhbGlkYXRpb24vdmFsaWRhdGlvbi5zY3NzIiwiLi4vLi4vLi4vLi4vc3R5bGUvc2Fzcy9ib290c3RyYXAvYm9vdHN0cmFwL192YXJpYWJsZXMuc2NzcyIsIi4uL3VpX2NvbXBvbmVudHMvc21hcnRDb21wb25lbnRzL3NtYXJ0VGFibGUvc21hcnRUYWJsZS5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgJy4vYW5pbWF0aW9ucy5zY3NzJztcblxuQGltcG9ydCAnLi4vdWlfY29tcG9uZW50cy9kdW1iQ29tcG9uZW50cy90YWJsZS90YWJsZS5zY3NzJztcbkBpbXBvcnQgJy4uL3VpX2NvbXBvbmVudHMvZHVtYkNvbXBvbmVudHMvYWxlcnQvYWxlcnQuc2Nzcyc7XG5AaW1wb3J0ICcuLi91aV9jb21wb25lbnRzL2R1bWJDb21wb25lbnRzL3ZhbGlkYXRpb24vdmFsaWRhdGlvbi5zY3NzJztcblxuQGltcG9ydCAnLi4vdWlfY29tcG9uZW50cy9zbWFydENvbXBvbmVudHMvc21hcnRUYWJsZS9zbWFydFRhYmxlLnNjc3MnO1xuXG5bbmdcXDpjbG9ha10sIFtuZy1jbG9ha10sIFtkYXRhLW5nLWNsb2FrXSwgW3gtbmctY2xvYWtdLCAubmctY2xvYWssIC54LW5nLWNsb2FrIHtcbiAgZGlzcGxheTogbm9uZSAhaW1wb3J0YW50O1xufVxuXG4ucm93ICsgLnJvdyB7XG4gIG1hcmdpbi10b3A6IDIwcHg7XG59IiwiQGtleWZyYW1lcyBzbGlkZUluUmlnaHQge1xuICBmcm9tIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDEwMCUsIDAsIDApO1xuICAgIHZpc2liaWxpdHk6IHZpc2libGU7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiB0cmFuc2xhdGUzZCgwLCAwLCAwKTtcbiAgfVxufVxuXG5Aa2V5ZnJhbWVzIHNsaWRlT3V0UmlnaHQge1xuICBmcm9tIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDAsIDAsIDApO1xuICB9XG5cbiAgdG8ge1xuICAgIHZpc2liaWxpdHk6IGhpZGRlbjtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDEwMCUsIDAsIDApO1xuICB9XG59XG5cbkBrZXlmcmFtZXMgZmFkZUluVXAge1xuICBmcm9tIHtcbiAgICBvcGFjaXR5OiAwO1xuICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlM2QoMCwgMTAwJSwgMCk7XG4gIH1cblxuICB0byB7XG4gICAgb3BhY2l0eTogMTtcbiAgICB0cmFuc2Zvcm06IG5vbmU7XG4gIH1cbn1cblxuQGtleWZyYW1lcyBmYWRlT3V0RG93biB7XG4gIGZyb20ge1xuICAgIG9wYWNpdHk6IDE7XG4gIH1cblxuICB0byB7XG4gICAgb3BhY2l0eTogMDtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDAsIDEwMCUsIDApO1xuICB9XG59IiwiQGltcG9ydCAnLi4vLi4vLi4vc3R5bGVzL2FuaW1hdGlvbnMuc2Nzcyc7XG5cbnhvcy10YWJsZSB7XG5cbiAgZGlzcGxheTogYmxvY2s7XG5cbiAgdHIubmctbW92ZSxcbiAgdHIubmctZW50ZXIsXG4gIHRyLm5nLWxlYXZlIHtcbiAgICB0cmFuc2l0aW9uOmFsbCBsaW5lYXIgMC41cztcbiAgfVxuXG4gIHRyLm5nLWxlYXZlLm5nLWxlYXZlLWFjdGl2ZSxcbiAgdHIubmctbW92ZSxcbiAgdHIubmctZW50ZXIge1xuICAgIG9wYWNpdHk6MDtcbiAgICBhbmltYXRpb246IDAuNXMgc2xpZGVPdXRSaWdodCBlYXNlLWluLW91dDtcbiAgfVxuXG4gIHRyLm5nLWxlYXZlLFxuICB0ci5uZy1tb3ZlLm5nLW1vdmUtYWN0aXZlLFxuICB0ci5uZy1lbnRlci5uZy1lbnRlci1hY3RpdmUge1xuICAgIG9wYWNpdHk6MTtcbiAgICBhbmltYXRpb246IDAuNXMgc2xpZGVJblJpZ2h0IGVhc2UtaW4tb3V0O1xuICB9XG59IiwiQGltcG9ydCAnLi4vLi4vLi4vc3R5bGVzL2FuaW1hdGlvbnMuc2Nzcyc7XG5cbnhvcy1hbGVydCB7XG5cbiAgLyogd2hlbiBoaWRpbmcgKi9cbiAgLm5nLWhpZGUtYWRkICAgICAgICAgeyBhbmltYXRpb246MC41cyBmYWRlT3V0RG93biBlYXNlLWluLW91dDsgfVxuXG4gIC8qIHdoZW4gc2hvd2luZyAqL1xuICAubmctaGlkZS1yZW1vdmUgICAgICB7IGFuaW1hdGlvbjowLjVzIGZhZGVJblVwIGVhc2UtaW4tb3V0OyB9XG59IiwiQGltcG9ydCAnLi4vLi4vLi4vc3R5bGVzL2FuaW1hdGlvbnMuc2Nzcyc7XG5AaW1wb3J0ICcuLi8uLi8uLi8uLi8uLi8uLi9zdHlsZS9zYXNzL2Jvb3RzdHJhcC9ib290c3RyYXAvX3ZhcmlhYmxlcy5zY3NzJztcblxuaW5wdXQgKyB4b3MtdmFsaWRhdGlvbiB7XG4gIG1hcmdpbi10b3A6ICRmb3JtLWdyb3VwLW1hcmdpbi1ib3R0b207XG4gIGRpc3BsYXk6IGJsb2NrO1xufSIsIiRib290c3RyYXAtc2Fzcy1hc3NldC1oZWxwZXI6IGZhbHNlICFkZWZhdWx0O1xuLy9cbi8vIFZhcmlhYmxlc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuXG4vLz09IENvbG9yc1xuLy9cbi8vIyMgR3JheSBhbmQgYnJhbmQgY29sb3JzIGZvciB1c2UgYWNyb3NzIEJvb3RzdHJhcC5cblxuJGdyYXktYmFzZTogICAgICAgICAgICAgICMwMDAgIWRlZmF1bHQ7XG4kZ3JheS1kYXJrZXI6ICAgICAgICAgICAgbGlnaHRlbigkZ3JheS1iYXNlLCAxMy41JSkgIWRlZmF1bHQ7IC8vICMyMjJcbiRncmF5LWRhcms6ICAgICAgICAgICAgICBsaWdodGVuKCRncmF5LWJhc2UsIDIwJSkgIWRlZmF1bHQ7ICAgLy8gIzMzM1xuJGdyYXk6ICAgICAgICAgICAgICAgICAgIGxpZ2h0ZW4oJGdyYXktYmFzZSwgMzMuNSUpICFkZWZhdWx0OyAvLyAjNTU1XG4kZ3JheS1saWdodDogICAgICAgICAgICAgbGlnaHRlbigkZ3JheS1iYXNlLCA0Ni43JSkgIWRlZmF1bHQ7IC8vICM3NzdcbiRncmF5LWxpZ2h0ZXI6ICAgICAgICAgICBsaWdodGVuKCRncmF5LWJhc2UsIDkzLjUlKSAhZGVmYXVsdDsgLy8gI2VlZVxuXG4kYnJhbmQtcHJpbWFyeTogICAgICAgICBkYXJrZW4oIzQyOGJjYSwgNi41JSkgIWRlZmF1bHQ7IC8vICMzMzdhYjdcbiRicmFuZC1zdWNjZXNzOiAgICAgICAgICM1Y2I4NWMgIWRlZmF1bHQ7XG4kYnJhbmQtaW5mbzogICAgICAgICAgICAjNWJjMGRlICFkZWZhdWx0O1xuJGJyYW5kLXdhcm5pbmc6ICAgICAgICAgI2YwYWQ0ZSAhZGVmYXVsdDtcbiRicmFuZC1kYW5nZXI6ICAgICAgICAgICNkOTUzNGYgIWRlZmF1bHQ7XG5cblxuLy89PSBTY2FmZm9sZGluZ1xuLy9cbi8vIyMgU2V0dGluZ3MgZm9yIHNvbWUgb2YgdGhlIG1vc3QgZ2xvYmFsIHN0eWxlcy5cblxuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIGZvciBgPGJvZHk+YC5cbiRib2R5LWJnOiAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIEdsb2JhbCB0ZXh0IGNvbG9yIG9uIGA8Ym9keT5gLlxuJHRleHQtY29sb3I6ICAgICAgICAgICAgJGdyYXktZGFyayAhZGVmYXVsdDtcblxuLy8qKiBHbG9iYWwgdGV4dHVhbCBsaW5rIGNvbG9yLlxuJGxpbmstY29sb3I6ICAgICAgICAgICAgJGJyYW5kLXByaW1hcnkgIWRlZmF1bHQ7XG4vLyoqIExpbmsgaG92ZXIgY29sb3Igc2V0IHZpYSBgZGFya2VuKClgIGZ1bmN0aW9uLlxuJGxpbmstaG92ZXItY29sb3I6ICAgICAgZGFya2VuKCRsaW5rLWNvbG9yLCAxNSUpICFkZWZhdWx0O1xuLy8qKiBMaW5rIGhvdmVyIGRlY29yYXRpb24uXG4kbGluay1ob3Zlci1kZWNvcmF0aW9uOiB1bmRlcmxpbmUgIWRlZmF1bHQ7XG5cblxuLy89PSBUeXBvZ3JhcGh5XG4vL1xuLy8jIyBGb250LCBsaW5lLWhlaWdodCwgYW5kIGNvbG9yIGZvciBib2R5IHRleHQsIGhlYWRpbmdzLCBhbmQgbW9yZS5cblxuJGZvbnQtZmFtaWx5LXNhbnMtc2VyaWY6ICBcIkhlbHZldGljYSBOZXVlXCIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWYgIWRlZmF1bHQ7XG4kZm9udC1mYW1pbHktc2VyaWY6ICAgICAgIEdlb3JnaWEsIFwiVGltZXMgTmV3IFJvbWFuXCIsIFRpbWVzLCBzZXJpZiAhZGVmYXVsdDtcbi8vKiogRGVmYXVsdCBtb25vc3BhY2UgZm9udHMgZm9yIGA8Y29kZT5gLCBgPGtiZD5gLCBhbmQgYDxwcmU+YC5cbiRmb250LWZhbWlseS1tb25vc3BhY2U6ICAgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsIFwiQ291cmllciBOZXdcIiwgbW9ub3NwYWNlICFkZWZhdWx0O1xuJGZvbnQtZmFtaWx5LWJhc2U6ICAgICAgICAkZm9udC1mYW1pbHktc2Fucy1zZXJpZiAhZGVmYXVsdDtcblxuJGZvbnQtc2l6ZS1iYXNlOiAgICAgICAgICAxNHB4ICFkZWZhdWx0O1xuJGZvbnQtc2l6ZS1sYXJnZTogICAgICAgICBjZWlsKCgkZm9udC1zaXplLWJhc2UgKiAxLjI1KSkgIWRlZmF1bHQ7IC8vIH4xOHB4XG4kZm9udC1zaXplLXNtYWxsOiAgICAgICAgIGNlaWwoKCRmb250LXNpemUtYmFzZSAqIDAuODUpKSAhZGVmYXVsdDsgLy8gfjEycHhcblxuJGZvbnQtc2l6ZS1oMTogICAgICAgICAgICBmbG9vcigoJGZvbnQtc2l6ZS1iYXNlICogMi42KSkgIWRlZmF1bHQ7IC8vIH4zNnB4XG4kZm9udC1zaXplLWgyOiAgICAgICAgICAgIGZsb29yKCgkZm9udC1zaXplLWJhc2UgKiAyLjE1KSkgIWRlZmF1bHQ7IC8vIH4zMHB4XG4kZm9udC1zaXplLWgzOiAgICAgICAgICAgIGNlaWwoKCRmb250LXNpemUtYmFzZSAqIDEuNykpICFkZWZhdWx0OyAvLyB+MjRweFxuJGZvbnQtc2l6ZS1oNDogICAgICAgICAgICBjZWlsKCgkZm9udC1zaXplLWJhc2UgKiAxLjI1KSkgIWRlZmF1bHQ7IC8vIH4xOHB4XG4kZm9udC1zaXplLWg1OiAgICAgICAgICAgICRmb250LXNpemUtYmFzZSAhZGVmYXVsdDtcbiRmb250LXNpemUtaDY6ICAgICAgICAgICAgY2VpbCgoJGZvbnQtc2l6ZS1iYXNlICogMC44NSkpICFkZWZhdWx0OyAvLyB+MTJweFxuXG4vLyoqIFVuaXQtbGVzcyBgbGluZS1oZWlnaHRgIGZvciB1c2UgaW4gY29tcG9uZW50cyBsaWtlIGJ1dHRvbnMuXG4kbGluZS1oZWlnaHQtYmFzZTogICAgICAgIDEuNDI4NTcxNDI5ICFkZWZhdWx0OyAvLyAyMC8xNFxuLy8qKiBDb21wdXRlZCBcImxpbmUtaGVpZ2h0XCIgKGBmb250LXNpemVgICogYGxpbmUtaGVpZ2h0YCkgZm9yIHVzZSB3aXRoIGBtYXJnaW5gLCBgcGFkZGluZ2AsIGV0Yy5cbiRsaW5lLWhlaWdodC1jb21wdXRlZDogICAgZmxvb3IoKCRmb250LXNpemUtYmFzZSAqICRsaW5lLWhlaWdodC1iYXNlKSkgIWRlZmF1bHQ7IC8vIH4yMHB4XG5cbi8vKiogQnkgZGVmYXVsdCwgdGhpcyBpbmhlcml0cyBmcm9tIHRoZSBgPGJvZHk+YC5cbiRoZWFkaW5ncy1mb250LWZhbWlseTogICAgaW5oZXJpdCAhZGVmYXVsdDtcbiRoZWFkaW5ncy1mb250LXdlaWdodDogICAgNTAwICFkZWZhdWx0O1xuJGhlYWRpbmdzLWxpbmUtaGVpZ2h0OiAgICAxLjEgIWRlZmF1bHQ7XG4kaGVhZGluZ3MtY29sb3I6ICAgICAgICAgIGluaGVyaXQgIWRlZmF1bHQ7XG5cblxuLy89PSBJY29ub2dyYXBoeVxuLy9cbi8vIyMgU3BlY2lmeSBjdXN0b20gbG9jYXRpb24gYW5kIGZpbGVuYW1lIG9mIHRoZSBpbmNsdWRlZCBHbHlwaGljb25zIGljb24gZm9udC4gVXNlZnVsIGZvciB0aG9zZSBpbmNsdWRpbmcgQm9vdHN0cmFwIHZpYSBCb3dlci5cblxuLy8qKiBMb2FkIGZvbnRzIGZyb20gdGhpcyBkaXJlY3RvcnkuXG5cbi8vIFtjb252ZXJ0ZXJdIElmICRib290c3RyYXAtc2Fzcy1hc3NldC1oZWxwZXIgaWYgdXNlZCwgcHJvdmlkZSBwYXRoIHJlbGF0aXZlIHRvIHRoZSBhc3NldHMgbG9hZCBwYXRoLlxuLy8gW2NvbnZlcnRlcl0gVGhpcyBpcyBiZWNhdXNlIHNvbWUgYXNzZXQgaGVscGVycywgc3VjaCBhcyBTcHJvY2tldHMsIGRvIG5vdCB3b3JrIHdpdGggZmlsZS1yZWxhdGl2ZSBwYXRocy5cbiRpY29uLWZvbnQtcGF0aDogaWYoJGJvb3RzdHJhcC1zYXNzLWFzc2V0LWhlbHBlciwgXCJib290c3RyYXAvXCIsIFwiLi4vZm9udHMvYm9vdHN0cmFwL1wiKSAhZGVmYXVsdDtcblxuLy8qKiBGaWxlIG5hbWUgZm9yIGFsbCBmb250IGZpbGVzLlxuJGljb24tZm9udC1uYW1lOiAgICAgICAgICBcImdseXBoaWNvbnMtaGFsZmxpbmdzLXJlZ3VsYXJcIiAhZGVmYXVsdDtcbi8vKiogRWxlbWVudCBJRCB3aXRoaW4gU1ZHIGljb24gZmlsZS5cbiRpY29uLWZvbnQtc3ZnLWlkOiAgICAgICAgXCJnbHlwaGljb25zX2hhbGZsaW5nc3JlZ3VsYXJcIiAhZGVmYXVsdDtcblxuXG4vLz09IENvbXBvbmVudHNcbi8vXG4vLyMjIERlZmluZSBjb21tb24gcGFkZGluZyBhbmQgYm9yZGVyIHJhZGl1cyBzaXplcyBhbmQgbW9yZS4gVmFsdWVzIGJhc2VkIG9uIDE0cHggdGV4dCBhbmQgMS40MjggbGluZS1oZWlnaHQgKH4yMHB4IHRvIHN0YXJ0KS5cblxuJHBhZGRpbmctYmFzZS12ZXJ0aWNhbDogICAgIDZweCAhZGVmYXVsdDtcbiRwYWRkaW5nLWJhc2UtaG9yaXpvbnRhbDogICAxMnB4ICFkZWZhdWx0O1xuXG4kcGFkZGluZy1sYXJnZS12ZXJ0aWNhbDogICAgMTBweCAhZGVmYXVsdDtcbiRwYWRkaW5nLWxhcmdlLWhvcml6b250YWw6ICAxNnB4ICFkZWZhdWx0O1xuXG4kcGFkZGluZy1zbWFsbC12ZXJ0aWNhbDogICAgNXB4ICFkZWZhdWx0O1xuJHBhZGRpbmctc21hbGwtaG9yaXpvbnRhbDogIDEwcHggIWRlZmF1bHQ7XG5cbiRwYWRkaW5nLXhzLXZlcnRpY2FsOiAgICAgICAxcHggIWRlZmF1bHQ7XG4kcGFkZGluZy14cy1ob3Jpem9udGFsOiAgICAgNXB4ICFkZWZhdWx0O1xuXG4kbGluZS1oZWlnaHQtbGFyZ2U6ICAgICAgICAgMS4zMzMzMzMzICFkZWZhdWx0OyAvLyBleHRyYSBkZWNpbWFscyBmb3IgV2luIDguMSBDaHJvbWVcbiRsaW5lLWhlaWdodC1zbWFsbDogICAgICAgICAxLjUgIWRlZmF1bHQ7XG5cbiRib3JkZXItcmFkaXVzLWJhc2U6ICAgICAgICA0cHggIWRlZmF1bHQ7XG4kYm9yZGVyLXJhZGl1cy1sYXJnZTogICAgICAgNnB4ICFkZWZhdWx0O1xuJGJvcmRlci1yYWRpdXMtc21hbGw6ICAgICAgIDNweCAhZGVmYXVsdDtcblxuLy8qKiBHbG9iYWwgY29sb3IgZm9yIGFjdGl2ZSBpdGVtcyAoZS5nLiwgbmF2cyBvciBkcm9wZG93bnMpLlxuJGNvbXBvbmVudC1hY3RpdmUtY29sb3I6ICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIEdsb2JhbCBiYWNrZ3JvdW5kIGNvbG9yIGZvciBhY3RpdmUgaXRlbXMgKGUuZy4sIG5hdnMgb3IgZHJvcGRvd25zKS5cbiRjb21wb25lbnQtYWN0aXZlLWJnOiAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcblxuLy8qKiBXaWR0aCBvZiB0aGUgYGJvcmRlcmAgZm9yIGdlbmVyYXRpbmcgY2FyZXRzIHRoYXQgaW5kaWNhdG9yIGRyb3Bkb3ducy5cbiRjYXJldC13aWR0aC1iYXNlOiAgICAgICAgICA0cHggIWRlZmF1bHQ7XG4vLyoqIENhcmV0cyBpbmNyZWFzZSBzbGlnaHRseSBpbiBzaXplIGZvciBsYXJnZXIgY29tcG9uZW50cy5cbiRjYXJldC13aWR0aC1sYXJnZTogICAgICAgICA1cHggIWRlZmF1bHQ7XG5cblxuLy89PSBUYWJsZXNcbi8vXG4vLyMjIEN1c3RvbWl6ZXMgdGhlIGAudGFibGVgIGNvbXBvbmVudCB3aXRoIGJhc2ljIHZhbHVlcywgZWFjaCB1c2VkIGFjcm9zcyBhbGwgdGFibGUgdmFyaWF0aW9ucy5cblxuLy8qKiBQYWRkaW5nIGZvciBgPHRoPmBzIGFuZCBgPHRkPmBzLlxuJHRhYmxlLWNlbGwtcGFkZGluZzogICAgICAgICAgICA4cHggIWRlZmF1bHQ7XG4vLyoqIFBhZGRpbmcgZm9yIGNlbGxzIGluIGAudGFibGUtY29uZGVuc2VkYC5cbiR0YWJsZS1jb25kZW5zZWQtY2VsbC1wYWRkaW5nOiAgNXB4ICFkZWZhdWx0O1xuXG4vLyoqIERlZmF1bHQgYmFja2dyb3VuZCBjb2xvciB1c2VkIGZvciBhbGwgdGFibGVzLlxuJHRhYmxlLWJnOiAgICAgICAgICAgICAgICAgICAgICB0cmFuc3BhcmVudCAhZGVmYXVsdDtcbi8vKiogQmFja2dyb3VuZCBjb2xvciB1c2VkIGZvciBgLnRhYmxlLXN0cmlwZWRgLlxuJHRhYmxlLWJnLWFjY2VudDogICAgICAgICAgICAgICAjZjlmOWY5ICFkZWZhdWx0O1xuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIHVzZWQgZm9yIGAudGFibGUtaG92ZXJgLlxuJHRhYmxlLWJnLWhvdmVyOiAgICAgICAgICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuJHRhYmxlLWJnLWFjdGl2ZTogICAgICAgICAgICAgICAkdGFibGUtYmctaG92ZXIgIWRlZmF1bHQ7XG5cbi8vKiogQm9yZGVyIGNvbG9yIGZvciB0YWJsZSBhbmQgY2VsbCBib3JkZXJzLlxuJHRhYmxlLWJvcmRlci1jb2xvcjogICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuXG5cbi8vPT0gQnV0dG9uc1xuLy9cbi8vIyMgRm9yIGVhY2ggb2YgQm9vdHN0cmFwJ3MgYnV0dG9ucywgZGVmaW5lIHRleHQsIGJhY2tncm91bmQgYW5kIGJvcmRlciBjb2xvci5cblxuJGJ0bi1mb250LXdlaWdodDogICAgICAgICAgICAgICAgbm9ybWFsICFkZWZhdWx0O1xuXG4kYnRuLWRlZmF1bHQtY29sb3I6ICAgICAgICAgICAgICAjMzMzICFkZWZhdWx0O1xuJGJ0bi1kZWZhdWx0LWJnOiAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRidG4tZGVmYXVsdC1ib3JkZXI6ICAgICAgICAgICAgICNjY2MgIWRlZmF1bHQ7XG5cbiRidG4tcHJpbWFyeS1jb2xvcjogICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kYnRuLXByaW1hcnktYmc6ICAgICAgICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbiRidG4tcHJpbWFyeS1ib3JkZXI6ICAgICAgICAgICAgIGRhcmtlbigkYnRuLXByaW1hcnktYmcsIDUlKSAhZGVmYXVsdDtcblxuJGJ0bi1zdWNjZXNzLWNvbG9yOiAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRidG4tc3VjY2Vzcy1iZzogICAgICAgICAgICAgICAgICRicmFuZC1zdWNjZXNzICFkZWZhdWx0O1xuJGJ0bi1zdWNjZXNzLWJvcmRlcjogICAgICAgICAgICAgZGFya2VuKCRidG4tc3VjY2Vzcy1iZywgNSUpICFkZWZhdWx0O1xuXG4kYnRuLWluZm8tY29sb3I6ICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJGJ0bi1pbmZvLWJnOiAgICAgICAgICAgICAgICAgICAgJGJyYW5kLWluZm8gIWRlZmF1bHQ7XG4kYnRuLWluZm8tYm9yZGVyOiAgICAgICAgICAgICAgICBkYXJrZW4oJGJ0bi1pbmZvLWJnLCA1JSkgIWRlZmF1bHQ7XG5cbiRidG4td2FybmluZy1jb2xvcjogICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kYnRuLXdhcm5pbmctYmc6ICAgICAgICAgICAgICAgICAkYnJhbmQtd2FybmluZyAhZGVmYXVsdDtcbiRidG4td2FybmluZy1ib3JkZXI6ICAgICAgICAgICAgIGRhcmtlbigkYnRuLXdhcm5pbmctYmcsIDUlKSAhZGVmYXVsdDtcblxuJGJ0bi1kYW5nZXItY29sb3I6ICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRidG4tZGFuZ2VyLWJnOiAgICAgICAgICAgICAgICAgICRicmFuZC1kYW5nZXIgIWRlZmF1bHQ7XG4kYnRuLWRhbmdlci1ib3JkZXI6ICAgICAgICAgICAgICBkYXJrZW4oJGJ0bi1kYW5nZXItYmcsIDUlKSAhZGVmYXVsdDtcblxuJGJ0bi1saW5rLWRpc2FibGVkLWNvbG9yOiAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG5cbi8vIEFsbG93cyBmb3IgY3VzdG9taXppbmcgYnV0dG9uIHJhZGl1cyBpbmRlcGVuZGVudGx5IGZyb20gZ2xvYmFsIGJvcmRlciByYWRpdXNcbiRidG4tYm9yZGVyLXJhZGl1cy1iYXNlOiAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG4kYnRuLWJvcmRlci1yYWRpdXMtbGFyZ2U6ICAgICAgICAkYm9yZGVyLXJhZGl1cy1sYXJnZSAhZGVmYXVsdDtcbiRidG4tYm9yZGVyLXJhZGl1cy1zbWFsbDogICAgICAgICRib3JkZXItcmFkaXVzLXNtYWxsICFkZWZhdWx0O1xuXG5cbi8vPT0gRm9ybXNcbi8vXG4vLyMjXG5cbi8vKiogYDxpbnB1dD5gIGJhY2tncm91bmQgY29sb3JcbiRpbnB1dC1iZzogICAgICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIGA8aW5wdXQgZGlzYWJsZWQ+YCBiYWNrZ3JvdW5kIGNvbG9yXG4kaW5wdXQtYmctZGlzYWJsZWQ6ICAgICAgICAgICAgICAkZ3JheS1saWdodGVyICFkZWZhdWx0O1xuXG4vLyoqIFRleHQgY29sb3IgZm9yIGA8aW5wdXQ+YHNcbiRpbnB1dC1jb2xvcjogICAgICAgICAgICAgICAgICAgICRncmF5ICFkZWZhdWx0O1xuLy8qKiBgPGlucHV0PmAgYm9yZGVyIGNvbG9yXG4kaW5wdXQtYm9yZGVyOiAgICAgICAgICAgICAgICAgICAjY2NjICFkZWZhdWx0O1xuXG4vLyBUT0RPOiBSZW5hbWUgYCRpbnB1dC1ib3JkZXItcmFkaXVzYCB0byBgJGlucHV0LWJvcmRlci1yYWRpdXMtYmFzZWAgaW4gdjRcbi8vKiogRGVmYXVsdCBgLmZvcm0tY29udHJvbGAgYm9yZGVyIHJhZGl1c1xuLy8gVGhpcyBoYXMgbm8gZWZmZWN0IG9uIGA8c2VsZWN0PmBzIGluIHNvbWUgYnJvd3NlcnMsIGR1ZSB0byB0aGUgbGltaXRlZCBzdHlsYWJpbGl0eSBvZiBgPHNlbGVjdD5gcyBpbiBDU1MuXG4kaW5wdXQtYm9yZGVyLXJhZGl1czogICAgICAgICAgICAkYm9yZGVyLXJhZGl1cy1iYXNlICFkZWZhdWx0O1xuLy8qKiBMYXJnZSBgLmZvcm0tY29udHJvbGAgYm9yZGVyIHJhZGl1c1xuJGlucHV0LWJvcmRlci1yYWRpdXMtbGFyZ2U6ICAgICAgJGJvcmRlci1yYWRpdXMtbGFyZ2UgIWRlZmF1bHQ7XG4vLyoqIFNtYWxsIGAuZm9ybS1jb250cm9sYCBib3JkZXIgcmFkaXVzXG4kaW5wdXQtYm9yZGVyLXJhZGl1cy1zbWFsbDogICAgICAkYm9yZGVyLXJhZGl1cy1zbWFsbCAhZGVmYXVsdDtcblxuLy8qKiBCb3JkZXIgY29sb3IgZm9yIGlucHV0cyBvbiBmb2N1c1xuJGlucHV0LWJvcmRlci1mb2N1czogICAgICAgICAgICAgIzY2YWZlOSAhZGVmYXVsdDtcblxuLy8qKiBQbGFjZWhvbGRlciB0ZXh0IGNvbG9yXG4kaW5wdXQtY29sb3ItcGxhY2Vob2xkZXI6ICAgICAgICAjOTk5ICFkZWZhdWx0O1xuXG4vLyoqIERlZmF1bHQgYC5mb3JtLWNvbnRyb2xgIGhlaWdodFxuJGlucHV0LWhlaWdodC1iYXNlOiAgICAgICAgICAgICAgKCRsaW5lLWhlaWdodC1jb21wdXRlZCArICgkcGFkZGluZy1iYXNlLXZlcnRpY2FsICogMikgKyAyKSAhZGVmYXVsdDtcbi8vKiogTGFyZ2UgYC5mb3JtLWNvbnRyb2xgIGhlaWdodFxuJGlucHV0LWhlaWdodC1sYXJnZTogICAgICAgICAgICAgKGNlaWwoJGZvbnQtc2l6ZS1sYXJnZSAqICRsaW5lLWhlaWdodC1sYXJnZSkgKyAoJHBhZGRpbmctbGFyZ2UtdmVydGljYWwgKiAyKSArIDIpICFkZWZhdWx0O1xuLy8qKiBTbWFsbCBgLmZvcm0tY29udHJvbGAgaGVpZ2h0XG4kaW5wdXQtaGVpZ2h0LXNtYWxsOiAgICAgICAgICAgICAoZmxvb3IoJGZvbnQtc2l6ZS1zbWFsbCAqICRsaW5lLWhlaWdodC1zbWFsbCkgKyAoJHBhZGRpbmctc21hbGwtdmVydGljYWwgKiAyKSArIDIpICFkZWZhdWx0O1xuXG4vLyoqIGAuZm9ybS1ncm91cGAgbWFyZ2luXG4kZm9ybS1ncm91cC1tYXJnaW4tYm90dG9tOiAgICAgICAxNXB4ICFkZWZhdWx0O1xuXG4kbGVnZW5kLWNvbG9yOiAgICAgICAgICAgICAgICAgICAkZ3JheS1kYXJrICFkZWZhdWx0O1xuJGxlZ2VuZC1ib3JkZXItY29sb3I6ICAgICAgICAgICAgI2U1ZTVlNSAhZGVmYXVsdDtcblxuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIGZvciB0ZXh0dWFsIGlucHV0IGFkZG9uc1xuJGlucHV0LWdyb3VwLWFkZG9uLWJnOiAgICAgICAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbi8vKiogQm9yZGVyIGNvbG9yIGZvciB0ZXh0dWFsIGlucHV0IGFkZG9uc1xuJGlucHV0LWdyb3VwLWFkZG9uLWJvcmRlci1jb2xvcjogJGlucHV0LWJvcmRlciAhZGVmYXVsdDtcblxuLy8qKiBEaXNhYmxlZCBjdXJzb3IgZm9yIGZvcm0gY29udHJvbHMgYW5kIGJ1dHRvbnMuXG4kY3Vyc29yLWRpc2FibGVkOiAgICAgICAgICAgICAgICBub3QtYWxsb3dlZCAhZGVmYXVsdDtcblxuXG4vLz09IERyb3Bkb3duc1xuLy9cbi8vIyMgRHJvcGRvd24gbWVudSBjb250YWluZXIgYW5kIGNvbnRlbnRzLlxuXG4vLyoqIEJhY2tncm91bmQgZm9yIHRoZSBkcm9wZG93biBtZW51LlxuJGRyb3Bkb3duLWJnOiAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogRHJvcGRvd24gbWVudSBgYm9yZGVyLWNvbG9yYC5cbiRkcm9wZG93bi1ib3JkZXI6ICAgICAgICAgICAgICAgIHJnYmEoMCwwLDAsLjE1KSAhZGVmYXVsdDtcbi8vKiogRHJvcGRvd24gbWVudSBgYm9yZGVyLWNvbG9yYCAqKmZvciBJRTgqKi5cbiRkcm9wZG93bi1mYWxsYmFjay1ib3JkZXI6ICAgICAgICNjY2MgIWRlZmF1bHQ7XG4vLyoqIERpdmlkZXIgY29sb3IgZm9yIGJldHdlZW4gZHJvcGRvd24gaXRlbXMuXG4kZHJvcGRvd24tZGl2aWRlci1iZzogICAgICAgICAgICAjZTVlNWU1ICFkZWZhdWx0O1xuXG4vLyoqIERyb3Bkb3duIGxpbmsgdGV4dCBjb2xvci5cbiRkcm9wZG93bi1saW5rLWNvbG9yOiAgICAgICAgICAgICRncmF5LWRhcmsgIWRlZmF1bHQ7XG4vLyoqIEhvdmVyIGNvbG9yIGZvciBkcm9wZG93biBsaW5rcy5cbiRkcm9wZG93bi1saW5rLWhvdmVyLWNvbG9yOiAgICAgIGRhcmtlbigkZ3JheS1kYXJrLCA1JSkgIWRlZmF1bHQ7XG4vLyoqIEhvdmVyIGJhY2tncm91bmQgZm9yIGRyb3Bkb3duIGxpbmtzLlxuJGRyb3Bkb3duLWxpbmstaG92ZXItYmc6ICAgICAgICAgI2Y1ZjVmNSAhZGVmYXVsdDtcblxuLy8qKiBBY3RpdmUgZHJvcGRvd24gbWVudSBpdGVtIHRleHQgY29sb3IuXG4kZHJvcGRvd24tbGluay1hY3RpdmUtY29sb3I6ICAgICAkY29tcG9uZW50LWFjdGl2ZS1jb2xvciAhZGVmYXVsdDtcbi8vKiogQWN0aXZlIGRyb3Bkb3duIG1lbnUgaXRlbSBiYWNrZ3JvdW5kIGNvbG9yLlxuJGRyb3Bkb3duLWxpbmstYWN0aXZlLWJnOiAgICAgICAgJGNvbXBvbmVudC1hY3RpdmUtYmcgIWRlZmF1bHQ7XG5cbi8vKiogRGlzYWJsZWQgZHJvcGRvd24gbWVudSBpdGVtIGJhY2tncm91bmQgY29sb3IuXG4kZHJvcGRvd24tbGluay1kaXNhYmxlZC1jb2xvcjogICAkZ3JheS1saWdodCAhZGVmYXVsdDtcblxuLy8qKiBUZXh0IGNvbG9yIGZvciBoZWFkZXJzIHdpdGhpbiBkcm9wZG93biBtZW51cy5cbiRkcm9wZG93bi1oZWFkZXItY29sb3I6ICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuXG4vLyoqIERlcHJlY2F0ZWQgYCRkcm9wZG93bi1jYXJldC1jb2xvcmAgYXMgb2YgdjMuMS4wXG4kZHJvcGRvd24tY2FyZXQtY29sb3I6ICAgICAgICAgICAjMDAwICFkZWZhdWx0O1xuXG5cbi8vLS0gWi1pbmRleCBtYXN0ZXIgbGlzdFxuLy9cbi8vIFdhcm5pbmc6IEF2b2lkIGN1c3RvbWl6aW5nIHRoZXNlIHZhbHVlcy4gVGhleSdyZSB1c2VkIGZvciBhIGJpcmQncyBleWUgdmlld1xuLy8gb2YgY29tcG9uZW50cyBkZXBlbmRlbnQgb24gdGhlIHotYXhpcyBhbmQgYXJlIGRlc2lnbmVkIHRvIGFsbCB3b3JrIHRvZ2V0aGVyLlxuLy9cbi8vIE5vdGU6IFRoZXNlIHZhcmlhYmxlcyBhcmUgbm90IGdlbmVyYXRlZCBpbnRvIHRoZSBDdXN0b21pemVyLlxuXG4kemluZGV4LW5hdmJhcjogICAgICAgICAgICAxMDAwICFkZWZhdWx0O1xuJHppbmRleC1kcm9wZG93bjogICAgICAgICAgMTAwMCAhZGVmYXVsdDtcbiR6aW5kZXgtcG9wb3ZlcjogICAgICAgICAgIDEwNjAgIWRlZmF1bHQ7XG4kemluZGV4LXRvb2x0aXA6ICAgICAgICAgICAxMDcwICFkZWZhdWx0O1xuJHppbmRleC1uYXZiYXItZml4ZWQ6ICAgICAgMTAzMCAhZGVmYXVsdDtcbiR6aW5kZXgtbW9kYWwtYmFja2dyb3VuZDogIDEwNDAgIWRlZmF1bHQ7XG4kemluZGV4LW1vZGFsOiAgICAgICAgICAgICAxMDUwICFkZWZhdWx0O1xuXG5cbi8vPT0gTWVkaWEgcXVlcmllcyBicmVha3BvaW50c1xuLy9cbi8vIyMgRGVmaW5lIHRoZSBicmVha3BvaW50cyBhdCB3aGljaCB5b3VyIGxheW91dCB3aWxsIGNoYW5nZSwgYWRhcHRpbmcgdG8gZGlmZmVyZW50IHNjcmVlbiBzaXplcy5cblxuLy8gRXh0cmEgc21hbGwgc2NyZWVuIC8gcGhvbmVcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi14c2AgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLXhzOiAgICAgICAgICAgICAgICAgIDQ4MHB4ICFkZWZhdWx0O1xuLy8qKiBEZXByZWNhdGVkIGAkc2NyZWVuLXhzLW1pbmAgYXMgb2YgdjMuMi4wXG4kc2NyZWVuLXhzLW1pbjogICAgICAgICAgICAgICRzY3JlZW4teHMgIWRlZmF1bHQ7XG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4tcGhvbmVgIGFzIG9mIHYzLjAuMVxuJHNjcmVlbi1waG9uZTogICAgICAgICAgICAgICAkc2NyZWVuLXhzLW1pbiAhZGVmYXVsdDtcblxuLy8gU21hbGwgc2NyZWVuIC8gdGFibGV0XG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4tc21gIGFzIG9mIHYzLjAuMVxuJHNjcmVlbi1zbTogICAgICAgICAgICAgICAgICA3NjhweCAhZGVmYXVsdDtcbiRzY3JlZW4tc20tbWluOiAgICAgICAgICAgICAgJHNjcmVlbi1zbSAhZGVmYXVsdDtcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi10YWJsZXRgIGFzIG9mIHYzLjAuMVxuJHNjcmVlbi10YWJsZXQ6ICAgICAgICAgICAgICAkc2NyZWVuLXNtLW1pbiAhZGVmYXVsdDtcblxuLy8gTWVkaXVtIHNjcmVlbiAvIGRlc2t0b3Bcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi1tZGAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLW1kOiAgICAgICAgICAgICAgICAgIDk5MnB4ICFkZWZhdWx0O1xuJHNjcmVlbi1tZC1taW46ICAgICAgICAgICAgICAkc2NyZWVuLW1kICFkZWZhdWx0O1xuLy8qKiBEZXByZWNhdGVkIGAkc2NyZWVuLWRlc2t0b3BgIGFzIG9mIHYzLjAuMVxuJHNjcmVlbi1kZXNrdG9wOiAgICAgICAgICAgICAkc2NyZWVuLW1kLW1pbiAhZGVmYXVsdDtcblxuLy8gTGFyZ2Ugc2NyZWVuIC8gd2lkZSBkZXNrdG9wXG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4tbGdgIGFzIG9mIHYzLjAuMVxuJHNjcmVlbi1sZzogICAgICAgICAgICAgICAgICAxMjAwcHggIWRlZmF1bHQ7XG4kc2NyZWVuLWxnLW1pbjogICAgICAgICAgICAgICRzY3JlZW4tbGcgIWRlZmF1bHQ7XG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4tbGctZGVza3RvcGAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLWxnLWRlc2t0b3A6ICAgICAgICAgICRzY3JlZW4tbGctbWluICFkZWZhdWx0O1xuXG4vLyBTbyBtZWRpYSBxdWVyaWVzIGRvbid0IG92ZXJsYXAgd2hlbiByZXF1aXJlZCwgcHJvdmlkZSBhIG1heGltdW1cbiRzY3JlZW4teHMtbWF4OiAgICAgICAgICAgICAgKCRzY3JlZW4tc20tbWluIC0gMSkgIWRlZmF1bHQ7XG4kc2NyZWVuLXNtLW1heDogICAgICAgICAgICAgICgkc2NyZWVuLW1kLW1pbiAtIDEpICFkZWZhdWx0O1xuJHNjcmVlbi1tZC1tYXg6ICAgICAgICAgICAgICAoJHNjcmVlbi1sZy1taW4gLSAxKSAhZGVmYXVsdDtcblxuXG4vLz09IEdyaWQgc3lzdGVtXG4vL1xuLy8jIyBEZWZpbmUgeW91ciBjdXN0b20gcmVzcG9uc2l2ZSBncmlkLlxuXG4vLyoqIE51bWJlciBvZiBjb2x1bW5zIGluIHRoZSBncmlkLlxuJGdyaWQtY29sdW1uczogICAgICAgICAgICAgIDEyICFkZWZhdWx0O1xuLy8qKiBQYWRkaW5nIGJldHdlZW4gY29sdW1ucy4gR2V0cyBkaXZpZGVkIGluIGhhbGYgZm9yIHRoZSBsZWZ0IGFuZCByaWdodC5cbiRncmlkLWd1dHRlci13aWR0aDogICAgICAgICAzMHB4ICFkZWZhdWx0O1xuLy8gTmF2YmFyIGNvbGxhcHNlXG4vLyoqIFBvaW50IGF0IHdoaWNoIHRoZSBuYXZiYXIgYmVjb21lcyB1bmNvbGxhcHNlZC5cbiRncmlkLWZsb2F0LWJyZWFrcG9pbnQ6ICAgICAkc2NyZWVuLXNtLW1pbiAhZGVmYXVsdDtcbi8vKiogUG9pbnQgYXQgd2hpY2ggdGhlIG5hdmJhciBiZWdpbnMgY29sbGFwc2luZy5cbiRncmlkLWZsb2F0LWJyZWFrcG9pbnQtbWF4OiAoJGdyaWQtZmxvYXQtYnJlYWtwb2ludCAtIDEpICFkZWZhdWx0O1xuXG5cbi8vPT0gQ29udGFpbmVyIHNpemVzXG4vL1xuLy8jIyBEZWZpbmUgdGhlIG1heGltdW0gd2lkdGggb2YgYC5jb250YWluZXJgIGZvciBkaWZmZXJlbnQgc2NyZWVuIHNpemVzLlxuXG4vLyBTbWFsbCBzY3JlZW4gLyB0YWJsZXRcbiRjb250YWluZXItdGFibGV0OiAgICAgICAgICAgICAoNzIwcHggKyAkZ3JpZC1ndXR0ZXItd2lkdGgpICFkZWZhdWx0O1xuLy8qKiBGb3IgYCRzY3JlZW4tc20tbWluYCBhbmQgdXAuXG4kY29udGFpbmVyLXNtOiAgICAgICAgICAgICAgICAgJGNvbnRhaW5lci10YWJsZXQgIWRlZmF1bHQ7XG5cbi8vIE1lZGl1bSBzY3JlZW4gLyBkZXNrdG9wXG4kY29udGFpbmVyLWRlc2t0b3A6ICAgICAgICAgICAgKDk0MHB4ICsgJGdyaWQtZ3V0dGVyLXdpZHRoKSAhZGVmYXVsdDtcbi8vKiogRm9yIGAkc2NyZWVuLW1kLW1pbmAgYW5kIHVwLlxuJGNvbnRhaW5lci1tZDogICAgICAgICAgICAgICAgICRjb250YWluZXItZGVza3RvcCAhZGVmYXVsdDtcblxuLy8gTGFyZ2Ugc2NyZWVuIC8gd2lkZSBkZXNrdG9wXG4kY29udGFpbmVyLWxhcmdlLWRlc2t0b3A6ICAgICAgKDExNDBweCArICRncmlkLWd1dHRlci13aWR0aCkgIWRlZmF1bHQ7XG4vLyoqIEZvciBgJHNjcmVlbi1sZy1taW5gIGFuZCB1cC5cbiRjb250YWluZXItbGc6ICAgICAgICAgICAgICAgICAkY29udGFpbmVyLWxhcmdlLWRlc2t0b3AgIWRlZmF1bHQ7XG5cblxuLy89PSBOYXZiYXJcbi8vXG4vLyMjXG5cbi8vIEJhc2ljcyBvZiBhIG5hdmJhclxuJG5hdmJhci1oZWlnaHQ6ICAgICAgICAgICAgICAgICAgICA1MHB4ICFkZWZhdWx0O1xuJG5hdmJhci1tYXJnaW4tYm90dG9tOiAgICAgICAgICAgICAkbGluZS1oZWlnaHQtY29tcHV0ZWQgIWRlZmF1bHQ7XG4kbmF2YmFyLWJvcmRlci1yYWRpdXM6ICAgICAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG4kbmF2YmFyLXBhZGRpbmctaG9yaXpvbnRhbDogICAgICAgIGZsb29yKCgkZ3JpZC1ndXR0ZXItd2lkdGggLyAyKSkgIWRlZmF1bHQ7XG4kbmF2YmFyLXBhZGRpbmctdmVydGljYWw6ICAgICAgICAgICgoJG5hdmJhci1oZWlnaHQgLSAkbGluZS1oZWlnaHQtY29tcHV0ZWQpIC8gMikgIWRlZmF1bHQ7XG4kbmF2YmFyLWNvbGxhcHNlLW1heC1oZWlnaHQ6ICAgICAgIDM0MHB4ICFkZWZhdWx0O1xuXG4kbmF2YmFyLWRlZmF1bHQtY29sb3I6ICAgICAgICAgICAgICM3NzcgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtYmc6ICAgICAgICAgICAgICAgICNmOGY4ZjggIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtYm9yZGVyOiAgICAgICAgICAgIGRhcmtlbigkbmF2YmFyLWRlZmF1bHQtYmcsIDYuNSUpICFkZWZhdWx0O1xuXG4vLyBOYXZiYXIgbGlua3NcbiRuYXZiYXItZGVmYXVsdC1saW5rLWNvbG9yOiAgICAgICAgICAgICAgICAjNzc3ICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWxpbmstaG92ZXItY29sb3I6ICAgICAgICAgICMzMzMgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtbGluay1ob3Zlci1iZzogICAgICAgICAgICAgdHJhbnNwYXJlbnQgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtbGluay1hY3RpdmUtY29sb3I6ICAgICAgICAgIzU1NSAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1saW5rLWFjdGl2ZS1iZzogICAgICAgICAgICBkYXJrZW4oJG5hdmJhci1kZWZhdWx0LWJnLCA2LjUlKSAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1saW5rLWRpc2FibGVkLWNvbG9yOiAgICAgICAjY2NjICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWxpbmstZGlzYWJsZWQtYmc6ICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuXG4vLyBOYXZiYXIgYnJhbmQgbGFiZWxcbiRuYXZiYXItZGVmYXVsdC1icmFuZC1jb2xvcjogICAgICAgICAgICAgICAkbmF2YmFyLWRlZmF1bHQtbGluay1jb2xvciAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1icmFuZC1ob3Zlci1jb2xvcjogICAgICAgICBkYXJrZW4oJG5hdmJhci1kZWZhdWx0LWJyYW5kLWNvbG9yLCAxMCUpICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWJyYW5kLWhvdmVyLWJnOiAgICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuXG4vLyBOYXZiYXIgdG9nZ2xlXG4kbmF2YmFyLWRlZmF1bHQtdG9nZ2xlLWhvdmVyLWJnOiAgICAgICAgICAgI2RkZCAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC10b2dnbGUtaWNvbi1iYXItYmc6ICAgICAgICAjODg4ICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LXRvZ2dsZS1ib3JkZXItY29sb3I6ICAgICAgICNkZGQgIWRlZmF1bHQ7XG5cblxuLy89PT0gSW52ZXJ0ZWQgbmF2YmFyXG4vLyBSZXNldCBpbnZlcnRlZCBuYXZiYXIgYmFzaWNzXG4kbmF2YmFyLWludmVyc2UtY29sb3I6ICAgICAgICAgICAgICAgICAgICAgIGxpZ2h0ZW4oJGdyYXktbGlnaHQsIDE1JSkgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtYmc6ICAgICAgICAgICAgICAgICAgICAgICAgICMyMjIgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtYm9yZGVyOiAgICAgICAgICAgICAgICAgICAgIGRhcmtlbigkbmF2YmFyLWludmVyc2UtYmcsIDEwJSkgIWRlZmF1bHQ7XG5cbi8vIEludmVydGVkIG5hdmJhciBsaW5rc1xuJG5hdmJhci1pbnZlcnNlLWxpbmstY29sb3I6ICAgICAgICAgICAgICAgICBsaWdodGVuKCRncmF5LWxpZ2h0LCAxNSUpICFkZWZhdWx0O1xuJG5hdmJhci1pbnZlcnNlLWxpbmstaG92ZXItY29sb3I6ICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJG5hdmJhci1pbnZlcnNlLWxpbmstaG92ZXItYmc6ICAgICAgICAgICAgICB0cmFuc3BhcmVudCAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1saW5rLWFjdGl2ZS1jb2xvcjogICAgICAgICAgJG5hdmJhci1pbnZlcnNlLWxpbmstaG92ZXItY29sb3IgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtbGluay1hY3RpdmUtYmc6ICAgICAgICAgICAgIGRhcmtlbigkbmF2YmFyLWludmVyc2UtYmcsIDEwJSkgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtbGluay1kaXNhYmxlZC1jb2xvcjogICAgICAgICM0NDQgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtbGluay1kaXNhYmxlZC1iZzogICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuXG4vLyBJbnZlcnRlZCBuYXZiYXIgYnJhbmQgbGFiZWxcbiRuYXZiYXItaW52ZXJzZS1icmFuZC1jb2xvcjogICAgICAgICAgICAgICAgJG5hdmJhci1pbnZlcnNlLWxpbmstY29sb3IgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtYnJhbmQtaG92ZXItY29sb3I6ICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtYnJhbmQtaG92ZXItYmc6ICAgICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuXG4vLyBJbnZlcnRlZCBuYXZiYXIgdG9nZ2xlXG4kbmF2YmFyLWludmVyc2UtdG9nZ2xlLWhvdmVyLWJnOiAgICAgICAgICAgICMzMzMgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtdG9nZ2xlLWljb24tYmFyLWJnOiAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtdG9nZ2xlLWJvcmRlci1jb2xvcjogICAgICAgICMzMzMgIWRlZmF1bHQ7XG5cblxuLy89PSBOYXZzXG4vL1xuLy8jI1xuXG4vLz09PSBTaGFyZWQgbmF2IHN0eWxlc1xuJG5hdi1saW5rLXBhZGRpbmc6ICAgICAgICAgICAgICAgICAgICAgICAgICAxMHB4IDE1cHggIWRlZmF1bHQ7XG4kbmF2LWxpbmstaG92ZXItYmc6ICAgICAgICAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG5cbiRuYXYtZGlzYWJsZWQtbGluay1jb2xvcjogICAgICAgICAgICAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG4kbmF2LWRpc2FibGVkLWxpbmstaG92ZXItY29sb3I6ICAgICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuXG4vLz09IFRhYnNcbiRuYXYtdGFicy1ib3JkZXItY29sb3I6ICAgICAgICAgICAgICAgICAgICAgI2RkZCAhZGVmYXVsdDtcblxuJG5hdi10YWJzLWxpbmstaG92ZXItYm9yZGVyLWNvbG9yOiAgICAgICAgICAkZ3JheS1saWdodGVyICFkZWZhdWx0O1xuXG4kbmF2LXRhYnMtYWN0aXZlLWxpbmstaG92ZXItYmc6ICAgICAgICAgICAgICRib2R5LWJnICFkZWZhdWx0O1xuJG5hdi10YWJzLWFjdGl2ZS1saW5rLWhvdmVyLWNvbG9yOiAgICAgICAgICAkZ3JheSAhZGVmYXVsdDtcbiRuYXYtdGFicy1hY3RpdmUtbGluay1ob3Zlci1ib3JkZXItY29sb3I6ICAgI2RkZCAhZGVmYXVsdDtcblxuJG5hdi10YWJzLWp1c3RpZmllZC1saW5rLWJvcmRlci1jb2xvcjogICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuJG5hdi10YWJzLWp1c3RpZmllZC1hY3RpdmUtbGluay1ib3JkZXItY29sb3I6ICAgICAkYm9keS1iZyAhZGVmYXVsdDtcblxuLy89PSBQaWxsc1xuJG5hdi1waWxscy1ib3JkZXItcmFkaXVzOiAgICAgICAgICAgICAgICAgICAkYm9yZGVyLXJhZGl1cy1iYXNlICFkZWZhdWx0O1xuJG5hdi1waWxscy1hY3RpdmUtbGluay1ob3Zlci1iZzogICAgICAgICAgICAkY29tcG9uZW50LWFjdGl2ZS1iZyAhZGVmYXVsdDtcbiRuYXYtcGlsbHMtYWN0aXZlLWxpbmstaG92ZXItY29sb3I6ICAgICAgICAgJGNvbXBvbmVudC1hY3RpdmUtY29sb3IgIWRlZmF1bHQ7XG5cblxuLy89PSBQYWdpbmF0aW9uXG4vL1xuLy8jI1xuXG4kcGFnaW5hdGlvbi1jb2xvcjogICAgICAgICAgICAgICAgICAgICAkbGluay1jb2xvciAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWJnOiAgICAgICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1ib3JkZXI6ICAgICAgICAgICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuXG4kcGFnaW5hdGlvbi1ob3Zlci1jb2xvcjogICAgICAgICAgICAgICAkbGluay1ob3Zlci1jb2xvciAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWhvdmVyLWJnOiAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1ob3Zlci1ib3JkZXI6ICAgICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuXG4kcGFnaW5hdGlvbi1hY3RpdmUtY29sb3I6ICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJHBhZ2luYXRpb24tYWN0aXZlLWJnOiAgICAgICAgICAgICAgICAgJGJyYW5kLXByaW1hcnkgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1hY3RpdmUtYm9yZGVyOiAgICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcblxuJHBhZ2luYXRpb24tZGlzYWJsZWQtY29sb3I6ICAgICAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1kaXNhYmxlZC1iZzogICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJHBhZ2luYXRpb24tZGlzYWJsZWQtYm9yZGVyOiAgICAgICAgICAgI2RkZCAhZGVmYXVsdDtcblxuXG4vLz09IFBhZ2VyXG4vL1xuLy8jI1xuXG4kcGFnZXItYmc6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkcGFnaW5hdGlvbi1iZyAhZGVmYXVsdDtcbiRwYWdlci1ib3JkZXI6ICAgICAgICAgICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWJvcmRlciAhZGVmYXVsdDtcbiRwYWdlci1ib3JkZXItcmFkaXVzOiAgICAgICAgICAgICAgICAgIDE1cHggIWRlZmF1bHQ7XG5cbiRwYWdlci1ob3Zlci1iZzogICAgICAgICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWhvdmVyLWJnICFkZWZhdWx0O1xuXG4kcGFnZXItYWN0aXZlLWJnOiAgICAgICAgICAgICAgICAgICAgICAkcGFnaW5hdGlvbi1hY3RpdmUtYmcgIWRlZmF1bHQ7XG4kcGFnZXItYWN0aXZlLWNvbG9yOiAgICAgICAgICAgICAgICAgICAkcGFnaW5hdGlvbi1hY3RpdmUtY29sb3IgIWRlZmF1bHQ7XG5cbiRwYWdlci1kaXNhYmxlZC1jb2xvcjogICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWRpc2FibGVkLWNvbG9yICFkZWZhdWx0O1xuXG5cbi8vPT0gSnVtYm90cm9uXG4vL1xuLy8jI1xuXG4kanVtYm90cm9uLXBhZGRpbmc6ICAgICAgICAgICAgICAzMHB4ICFkZWZhdWx0O1xuJGp1bWJvdHJvbi1jb2xvcjogICAgICAgICAgICAgICAgaW5oZXJpdCAhZGVmYXVsdDtcbiRqdW1ib3Ryb24tYmc6ICAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG4kanVtYm90cm9uLWhlYWRpbmctY29sb3I6ICAgICAgICBpbmhlcml0ICFkZWZhdWx0O1xuJGp1bWJvdHJvbi1mb250LXNpemU6ICAgICAgICAgICAgY2VpbCgoJGZvbnQtc2l6ZS1iYXNlICogMS41KSkgIWRlZmF1bHQ7XG4kanVtYm90cm9uLWhlYWRpbmctZm9udC1zaXplOiAgICBjZWlsKCgkZm9udC1zaXplLWJhc2UgKiA0LjUpKSAhZGVmYXVsdDtcblxuXG4vLz09IEZvcm0gc3RhdGVzIGFuZCBhbGVydHNcbi8vXG4vLyMjIERlZmluZSBjb2xvcnMgZm9yIGZvcm0gZmVlZGJhY2sgc3RhdGVzIGFuZCwgYnkgZGVmYXVsdCwgYWxlcnRzLlxuXG4kc3RhdGUtc3VjY2Vzcy10ZXh0OiAgICAgICAgICAgICAjM2M3NjNkICFkZWZhdWx0O1xuJHN0YXRlLXN1Y2Nlc3MtYmc6ICAgICAgICAgICAgICAgI2RmZjBkOCAhZGVmYXVsdDtcbiRzdGF0ZS1zdWNjZXNzLWJvcmRlcjogICAgICAgICAgIGRhcmtlbihhZGp1c3QtaHVlKCRzdGF0ZS1zdWNjZXNzLWJnLCAtMTApLCA1JSkgIWRlZmF1bHQ7XG5cbiRzdGF0ZS1pbmZvLXRleHQ6ICAgICAgICAgICAgICAgICMzMTcwOGYgIWRlZmF1bHQ7XG4kc3RhdGUtaW5mby1iZzogICAgICAgICAgICAgICAgICAjZDllZGY3ICFkZWZhdWx0O1xuJHN0YXRlLWluZm8tYm9yZGVyOiAgICAgICAgICAgICAgZGFya2VuKGFkanVzdC1odWUoJHN0YXRlLWluZm8tYmcsIC0xMCksIDclKSAhZGVmYXVsdDtcblxuJHN0YXRlLXdhcm5pbmctdGV4dDogICAgICAgICAgICAgIzhhNmQzYiAhZGVmYXVsdDtcbiRzdGF0ZS13YXJuaW5nLWJnOiAgICAgICAgICAgICAgICNmY2Y4ZTMgIWRlZmF1bHQ7XG4kc3RhdGUtd2FybmluZy1ib3JkZXI6ICAgICAgICAgICBkYXJrZW4oYWRqdXN0LWh1ZSgkc3RhdGUtd2FybmluZy1iZywgLTEwKSwgNSUpICFkZWZhdWx0O1xuXG4kc3RhdGUtZGFuZ2VyLXRleHQ6ICAgICAgICAgICAgICAjYTk0NDQyICFkZWZhdWx0O1xuJHN0YXRlLWRhbmdlci1iZzogICAgICAgICAgICAgICAgI2YyZGVkZSAhZGVmYXVsdDtcbiRzdGF0ZS1kYW5nZXItYm9yZGVyOiAgICAgICAgICAgIGRhcmtlbihhZGp1c3QtaHVlKCRzdGF0ZS1kYW5nZXItYmcsIC0xMCksIDUlKSAhZGVmYXVsdDtcblxuXG4vLz09IFRvb2x0aXBzXG4vL1xuLy8jI1xuXG4vLyoqIFRvb2x0aXAgbWF4IHdpZHRoXG4kdG9vbHRpcC1tYXgtd2lkdGg6ICAgICAgICAgICAyMDBweCAhZGVmYXVsdDtcbi8vKiogVG9vbHRpcCB0ZXh0IGNvbG9yXG4kdG9vbHRpcC1jb2xvcjogICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBUb29sdGlwIGJhY2tncm91bmQgY29sb3JcbiR0b29sdGlwLWJnOiAgICAgICAgICAgICAgICAgICMwMDAgIWRlZmF1bHQ7XG4kdG9vbHRpcC1vcGFjaXR5OiAgICAgICAgICAgICAuOSAhZGVmYXVsdDtcblxuLy8qKiBUb29sdGlwIGFycm93IHdpZHRoXG4kdG9vbHRpcC1hcnJvdy13aWR0aDogICAgICAgICA1cHggIWRlZmF1bHQ7XG4vLyoqIFRvb2x0aXAgYXJyb3cgY29sb3JcbiR0b29sdGlwLWFycm93LWNvbG9yOiAgICAgICAgICR0b29sdGlwLWJnICFkZWZhdWx0O1xuXG5cbi8vPT0gUG9wb3ZlcnNcbi8vXG4vLyMjXG5cbi8vKiogUG9wb3ZlciBib2R5IGJhY2tncm91bmQgY29sb3JcbiRwb3BvdmVyLWJnOiAgICAgICAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogUG9wb3ZlciBtYXhpbXVtIHdpZHRoXG4kcG9wb3Zlci1tYXgtd2lkdGg6ICAgICAgICAgICAgICAgICAgIDI3NnB4ICFkZWZhdWx0O1xuLy8qKiBQb3BvdmVyIGJvcmRlciBjb2xvclxuJHBvcG92ZXItYm9yZGVyLWNvbG9yOiAgICAgICAgICAgICAgICByZ2JhKDAsMCwwLC4yKSAhZGVmYXVsdDtcbi8vKiogUG9wb3ZlciBmYWxsYmFjayBib3JkZXIgY29sb3JcbiRwb3BvdmVyLWZhbGxiYWNrLWJvcmRlci1jb2xvcjogICAgICAgI2NjYyAhZGVmYXVsdDtcblxuLy8qKiBQb3BvdmVyIHRpdGxlIGJhY2tncm91bmQgY29sb3JcbiRwb3BvdmVyLXRpdGxlLWJnOiAgICAgICAgICAgICAgICAgICAgZGFya2VuKCRwb3BvdmVyLWJnLCAzJSkgIWRlZmF1bHQ7XG5cbi8vKiogUG9wb3ZlciBhcnJvdyB3aWR0aFxuJHBvcG92ZXItYXJyb3ctd2lkdGg6ICAgICAgICAgICAgICAgICAxMHB4ICFkZWZhdWx0O1xuLy8qKiBQb3BvdmVyIGFycm93IGNvbG9yXG4kcG9wb3Zlci1hcnJvdy1jb2xvcjogICAgICAgICAgICAgICAgICRwb3BvdmVyLWJnICFkZWZhdWx0O1xuXG4vLyoqIFBvcG92ZXIgb3V0ZXIgYXJyb3cgd2lkdGhcbiRwb3BvdmVyLWFycm93LW91dGVyLXdpZHRoOiAgICAgICAgICAgKCRwb3BvdmVyLWFycm93LXdpZHRoICsgMSkgIWRlZmF1bHQ7XG4vLyoqIFBvcG92ZXIgb3V0ZXIgYXJyb3cgY29sb3JcbiRwb3BvdmVyLWFycm93LW91dGVyLWNvbG9yOiAgICAgICAgICAgZmFkZV9pbigkcG9wb3Zlci1ib3JkZXItY29sb3IsIDAuMDUpICFkZWZhdWx0O1xuLy8qKiBQb3BvdmVyIG91dGVyIGFycm93IGZhbGxiYWNrIGNvbG9yXG4kcG9wb3Zlci1hcnJvdy1vdXRlci1mYWxsYmFjay1jb2xvcjogIGRhcmtlbigkcG9wb3Zlci1mYWxsYmFjay1ib3JkZXItY29sb3IsIDIwJSkgIWRlZmF1bHQ7XG5cblxuLy89PSBMYWJlbHNcbi8vXG4vLyMjXG5cbi8vKiogRGVmYXVsdCBsYWJlbCBiYWNrZ3JvdW5kIGNvbG9yXG4kbGFiZWwtZGVmYXVsdC1iZzogICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbi8vKiogUHJpbWFyeSBsYWJlbCBiYWNrZ3JvdW5kIGNvbG9yXG4kbGFiZWwtcHJpbWFyeS1iZzogICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbi8vKiogU3VjY2VzcyBsYWJlbCBiYWNrZ3JvdW5kIGNvbG9yXG4kbGFiZWwtc3VjY2Vzcy1iZzogICAgICAgICAgICAkYnJhbmQtc3VjY2VzcyAhZGVmYXVsdDtcbi8vKiogSW5mbyBsYWJlbCBiYWNrZ3JvdW5kIGNvbG9yXG4kbGFiZWwtaW5mby1iZzogICAgICAgICAgICAgICAkYnJhbmQtaW5mbyAhZGVmYXVsdDtcbi8vKiogV2FybmluZyBsYWJlbCBiYWNrZ3JvdW5kIGNvbG9yXG4kbGFiZWwtd2FybmluZy1iZzogICAgICAgICAgICAkYnJhbmQtd2FybmluZyAhZGVmYXVsdDtcbi8vKiogRGFuZ2VyIGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC1kYW5nZXItYmc6ICAgICAgICAgICAgICRicmFuZC1kYW5nZXIgIWRlZmF1bHQ7XG5cbi8vKiogRGVmYXVsdCBsYWJlbCB0ZXh0IGNvbG9yXG4kbGFiZWwtY29sb3I6ICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBEZWZhdWx0IHRleHQgY29sb3Igb2YgYSBsaW5rZWQgbGFiZWxcbiRsYWJlbC1saW5rLWhvdmVyLWNvbG9yOiAgICAgICNmZmYgIWRlZmF1bHQ7XG5cblxuLy89PSBNb2RhbHNcbi8vXG4vLyMjXG5cbi8vKiogUGFkZGluZyBhcHBsaWVkIHRvIHRoZSBtb2RhbCBib2R5XG4kbW9kYWwtaW5uZXItcGFkZGluZzogICAgICAgICAxNXB4ICFkZWZhdWx0O1xuXG4vLyoqIFBhZGRpbmcgYXBwbGllZCB0byB0aGUgbW9kYWwgdGl0bGVcbiRtb2RhbC10aXRsZS1wYWRkaW5nOiAgICAgICAgIDE1cHggIWRlZmF1bHQ7XG4vLyoqIE1vZGFsIHRpdGxlIGxpbmUtaGVpZ2h0XG4kbW9kYWwtdGl0bGUtbGluZS1oZWlnaHQ6ICAgICAkbGluZS1oZWlnaHQtYmFzZSAhZGVmYXVsdDtcblxuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIG1vZGFsIGNvbnRlbnQgYXJlYVxuJG1vZGFsLWNvbnRlbnQtYmc6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBNb2RhbCBjb250ZW50IGJvcmRlciBjb2xvclxuJG1vZGFsLWNvbnRlbnQtYm9yZGVyLWNvbG9yOiAgICAgICAgICAgICAgICAgICByZ2JhKDAsMCwwLC4yKSAhZGVmYXVsdDtcbi8vKiogTW9kYWwgY29udGVudCBib3JkZXIgY29sb3IgKipmb3IgSUU4KipcbiRtb2RhbC1jb250ZW50LWZhbGxiYWNrLWJvcmRlci1jb2xvcjogICAgICAgICAgIzk5OSAhZGVmYXVsdDtcblxuLy8qKiBNb2RhbCBiYWNrZHJvcCBiYWNrZ3JvdW5kIGNvbG9yXG4kbW9kYWwtYmFja2Ryb3AtYmc6ICAgICAgICAgICAjMDAwICFkZWZhdWx0O1xuLy8qKiBNb2RhbCBiYWNrZHJvcCBvcGFjaXR5XG4kbW9kYWwtYmFja2Ryb3Atb3BhY2l0eTogICAgICAuNSAhZGVmYXVsdDtcbi8vKiogTW9kYWwgaGVhZGVyIGJvcmRlciBjb2xvclxuJG1vZGFsLWhlYWRlci1ib3JkZXItY29sb3I6ICAgI2U1ZTVlNSAhZGVmYXVsdDtcbi8vKiogTW9kYWwgZm9vdGVyIGJvcmRlciBjb2xvclxuJG1vZGFsLWZvb3Rlci1ib3JkZXItY29sb3I6ICAgJG1vZGFsLWhlYWRlci1ib3JkZXItY29sb3IgIWRlZmF1bHQ7XG5cbiRtb2RhbC1sZzogICAgICAgICAgICAgICAgICAgIDkwMHB4ICFkZWZhdWx0O1xuJG1vZGFsLW1kOiAgICAgICAgICAgICAgICAgICAgNjAwcHggIWRlZmF1bHQ7XG4kbW9kYWwtc206ICAgICAgICAgICAgICAgICAgICAzMDBweCAhZGVmYXVsdDtcblxuXG4vLz09IEFsZXJ0c1xuLy9cbi8vIyMgRGVmaW5lIGFsZXJ0IGNvbG9ycywgYm9yZGVyIHJhZGl1cywgYW5kIHBhZGRpbmcuXG5cbiRhbGVydC1wYWRkaW5nOiAgICAgICAgICAgICAgIDE1cHggIWRlZmF1bHQ7XG4kYWxlcnQtYm9yZGVyLXJhZGl1czogICAgICAgICAkYm9yZGVyLXJhZGl1cy1iYXNlICFkZWZhdWx0O1xuJGFsZXJ0LWxpbmstZm9udC13ZWlnaHQ6ICAgICAgYm9sZCAhZGVmYXVsdDtcblxuJGFsZXJ0LXN1Y2Nlc3MtYmc6ICAgICAgICAgICAgJHN0YXRlLXN1Y2Nlc3MtYmcgIWRlZmF1bHQ7XG4kYWxlcnQtc3VjY2Vzcy10ZXh0OiAgICAgICAgICAkc3RhdGUtc3VjY2Vzcy10ZXh0ICFkZWZhdWx0O1xuJGFsZXJ0LXN1Y2Nlc3MtYm9yZGVyOiAgICAgICAgJHN0YXRlLXN1Y2Nlc3MtYm9yZGVyICFkZWZhdWx0O1xuXG4kYWxlcnQtaW5mby1iZzogICAgICAgICAgICAgICAkc3RhdGUtaW5mby1iZyAhZGVmYXVsdDtcbiRhbGVydC1pbmZvLXRleHQ6ICAgICAgICAgICAgICRzdGF0ZS1pbmZvLXRleHQgIWRlZmF1bHQ7XG4kYWxlcnQtaW5mby1ib3JkZXI6ICAgICAgICAgICAkc3RhdGUtaW5mby1ib3JkZXIgIWRlZmF1bHQ7XG5cbiRhbGVydC13YXJuaW5nLWJnOiAgICAgICAgICAgICRzdGF0ZS13YXJuaW5nLWJnICFkZWZhdWx0O1xuJGFsZXJ0LXdhcm5pbmctdGV4dDogICAgICAgICAgJHN0YXRlLXdhcm5pbmctdGV4dCAhZGVmYXVsdDtcbiRhbGVydC13YXJuaW5nLWJvcmRlcjogICAgICAgICRzdGF0ZS13YXJuaW5nLWJvcmRlciAhZGVmYXVsdDtcblxuJGFsZXJ0LWRhbmdlci1iZzogICAgICAgICAgICAgJHN0YXRlLWRhbmdlci1iZyAhZGVmYXVsdDtcbiRhbGVydC1kYW5nZXItdGV4dDogICAgICAgICAgICRzdGF0ZS1kYW5nZXItdGV4dCAhZGVmYXVsdDtcbiRhbGVydC1kYW5nZXItYm9yZGVyOiAgICAgICAgICRzdGF0ZS1kYW5nZXItYm9yZGVyICFkZWZhdWx0O1xuXG5cbi8vPT0gUHJvZ3Jlc3MgYmFyc1xuLy9cbi8vIyNcblxuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIHRoZSB3aG9sZSBwcm9ncmVzcyBjb21wb25lbnRcbiRwcm9ncmVzcy1iZzogICAgICAgICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4vLyoqIFByb2dyZXNzIGJhciB0ZXh0IGNvbG9yXG4kcHJvZ3Jlc3MtYmFyLWNvbG9yOiAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBWYXJpYWJsZSBmb3Igc2V0dGluZyByb3VuZGVkIGNvcm5lcnMgb24gcHJvZ3Jlc3MgYmFyLlxuJHByb2dyZXNzLWJvcmRlci1yYWRpdXM6ICAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcblxuLy8qKiBEZWZhdWx0IHByb2dyZXNzIGJhciBjb2xvclxuJHByb2dyZXNzLWJhci1iZzogICAgICAgICAgICAgJGJyYW5kLXByaW1hcnkgIWRlZmF1bHQ7XG4vLyoqIFN1Y2Nlc3MgcHJvZ3Jlc3MgYmFyIGNvbG9yXG4kcHJvZ3Jlc3MtYmFyLXN1Y2Nlc3MtYmc6ICAgICAkYnJhbmQtc3VjY2VzcyAhZGVmYXVsdDtcbi8vKiogV2FybmluZyBwcm9ncmVzcyBiYXIgY29sb3JcbiRwcm9ncmVzcy1iYXItd2FybmluZy1iZzogICAgICRicmFuZC13YXJuaW5nICFkZWZhdWx0O1xuLy8qKiBEYW5nZXIgcHJvZ3Jlc3MgYmFyIGNvbG9yXG4kcHJvZ3Jlc3MtYmFyLWRhbmdlci1iZzogICAgICAkYnJhbmQtZGFuZ2VyICFkZWZhdWx0O1xuLy8qKiBJbmZvIHByb2dyZXNzIGJhciBjb2xvclxuJHByb2dyZXNzLWJhci1pbmZvLWJnOiAgICAgICAgJGJyYW5kLWluZm8gIWRlZmF1bHQ7XG5cblxuLy89PSBMaXN0IGdyb3VwXG4vL1xuLy8jI1xuXG4vLyoqIEJhY2tncm91bmQgY29sb3Igb24gYC5saXN0LWdyb3VwLWl0ZW1gXG4kbGlzdC1ncm91cC1iZzogICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIGAubGlzdC1ncm91cC1pdGVtYCBib3JkZXIgY29sb3JcbiRsaXN0LWdyb3VwLWJvcmRlcjogICAgICAgICAgICAgI2RkZCAhZGVmYXVsdDtcbi8vKiogTGlzdCBncm91cCBib3JkZXIgcmFkaXVzXG4kbGlzdC1ncm91cC1ib3JkZXItcmFkaXVzOiAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG5cbi8vKiogQmFja2dyb3VuZCBjb2xvciBvZiBzaW5nbGUgbGlzdCBpdGVtcyBvbiBob3ZlclxuJGxpc3QtZ3JvdXAtaG92ZXItYmc6ICAgICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuLy8qKiBUZXh0IGNvbG9yIG9mIGFjdGl2ZSBsaXN0IGl0ZW1zXG4kbGlzdC1ncm91cC1hY3RpdmUtY29sb3I6ICAgICAgICRjb21wb25lbnQtYWN0aXZlLWNvbG9yICFkZWZhdWx0O1xuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIGFjdGl2ZSBsaXN0IGl0ZW1zXG4kbGlzdC1ncm91cC1hY3RpdmUtYmc6ICAgICAgICAgICRjb21wb25lbnQtYWN0aXZlLWJnICFkZWZhdWx0O1xuLy8qKiBCb3JkZXIgY29sb3Igb2YgYWN0aXZlIGxpc3QgZWxlbWVudHNcbiRsaXN0LWdyb3VwLWFjdGl2ZS1ib3JkZXI6ICAgICAgJGxpc3QtZ3JvdXAtYWN0aXZlLWJnICFkZWZhdWx0O1xuLy8qKiBUZXh0IGNvbG9yIGZvciBjb250ZW50IHdpdGhpbiBhY3RpdmUgbGlzdCBpdGVtc1xuJGxpc3QtZ3JvdXAtYWN0aXZlLXRleHQtY29sb3I6ICBsaWdodGVuKCRsaXN0LWdyb3VwLWFjdGl2ZS1iZywgNDAlKSAhZGVmYXVsdDtcblxuLy8qKiBUZXh0IGNvbG9yIG9mIGRpc2FibGVkIGxpc3QgaXRlbXNcbiRsaXN0LWdyb3VwLWRpc2FibGVkLWNvbG9yOiAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIGRpc2FibGVkIGxpc3QgaXRlbXNcbiRsaXN0LWdyb3VwLWRpc2FibGVkLWJnOiAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG4vLyoqIFRleHQgY29sb3IgZm9yIGNvbnRlbnQgd2l0aGluIGRpc2FibGVkIGxpc3QgaXRlbXNcbiRsaXN0LWdyb3VwLWRpc2FibGVkLXRleHQtY29sb3I6ICRsaXN0LWdyb3VwLWRpc2FibGVkLWNvbG9yICFkZWZhdWx0O1xuXG4kbGlzdC1ncm91cC1saW5rLWNvbG9yOiAgICAgICAgICM1NTUgIWRlZmF1bHQ7XG4kbGlzdC1ncm91cC1saW5rLWhvdmVyLWNvbG9yOiAgICRsaXN0LWdyb3VwLWxpbmstY29sb3IgIWRlZmF1bHQ7XG4kbGlzdC1ncm91cC1saW5rLWhlYWRpbmctY29sb3I6ICMzMzMgIWRlZmF1bHQ7XG5cblxuLy89PSBQYW5lbHNcbi8vXG4vLyMjXG5cbiRwYW5lbC1iZzogICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kcGFuZWwtYm9keS1wYWRkaW5nOiAgICAgICAgICAxNXB4ICFkZWZhdWx0O1xuJHBhbmVsLWhlYWRpbmctcGFkZGluZzogICAgICAgMTBweCAxNXB4ICFkZWZhdWx0O1xuJHBhbmVsLWZvb3Rlci1wYWRkaW5nOiAgICAgICAgJHBhbmVsLWhlYWRpbmctcGFkZGluZyAhZGVmYXVsdDtcbiRwYW5lbC1ib3JkZXItcmFkaXVzOiAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG5cbi8vKiogQm9yZGVyIGNvbG9yIGZvciBlbGVtZW50cyB3aXRoaW4gcGFuZWxzXG4kcGFuZWwtaW5uZXItYm9yZGVyOiAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuJHBhbmVsLWZvb3Rlci1iZzogICAgICAgICAgICAgI2Y1ZjVmNSAhZGVmYXVsdDtcblxuJHBhbmVsLWRlZmF1bHQtdGV4dDogICAgICAgICAgJGdyYXktZGFyayAhZGVmYXVsdDtcbiRwYW5lbC1kZWZhdWx0LWJvcmRlcjogICAgICAgICNkZGQgIWRlZmF1bHQ7XG4kcGFuZWwtZGVmYXVsdC1oZWFkaW5nLWJnOiAgICAjZjVmNWY1ICFkZWZhdWx0O1xuXG4kcGFuZWwtcHJpbWFyeS10ZXh0OiAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJHBhbmVsLXByaW1hcnktYm9yZGVyOiAgICAgICAgJGJyYW5kLXByaW1hcnkgIWRlZmF1bHQ7XG4kcGFuZWwtcHJpbWFyeS1oZWFkaW5nLWJnOiAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcblxuJHBhbmVsLXN1Y2Nlc3MtdGV4dDogICAgICAgICAgJHN0YXRlLXN1Y2Nlc3MtdGV4dCAhZGVmYXVsdDtcbiRwYW5lbC1zdWNjZXNzLWJvcmRlcjogICAgICAgICRzdGF0ZS1zdWNjZXNzLWJvcmRlciAhZGVmYXVsdDtcbiRwYW5lbC1zdWNjZXNzLWhlYWRpbmctYmc6ICAgICRzdGF0ZS1zdWNjZXNzLWJnICFkZWZhdWx0O1xuXG4kcGFuZWwtaW5mby10ZXh0OiAgICAgICAgICAgICAkc3RhdGUtaW5mby10ZXh0ICFkZWZhdWx0O1xuJHBhbmVsLWluZm8tYm9yZGVyOiAgICAgICAgICAgJHN0YXRlLWluZm8tYm9yZGVyICFkZWZhdWx0O1xuJHBhbmVsLWluZm8taGVhZGluZy1iZzogICAgICAgJHN0YXRlLWluZm8tYmcgIWRlZmF1bHQ7XG5cbiRwYW5lbC13YXJuaW5nLXRleHQ6ICAgICAgICAgICRzdGF0ZS13YXJuaW5nLXRleHQgIWRlZmF1bHQ7XG4kcGFuZWwtd2FybmluZy1ib3JkZXI6ICAgICAgICAkc3RhdGUtd2FybmluZy1ib3JkZXIgIWRlZmF1bHQ7XG4kcGFuZWwtd2FybmluZy1oZWFkaW5nLWJnOiAgICAkc3RhdGUtd2FybmluZy1iZyAhZGVmYXVsdDtcblxuJHBhbmVsLWRhbmdlci10ZXh0OiAgICAgICAgICAgJHN0YXRlLWRhbmdlci10ZXh0ICFkZWZhdWx0O1xuJHBhbmVsLWRhbmdlci1ib3JkZXI6ICAgICAgICAgJHN0YXRlLWRhbmdlci1ib3JkZXIgIWRlZmF1bHQ7XG4kcGFuZWwtZGFuZ2VyLWhlYWRpbmctYmc6ICAgICAkc3RhdGUtZGFuZ2VyLWJnICFkZWZhdWx0O1xuXG5cbi8vPT0gVGh1bWJuYWlsc1xuLy9cbi8vIyNcblxuLy8qKiBQYWRkaW5nIGFyb3VuZCB0aGUgdGh1bWJuYWlsIGltYWdlXG4kdGh1bWJuYWlsLXBhZGRpbmc6ICAgICAgICAgICA0cHggIWRlZmF1bHQ7XG4vLyoqIFRodW1ibmFpbCBiYWNrZ3JvdW5kIGNvbG9yXG4kdGh1bWJuYWlsLWJnOiAgICAgICAgICAgICAgICAkYm9keS1iZyAhZGVmYXVsdDtcbi8vKiogVGh1bWJuYWlsIGJvcmRlciBjb2xvclxuJHRodW1ibmFpbC1ib3JkZXI6ICAgICAgICAgICAgI2RkZCAhZGVmYXVsdDtcbi8vKiogVGh1bWJuYWlsIGJvcmRlciByYWRpdXNcbiR0aHVtYm5haWwtYm9yZGVyLXJhZGl1czogICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG5cbi8vKiogQ3VzdG9tIHRleHQgY29sb3IgZm9yIHRodW1ibmFpbCBjYXB0aW9uc1xuJHRodW1ibmFpbC1jYXB0aW9uLWNvbG9yOiAgICAgJHRleHQtY29sb3IgIWRlZmF1bHQ7XG4vLyoqIFBhZGRpbmcgYXJvdW5kIHRoZSB0aHVtYm5haWwgY2FwdGlvblxuJHRodW1ibmFpbC1jYXB0aW9uLXBhZGRpbmc6ICAgOXB4ICFkZWZhdWx0O1xuXG5cbi8vPT0gV2VsbHNcbi8vXG4vLyMjXG5cbiR3ZWxsLWJnOiAgICAgICAgICAgICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4kd2VsbC1ib3JkZXI6ICAgICAgICAgICAgICAgICBkYXJrZW4oJHdlbGwtYmcsIDclKSAhZGVmYXVsdDtcblxuXG4vLz09IEJhZGdlc1xuLy9cbi8vIyNcblxuJGJhZGdlLWNvbG9yOiAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogTGlua2VkIGJhZGdlIHRleHQgY29sb3Igb24gaG92ZXJcbiRiYWRnZS1saW5rLWhvdmVyLWNvbG9yOiAgICAgICNmZmYgIWRlZmF1bHQ7XG4kYmFkZ2UtYmc6ICAgICAgICAgICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcblxuLy8qKiBCYWRnZSB0ZXh0IGNvbG9yIGluIGFjdGl2ZSBuYXYgbGlua1xuJGJhZGdlLWFjdGl2ZS1jb2xvcjogICAgICAgICAgJGxpbmstY29sb3IgIWRlZmF1bHQ7XG4vLyoqIEJhZGdlIGJhY2tncm91bmQgY29sb3IgaW4gYWN0aXZlIG5hdiBsaW5rXG4kYmFkZ2UtYWN0aXZlLWJnOiAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuXG4kYmFkZ2UtZm9udC13ZWlnaHQ6ICAgICAgICAgICBib2xkICFkZWZhdWx0O1xuJGJhZGdlLWxpbmUtaGVpZ2h0OiAgICAgICAgICAgMSAhZGVmYXVsdDtcbiRiYWRnZS1ib3JkZXItcmFkaXVzOiAgICAgICAgIDEwcHggIWRlZmF1bHQ7XG5cblxuLy89PSBCcmVhZGNydW1ic1xuLy9cbi8vIyNcblxuJGJyZWFkY3J1bWItcGFkZGluZy12ZXJ0aWNhbDogICA4cHggIWRlZmF1bHQ7XG4kYnJlYWRjcnVtYi1wYWRkaW5nLWhvcml6b250YWw6IDE1cHggIWRlZmF1bHQ7XG4vLyoqIEJyZWFkY3J1bWIgYmFja2dyb3VuZCBjb2xvclxuJGJyZWFkY3J1bWItYmc6ICAgICAgICAgICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuLy8qKiBCcmVhZGNydW1iIHRleHQgY29sb3JcbiRicmVhZGNydW1iLWNvbG9yOiAgICAgICAgICAgICAgI2NjYyAhZGVmYXVsdDtcbi8vKiogVGV4dCBjb2xvciBvZiBjdXJyZW50IHBhZ2UgaW4gdGhlIGJyZWFkY3J1bWJcbiRicmVhZGNydW1iLWFjdGl2ZS1jb2xvcjogICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG4vLyoqIFRleHR1YWwgc2VwYXJhdG9yIGZvciBiZXR3ZWVuIGJyZWFkY3J1bWIgZWxlbWVudHNcbiRicmVhZGNydW1iLXNlcGFyYXRvcjogICAgICAgICAgXCIvXCIgIWRlZmF1bHQ7XG5cblxuLy89PSBDYXJvdXNlbFxuLy9cbi8vIyNcblxuJGNhcm91c2VsLXRleHQtc2hhZG93OiAgICAgICAgICAgICAgICAgICAgICAgIDAgMXB4IDJweCByZ2JhKDAsMCwwLC42KSAhZGVmYXVsdDtcblxuJGNhcm91c2VsLWNvbnRyb2wtY29sb3I6ICAgICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kY2Fyb3VzZWwtY29udHJvbC13aWR0aDogICAgICAgICAgICAgICAgICAgICAgMTUlICFkZWZhdWx0O1xuJGNhcm91c2VsLWNvbnRyb2wtb3BhY2l0eTogICAgICAgICAgICAgICAgICAgIC41ICFkZWZhdWx0O1xuJGNhcm91c2VsLWNvbnRyb2wtZm9udC1zaXplOiAgICAgICAgICAgICAgICAgIDIwcHggIWRlZmF1bHQ7XG5cbiRjYXJvdXNlbC1pbmRpY2F0b3ItYWN0aXZlLWJnOiAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJGNhcm91c2VsLWluZGljYXRvci1ib3JkZXItY29sb3I6ICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG5cbiRjYXJvdXNlbC1jYXB0aW9uLWNvbG9yOiAgICAgICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuXG5cbi8vPT0gQ2xvc2Vcbi8vXG4vLyMjXG5cbiRjbG9zZS1mb250LXdlaWdodDogICAgICAgICAgIGJvbGQgIWRlZmF1bHQ7XG4kY2xvc2UtY29sb3I6ICAgICAgICAgICAgICAgICAjMDAwICFkZWZhdWx0O1xuJGNsb3NlLXRleHQtc2hhZG93OiAgICAgICAgICAgMCAxcHggMCAjZmZmICFkZWZhdWx0O1xuXG5cbi8vPT0gQ29kZVxuLy9cbi8vIyNcblxuJGNvZGUtY29sb3I6ICAgICAgICAgICAgICAgICAgI2M3MjU0ZSAhZGVmYXVsdDtcbiRjb2RlLWJnOiAgICAgICAgICAgICAgICAgICAgICNmOWYyZjQgIWRlZmF1bHQ7XG5cbiRrYmQtY29sb3I6ICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4ka2JkLWJnOiAgICAgICAgICAgICAgICAgICAgICAjMzMzICFkZWZhdWx0O1xuXG4kcHJlLWJnOiAgICAgICAgICAgICAgICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuJHByZS1jb2xvcjogICAgICAgICAgICAgICAgICAgJGdyYXktZGFyayAhZGVmYXVsdDtcbiRwcmUtYm9yZGVyLWNvbG9yOiAgICAgICAgICAgICNjY2MgIWRlZmF1bHQ7XG4kcHJlLXNjcm9sbGFibGUtbWF4LWhlaWdodDogICAzNDBweCAhZGVmYXVsdDtcblxuXG4vLz09IFR5cGVcbi8vXG4vLyMjXG5cbi8vKiogSG9yaXpvbnRhbCBvZmZzZXQgZm9yIGZvcm1zIGFuZCBsaXN0cy5cbiRjb21wb25lbnQtb2Zmc2V0LWhvcml6b250YWw6IDE4MHB4ICFkZWZhdWx0O1xuLy8qKiBUZXh0IG11dGVkIGNvbG9yXG4kdGV4dC1tdXRlZDogICAgICAgICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbi8vKiogQWJicmV2aWF0aW9ucyBhbmQgYWNyb255bXMgYm9yZGVyIGNvbG9yXG4kYWJici1ib3JkZXItY29sb3I6ICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbi8vKiogSGVhZGluZ3Mgc21hbGwgY29sb3JcbiRoZWFkaW5ncy1zbWFsbC1jb2xvcjogICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBCbG9ja3F1b3RlIHNtYWxsIGNvbG9yXG4kYmxvY2txdW90ZS1zbWFsbC1jb2xvcjogICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbi8vKiogQmxvY2txdW90ZSBmb250IHNpemVcbiRibG9ja3F1b3RlLWZvbnQtc2l6ZTogICAgICAgICgkZm9udC1zaXplLWJhc2UgKiAxLjI1KSAhZGVmYXVsdDtcbi8vKiogQmxvY2txdW90ZSBib3JkZXIgY29sb3JcbiRibG9ja3F1b3RlLWJvcmRlci1jb2xvcjogICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG4vLyoqIFBhZ2UgaGVhZGVyIGJvcmRlciBjb2xvclxuJHBhZ2UtaGVhZGVyLWJvcmRlci1jb2xvcjogICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbi8vKiogV2lkdGggb2YgaG9yaXpvbnRhbCBkZXNjcmlwdGlvbiBsaXN0IHRpdGxlc1xuJGRsLWhvcml6b250YWwtb2Zmc2V0OiAgICAgICAgJGNvbXBvbmVudC1vZmZzZXQtaG9yaXpvbnRhbCAhZGVmYXVsdDtcbi8vKiogUG9pbnQgYXQgd2hpY2ggLmRsLWhvcml6b250YWwgYmVjb21lcyBob3Jpem9udGFsXG4kZGwtaG9yaXpvbnRhbC1icmVha3BvaW50OiAgICAkZ3JpZC1mbG9hdC1icmVha3BvaW50ICFkZWZhdWx0O1xuLy8qKiBIb3Jpem9udGFsIGxpbmUgY29sb3IuXG4kaHItYm9yZGVyOiAgICAgICAgICAgICAgICAgICAkZ3JheS1saWdodGVyICFkZWZhdWx0O1xuIiwieG9zLXNtYXJ0LXRhYmxle1xuICAucm93ICsgeG9zLXRhYmxlIHtcbiAgICBtYXJnaW4tdG9wOiAxNXB4O1xuICB9XG59Il0sIm1hcHBpbmdzIjoiQUNBQSxVQUFVLENBQUMsQUFBQSxZQUFZO0VBQ3JCLEFBQUEsSUFBSTtJQUNGLFNBQVMsRUFBRSx1QkFBVztJQUN0QixVQUFVLEVBQUUsT0FBUTtFQUd0QixBQUFBLEVBQUU7SUFDQSxTQUFTLEVBQUUsb0JBQVc7O0FBSTFCLFVBQVUsQ0FBQyxBQUFBLGFBQWE7RUFDdEIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLG9CQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLFVBQVUsRUFBRSxNQUFPO0lBQ25CLFNBQVMsRUFBRSx1QkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsUUFBUTtFQUNqQixBQUFBLElBQUk7SUFDRixPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVztFQUd4QixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSxJQUFLOztBQUlwQixVQUFVLENBQUMsQUFBQSxXQUFXO0VBQ3BCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0VBR2IsQUFBQSxFQUFFO0lBQ0EsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsdUJBQVc7O0FBekMxQixVQUFVLENBQUMsQUFBQSxZQUFZO0VBQ3JCLEFBQUEsSUFBSTtJQUNGLFNBQVMsRUFBRSx1QkFBVztJQUN0QixVQUFVLEVBQUUsT0FBUTtFQUd0QixBQUFBLEVBQUU7SUFDQSxTQUFTLEVBQUUsb0JBQVc7O0FBSTFCLFVBQVUsQ0FBQyxBQUFBLGFBQWE7RUFDdEIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLG9CQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLFVBQVUsRUFBRSxNQUFPO0lBQ25CLFNBQVMsRUFBRSx1QkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsUUFBUTtFQUNqQixBQUFBLElBQUk7SUFDRixPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVztFQUd4QixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSxJQUFLOztBQUlwQixVQUFVLENBQUMsQUFBQSxXQUFXO0VBQ3BCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0VBR2IsQUFBQSxFQUFFO0lBQ0EsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsdUJBQVc7O0FDdkMxQixBQUFBLFNBQVMsQ0FBQztFQUVSLE9BQU8sRUFBRSxLQUFNLEdBcUJoQjtFQXZCRCxBQUlJLFNBSkssQ0FJUCxFQUFFLEFBQUEsUUFBUTtFQUpaLEFBS0ksU0FMSyxDQUtQLEVBQUUsQUFBQSxTQUFTO0VBTGIsQUFNSSxTQU5LLENBTVAsRUFBRSxBQUFBLFNBQVMsQ0FBQztJQUNWLFVBQVUsRUFBQyxlQUFnQixHQUM1QjtFQVJILEFBVWEsU0FWSixDQVVQLEVBQUUsQUFBQSxTQUFTLEFBQUEsZ0JBQWdCO0VBVjdCLEFBV0ksU0FYSyxDQVdQLEVBQUUsQUFBQSxRQUFRO0VBWFosQUFZSSxTQVpLLENBWVAsRUFBRSxBQUFBLFNBQVMsQ0FBQztJQUNWLE9BQU8sRUFBQyxDQUFFO0lBQ1YsU0FBUyxFQUFFLDhCQUErQixHQUMzQztFQWZILEFBaUJJLFNBakJLLENBaUJQLEVBQUUsQUFBQSxTQUFTO0VBakJiLEFBa0JZLFNBbEJILENBa0JQLEVBQUUsQUFBQSxRQUFRLEFBQUEsZUFBZTtFQWxCM0IsQUFtQmEsU0FuQkosQ0FtQlAsRUFBRSxBQUFBLFNBQVMsQUFBQSxnQkFBZ0IsQ0FBQztJQUMxQixPQUFPLEVBQUMsQ0FBRTtJQUNWLFNBQVMsRUFBRSw2QkFBOEIsR0FDMUM7O0FEeEJILFVBQVUsQ0FBQyxBQUFBLFlBQVk7RUFDckIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLHVCQUFXO0lBQ3RCLFVBQVUsRUFBRSxPQUFRO0VBR3RCLEFBQUEsRUFBRTtJQUNBLFNBQVMsRUFBRSxvQkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsYUFBYTtFQUN0QixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsb0JBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsVUFBVSxFQUFFLE1BQU87SUFDbkIsU0FBUyxFQUFFLHVCQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxRQUFRO0VBQ2pCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLElBQUs7O0FBSXBCLFVBQVUsQ0FBQyxBQUFBLFdBQVc7RUFDcEIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7RUFHYixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVzs7QUV2QzFCLEFBQUEsU0FBUyxDQUFDO0VBRVIsaUJBQWlCO0VBR2pCLGtCQUFrQixFQUVuQjtFQVBELEFBR0UsU0FITyxDQUdQLFlBQVksQ0FBUztJQUFFLFNBQVMsRUFBQyw0QkFBNkIsR0FBSTtFQUhwRSxBQU1FLFNBTk8sQ0FNUCxlQUFlLENBQU07SUFBRSxTQUFTLEVBQUMseUJBQTBCLEdBQUk7O0FGUmpFLFVBQVUsQ0FBQyxBQUFBLFlBQVk7RUFDckIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLHVCQUFXO0lBQ3RCLFVBQVUsRUFBRSxPQUFRO0VBR3RCLEFBQUEsRUFBRTtJQUNBLFNBQVMsRUFBRSxvQkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsYUFBYTtFQUN0QixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsb0JBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsVUFBVSxFQUFFLE1BQU87SUFDbkIsU0FBUyxFQUFFLHVCQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxRQUFRO0VBQ2pCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLElBQUs7O0FBSXBCLFVBQVUsQ0FBQyxBQUFBLFdBQVc7RUFDcEIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7RUFHYixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVzs7QUd0QzFCLEFBQVEsS0FBSCxHQUFHLGNBQWMsQ0FBQztFQUNyQixVQUFVLEVDd05xQixJQUFJO0VEdk5uQyxPQUFPLEVBQUUsS0FBTSxHQUNoQjs7QUVORCxBQUNTLGVBRE0sQ0FDYixJQUFJLEdBQUcsU0FBUyxDQUFDO0VBQ2YsVUFBVSxFQUFFLElBQUssR0FDbEI7O0NOS0gsQUFBQSxBQUFVLFNBQVQsQUFBQSxJQUFZLEFBQUEsQUFBUyxRQUFSLEFBQUEsSUFBVyxBQUFBLEFBQWMsYUFBYixBQUFBLElBQWdCLEFBQUEsQUFBVyxVQUFWLEFBQUEsR0FBYSxBQUFBLFNBQVMsRUFBRSxBQUFBLFdBQVcsQ0FBQztFQUM3RSxPQUFPLEVBQUUsZUFBZ0IsR0FDMUI7O0FBRUQsQUFBTyxJQUFILEdBQUcsSUFBSSxDQUFDO0VBQ1YsVUFBVSxFQUFFLElBQUssR0FDbEIiLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 */
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieG9zTmdMaWIuY3NzIiwic291cmNlcyI6WyJtYWluLnNjc3MiLCJhbmltYXRpb25zLnNjc3MiLCIuLi8uLi8uLi8uLi9zdHlsZS9zYXNzL2Jvb3RzdHJhcC9ib290c3RyYXAvX3ZhcmlhYmxlcy5zY3NzIiwiLi4vdWlfY29tcG9uZW50cy9kdW1iQ29tcG9uZW50cy90YWJsZS90YWJsZS5zY3NzIiwiLi4vdWlfY29tcG9uZW50cy9kdW1iQ29tcG9uZW50cy9hbGVydC9hbGVydC5zY3NzIiwiLi4vdWlfY29tcG9uZW50cy9kdW1iQ29tcG9uZW50cy92YWxpZGF0aW9uL3ZhbGlkYXRpb24uc2NzcyIsIi4uL3VpX2NvbXBvbmVudHMvc21hcnRDb21wb25lbnRzL3NtYXJ0VGFibGUvc21hcnRUYWJsZS5zY3NzIl0sInNvdXJjZXNDb250ZW50IjpbIkBpbXBvcnQgJy4vYW5pbWF0aW9ucy5zY3NzJztcbkBpbXBvcnQgJy4uLy4uLy4uLy4uLy4uL3ZpZXdzL3N0eWxlL3Nhc3MvYm9vdHN0cmFwL2Jvb3RzdHJhcC9fdmFyaWFibGVzLnNjc3MnO1xuXG5AaW1wb3J0ICcuLi91aV9jb21wb25lbnRzL2R1bWJDb21wb25lbnRzL3RhYmxlL3RhYmxlLnNjc3MnO1xuQGltcG9ydCAnLi4vdWlfY29tcG9uZW50cy9kdW1iQ29tcG9uZW50cy9hbGVydC9hbGVydC5zY3NzJztcbkBpbXBvcnQgJy4uL3VpX2NvbXBvbmVudHMvZHVtYkNvbXBvbmVudHMvdmFsaWRhdGlvbi92YWxpZGF0aW9uLnNjc3MnO1xuXG5AaW1wb3J0ICcuLi91aV9jb21wb25lbnRzL3NtYXJ0Q29tcG9uZW50cy9zbWFydFRhYmxlL3NtYXJ0VGFibGUuc2Nzcyc7XG5cbltuZ1xcOmNsb2FrXSwgW25nLWNsb2FrXSwgW2RhdGEtbmctY2xvYWtdLCBbeC1uZy1jbG9ha10sIC5uZy1jbG9haywgLngtbmctY2xvYWsge1xuICBkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7XG59XG5cbi5yb3cgKyAucm93IHtcbiAgLyogVE9ETyBtb3ZlIGluIHhvcy5zY3NzKi8gXG4gIG1hcmdpbi10b3A6ICRmb3JtLWdyb3VwLW1hcmdpbi1ib3R0b207XG59IiwiQGtleWZyYW1lcyBzbGlkZUluUmlnaHQge1xuICBmcm9tIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDEwMCUsIDAsIDApO1xuICAgIHZpc2liaWxpdHk6IHZpc2libGU7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiB0cmFuc2xhdGUzZCgwLCAwLCAwKTtcbiAgfVxufVxuXG5Aa2V5ZnJhbWVzIHNsaWRlT3V0UmlnaHQge1xuICBmcm9tIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDAsIDAsIDApO1xuICB9XG5cbiAgdG8ge1xuICAgIHZpc2liaWxpdHk6IGhpZGRlbjtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDEwMCUsIDAsIDApO1xuICB9XG59XG5cbkBrZXlmcmFtZXMgZmFkZUluVXAge1xuICBmcm9tIHtcbiAgICBvcGFjaXR5OiAwO1xuICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlM2QoMCwgMTAwJSwgMCk7XG4gIH1cblxuICB0byB7XG4gICAgb3BhY2l0eTogMTtcbiAgICB0cmFuc2Zvcm06IG5vbmU7XG4gIH1cbn1cblxuQGtleWZyYW1lcyBmYWRlT3V0RG93biB7XG4gIGZyb20ge1xuICAgIG9wYWNpdHk6IDE7XG4gIH1cblxuICB0byB7XG4gICAgb3BhY2l0eTogMDtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZTNkKDAsIDEwMCUsIDApO1xuICB9XG59IiwiJGJvb3RzdHJhcC1zYXNzLWFzc2V0LWhlbHBlcjogZmFsc2UgIWRlZmF1bHQ7XG4vL1xuLy8gVmFyaWFibGVzXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5cbi8vPT0gQ29sb3JzXG4vL1xuLy8jIyBHcmF5IGFuZCBicmFuZCBjb2xvcnMgZm9yIHVzZSBhY3Jvc3MgQm9vdHN0cmFwLlxuXG4kZ3JheS1iYXNlOiAgICAgICAgICAgICAgIzAwMCAhZGVmYXVsdDtcbiRncmF5LWRhcmtlcjogICAgICAgICAgICBsaWdodGVuKCRncmF5LWJhc2UsIDEzLjUlKSAhZGVmYXVsdDsgLy8gIzIyMlxuJGdyYXktZGFyazogICAgICAgICAgICAgIGxpZ2h0ZW4oJGdyYXktYmFzZSwgMjAlKSAhZGVmYXVsdDsgICAvLyAjMzMzXG4kZ3JheTogICAgICAgICAgICAgICAgICAgbGlnaHRlbigkZ3JheS1iYXNlLCAzMy41JSkgIWRlZmF1bHQ7IC8vICM1NTVcbiRncmF5LWxpZ2h0OiAgICAgICAgICAgICBsaWdodGVuKCRncmF5LWJhc2UsIDQ2LjclKSAhZGVmYXVsdDsgLy8gIzc3N1xuJGdyYXktbGlnaHRlcjogICAgICAgICAgIGxpZ2h0ZW4oJGdyYXktYmFzZSwgOTMuNSUpICFkZWZhdWx0OyAvLyAjZWVlXG5cbiRicmFuZC1wcmltYXJ5OiAgICAgICAgIGRhcmtlbigjNDI4YmNhLCA2LjUlKSAhZGVmYXVsdDsgLy8gIzMzN2FiN1xuJGJyYW5kLXN1Y2Nlc3M6ICAgICAgICAgIzVjYjg1YyAhZGVmYXVsdDtcbiRicmFuZC1pbmZvOiAgICAgICAgICAgICM1YmMwZGUgIWRlZmF1bHQ7XG4kYnJhbmQtd2FybmluZzogICAgICAgICAjZjBhZDRlICFkZWZhdWx0O1xuJGJyYW5kLWRhbmdlcjogICAgICAgICAgI2Q5NTM0ZiAhZGVmYXVsdDtcblxuXG4vLz09IFNjYWZmb2xkaW5nXG4vL1xuLy8jIyBTZXR0aW5ncyBmb3Igc29tZSBvZiB0aGUgbW9zdCBnbG9iYWwgc3R5bGVzLlxuXG4vLyoqIEJhY2tncm91bmQgY29sb3IgZm9yIGA8Ym9keT5gLlxuJGJvZHktYmc6ICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogR2xvYmFsIHRleHQgY29sb3Igb24gYDxib2R5PmAuXG4kdGV4dC1jb2xvcjogICAgICAgICAgICAkZ3JheS1kYXJrICFkZWZhdWx0O1xuXG4vLyoqIEdsb2JhbCB0ZXh0dWFsIGxpbmsgY29sb3IuXG4kbGluay1jb2xvcjogICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbi8vKiogTGluayBob3ZlciBjb2xvciBzZXQgdmlhIGBkYXJrZW4oKWAgZnVuY3Rpb24uXG4kbGluay1ob3Zlci1jb2xvcjogICAgICBkYXJrZW4oJGxpbmstY29sb3IsIDE1JSkgIWRlZmF1bHQ7XG4vLyoqIExpbmsgaG92ZXIgZGVjb3JhdGlvbi5cbiRsaW5rLWhvdmVyLWRlY29yYXRpb246IHVuZGVybGluZSAhZGVmYXVsdDtcblxuXG4vLz09IFR5cG9ncmFwaHlcbi8vXG4vLyMjIEZvbnQsIGxpbmUtaGVpZ2h0LCBhbmQgY29sb3IgZm9yIGJvZHkgdGV4dCwgaGVhZGluZ3MsIGFuZCBtb3JlLlxuXG4kZm9udC1mYW1pbHktc2Fucy1zZXJpZjogIFwiSGVsdmV0aWNhIE5ldWVcIiwgSGVsdmV0aWNhLCBBcmlhbCwgc2Fucy1zZXJpZiAhZGVmYXVsdDtcbiRmb250LWZhbWlseS1zZXJpZjogICAgICAgR2VvcmdpYSwgXCJUaW1lcyBOZXcgUm9tYW5cIiwgVGltZXMsIHNlcmlmICFkZWZhdWx0O1xuLy8qKiBEZWZhdWx0IG1vbm9zcGFjZSBmb250cyBmb3IgYDxjb2RlPmAsIGA8a2JkPmAsIGFuZCBgPHByZT5gLlxuJGZvbnQtZmFtaWx5LW1vbm9zcGFjZTogICBNZW5sbywgTW9uYWNvLCBDb25zb2xhcywgXCJDb3VyaWVyIE5ld1wiLCBtb25vc3BhY2UgIWRlZmF1bHQ7XG4kZm9udC1mYW1pbHktYmFzZTogICAgICAgICRmb250LWZhbWlseS1zYW5zLXNlcmlmICFkZWZhdWx0O1xuXG4kZm9udC1zaXplLWJhc2U6ICAgICAgICAgIDE0cHggIWRlZmF1bHQ7XG4kZm9udC1zaXplLWxhcmdlOiAgICAgICAgIGNlaWwoKCRmb250LXNpemUtYmFzZSAqIDEuMjUpKSAhZGVmYXVsdDsgLy8gfjE4cHhcbiRmb250LXNpemUtc21hbGw6ICAgICAgICAgY2VpbCgoJGZvbnQtc2l6ZS1iYXNlICogMC44NSkpICFkZWZhdWx0OyAvLyB+MTJweFxuXG4kZm9udC1zaXplLWgxOiAgICAgICAgICAgIGZsb29yKCgkZm9udC1zaXplLWJhc2UgKiAyLjYpKSAhZGVmYXVsdDsgLy8gfjM2cHhcbiRmb250LXNpemUtaDI6ICAgICAgICAgICAgZmxvb3IoKCRmb250LXNpemUtYmFzZSAqIDIuMTUpKSAhZGVmYXVsdDsgLy8gfjMwcHhcbiRmb250LXNpemUtaDM6ICAgICAgICAgICAgY2VpbCgoJGZvbnQtc2l6ZS1iYXNlICogMS43KSkgIWRlZmF1bHQ7IC8vIH4yNHB4XG4kZm9udC1zaXplLWg0OiAgICAgICAgICAgIGNlaWwoKCRmb250LXNpemUtYmFzZSAqIDEuMjUpKSAhZGVmYXVsdDsgLy8gfjE4cHhcbiRmb250LXNpemUtaDU6ICAgICAgICAgICAgJGZvbnQtc2l6ZS1iYXNlICFkZWZhdWx0O1xuJGZvbnQtc2l6ZS1oNjogICAgICAgICAgICBjZWlsKCgkZm9udC1zaXplLWJhc2UgKiAwLjg1KSkgIWRlZmF1bHQ7IC8vIH4xMnB4XG5cbi8vKiogVW5pdC1sZXNzIGBsaW5lLWhlaWdodGAgZm9yIHVzZSBpbiBjb21wb25lbnRzIGxpa2UgYnV0dG9ucy5cbiRsaW5lLWhlaWdodC1iYXNlOiAgICAgICAgMS40Mjg1NzE0MjkgIWRlZmF1bHQ7IC8vIDIwLzE0XG4vLyoqIENvbXB1dGVkIFwibGluZS1oZWlnaHRcIiAoYGZvbnQtc2l6ZWAgKiBgbGluZS1oZWlnaHRgKSBmb3IgdXNlIHdpdGggYG1hcmdpbmAsIGBwYWRkaW5nYCwgZXRjLlxuJGxpbmUtaGVpZ2h0LWNvbXB1dGVkOiAgICBmbG9vcigoJGZvbnQtc2l6ZS1iYXNlICogJGxpbmUtaGVpZ2h0LWJhc2UpKSAhZGVmYXVsdDsgLy8gfjIwcHhcblxuLy8qKiBCeSBkZWZhdWx0LCB0aGlzIGluaGVyaXRzIGZyb20gdGhlIGA8Ym9keT5gLlxuJGhlYWRpbmdzLWZvbnQtZmFtaWx5OiAgICBpbmhlcml0ICFkZWZhdWx0O1xuJGhlYWRpbmdzLWZvbnQtd2VpZ2h0OiAgICA1MDAgIWRlZmF1bHQ7XG4kaGVhZGluZ3MtbGluZS1oZWlnaHQ6ICAgIDEuMSAhZGVmYXVsdDtcbiRoZWFkaW5ncy1jb2xvcjogICAgICAgICAgaW5oZXJpdCAhZGVmYXVsdDtcblxuXG4vLz09IEljb25vZ3JhcGh5XG4vL1xuLy8jIyBTcGVjaWZ5IGN1c3RvbSBsb2NhdGlvbiBhbmQgZmlsZW5hbWUgb2YgdGhlIGluY2x1ZGVkIEdseXBoaWNvbnMgaWNvbiBmb250LiBVc2VmdWwgZm9yIHRob3NlIGluY2x1ZGluZyBCb290c3RyYXAgdmlhIEJvd2VyLlxuXG4vLyoqIExvYWQgZm9udHMgZnJvbSB0aGlzIGRpcmVjdG9yeS5cblxuLy8gW2NvbnZlcnRlcl0gSWYgJGJvb3RzdHJhcC1zYXNzLWFzc2V0LWhlbHBlciBpZiB1c2VkLCBwcm92aWRlIHBhdGggcmVsYXRpdmUgdG8gdGhlIGFzc2V0cyBsb2FkIHBhdGguXG4vLyBbY29udmVydGVyXSBUaGlzIGlzIGJlY2F1c2Ugc29tZSBhc3NldCBoZWxwZXJzLCBzdWNoIGFzIFNwcm9ja2V0cywgZG8gbm90IHdvcmsgd2l0aCBmaWxlLXJlbGF0aXZlIHBhdGhzLlxuJGljb24tZm9udC1wYXRoOiBpZigkYm9vdHN0cmFwLXNhc3MtYXNzZXQtaGVscGVyLCBcImJvb3RzdHJhcC9cIiwgXCIuLi9mb250cy9ib290c3RyYXAvXCIpICFkZWZhdWx0O1xuXG4vLyoqIEZpbGUgbmFtZSBmb3IgYWxsIGZvbnQgZmlsZXMuXG4kaWNvbi1mb250LW5hbWU6ICAgICAgICAgIFwiZ2x5cGhpY29ucy1oYWxmbGluZ3MtcmVndWxhclwiICFkZWZhdWx0O1xuLy8qKiBFbGVtZW50IElEIHdpdGhpbiBTVkcgaWNvbiBmaWxlLlxuJGljb24tZm9udC1zdmctaWQ6ICAgICAgICBcImdseXBoaWNvbnNfaGFsZmxpbmdzcmVndWxhclwiICFkZWZhdWx0O1xuXG5cbi8vPT0gQ29tcG9uZW50c1xuLy9cbi8vIyMgRGVmaW5lIGNvbW1vbiBwYWRkaW5nIGFuZCBib3JkZXIgcmFkaXVzIHNpemVzIGFuZCBtb3JlLiBWYWx1ZXMgYmFzZWQgb24gMTRweCB0ZXh0IGFuZCAxLjQyOCBsaW5lLWhlaWdodCAofjIwcHggdG8gc3RhcnQpLlxuXG4kcGFkZGluZy1iYXNlLXZlcnRpY2FsOiAgICAgNnB4ICFkZWZhdWx0O1xuJHBhZGRpbmctYmFzZS1ob3Jpem9udGFsOiAgIDEycHggIWRlZmF1bHQ7XG5cbiRwYWRkaW5nLWxhcmdlLXZlcnRpY2FsOiAgICAxMHB4ICFkZWZhdWx0O1xuJHBhZGRpbmctbGFyZ2UtaG9yaXpvbnRhbDogIDE2cHggIWRlZmF1bHQ7XG5cbiRwYWRkaW5nLXNtYWxsLXZlcnRpY2FsOiAgICA1cHggIWRlZmF1bHQ7XG4kcGFkZGluZy1zbWFsbC1ob3Jpem9udGFsOiAgMTBweCAhZGVmYXVsdDtcblxuJHBhZGRpbmcteHMtdmVydGljYWw6ICAgICAgIDFweCAhZGVmYXVsdDtcbiRwYWRkaW5nLXhzLWhvcml6b250YWw6ICAgICA1cHggIWRlZmF1bHQ7XG5cbiRsaW5lLWhlaWdodC1sYXJnZTogICAgICAgICAxLjMzMzMzMzMgIWRlZmF1bHQ7IC8vIGV4dHJhIGRlY2ltYWxzIGZvciBXaW4gOC4xIENocm9tZVxuJGxpbmUtaGVpZ2h0LXNtYWxsOiAgICAgICAgIDEuNSAhZGVmYXVsdDtcblxuJGJvcmRlci1yYWRpdXMtYmFzZTogICAgICAgIDRweCAhZGVmYXVsdDtcbiRib3JkZXItcmFkaXVzLWxhcmdlOiAgICAgICA2cHggIWRlZmF1bHQ7XG4kYm9yZGVyLXJhZGl1cy1zbWFsbDogICAgICAgM3B4ICFkZWZhdWx0O1xuXG4vLyoqIEdsb2JhbCBjb2xvciBmb3IgYWN0aXZlIGl0ZW1zIChlLmcuLCBuYXZzIG9yIGRyb3Bkb3ducykuXG4kY29tcG9uZW50LWFjdGl2ZS1jb2xvcjogICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogR2xvYmFsIGJhY2tncm91bmQgY29sb3IgZm9yIGFjdGl2ZSBpdGVtcyAoZS5nLiwgbmF2cyBvciBkcm9wZG93bnMpLlxuJGNvbXBvbmVudC1hY3RpdmUtYmc6ICAgICAgICRicmFuZC1wcmltYXJ5ICFkZWZhdWx0O1xuXG4vLyoqIFdpZHRoIG9mIHRoZSBgYm9yZGVyYCBmb3IgZ2VuZXJhdGluZyBjYXJldHMgdGhhdCBpbmRpY2F0b3IgZHJvcGRvd25zLlxuJGNhcmV0LXdpZHRoLWJhc2U6ICAgICAgICAgIDRweCAhZGVmYXVsdDtcbi8vKiogQ2FyZXRzIGluY3JlYXNlIHNsaWdodGx5IGluIHNpemUgZm9yIGxhcmdlciBjb21wb25lbnRzLlxuJGNhcmV0LXdpZHRoLWxhcmdlOiAgICAgICAgIDVweCAhZGVmYXVsdDtcblxuXG4vLz09IFRhYmxlc1xuLy9cbi8vIyMgQ3VzdG9taXplcyB0aGUgYC50YWJsZWAgY29tcG9uZW50IHdpdGggYmFzaWMgdmFsdWVzLCBlYWNoIHVzZWQgYWNyb3NzIGFsbCB0YWJsZSB2YXJpYXRpb25zLlxuXG4vLyoqIFBhZGRpbmcgZm9yIGA8dGg+YHMgYW5kIGA8dGQ+YHMuXG4kdGFibGUtY2VsbC1wYWRkaW5nOiAgICAgICAgICAgIDhweCAhZGVmYXVsdDtcbi8vKiogUGFkZGluZyBmb3IgY2VsbHMgaW4gYC50YWJsZS1jb25kZW5zZWRgLlxuJHRhYmxlLWNvbmRlbnNlZC1jZWxsLXBhZGRpbmc6ICA1cHggIWRlZmF1bHQ7XG5cbi8vKiogRGVmYXVsdCBiYWNrZ3JvdW5kIGNvbG9yIHVzZWQgZm9yIGFsbCB0YWJsZXMuXG4kdGFibGUtYmc6ICAgICAgICAgICAgICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIHVzZWQgZm9yIGAudGFibGUtc3RyaXBlZGAuXG4kdGFibGUtYmctYWNjZW50OiAgICAgICAgICAgICAgICNmOWY5ZjkgIWRlZmF1bHQ7XG4vLyoqIEJhY2tncm91bmQgY29sb3IgdXNlZCBmb3IgYC50YWJsZS1ob3ZlcmAuXG4kdGFibGUtYmctaG92ZXI6ICAgICAgICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4kdGFibGUtYmctYWN0aXZlOiAgICAgICAgICAgICAgICR0YWJsZS1iZy1ob3ZlciAhZGVmYXVsdDtcblxuLy8qKiBCb3JkZXIgY29sb3IgZm9yIHRhYmxlIGFuZCBjZWxsIGJvcmRlcnMuXG4kdGFibGUtYm9yZGVyLWNvbG9yOiAgICAgICAgICAgICNkZGQgIWRlZmF1bHQ7XG5cblxuLy89PSBCdXR0b25zXG4vL1xuLy8jIyBGb3IgZWFjaCBvZiBCb290c3RyYXAncyBidXR0b25zLCBkZWZpbmUgdGV4dCwgYmFja2dyb3VuZCBhbmQgYm9yZGVyIGNvbG9yLlxuXG4kYnRuLWZvbnQtd2VpZ2h0OiAgICAgICAgICAgICAgICBub3JtYWwgIWRlZmF1bHQ7XG5cbiRidG4tZGVmYXVsdC1jb2xvcjogICAgICAgICAgICAgICMzMzMgIWRlZmF1bHQ7XG4kYnRuLWRlZmF1bHQtYmc6ICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJGJ0bi1kZWZhdWx0LWJvcmRlcjogICAgICAgICAgICAgI2NjYyAhZGVmYXVsdDtcblxuJGJ0bi1wcmltYXJ5LWNvbG9yOiAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRidG4tcHJpbWFyeS1iZzogICAgICAgICAgICAgICAgICRicmFuZC1wcmltYXJ5ICFkZWZhdWx0O1xuJGJ0bi1wcmltYXJ5LWJvcmRlcjogICAgICAgICAgICAgZGFya2VuKCRidG4tcHJpbWFyeS1iZywgNSUpICFkZWZhdWx0O1xuXG4kYnRuLXN1Y2Nlc3MtY29sb3I6ICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJGJ0bi1zdWNjZXNzLWJnOiAgICAgICAgICAgICAgICAgJGJyYW5kLXN1Y2Nlc3MgIWRlZmF1bHQ7XG4kYnRuLXN1Y2Nlc3MtYm9yZGVyOiAgICAgICAgICAgICBkYXJrZW4oJGJ0bi1zdWNjZXNzLWJnLCA1JSkgIWRlZmF1bHQ7XG5cbiRidG4taW5mby1jb2xvcjogICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kYnRuLWluZm8tYmc6ICAgICAgICAgICAgICAgICAgICAkYnJhbmQtaW5mbyAhZGVmYXVsdDtcbiRidG4taW5mby1ib3JkZXI6ICAgICAgICAgICAgICAgIGRhcmtlbigkYnRuLWluZm8tYmcsIDUlKSAhZGVmYXVsdDtcblxuJGJ0bi13YXJuaW5nLWNvbG9yOiAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRidG4td2FybmluZy1iZzogICAgICAgICAgICAgICAgICRicmFuZC13YXJuaW5nICFkZWZhdWx0O1xuJGJ0bi13YXJuaW5nLWJvcmRlcjogICAgICAgICAgICAgZGFya2VuKCRidG4td2FybmluZy1iZywgNSUpICFkZWZhdWx0O1xuXG4kYnRuLWRhbmdlci1jb2xvcjogICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuJGJ0bi1kYW5nZXItYmc6ICAgICAgICAgICAgICAgICAgJGJyYW5kLWRhbmdlciAhZGVmYXVsdDtcbiRidG4tZGFuZ2VyLWJvcmRlcjogICAgICAgICAgICAgIGRhcmtlbigkYnRuLWRhbmdlci1iZywgNSUpICFkZWZhdWx0O1xuXG4kYnRuLWxpbmstZGlzYWJsZWQtY29sb3I6ICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcblxuLy8gQWxsb3dzIGZvciBjdXN0b21pemluZyBidXR0b24gcmFkaXVzIGluZGVwZW5kZW50bHkgZnJvbSBnbG9iYWwgYm9yZGVyIHJhZGl1c1xuJGJ0bi1ib3JkZXItcmFkaXVzLWJhc2U6ICAgICAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcbiRidG4tYm9yZGVyLXJhZGl1cy1sYXJnZTogICAgICAgICRib3JkZXItcmFkaXVzLWxhcmdlICFkZWZhdWx0O1xuJGJ0bi1ib3JkZXItcmFkaXVzLXNtYWxsOiAgICAgICAgJGJvcmRlci1yYWRpdXMtc21hbGwgIWRlZmF1bHQ7XG5cblxuLy89PSBGb3Jtc1xuLy9cbi8vIyNcblxuLy8qKiBgPGlucHV0PmAgYmFja2dyb3VuZCBjb2xvclxuJGlucHV0LWJnOiAgICAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogYDxpbnB1dCBkaXNhYmxlZD5gIGJhY2tncm91bmQgY29sb3JcbiRpbnB1dC1iZy1kaXNhYmxlZDogICAgICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG5cbi8vKiogVGV4dCBjb2xvciBmb3IgYDxpbnB1dD5gc1xuJGlucHV0LWNvbG9yOiAgICAgICAgICAgICAgICAgICAgJGdyYXkgIWRlZmF1bHQ7XG4vLyoqIGA8aW5wdXQ+YCBib3JkZXIgY29sb3JcbiRpbnB1dC1ib3JkZXI6ICAgICAgICAgICAgICAgICAgICNjY2MgIWRlZmF1bHQ7XG5cbi8vIFRPRE86IFJlbmFtZSBgJGlucHV0LWJvcmRlci1yYWRpdXNgIHRvIGAkaW5wdXQtYm9yZGVyLXJhZGl1cy1iYXNlYCBpbiB2NFxuLy8qKiBEZWZhdWx0IGAuZm9ybS1jb250cm9sYCBib3JkZXIgcmFkaXVzXG4vLyBUaGlzIGhhcyBubyBlZmZlY3Qgb24gYDxzZWxlY3Q+YHMgaW4gc29tZSBicm93c2VycywgZHVlIHRvIHRoZSBsaW1pdGVkIHN0eWxhYmlsaXR5IG9mIGA8c2VsZWN0PmBzIGluIENTUy5cbiRpbnB1dC1ib3JkZXItcmFkaXVzOiAgICAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG4vLyoqIExhcmdlIGAuZm9ybS1jb250cm9sYCBib3JkZXIgcmFkaXVzXG4kaW5wdXQtYm9yZGVyLXJhZGl1cy1sYXJnZTogICAgICAkYm9yZGVyLXJhZGl1cy1sYXJnZSAhZGVmYXVsdDtcbi8vKiogU21hbGwgYC5mb3JtLWNvbnRyb2xgIGJvcmRlciByYWRpdXNcbiRpbnB1dC1ib3JkZXItcmFkaXVzLXNtYWxsOiAgICAgICRib3JkZXItcmFkaXVzLXNtYWxsICFkZWZhdWx0O1xuXG4vLyoqIEJvcmRlciBjb2xvciBmb3IgaW5wdXRzIG9uIGZvY3VzXG4kaW5wdXQtYm9yZGVyLWZvY3VzOiAgICAgICAgICAgICAjNjZhZmU5ICFkZWZhdWx0O1xuXG4vLyoqIFBsYWNlaG9sZGVyIHRleHQgY29sb3JcbiRpbnB1dC1jb2xvci1wbGFjZWhvbGRlcjogICAgICAgICM5OTkgIWRlZmF1bHQ7XG5cbi8vKiogRGVmYXVsdCBgLmZvcm0tY29udHJvbGAgaGVpZ2h0XG4kaW5wdXQtaGVpZ2h0LWJhc2U6ICAgICAgICAgICAgICAoJGxpbmUtaGVpZ2h0LWNvbXB1dGVkICsgKCRwYWRkaW5nLWJhc2UtdmVydGljYWwgKiAyKSArIDIpICFkZWZhdWx0O1xuLy8qKiBMYXJnZSBgLmZvcm0tY29udHJvbGAgaGVpZ2h0XG4kaW5wdXQtaGVpZ2h0LWxhcmdlOiAgICAgICAgICAgICAoY2VpbCgkZm9udC1zaXplLWxhcmdlICogJGxpbmUtaGVpZ2h0LWxhcmdlKSArICgkcGFkZGluZy1sYXJnZS12ZXJ0aWNhbCAqIDIpICsgMikgIWRlZmF1bHQ7XG4vLyoqIFNtYWxsIGAuZm9ybS1jb250cm9sYCBoZWlnaHRcbiRpbnB1dC1oZWlnaHQtc21hbGw6ICAgICAgICAgICAgIChmbG9vcigkZm9udC1zaXplLXNtYWxsICogJGxpbmUtaGVpZ2h0LXNtYWxsKSArICgkcGFkZGluZy1zbWFsbC12ZXJ0aWNhbCAqIDIpICsgMikgIWRlZmF1bHQ7XG5cbi8vKiogYC5mb3JtLWdyb3VwYCBtYXJnaW5cbiRmb3JtLWdyb3VwLW1hcmdpbi1ib3R0b206ICAgICAgIDE1cHggIWRlZmF1bHQ7XG5cbiRsZWdlbmQtY29sb3I6ICAgICAgICAgICAgICAgICAgICRncmF5LWRhcmsgIWRlZmF1bHQ7XG4kbGVnZW5kLWJvcmRlci1jb2xvcjogICAgICAgICAgICAjZTVlNWU1ICFkZWZhdWx0O1xuXG4vLyoqIEJhY2tncm91bmQgY29sb3IgZm9yIHRleHR1YWwgaW5wdXQgYWRkb25zXG4kaW5wdXQtZ3JvdXAtYWRkb24tYmc6ICAgICAgICAgICAkZ3JheS1saWdodGVyICFkZWZhdWx0O1xuLy8qKiBCb3JkZXIgY29sb3IgZm9yIHRleHR1YWwgaW5wdXQgYWRkb25zXG4kaW5wdXQtZ3JvdXAtYWRkb24tYm9yZGVyLWNvbG9yOiAkaW5wdXQtYm9yZGVyICFkZWZhdWx0O1xuXG4vLyoqIERpc2FibGVkIGN1cnNvciBmb3IgZm9ybSBjb250cm9scyBhbmQgYnV0dG9ucy5cbiRjdXJzb3ItZGlzYWJsZWQ6ICAgICAgICAgICAgICAgIG5vdC1hbGxvd2VkICFkZWZhdWx0O1xuXG5cbi8vPT0gRHJvcGRvd25zXG4vL1xuLy8jIyBEcm9wZG93biBtZW51IGNvbnRhaW5lciBhbmQgY29udGVudHMuXG5cbi8vKiogQmFja2dyb3VuZCBmb3IgdGhlIGRyb3Bkb3duIG1lbnUuXG4kZHJvcGRvd24tYmc6ICAgICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBEcm9wZG93biBtZW51IGBib3JkZXItY29sb3JgLlxuJGRyb3Bkb3duLWJvcmRlcjogICAgICAgICAgICAgICAgcmdiYSgwLDAsMCwuMTUpICFkZWZhdWx0O1xuLy8qKiBEcm9wZG93biBtZW51IGBib3JkZXItY29sb3JgICoqZm9yIElFOCoqLlxuJGRyb3Bkb3duLWZhbGxiYWNrLWJvcmRlcjogICAgICAgI2NjYyAhZGVmYXVsdDtcbi8vKiogRGl2aWRlciBjb2xvciBmb3IgYmV0d2VlbiBkcm9wZG93biBpdGVtcy5cbiRkcm9wZG93bi1kaXZpZGVyLWJnOiAgICAgICAgICAgICNlNWU1ZTUgIWRlZmF1bHQ7XG5cbi8vKiogRHJvcGRvd24gbGluayB0ZXh0IGNvbG9yLlxuJGRyb3Bkb3duLWxpbmstY29sb3I6ICAgICAgICAgICAgJGdyYXktZGFyayAhZGVmYXVsdDtcbi8vKiogSG92ZXIgY29sb3IgZm9yIGRyb3Bkb3duIGxpbmtzLlxuJGRyb3Bkb3duLWxpbmstaG92ZXItY29sb3I6ICAgICAgZGFya2VuKCRncmF5LWRhcmssIDUlKSAhZGVmYXVsdDtcbi8vKiogSG92ZXIgYmFja2dyb3VuZCBmb3IgZHJvcGRvd24gbGlua3MuXG4kZHJvcGRvd24tbGluay1ob3Zlci1iZzogICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuXG4vLyoqIEFjdGl2ZSBkcm9wZG93biBtZW51IGl0ZW0gdGV4dCBjb2xvci5cbiRkcm9wZG93bi1saW5rLWFjdGl2ZS1jb2xvcjogICAgICRjb21wb25lbnQtYWN0aXZlLWNvbG9yICFkZWZhdWx0O1xuLy8qKiBBY3RpdmUgZHJvcGRvd24gbWVudSBpdGVtIGJhY2tncm91bmQgY29sb3IuXG4kZHJvcGRvd24tbGluay1hY3RpdmUtYmc6ICAgICAgICAkY29tcG9uZW50LWFjdGl2ZS1iZyAhZGVmYXVsdDtcblxuLy8qKiBEaXNhYmxlZCBkcm9wZG93biBtZW51IGl0ZW0gYmFja2dyb3VuZCBjb2xvci5cbiRkcm9wZG93bi1saW5rLWRpc2FibGVkLWNvbG9yOiAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuXG4vLyoqIFRleHQgY29sb3IgZm9yIGhlYWRlcnMgd2l0aGluIGRyb3Bkb3duIG1lbnVzLlxuJGRyb3Bkb3duLWhlYWRlci1jb2xvcjogICAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG5cbi8vKiogRGVwcmVjYXRlZCBgJGRyb3Bkb3duLWNhcmV0LWNvbG9yYCBhcyBvZiB2My4xLjBcbiRkcm9wZG93bi1jYXJldC1jb2xvcjogICAgICAgICAgICMwMDAgIWRlZmF1bHQ7XG5cblxuLy8tLSBaLWluZGV4IG1hc3RlciBsaXN0XG4vL1xuLy8gV2FybmluZzogQXZvaWQgY3VzdG9taXppbmcgdGhlc2UgdmFsdWVzLiBUaGV5J3JlIHVzZWQgZm9yIGEgYmlyZCdzIGV5ZSB2aWV3XG4vLyBvZiBjb21wb25lbnRzIGRlcGVuZGVudCBvbiB0aGUgei1heGlzIGFuZCBhcmUgZGVzaWduZWQgdG8gYWxsIHdvcmsgdG9nZXRoZXIuXG4vL1xuLy8gTm90ZTogVGhlc2UgdmFyaWFibGVzIGFyZSBub3QgZ2VuZXJhdGVkIGludG8gdGhlIEN1c3RvbWl6ZXIuXG5cbiR6aW5kZXgtbmF2YmFyOiAgICAgICAgICAgIDEwMDAgIWRlZmF1bHQ7XG4kemluZGV4LWRyb3Bkb3duOiAgICAgICAgICAxMDAwICFkZWZhdWx0O1xuJHppbmRleC1wb3BvdmVyOiAgICAgICAgICAgMTA2MCAhZGVmYXVsdDtcbiR6aW5kZXgtdG9vbHRpcDogICAgICAgICAgIDEwNzAgIWRlZmF1bHQ7XG4kemluZGV4LW5hdmJhci1maXhlZDogICAgICAxMDMwICFkZWZhdWx0O1xuJHppbmRleC1tb2RhbC1iYWNrZ3JvdW5kOiAgMTA0MCAhZGVmYXVsdDtcbiR6aW5kZXgtbW9kYWw6ICAgICAgICAgICAgIDEwNTAgIWRlZmF1bHQ7XG5cblxuLy89PSBNZWRpYSBxdWVyaWVzIGJyZWFrcG9pbnRzXG4vL1xuLy8jIyBEZWZpbmUgdGhlIGJyZWFrcG9pbnRzIGF0IHdoaWNoIHlvdXIgbGF5b3V0IHdpbGwgY2hhbmdlLCBhZGFwdGluZyB0byBkaWZmZXJlbnQgc2NyZWVuIHNpemVzLlxuXG4vLyBFeHRyYSBzbWFsbCBzY3JlZW4gLyBwaG9uZVxuLy8qKiBEZXByZWNhdGVkIGAkc2NyZWVuLXhzYCBhcyBvZiB2My4wLjFcbiRzY3JlZW4teHM6ICAgICAgICAgICAgICAgICAgNDgwcHggIWRlZmF1bHQ7XG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4teHMtbWluYCBhcyBvZiB2My4yLjBcbiRzY3JlZW4teHMtbWluOiAgICAgICAgICAgICAgJHNjcmVlbi14cyAhZGVmYXVsdDtcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi1waG9uZWAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLXBob25lOiAgICAgICAgICAgICAgICRzY3JlZW4teHMtbWluICFkZWZhdWx0O1xuXG4vLyBTbWFsbCBzY3JlZW4gLyB0YWJsZXRcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi1zbWAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLXNtOiAgICAgICAgICAgICAgICAgIDc2OHB4ICFkZWZhdWx0O1xuJHNjcmVlbi1zbS1taW46ICAgICAgICAgICAgICAkc2NyZWVuLXNtICFkZWZhdWx0O1xuLy8qKiBEZXByZWNhdGVkIGAkc2NyZWVuLXRhYmxldGAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLXRhYmxldDogICAgICAgICAgICAgICRzY3JlZW4tc20tbWluICFkZWZhdWx0O1xuXG4vLyBNZWRpdW0gc2NyZWVuIC8gZGVza3RvcFxuLy8qKiBEZXByZWNhdGVkIGAkc2NyZWVuLW1kYCBhcyBvZiB2My4wLjFcbiRzY3JlZW4tbWQ6ICAgICAgICAgICAgICAgICAgOTkycHggIWRlZmF1bHQ7XG4kc2NyZWVuLW1kLW1pbjogICAgICAgICAgICAgICRzY3JlZW4tbWQgIWRlZmF1bHQ7XG4vLyoqIERlcHJlY2F0ZWQgYCRzY3JlZW4tZGVza3RvcGAgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLWRlc2t0b3A6ICAgICAgICAgICAgICRzY3JlZW4tbWQtbWluICFkZWZhdWx0O1xuXG4vLyBMYXJnZSBzY3JlZW4gLyB3aWRlIGRlc2t0b3Bcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi1sZ2AgYXMgb2YgdjMuMC4xXG4kc2NyZWVuLWxnOiAgICAgICAgICAgICAgICAgIDEyMDBweCAhZGVmYXVsdDtcbiRzY3JlZW4tbGctbWluOiAgICAgICAgICAgICAgJHNjcmVlbi1sZyAhZGVmYXVsdDtcbi8vKiogRGVwcmVjYXRlZCBgJHNjcmVlbi1sZy1kZXNrdG9wYCBhcyBvZiB2My4wLjFcbiRzY3JlZW4tbGctZGVza3RvcDogICAgICAgICAgJHNjcmVlbi1sZy1taW4gIWRlZmF1bHQ7XG5cbi8vIFNvIG1lZGlhIHF1ZXJpZXMgZG9uJ3Qgb3ZlcmxhcCB3aGVuIHJlcXVpcmVkLCBwcm92aWRlIGEgbWF4aW11bVxuJHNjcmVlbi14cy1tYXg6ICAgICAgICAgICAgICAoJHNjcmVlbi1zbS1taW4gLSAxKSAhZGVmYXVsdDtcbiRzY3JlZW4tc20tbWF4OiAgICAgICAgICAgICAgKCRzY3JlZW4tbWQtbWluIC0gMSkgIWRlZmF1bHQ7XG4kc2NyZWVuLW1kLW1heDogICAgICAgICAgICAgICgkc2NyZWVuLWxnLW1pbiAtIDEpICFkZWZhdWx0O1xuXG5cbi8vPT0gR3JpZCBzeXN0ZW1cbi8vXG4vLyMjIERlZmluZSB5b3VyIGN1c3RvbSByZXNwb25zaXZlIGdyaWQuXG5cbi8vKiogTnVtYmVyIG9mIGNvbHVtbnMgaW4gdGhlIGdyaWQuXG4kZ3JpZC1jb2x1bW5zOiAgICAgICAgICAgICAgMTIgIWRlZmF1bHQ7XG4vLyoqIFBhZGRpbmcgYmV0d2VlbiBjb2x1bW5zLiBHZXRzIGRpdmlkZWQgaW4gaGFsZiBmb3IgdGhlIGxlZnQgYW5kIHJpZ2h0LlxuJGdyaWQtZ3V0dGVyLXdpZHRoOiAgICAgICAgIDMwcHggIWRlZmF1bHQ7XG4vLyBOYXZiYXIgY29sbGFwc2Vcbi8vKiogUG9pbnQgYXQgd2hpY2ggdGhlIG5hdmJhciBiZWNvbWVzIHVuY29sbGFwc2VkLlxuJGdyaWQtZmxvYXQtYnJlYWtwb2ludDogICAgICRzY3JlZW4tc20tbWluICFkZWZhdWx0O1xuLy8qKiBQb2ludCBhdCB3aGljaCB0aGUgbmF2YmFyIGJlZ2lucyBjb2xsYXBzaW5nLlxuJGdyaWQtZmxvYXQtYnJlYWtwb2ludC1tYXg6ICgkZ3JpZC1mbG9hdC1icmVha3BvaW50IC0gMSkgIWRlZmF1bHQ7XG5cblxuLy89PSBDb250YWluZXIgc2l6ZXNcbi8vXG4vLyMjIERlZmluZSB0aGUgbWF4aW11bSB3aWR0aCBvZiBgLmNvbnRhaW5lcmAgZm9yIGRpZmZlcmVudCBzY3JlZW4gc2l6ZXMuXG5cbi8vIFNtYWxsIHNjcmVlbiAvIHRhYmxldFxuJGNvbnRhaW5lci10YWJsZXQ6ICAgICAgICAgICAgICg3MjBweCArICRncmlkLWd1dHRlci13aWR0aCkgIWRlZmF1bHQ7XG4vLyoqIEZvciBgJHNjcmVlbi1zbS1taW5gIGFuZCB1cC5cbiRjb250YWluZXItc206ICAgICAgICAgICAgICAgICAkY29udGFpbmVyLXRhYmxldCAhZGVmYXVsdDtcblxuLy8gTWVkaXVtIHNjcmVlbiAvIGRlc2t0b3BcbiRjb250YWluZXItZGVza3RvcDogICAgICAgICAgICAoOTQwcHggKyAkZ3JpZC1ndXR0ZXItd2lkdGgpICFkZWZhdWx0O1xuLy8qKiBGb3IgYCRzY3JlZW4tbWQtbWluYCBhbmQgdXAuXG4kY29udGFpbmVyLW1kOiAgICAgICAgICAgICAgICAgJGNvbnRhaW5lci1kZXNrdG9wICFkZWZhdWx0O1xuXG4vLyBMYXJnZSBzY3JlZW4gLyB3aWRlIGRlc2t0b3BcbiRjb250YWluZXItbGFyZ2UtZGVza3RvcDogICAgICAoMTE0MHB4ICsgJGdyaWQtZ3V0dGVyLXdpZHRoKSAhZGVmYXVsdDtcbi8vKiogRm9yIGAkc2NyZWVuLWxnLW1pbmAgYW5kIHVwLlxuJGNvbnRhaW5lci1sZzogICAgICAgICAgICAgICAgICRjb250YWluZXItbGFyZ2UtZGVza3RvcCAhZGVmYXVsdDtcblxuXG4vLz09IE5hdmJhclxuLy9cbi8vIyNcblxuLy8gQmFzaWNzIG9mIGEgbmF2YmFyXG4kbmF2YmFyLWhlaWdodDogICAgICAgICAgICAgICAgICAgIDUwcHggIWRlZmF1bHQ7XG4kbmF2YmFyLW1hcmdpbi1ib3R0b206ICAgICAgICAgICAgICRsaW5lLWhlaWdodC1jb21wdXRlZCAhZGVmYXVsdDtcbiRuYXZiYXItYm9yZGVyLXJhZGl1czogICAgICAgICAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcbiRuYXZiYXItcGFkZGluZy1ob3Jpem9udGFsOiAgICAgICAgZmxvb3IoKCRncmlkLWd1dHRlci13aWR0aCAvIDIpKSAhZGVmYXVsdDtcbiRuYXZiYXItcGFkZGluZy12ZXJ0aWNhbDogICAgICAgICAgKCgkbmF2YmFyLWhlaWdodCAtICRsaW5lLWhlaWdodC1jb21wdXRlZCkgLyAyKSAhZGVmYXVsdDtcbiRuYXZiYXItY29sbGFwc2UtbWF4LWhlaWdodDogICAgICAgMzQwcHggIWRlZmF1bHQ7XG5cbiRuYXZiYXItZGVmYXVsdC1jb2xvcjogICAgICAgICAgICAgIzc3NyAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1iZzogICAgICAgICAgICAgICAgI2Y4ZjhmOCAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1ib3JkZXI6ICAgICAgICAgICAgZGFya2VuKCRuYXZiYXItZGVmYXVsdC1iZywgNi41JSkgIWRlZmF1bHQ7XG5cbi8vIE5hdmJhciBsaW5rc1xuJG5hdmJhci1kZWZhdWx0LWxpbmstY29sb3I6ICAgICAgICAgICAgICAgICM3NzcgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtbGluay1ob3Zlci1jb2xvcjogICAgICAgICAgIzMzMyAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1saW5rLWhvdmVyLWJnOiAgICAgICAgICAgICB0cmFuc3BhcmVudCAhZGVmYXVsdDtcbiRuYXZiYXItZGVmYXVsdC1saW5rLWFjdGl2ZS1jb2xvcjogICAgICAgICAjNTU1ICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWxpbmstYWN0aXZlLWJnOiAgICAgICAgICAgIGRhcmtlbigkbmF2YmFyLWRlZmF1bHQtYmcsIDYuNSUpICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWxpbmstZGlzYWJsZWQtY29sb3I6ICAgICAgICNjY2MgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtbGluay1kaXNhYmxlZC1iZzogICAgICAgICAgdHJhbnNwYXJlbnQgIWRlZmF1bHQ7XG5cbi8vIE5hdmJhciBicmFuZCBsYWJlbFxuJG5hdmJhci1kZWZhdWx0LWJyYW5kLWNvbG9yOiAgICAgICAgICAgICAgICRuYXZiYXItZGVmYXVsdC1saW5rLWNvbG9yICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LWJyYW5kLWhvdmVyLWNvbG9yOiAgICAgICAgIGRhcmtlbigkbmF2YmFyLWRlZmF1bHQtYnJhbmQtY29sb3IsIDEwJSkgIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtYnJhbmQtaG92ZXItYmc6ICAgICAgICAgICAgdHJhbnNwYXJlbnQgIWRlZmF1bHQ7XG5cbi8vIE5hdmJhciB0b2dnbGVcbiRuYXZiYXItZGVmYXVsdC10b2dnbGUtaG92ZXItYmc6ICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuJG5hdmJhci1kZWZhdWx0LXRvZ2dsZS1pY29uLWJhci1iZzogICAgICAgICM4ODggIWRlZmF1bHQ7XG4kbmF2YmFyLWRlZmF1bHQtdG9nZ2xlLWJvcmRlci1jb2xvcjogICAgICAgI2RkZCAhZGVmYXVsdDtcblxuXG4vLz09PSBJbnZlcnRlZCBuYXZiYXJcbi8vIFJlc2V0IGludmVydGVkIG5hdmJhciBiYXNpY3NcbiRuYXZiYXItaW52ZXJzZS1jb2xvcjogICAgICAgICAgICAgICAgICAgICAgbGlnaHRlbigkZ3JheS1saWdodCwgMTUlKSAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1iZzogICAgICAgICAgICAgICAgICAgICAgICAgIzIyMiAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1ib3JkZXI6ICAgICAgICAgICAgICAgICAgICAgZGFya2VuKCRuYXZiYXItaW52ZXJzZS1iZywgMTAlKSAhZGVmYXVsdDtcblxuLy8gSW52ZXJ0ZWQgbmF2YmFyIGxpbmtzXG4kbmF2YmFyLWludmVyc2UtbGluay1jb2xvcjogICAgICAgICAgICAgICAgIGxpZ2h0ZW4oJGdyYXktbGlnaHQsIDE1JSkgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtbGluay1ob3Zlci1jb2xvcjogICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kbmF2YmFyLWludmVyc2UtbGluay1ob3Zlci1iZzogICAgICAgICAgICAgIHRyYW5zcGFyZW50ICFkZWZhdWx0O1xuJG5hdmJhci1pbnZlcnNlLWxpbmstYWN0aXZlLWNvbG9yOiAgICAgICAgICAkbmF2YmFyLWludmVyc2UtbGluay1ob3Zlci1jb2xvciAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1saW5rLWFjdGl2ZS1iZzogICAgICAgICAgICAgZGFya2VuKCRuYXZiYXItaW52ZXJzZS1iZywgMTAlKSAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1saW5rLWRpc2FibGVkLWNvbG9yOiAgICAgICAgIzQ0NCAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1saW5rLWRpc2FibGVkLWJnOiAgICAgICAgICAgdHJhbnNwYXJlbnQgIWRlZmF1bHQ7XG5cbi8vIEludmVydGVkIG5hdmJhciBicmFuZCBsYWJlbFxuJG5hdmJhci1pbnZlcnNlLWJyYW5kLWNvbG9yOiAgICAgICAgICAgICAgICAkbmF2YmFyLWludmVyc2UtbGluay1jb2xvciAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1icmFuZC1ob3Zlci1jb2xvcjogICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS1icmFuZC1ob3Zlci1iZzogICAgICAgICAgICAgdHJhbnNwYXJlbnQgIWRlZmF1bHQ7XG5cbi8vIEludmVydGVkIG5hdmJhciB0b2dnbGVcbiRuYXZiYXItaW52ZXJzZS10b2dnbGUtaG92ZXItYmc6ICAgICAgICAgICAgIzMzMyAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS10b2dnbGUtaWNvbi1iYXItYmc6ICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRuYXZiYXItaW52ZXJzZS10b2dnbGUtYm9yZGVyLWNvbG9yOiAgICAgICAgIzMzMyAhZGVmYXVsdDtcblxuXG4vLz09IE5hdnNcbi8vXG4vLyMjXG5cbi8vPT09IFNoYXJlZCBuYXYgc3R5bGVzXG4kbmF2LWxpbmstcGFkZGluZzogICAgICAgICAgICAgICAgICAgICAgICAgIDEwcHggMTVweCAhZGVmYXVsdDtcbiRuYXYtbGluay1ob3Zlci1iZzogICAgICAgICAgICAgICAgICAgICAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcblxuJG5hdi1kaXNhYmxlZC1saW5rLWNvbG9yOiAgICAgICAgICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbiRuYXYtZGlzYWJsZWQtbGluay1ob3Zlci1jb2xvcjogICAgICAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG5cbi8vPT0gVGFic1xuJG5hdi10YWJzLWJvcmRlci1jb2xvcjogICAgICAgICAgICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuXG4kbmF2LXRhYnMtbGluay1ob3Zlci1ib3JkZXItY29sb3I6ICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG5cbiRuYXYtdGFicy1hY3RpdmUtbGluay1ob3Zlci1iZzogICAgICAgICAgICAgJGJvZHktYmcgIWRlZmF1bHQ7XG4kbmF2LXRhYnMtYWN0aXZlLWxpbmstaG92ZXItY29sb3I6ICAgICAgICAgICRncmF5ICFkZWZhdWx0O1xuJG5hdi10YWJzLWFjdGl2ZS1saW5rLWhvdmVyLWJvcmRlci1jb2xvcjogICAjZGRkICFkZWZhdWx0O1xuXG4kbmF2LXRhYnMtanVzdGlmaWVkLWxpbmstYm9yZGVyLWNvbG9yOiAgICAgICAgICAgICNkZGQgIWRlZmF1bHQ7XG4kbmF2LXRhYnMtanVzdGlmaWVkLWFjdGl2ZS1saW5rLWJvcmRlci1jb2xvcjogICAgICRib2R5LWJnICFkZWZhdWx0O1xuXG4vLz09IFBpbGxzXG4kbmF2LXBpbGxzLWJvcmRlci1yYWRpdXM6ICAgICAgICAgICAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG4kbmF2LXBpbGxzLWFjdGl2ZS1saW5rLWhvdmVyLWJnOiAgICAgICAgICAgICRjb21wb25lbnQtYWN0aXZlLWJnICFkZWZhdWx0O1xuJG5hdi1waWxscy1hY3RpdmUtbGluay1ob3Zlci1jb2xvcjogICAgICAgICAkY29tcG9uZW50LWFjdGl2ZS1jb2xvciAhZGVmYXVsdDtcblxuXG4vLz09IFBhZ2luYXRpb25cbi8vXG4vLyMjXG5cbiRwYWdpbmF0aW9uLWNvbG9yOiAgICAgICAgICAgICAgICAgICAgICRsaW5rLWNvbG9yICFkZWZhdWx0O1xuJHBhZ2luYXRpb24tYmc6ICAgICAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWJvcmRlcjogICAgICAgICAgICAgICAgICAgICNkZGQgIWRlZmF1bHQ7XG5cbiRwYWdpbmF0aW9uLWhvdmVyLWNvbG9yOiAgICAgICAgICAgICAgICRsaW5rLWhvdmVyLWNvbG9yICFkZWZhdWx0O1xuJHBhZ2luYXRpb24taG92ZXItYmc6ICAgICAgICAgICAgICAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWhvdmVyLWJvcmRlcjogICAgICAgICAgICAgICNkZGQgIWRlZmF1bHQ7XG5cbiRwYWdpbmF0aW9uLWFjdGl2ZS1jb2xvcjogICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1hY3RpdmUtYmc6ICAgICAgICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWFjdGl2ZS1ib3JkZXI6ICAgICAgICAgICAgICRicmFuZC1wcmltYXJ5ICFkZWZhdWx0O1xuXG4kcGFnaW5hdGlvbi1kaXNhYmxlZC1jb2xvcjogICAgICAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbiRwYWdpbmF0aW9uLWRpc2FibGVkLWJnOiAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kcGFnaW5hdGlvbi1kaXNhYmxlZC1ib3JkZXI6ICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuXG5cbi8vPT0gUGFnZXJcbi8vXG4vLyMjXG5cbiRwYWdlci1iZzogICAgICAgICAgICAgICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWJnICFkZWZhdWx0O1xuJHBhZ2VyLWJvcmRlcjogICAgICAgICAgICAgICAgICAgICAgICAgJHBhZ2luYXRpb24tYm9yZGVyICFkZWZhdWx0O1xuJHBhZ2VyLWJvcmRlci1yYWRpdXM6ICAgICAgICAgICAgICAgICAgMTVweCAhZGVmYXVsdDtcblxuJHBhZ2VyLWhvdmVyLWJnOiAgICAgICAgICAgICAgICAgICAgICAgJHBhZ2luYXRpb24taG92ZXItYmcgIWRlZmF1bHQ7XG5cbiRwYWdlci1hY3RpdmUtYmc6ICAgICAgICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWFjdGl2ZS1iZyAhZGVmYXVsdDtcbiRwYWdlci1hY3RpdmUtY29sb3I6ICAgICAgICAgICAgICAgICAgICRwYWdpbmF0aW9uLWFjdGl2ZS1jb2xvciAhZGVmYXVsdDtcblxuJHBhZ2VyLWRpc2FibGVkLWNvbG9yOiAgICAgICAgICAgICAgICAgJHBhZ2luYXRpb24tZGlzYWJsZWQtY29sb3IgIWRlZmF1bHQ7XG5cblxuLy89PSBKdW1ib3Ryb25cbi8vXG4vLyMjXG5cbiRqdW1ib3Ryb24tcGFkZGluZzogICAgICAgICAgICAgIDMwcHggIWRlZmF1bHQ7XG4kanVtYm90cm9uLWNvbG9yOiAgICAgICAgICAgICAgICBpbmhlcml0ICFkZWZhdWx0O1xuJGp1bWJvdHJvbi1iZzogICAgICAgICAgICAgICAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbiRqdW1ib3Ryb24taGVhZGluZy1jb2xvcjogICAgICAgIGluaGVyaXQgIWRlZmF1bHQ7XG4kanVtYm90cm9uLWZvbnQtc2l6ZTogICAgICAgICAgICBjZWlsKCgkZm9udC1zaXplLWJhc2UgKiAxLjUpKSAhZGVmYXVsdDtcbiRqdW1ib3Ryb24taGVhZGluZy1mb250LXNpemU6ICAgIGNlaWwoKCRmb250LXNpemUtYmFzZSAqIDQuNSkpICFkZWZhdWx0O1xuXG5cbi8vPT0gRm9ybSBzdGF0ZXMgYW5kIGFsZXJ0c1xuLy9cbi8vIyMgRGVmaW5lIGNvbG9ycyBmb3IgZm9ybSBmZWVkYmFjayBzdGF0ZXMgYW5kLCBieSBkZWZhdWx0LCBhbGVydHMuXG5cbiRzdGF0ZS1zdWNjZXNzLXRleHQ6ICAgICAgICAgICAgICMzYzc2M2QgIWRlZmF1bHQ7XG4kc3RhdGUtc3VjY2Vzcy1iZzogICAgICAgICAgICAgICAjZGZmMGQ4ICFkZWZhdWx0O1xuJHN0YXRlLXN1Y2Nlc3MtYm9yZGVyOiAgICAgICAgICAgZGFya2VuKGFkanVzdC1odWUoJHN0YXRlLXN1Y2Nlc3MtYmcsIC0xMCksIDUlKSAhZGVmYXVsdDtcblxuJHN0YXRlLWluZm8tdGV4dDogICAgICAgICAgICAgICAgIzMxNzA4ZiAhZGVmYXVsdDtcbiRzdGF0ZS1pbmZvLWJnOiAgICAgICAgICAgICAgICAgICNkOWVkZjcgIWRlZmF1bHQ7XG4kc3RhdGUtaW5mby1ib3JkZXI6ICAgICAgICAgICAgICBkYXJrZW4oYWRqdXN0LWh1ZSgkc3RhdGUtaW5mby1iZywgLTEwKSwgNyUpICFkZWZhdWx0O1xuXG4kc3RhdGUtd2FybmluZy10ZXh0OiAgICAgICAgICAgICAjOGE2ZDNiICFkZWZhdWx0O1xuJHN0YXRlLXdhcm5pbmctYmc6ICAgICAgICAgICAgICAgI2ZjZjhlMyAhZGVmYXVsdDtcbiRzdGF0ZS13YXJuaW5nLWJvcmRlcjogICAgICAgICAgIGRhcmtlbihhZGp1c3QtaHVlKCRzdGF0ZS13YXJuaW5nLWJnLCAtMTApLCA1JSkgIWRlZmF1bHQ7XG5cbiRzdGF0ZS1kYW5nZXItdGV4dDogICAgICAgICAgICAgICNhOTQ0NDIgIWRlZmF1bHQ7XG4kc3RhdGUtZGFuZ2VyLWJnOiAgICAgICAgICAgICAgICAjZjJkZWRlICFkZWZhdWx0O1xuJHN0YXRlLWRhbmdlci1ib3JkZXI6ICAgICAgICAgICAgZGFya2VuKGFkanVzdC1odWUoJHN0YXRlLWRhbmdlci1iZywgLTEwKSwgNSUpICFkZWZhdWx0O1xuXG5cbi8vPT0gVG9vbHRpcHNcbi8vXG4vLyMjXG5cbi8vKiogVG9vbHRpcCBtYXggd2lkdGhcbiR0b29sdGlwLW1heC13aWR0aDogICAgICAgICAgIDIwMHB4ICFkZWZhdWx0O1xuLy8qKiBUb29sdGlwIHRleHQgY29sb3JcbiR0b29sdGlwLWNvbG9yOiAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIFRvb2x0aXAgYmFja2dyb3VuZCBjb2xvclxuJHRvb2x0aXAtYmc6ICAgICAgICAgICAgICAgICAgIzAwMCAhZGVmYXVsdDtcbiR0b29sdGlwLW9wYWNpdHk6ICAgICAgICAgICAgIC45ICFkZWZhdWx0O1xuXG4vLyoqIFRvb2x0aXAgYXJyb3cgd2lkdGhcbiR0b29sdGlwLWFycm93LXdpZHRoOiAgICAgICAgIDVweCAhZGVmYXVsdDtcbi8vKiogVG9vbHRpcCBhcnJvdyBjb2xvclxuJHRvb2x0aXAtYXJyb3ctY29sb3I6ICAgICAgICAgJHRvb2x0aXAtYmcgIWRlZmF1bHQ7XG5cblxuLy89PSBQb3BvdmVyc1xuLy9cbi8vIyNcblxuLy8qKiBQb3BvdmVyIGJvZHkgYmFja2dyb3VuZCBjb2xvclxuJHBvcG92ZXItYmc6ICAgICAgICAgICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBQb3BvdmVyIG1heGltdW0gd2lkdGhcbiRwb3BvdmVyLW1heC13aWR0aDogICAgICAgICAgICAgICAgICAgMjc2cHggIWRlZmF1bHQ7XG4vLyoqIFBvcG92ZXIgYm9yZGVyIGNvbG9yXG4kcG9wb3Zlci1ib3JkZXItY29sb3I6ICAgICAgICAgICAgICAgIHJnYmEoMCwwLDAsLjIpICFkZWZhdWx0O1xuLy8qKiBQb3BvdmVyIGZhbGxiYWNrIGJvcmRlciBjb2xvclxuJHBvcG92ZXItZmFsbGJhY2stYm9yZGVyLWNvbG9yOiAgICAgICAjY2NjICFkZWZhdWx0O1xuXG4vLyoqIFBvcG92ZXIgdGl0bGUgYmFja2dyb3VuZCBjb2xvclxuJHBvcG92ZXItdGl0bGUtYmc6ICAgICAgICAgICAgICAgICAgICBkYXJrZW4oJHBvcG92ZXItYmcsIDMlKSAhZGVmYXVsdDtcblxuLy8qKiBQb3BvdmVyIGFycm93IHdpZHRoXG4kcG9wb3Zlci1hcnJvdy13aWR0aDogICAgICAgICAgICAgICAgIDEwcHggIWRlZmF1bHQ7XG4vLyoqIFBvcG92ZXIgYXJyb3cgY29sb3JcbiRwb3BvdmVyLWFycm93LWNvbG9yOiAgICAgICAgICAgICAgICAgJHBvcG92ZXItYmcgIWRlZmF1bHQ7XG5cbi8vKiogUG9wb3ZlciBvdXRlciBhcnJvdyB3aWR0aFxuJHBvcG92ZXItYXJyb3ctb3V0ZXItd2lkdGg6ICAgICAgICAgICAoJHBvcG92ZXItYXJyb3ctd2lkdGggKyAxKSAhZGVmYXVsdDtcbi8vKiogUG9wb3ZlciBvdXRlciBhcnJvdyBjb2xvclxuJHBvcG92ZXItYXJyb3ctb3V0ZXItY29sb3I6ICAgICAgICAgICBmYWRlX2luKCRwb3BvdmVyLWJvcmRlci1jb2xvciwgMC4wNSkgIWRlZmF1bHQ7XG4vLyoqIFBvcG92ZXIgb3V0ZXIgYXJyb3cgZmFsbGJhY2sgY29sb3JcbiRwb3BvdmVyLWFycm93LW91dGVyLWZhbGxiYWNrLWNvbG9yOiAgZGFya2VuKCRwb3BvdmVyLWZhbGxiYWNrLWJvcmRlci1jb2xvciwgMjAlKSAhZGVmYXVsdDtcblxuXG4vLz09IExhYmVsc1xuLy9cbi8vIyNcblxuLy8qKiBEZWZhdWx0IGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC1kZWZhdWx0LWJnOiAgICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBQcmltYXJ5IGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC1wcmltYXJ5LWJnOiAgICAgICAgICAgICRicmFuZC1wcmltYXJ5ICFkZWZhdWx0O1xuLy8qKiBTdWNjZXNzIGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC1zdWNjZXNzLWJnOiAgICAgICAgICAgICRicmFuZC1zdWNjZXNzICFkZWZhdWx0O1xuLy8qKiBJbmZvIGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC1pbmZvLWJnOiAgICAgICAgICAgICAgICRicmFuZC1pbmZvICFkZWZhdWx0O1xuLy8qKiBXYXJuaW5nIGxhYmVsIGJhY2tncm91bmQgY29sb3JcbiRsYWJlbC13YXJuaW5nLWJnOiAgICAgICAgICAgICRicmFuZC13YXJuaW5nICFkZWZhdWx0O1xuLy8qKiBEYW5nZXIgbGFiZWwgYmFja2dyb3VuZCBjb2xvclxuJGxhYmVsLWRhbmdlci1iZzogICAgICAgICAgICAgJGJyYW5kLWRhbmdlciAhZGVmYXVsdDtcblxuLy8qKiBEZWZhdWx0IGxhYmVsIHRleHQgY29sb3JcbiRsYWJlbC1jb2xvcjogICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIERlZmF1bHQgdGV4dCBjb2xvciBvZiBhIGxpbmtlZCBsYWJlbFxuJGxhYmVsLWxpbmstaG92ZXItY29sb3I6ICAgICAgI2ZmZiAhZGVmYXVsdDtcblxuXG4vLz09IE1vZGFsc1xuLy9cbi8vIyNcblxuLy8qKiBQYWRkaW5nIGFwcGxpZWQgdG8gdGhlIG1vZGFsIGJvZHlcbiRtb2RhbC1pbm5lci1wYWRkaW5nOiAgICAgICAgIDE1cHggIWRlZmF1bHQ7XG5cbi8vKiogUGFkZGluZyBhcHBsaWVkIHRvIHRoZSBtb2RhbCB0aXRsZVxuJG1vZGFsLXRpdGxlLXBhZGRpbmc6ICAgICAgICAgMTVweCAhZGVmYXVsdDtcbi8vKiogTW9kYWwgdGl0bGUgbGluZS1oZWlnaHRcbiRtb2RhbC10aXRsZS1saW5lLWhlaWdodDogICAgICRsaW5lLWhlaWdodC1iYXNlICFkZWZhdWx0O1xuXG4vLyoqIEJhY2tncm91bmQgY29sb3Igb2YgbW9kYWwgY29udGVudCBhcmVhXG4kbW9kYWwtY29udGVudC1iZzogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIE1vZGFsIGNvbnRlbnQgYm9yZGVyIGNvbG9yXG4kbW9kYWwtY29udGVudC1ib3JkZXItY29sb3I6ICAgICAgICAgICAgICAgICAgIHJnYmEoMCwwLDAsLjIpICFkZWZhdWx0O1xuLy8qKiBNb2RhbCBjb250ZW50IGJvcmRlciBjb2xvciAqKmZvciBJRTgqKlxuJG1vZGFsLWNvbnRlbnQtZmFsbGJhY2stYm9yZGVyLWNvbG9yOiAgICAgICAgICAjOTk5ICFkZWZhdWx0O1xuXG4vLyoqIE1vZGFsIGJhY2tkcm9wIGJhY2tncm91bmQgY29sb3JcbiRtb2RhbC1iYWNrZHJvcC1iZzogICAgICAgICAgICMwMDAgIWRlZmF1bHQ7XG4vLyoqIE1vZGFsIGJhY2tkcm9wIG9wYWNpdHlcbiRtb2RhbC1iYWNrZHJvcC1vcGFjaXR5OiAgICAgIC41ICFkZWZhdWx0O1xuLy8qKiBNb2RhbCBoZWFkZXIgYm9yZGVyIGNvbG9yXG4kbW9kYWwtaGVhZGVyLWJvcmRlci1jb2xvcjogICAjZTVlNWU1ICFkZWZhdWx0O1xuLy8qKiBNb2RhbCBmb290ZXIgYm9yZGVyIGNvbG9yXG4kbW9kYWwtZm9vdGVyLWJvcmRlci1jb2xvcjogICAkbW9kYWwtaGVhZGVyLWJvcmRlci1jb2xvciAhZGVmYXVsdDtcblxuJG1vZGFsLWxnOiAgICAgICAgICAgICAgICAgICAgOTAwcHggIWRlZmF1bHQ7XG4kbW9kYWwtbWQ6ICAgICAgICAgICAgICAgICAgICA2MDBweCAhZGVmYXVsdDtcbiRtb2RhbC1zbTogICAgICAgICAgICAgICAgICAgIDMwMHB4ICFkZWZhdWx0O1xuXG5cbi8vPT0gQWxlcnRzXG4vL1xuLy8jIyBEZWZpbmUgYWxlcnQgY29sb3JzLCBib3JkZXIgcmFkaXVzLCBhbmQgcGFkZGluZy5cblxuJGFsZXJ0LXBhZGRpbmc6ICAgICAgICAgICAgICAgMTVweCAhZGVmYXVsdDtcbiRhbGVydC1ib3JkZXItcmFkaXVzOiAgICAgICAgICRib3JkZXItcmFkaXVzLWJhc2UgIWRlZmF1bHQ7XG4kYWxlcnQtbGluay1mb250LXdlaWdodDogICAgICBib2xkICFkZWZhdWx0O1xuXG4kYWxlcnQtc3VjY2Vzcy1iZzogICAgICAgICAgICAkc3RhdGUtc3VjY2Vzcy1iZyAhZGVmYXVsdDtcbiRhbGVydC1zdWNjZXNzLXRleHQ6ICAgICAgICAgICRzdGF0ZS1zdWNjZXNzLXRleHQgIWRlZmF1bHQ7XG4kYWxlcnQtc3VjY2Vzcy1ib3JkZXI6ICAgICAgICAkc3RhdGUtc3VjY2Vzcy1ib3JkZXIgIWRlZmF1bHQ7XG5cbiRhbGVydC1pbmZvLWJnOiAgICAgICAgICAgICAgICRzdGF0ZS1pbmZvLWJnICFkZWZhdWx0O1xuJGFsZXJ0LWluZm8tdGV4dDogICAgICAgICAgICAgJHN0YXRlLWluZm8tdGV4dCAhZGVmYXVsdDtcbiRhbGVydC1pbmZvLWJvcmRlcjogICAgICAgICAgICRzdGF0ZS1pbmZvLWJvcmRlciAhZGVmYXVsdDtcblxuJGFsZXJ0LXdhcm5pbmctYmc6ICAgICAgICAgICAgJHN0YXRlLXdhcm5pbmctYmcgIWRlZmF1bHQ7XG4kYWxlcnQtd2FybmluZy10ZXh0OiAgICAgICAgICAkc3RhdGUtd2FybmluZy10ZXh0ICFkZWZhdWx0O1xuJGFsZXJ0LXdhcm5pbmctYm9yZGVyOiAgICAgICAgJHN0YXRlLXdhcm5pbmctYm9yZGVyICFkZWZhdWx0O1xuXG4kYWxlcnQtZGFuZ2VyLWJnOiAgICAgICAgICAgICAkc3RhdGUtZGFuZ2VyLWJnICFkZWZhdWx0O1xuJGFsZXJ0LWRhbmdlci10ZXh0OiAgICAgICAgICAgJHN0YXRlLWRhbmdlci10ZXh0ICFkZWZhdWx0O1xuJGFsZXJ0LWRhbmdlci1ib3JkZXI6ICAgICAgICAgJHN0YXRlLWRhbmdlci1ib3JkZXIgIWRlZmF1bHQ7XG5cblxuLy89PSBQcm9ncmVzcyBiYXJzXG4vL1xuLy8jI1xuXG4vLyoqIEJhY2tncm91bmQgY29sb3Igb2YgdGhlIHdob2xlIHByb2dyZXNzIGNvbXBvbmVudFxuJHByb2dyZXNzLWJnOiAgICAgICAgICAgICAgICAgI2Y1ZjVmNSAhZGVmYXVsdDtcbi8vKiogUHJvZ3Jlc3MgYmFyIHRleHQgY29sb3JcbiRwcm9ncmVzcy1iYXItY29sb3I6ICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4vLyoqIFZhcmlhYmxlIGZvciBzZXR0aW5nIHJvdW5kZWQgY29ybmVycyBvbiBwcm9ncmVzcyBiYXIuXG4kcHJvZ3Jlc3MtYm9yZGVyLXJhZGl1czogICAgICAkYm9yZGVyLXJhZGl1cy1iYXNlICFkZWZhdWx0O1xuXG4vLyoqIERlZmF1bHQgcHJvZ3Jlc3MgYmFyIGNvbG9yXG4kcHJvZ3Jlc3MtYmFyLWJnOiAgICAgICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbi8vKiogU3VjY2VzcyBwcm9ncmVzcyBiYXIgY29sb3JcbiRwcm9ncmVzcy1iYXItc3VjY2Vzcy1iZzogICAgICRicmFuZC1zdWNjZXNzICFkZWZhdWx0O1xuLy8qKiBXYXJuaW5nIHByb2dyZXNzIGJhciBjb2xvclxuJHByb2dyZXNzLWJhci13YXJuaW5nLWJnOiAgICAgJGJyYW5kLXdhcm5pbmcgIWRlZmF1bHQ7XG4vLyoqIERhbmdlciBwcm9ncmVzcyBiYXIgY29sb3JcbiRwcm9ncmVzcy1iYXItZGFuZ2VyLWJnOiAgICAgICRicmFuZC1kYW5nZXIgIWRlZmF1bHQ7XG4vLyoqIEluZm8gcHJvZ3Jlc3MgYmFyIGNvbG9yXG4kcHJvZ3Jlc3MtYmFyLWluZm8tYmc6ICAgICAgICAkYnJhbmQtaW5mbyAhZGVmYXVsdDtcblxuXG4vLz09IExpc3QgZ3JvdXBcbi8vXG4vLyMjXG5cbi8vKiogQmFja2dyb3VuZCBjb2xvciBvbiBgLmxpc3QtZ3JvdXAtaXRlbWBcbiRsaXN0LWdyb3VwLWJnOiAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbi8vKiogYC5saXN0LWdyb3VwLWl0ZW1gIGJvcmRlciBjb2xvclxuJGxpc3QtZ3JvdXAtYm9yZGVyOiAgICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuLy8qKiBMaXN0IGdyb3VwIGJvcmRlciByYWRpdXNcbiRsaXN0LWdyb3VwLWJvcmRlci1yYWRpdXM6ICAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcblxuLy8qKiBCYWNrZ3JvdW5kIGNvbG9yIG9mIHNpbmdsZSBsaXN0IGl0ZW1zIG9uIGhvdmVyXG4kbGlzdC1ncm91cC1ob3Zlci1iZzogICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4vLyoqIFRleHQgY29sb3Igb2YgYWN0aXZlIGxpc3QgaXRlbXNcbiRsaXN0LWdyb3VwLWFjdGl2ZS1jb2xvcjogICAgICAgJGNvbXBvbmVudC1hY3RpdmUtY29sb3IgIWRlZmF1bHQ7XG4vLyoqIEJhY2tncm91bmQgY29sb3Igb2YgYWN0aXZlIGxpc3QgaXRlbXNcbiRsaXN0LWdyb3VwLWFjdGl2ZS1iZzogICAgICAgICAgJGNvbXBvbmVudC1hY3RpdmUtYmcgIWRlZmF1bHQ7XG4vLyoqIEJvcmRlciBjb2xvciBvZiBhY3RpdmUgbGlzdCBlbGVtZW50c1xuJGxpc3QtZ3JvdXAtYWN0aXZlLWJvcmRlcjogICAgICAkbGlzdC1ncm91cC1hY3RpdmUtYmcgIWRlZmF1bHQ7XG4vLyoqIFRleHQgY29sb3IgZm9yIGNvbnRlbnQgd2l0aGluIGFjdGl2ZSBsaXN0IGl0ZW1zXG4kbGlzdC1ncm91cC1hY3RpdmUtdGV4dC1jb2xvcjogIGxpZ2h0ZW4oJGxpc3QtZ3JvdXAtYWN0aXZlLWJnLCA0MCUpICFkZWZhdWx0O1xuXG4vLyoqIFRleHQgY29sb3Igb2YgZGlzYWJsZWQgbGlzdCBpdGVtc1xuJGxpc3QtZ3JvdXAtZGlzYWJsZWQtY29sb3I6ICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG4vLyoqIEJhY2tncm91bmQgY29sb3Igb2YgZGlzYWJsZWQgbGlzdCBpdGVtc1xuJGxpc3QtZ3JvdXAtZGlzYWJsZWQtYmc6ICAgICAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbi8vKiogVGV4dCBjb2xvciBmb3IgY29udGVudCB3aXRoaW4gZGlzYWJsZWQgbGlzdCBpdGVtc1xuJGxpc3QtZ3JvdXAtZGlzYWJsZWQtdGV4dC1jb2xvcjogJGxpc3QtZ3JvdXAtZGlzYWJsZWQtY29sb3IgIWRlZmF1bHQ7XG5cbiRsaXN0LWdyb3VwLWxpbmstY29sb3I6ICAgICAgICAgIzU1NSAhZGVmYXVsdDtcbiRsaXN0LWdyb3VwLWxpbmstaG92ZXItY29sb3I6ICAgJGxpc3QtZ3JvdXAtbGluay1jb2xvciAhZGVmYXVsdDtcbiRsaXN0LWdyb3VwLWxpbmstaGVhZGluZy1jb2xvcjogIzMzMyAhZGVmYXVsdDtcblxuXG4vLz09IFBhbmVsc1xuLy9cbi8vIyNcblxuJHBhbmVsLWJnOiAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRwYW5lbC1ib2R5LXBhZGRpbmc6ICAgICAgICAgIDE1cHggIWRlZmF1bHQ7XG4kcGFuZWwtaGVhZGluZy1wYWRkaW5nOiAgICAgICAxMHB4IDE1cHggIWRlZmF1bHQ7XG4kcGFuZWwtZm9vdGVyLXBhZGRpbmc6ICAgICAgICAkcGFuZWwtaGVhZGluZy1wYWRkaW5nICFkZWZhdWx0O1xuJHBhbmVsLWJvcmRlci1yYWRpdXM6ICAgICAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcblxuLy8qKiBCb3JkZXIgY29sb3IgZm9yIGVsZW1lbnRzIHdpdGhpbiBwYW5lbHNcbiRwYW5lbC1pbm5lci1ib3JkZXI6ICAgICAgICAgICNkZGQgIWRlZmF1bHQ7XG4kcGFuZWwtZm9vdGVyLWJnOiAgICAgICAgICAgICAjZjVmNWY1ICFkZWZhdWx0O1xuXG4kcGFuZWwtZGVmYXVsdC10ZXh0OiAgICAgICAgICAkZ3JheS1kYXJrICFkZWZhdWx0O1xuJHBhbmVsLWRlZmF1bHQtYm9yZGVyOiAgICAgICAgI2RkZCAhZGVmYXVsdDtcbiRwYW5lbC1kZWZhdWx0LWhlYWRpbmctYmc6ICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG5cbiRwYW5lbC1wcmltYXJ5LXRleHQ6ICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kcGFuZWwtcHJpbWFyeS1ib3JkZXI6ICAgICAgICAkYnJhbmQtcHJpbWFyeSAhZGVmYXVsdDtcbiRwYW5lbC1wcmltYXJ5LWhlYWRpbmctYmc6ICAgICRicmFuZC1wcmltYXJ5ICFkZWZhdWx0O1xuXG4kcGFuZWwtc3VjY2Vzcy10ZXh0OiAgICAgICAgICAkc3RhdGUtc3VjY2Vzcy10ZXh0ICFkZWZhdWx0O1xuJHBhbmVsLXN1Y2Nlc3MtYm9yZGVyOiAgICAgICAgJHN0YXRlLXN1Y2Nlc3MtYm9yZGVyICFkZWZhdWx0O1xuJHBhbmVsLXN1Y2Nlc3MtaGVhZGluZy1iZzogICAgJHN0YXRlLXN1Y2Nlc3MtYmcgIWRlZmF1bHQ7XG5cbiRwYW5lbC1pbmZvLXRleHQ6ICAgICAgICAgICAgICRzdGF0ZS1pbmZvLXRleHQgIWRlZmF1bHQ7XG4kcGFuZWwtaW5mby1ib3JkZXI6ICAgICAgICAgICAkc3RhdGUtaW5mby1ib3JkZXIgIWRlZmF1bHQ7XG4kcGFuZWwtaW5mby1oZWFkaW5nLWJnOiAgICAgICAkc3RhdGUtaW5mby1iZyAhZGVmYXVsdDtcblxuJHBhbmVsLXdhcm5pbmctdGV4dDogICAgICAgICAgJHN0YXRlLXdhcm5pbmctdGV4dCAhZGVmYXVsdDtcbiRwYW5lbC13YXJuaW5nLWJvcmRlcjogICAgICAgICRzdGF0ZS13YXJuaW5nLWJvcmRlciAhZGVmYXVsdDtcbiRwYW5lbC13YXJuaW5nLWhlYWRpbmctYmc6ICAgICRzdGF0ZS13YXJuaW5nLWJnICFkZWZhdWx0O1xuXG4kcGFuZWwtZGFuZ2VyLXRleHQ6ICAgICAgICAgICAkc3RhdGUtZGFuZ2VyLXRleHQgIWRlZmF1bHQ7XG4kcGFuZWwtZGFuZ2VyLWJvcmRlcjogICAgICAgICAkc3RhdGUtZGFuZ2VyLWJvcmRlciAhZGVmYXVsdDtcbiRwYW5lbC1kYW5nZXItaGVhZGluZy1iZzogICAgICRzdGF0ZS1kYW5nZXItYmcgIWRlZmF1bHQ7XG5cblxuLy89PSBUaHVtYm5haWxzXG4vL1xuLy8jI1xuXG4vLyoqIFBhZGRpbmcgYXJvdW5kIHRoZSB0aHVtYm5haWwgaW1hZ2VcbiR0aHVtYm5haWwtcGFkZGluZzogICAgICAgICAgIDRweCAhZGVmYXVsdDtcbi8vKiogVGh1bWJuYWlsIGJhY2tncm91bmQgY29sb3JcbiR0aHVtYm5haWwtYmc6ICAgICAgICAgICAgICAgICRib2R5LWJnICFkZWZhdWx0O1xuLy8qKiBUaHVtYm5haWwgYm9yZGVyIGNvbG9yXG4kdGh1bWJuYWlsLWJvcmRlcjogICAgICAgICAgICAjZGRkICFkZWZhdWx0O1xuLy8qKiBUaHVtYm5haWwgYm9yZGVyIHJhZGl1c1xuJHRodW1ibmFpbC1ib3JkZXItcmFkaXVzOiAgICAgJGJvcmRlci1yYWRpdXMtYmFzZSAhZGVmYXVsdDtcblxuLy8qKiBDdXN0b20gdGV4dCBjb2xvciBmb3IgdGh1bWJuYWlsIGNhcHRpb25zXG4kdGh1bWJuYWlsLWNhcHRpb24tY29sb3I6ICAgICAkdGV4dC1jb2xvciAhZGVmYXVsdDtcbi8vKiogUGFkZGluZyBhcm91bmQgdGhlIHRodW1ibmFpbCBjYXB0aW9uXG4kdGh1bWJuYWlsLWNhcHRpb24tcGFkZGluZzogICA5cHggIWRlZmF1bHQ7XG5cblxuLy89PSBXZWxsc1xuLy9cbi8vIyNcblxuJHdlbGwtYmc6ICAgICAgICAgICAgICAgICAgICAgI2Y1ZjVmNSAhZGVmYXVsdDtcbiR3ZWxsLWJvcmRlcjogICAgICAgICAgICAgICAgIGRhcmtlbigkd2VsbC1iZywgNyUpICFkZWZhdWx0O1xuXG5cbi8vPT0gQmFkZ2VzXG4vL1xuLy8jI1xuXG4kYmFkZ2UtY29sb3I6ICAgICAgICAgICAgICAgICAjZmZmICFkZWZhdWx0O1xuLy8qKiBMaW5rZWQgYmFkZ2UgdGV4dCBjb2xvciBvbiBob3ZlclxuJGJhZGdlLWxpbmstaG92ZXItY29sb3I6ICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRiYWRnZS1iZzogICAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuXG4vLyoqIEJhZGdlIHRleHQgY29sb3IgaW4gYWN0aXZlIG5hdiBsaW5rXG4kYmFkZ2UtYWN0aXZlLWNvbG9yOiAgICAgICAgICAkbGluay1jb2xvciAhZGVmYXVsdDtcbi8vKiogQmFkZ2UgYmFja2dyb3VuZCBjb2xvciBpbiBhY3RpdmUgbmF2IGxpbmtcbiRiYWRnZS1hY3RpdmUtYmc6ICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG5cbiRiYWRnZS1mb250LXdlaWdodDogICAgICAgICAgIGJvbGQgIWRlZmF1bHQ7XG4kYmFkZ2UtbGluZS1oZWlnaHQ6ICAgICAgICAgICAxICFkZWZhdWx0O1xuJGJhZGdlLWJvcmRlci1yYWRpdXM6ICAgICAgICAgMTBweCAhZGVmYXVsdDtcblxuXG4vLz09IEJyZWFkY3J1bWJzXG4vL1xuLy8jI1xuXG4kYnJlYWRjcnVtYi1wYWRkaW5nLXZlcnRpY2FsOiAgIDhweCAhZGVmYXVsdDtcbiRicmVhZGNydW1iLXBhZGRpbmctaG9yaXpvbnRhbDogMTVweCAhZGVmYXVsdDtcbi8vKiogQnJlYWRjcnVtYiBiYWNrZ3JvdW5kIGNvbG9yXG4kYnJlYWRjcnVtYi1iZzogICAgICAgICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4vLyoqIEJyZWFkY3J1bWIgdGV4dCBjb2xvclxuJGJyZWFkY3J1bWItY29sb3I6ICAgICAgICAgICAgICAjY2NjICFkZWZhdWx0O1xuLy8qKiBUZXh0IGNvbG9yIG9mIGN1cnJlbnQgcGFnZSBpbiB0aGUgYnJlYWRjcnVtYlxuJGJyZWFkY3J1bWItYWN0aXZlLWNvbG9yOiAgICAgICAkZ3JheS1saWdodCAhZGVmYXVsdDtcbi8vKiogVGV4dHVhbCBzZXBhcmF0b3IgZm9yIGJldHdlZW4gYnJlYWRjcnVtYiBlbGVtZW50c1xuJGJyZWFkY3J1bWItc2VwYXJhdG9yOiAgICAgICAgICBcIi9cIiAhZGVmYXVsdDtcblxuXG4vLz09IENhcm91c2VsXG4vL1xuLy8jI1xuXG4kY2Fyb3VzZWwtdGV4dC1zaGFkb3c6ICAgICAgICAgICAgICAgICAgICAgICAgMCAxcHggMnB4IHJnYmEoMCwwLDAsLjYpICFkZWZhdWx0O1xuXG4kY2Fyb3VzZWwtY29udHJvbC1jb2xvcjogICAgICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRjYXJvdXNlbC1jb250cm9sLXdpZHRoOiAgICAgICAgICAgICAgICAgICAgICAxNSUgIWRlZmF1bHQ7XG4kY2Fyb3VzZWwtY29udHJvbC1vcGFjaXR5OiAgICAgICAgICAgICAgICAgICAgLjUgIWRlZmF1bHQ7XG4kY2Fyb3VzZWwtY29udHJvbC1mb250LXNpemU6ICAgICAgICAgICAgICAgICAgMjBweCAhZGVmYXVsdDtcblxuJGNhcm91c2VsLWluZGljYXRvci1hY3RpdmUtYmc6ICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG4kY2Fyb3VzZWwtaW5kaWNhdG9yLWJvcmRlci1jb2xvcjogICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcblxuJGNhcm91c2VsLWNhcHRpb24tY29sb3I6ICAgICAgICAgICAgICAgICAgICAgICNmZmYgIWRlZmF1bHQ7XG5cblxuLy89PSBDbG9zZVxuLy9cbi8vIyNcblxuJGNsb3NlLWZvbnQtd2VpZ2h0OiAgICAgICAgICAgYm9sZCAhZGVmYXVsdDtcbiRjbG9zZS1jb2xvcjogICAgICAgICAgICAgICAgICMwMDAgIWRlZmF1bHQ7XG4kY2xvc2UtdGV4dC1zaGFkb3c6ICAgICAgICAgICAwIDFweCAwICNmZmYgIWRlZmF1bHQ7XG5cblxuLy89PSBDb2RlXG4vL1xuLy8jI1xuXG4kY29kZS1jb2xvcjogICAgICAgICAgICAgICAgICAjYzcyNTRlICFkZWZhdWx0O1xuJGNvZGUtYmc6ICAgICAgICAgICAgICAgICAgICAgI2Y5ZjJmNCAhZGVmYXVsdDtcblxuJGtiZC1jb2xvcjogICAgICAgICAgICAgICAgICAgI2ZmZiAhZGVmYXVsdDtcbiRrYmQtYmc6ICAgICAgICAgICAgICAgICAgICAgICMzMzMgIWRlZmF1bHQ7XG5cbiRwcmUtYmc6ICAgICAgICAgICAgICAgICAgICAgICNmNWY1ZjUgIWRlZmF1bHQ7XG4kcHJlLWNvbG9yOiAgICAgICAgICAgICAgICAgICAkZ3JheS1kYXJrICFkZWZhdWx0O1xuJHByZS1ib3JkZXItY29sb3I6ICAgICAgICAgICAgI2NjYyAhZGVmYXVsdDtcbiRwcmUtc2Nyb2xsYWJsZS1tYXgtaGVpZ2h0OiAgIDM0MHB4ICFkZWZhdWx0O1xuXG5cbi8vPT0gVHlwZVxuLy9cbi8vIyNcblxuLy8qKiBIb3Jpem9udGFsIG9mZnNldCBmb3IgZm9ybXMgYW5kIGxpc3RzLlxuJGNvbXBvbmVudC1vZmZzZXQtaG9yaXpvbnRhbDogMTgwcHggIWRlZmF1bHQ7XG4vLyoqIFRleHQgbXV0ZWQgY29sb3JcbiR0ZXh0LW11dGVkOiAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBBYmJyZXZpYXRpb25zIGFuZCBhY3JvbnltcyBib3JkZXIgY29sb3JcbiRhYmJyLWJvcmRlci1jb2xvcjogICAgICAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBIZWFkaW5ncyBzbWFsbCBjb2xvclxuJGhlYWRpbmdzLXNtYWxsLWNvbG9yOiAgICAgICAgJGdyYXktbGlnaHQgIWRlZmF1bHQ7XG4vLyoqIEJsb2NrcXVvdGUgc21hbGwgY29sb3JcbiRibG9ja3F1b3RlLXNtYWxsLWNvbG9yOiAgICAgICRncmF5LWxpZ2h0ICFkZWZhdWx0O1xuLy8qKiBCbG9ja3F1b3RlIGZvbnQgc2l6ZVxuJGJsb2NrcXVvdGUtZm9udC1zaXplOiAgICAgICAgKCRmb250LXNpemUtYmFzZSAqIDEuMjUpICFkZWZhdWx0O1xuLy8qKiBCbG9ja3F1b3RlIGJvcmRlciBjb2xvclxuJGJsb2NrcXVvdGUtYm9yZGVyLWNvbG9yOiAgICAgJGdyYXktbGlnaHRlciAhZGVmYXVsdDtcbi8vKiogUGFnZSBoZWFkZXIgYm9yZGVyIGNvbG9yXG4kcGFnZS1oZWFkZXItYm9yZGVyLWNvbG9yOiAgICAkZ3JheS1saWdodGVyICFkZWZhdWx0O1xuLy8qKiBXaWR0aCBvZiBob3Jpem9udGFsIGRlc2NyaXB0aW9uIGxpc3QgdGl0bGVzXG4kZGwtaG9yaXpvbnRhbC1vZmZzZXQ6ICAgICAgICAkY29tcG9uZW50LW9mZnNldC1ob3Jpem9udGFsICFkZWZhdWx0O1xuLy8qKiBQb2ludCBhdCB3aGljaCAuZGwtaG9yaXpvbnRhbCBiZWNvbWVzIGhvcml6b250YWxcbiRkbC1ob3Jpem9udGFsLWJyZWFrcG9pbnQ6ICAgICRncmlkLWZsb2F0LWJyZWFrcG9pbnQgIWRlZmF1bHQ7XG4vLyoqIEhvcml6b250YWwgbGluZSBjb2xvci5cbiRoci1ib3JkZXI6ICAgICAgICAgICAgICAgICAgICRncmF5LWxpZ2h0ZXIgIWRlZmF1bHQ7XG4iLCJAaW1wb3J0ICcuLi8uLi8uLi9zdHlsZXMvYW5pbWF0aW9ucy5zY3NzJztcblxueG9zLXRhYmxlIHtcblxuICBkaXNwbGF5OiBibG9jaztcblxuICB0ci5uZy1tb3ZlLFxuICB0ci5uZy1lbnRlcixcbiAgdHIubmctbGVhdmUge1xuICAgIHRyYW5zaXRpb246YWxsIGxpbmVhciAwLjVzO1xuICB9XG5cbiAgdHIubmctbGVhdmUubmctbGVhdmUtYWN0aXZlLFxuICB0ci5uZy1tb3ZlLFxuICB0ci5uZy1lbnRlciB7XG4gICAgb3BhY2l0eTowO1xuICAgIGFuaW1hdGlvbjogMC41cyBzbGlkZU91dFJpZ2h0IGVhc2UtaW4tb3V0O1xuICB9XG5cbiAgdHIubmctbGVhdmUsXG4gIHRyLm5nLW1vdmUubmctbW92ZS1hY3RpdmUsXG4gIHRyLm5nLWVudGVyLm5nLWVudGVyLWFjdGl2ZSB7XG4gICAgb3BhY2l0eToxO1xuICAgIGFuaW1hdGlvbjogMC41cyBzbGlkZUluUmlnaHQgZWFzZS1pbi1vdXQ7XG4gIH1cbn0iLCJAaW1wb3J0ICcuLi8uLi8uLi9zdHlsZXMvYW5pbWF0aW9ucy5zY3NzJztcblxueG9zLWFsZXJ0IHtcblxuICAvKiB3aGVuIGhpZGluZyAqL1xuICAubmctaGlkZS1hZGQgICAgICAgICB7IGFuaW1hdGlvbjowLjVzIGZhZGVPdXREb3duIGVhc2UtaW4tb3V0OyB9XG5cbiAgLyogd2hlbiBzaG93aW5nICovXG4gIC5uZy1oaWRlLXJlbW92ZSAgICAgIHsgYW5pbWF0aW9uOjAuNXMgZmFkZUluVXAgZWFzZS1pbi1vdXQ7IH1cbn0iLCJAaW1wb3J0ICcuLi8uLi8uLi9zdHlsZXMvYW5pbWF0aW9ucy5zY3NzJztcbkBpbXBvcnQgJy4uLy4uLy4uLy4uLy4uLy4uL3N0eWxlL3Nhc3MvYm9vdHN0cmFwL2Jvb3RzdHJhcC9fdmFyaWFibGVzLnNjc3MnO1xuXG5pbnB1dCArIHhvcy12YWxpZGF0aW9uIHtcbiAgbWFyZ2luLXRvcDogJGZvcm0tZ3JvdXAtbWFyZ2luLWJvdHRvbTtcbiAgZGlzcGxheTogYmxvY2s7XG59IiwieG9zLXNtYXJ0LXRhYmxle1xuICBcbn0iXSwibWFwcGluZ3MiOiJBQ0FBLFVBQVUsQ0FBQyxBQUFBLFlBQVk7RUFDckIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLHVCQUFXO0lBQ3RCLFVBQVUsRUFBRSxPQUFRO0VBR3RCLEFBQUEsRUFBRTtJQUNBLFNBQVMsRUFBRSxvQkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsYUFBYTtFQUN0QixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsb0JBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsVUFBVSxFQUFFLE1BQU87SUFDbkIsU0FBUyxFQUFFLHVCQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxRQUFRO0VBQ2pCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLElBQUs7O0FBSXBCLFVBQVUsQ0FBQyxBQUFBLFdBQVc7RUFDcEIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7RUFHYixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVzs7QUF6QzFCLFVBQVUsQ0FBQyxBQUFBLFlBQVk7RUFDckIsQUFBQSxJQUFJO0lBQ0YsU0FBUyxFQUFFLHVCQUFXO0lBQ3RCLFVBQVUsRUFBRSxPQUFRO0VBR3RCLEFBQUEsRUFBRTtJQUNBLFNBQVMsRUFBRSxvQkFBVzs7QUFJMUIsVUFBVSxDQUFDLEFBQUEsYUFBYTtFQUN0QixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsb0JBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsVUFBVSxFQUFFLE1BQU87SUFDbkIsU0FBUyxFQUFFLHVCQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxRQUFRO0VBQ2pCLEFBQUEsSUFBSTtJQUNGLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXO0VBR3hCLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLElBQUs7O0FBSXBCLFVBQVUsQ0FBQyxBQUFBLFdBQVc7RUFDcEIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7RUFHYixBQUFBLEVBQUU7SUFDQSxPQUFPLEVBQUUsQ0FBRTtJQUNYLFNBQVMsRUFBRSx1QkFBVzs7QUV2QzFCLEFBQUEsU0FBUyxDQUFDO0VBRVIsT0FBTyxFQUFFLEtBQU0sR0FxQmhCO0VBdkJELEFBSUksU0FKSyxDQUlQLEVBQUUsQUFBQSxRQUFRO0VBSlosQUFLSSxTQUxLLENBS1AsRUFBRSxBQUFBLFNBQVM7RUFMYixBQU1JLFNBTkssQ0FNUCxFQUFFLEFBQUEsU0FBUyxDQUFDO0lBQ1YsVUFBVSxFQUFDLGVBQWdCLEdBQzVCO0VBUkgsQUFVYSxTQVZKLENBVVAsRUFBRSxBQUFBLFNBQVMsQUFBQSxnQkFBZ0I7RUFWN0IsQUFXSSxTQVhLLENBV1AsRUFBRSxBQUFBLFFBQVE7RUFYWixBQVlJLFNBWkssQ0FZUCxFQUFFLEFBQUEsU0FBUyxDQUFDO0lBQ1YsT0FBTyxFQUFDLENBQUU7SUFDVixTQUFTLEVBQUUsOEJBQStCLEdBQzNDO0VBZkgsQUFpQkksU0FqQkssQ0FpQlAsRUFBRSxBQUFBLFNBQVM7RUFqQmIsQUFrQlksU0FsQkgsQ0FrQlAsRUFBRSxBQUFBLFFBQVEsQUFBQSxlQUFlO0VBbEIzQixBQW1CYSxTQW5CSixDQW1CUCxFQUFFLEFBQUEsU0FBUyxBQUFBLGdCQUFnQixDQUFDO0lBQzFCLE9BQU8sRUFBQyxDQUFFO0lBQ1YsU0FBUyxFQUFFLDZCQUE4QixHQUMxQzs7QUZ4QkgsVUFBVSxDQUFDLEFBQUEsWUFBWTtFQUNyQixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsdUJBQVc7SUFDdEIsVUFBVSxFQUFFLE9BQVE7RUFHdEIsQUFBQSxFQUFFO0lBQ0EsU0FBUyxFQUFFLG9CQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxhQUFhO0VBQ3RCLEFBQUEsSUFBSTtJQUNGLFNBQVMsRUFBRSxvQkFBVztFQUd4QixBQUFBLEVBQUU7SUFDQSxVQUFVLEVBQUUsTUFBTztJQUNuQixTQUFTLEVBQUUsdUJBQVc7O0FBSTFCLFVBQVUsQ0FBQyxBQUFBLFFBQVE7RUFDakIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsdUJBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsSUFBSzs7QUFJcEIsVUFBVSxDQUFDLEFBQUEsV0FBVztFQUNwQixBQUFBLElBQUk7SUFDRixPQUFPLEVBQUUsQ0FBRTtFQUdiLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXOztBR3ZDMUIsQUFBQSxTQUFTLENBQUM7RUFFUixpQkFBaUI7RUFHakIsa0JBQWtCLEVBRW5CO0VBUEQsQUFHRSxTQUhPLENBR1AsWUFBWSxDQUFTO0lBQUUsU0FBUyxFQUFDLDRCQUE2QixHQUFJO0VBSHBFLEFBTUUsU0FOTyxDQU1QLGVBQWUsQ0FBTTtJQUFFLFNBQVMsRUFBQyx5QkFBMEIsR0FBSTs7QUhSakUsVUFBVSxDQUFDLEFBQUEsWUFBWTtFQUNyQixBQUFBLElBQUk7SUFDRixTQUFTLEVBQUUsdUJBQVc7SUFDdEIsVUFBVSxFQUFFLE9BQVE7RUFHdEIsQUFBQSxFQUFFO0lBQ0EsU0FBUyxFQUFFLG9CQUFXOztBQUkxQixVQUFVLENBQUMsQUFBQSxhQUFhO0VBQ3RCLEFBQUEsSUFBSTtJQUNGLFNBQVMsRUFBRSxvQkFBVztFQUd4QixBQUFBLEVBQUU7SUFDQSxVQUFVLEVBQUUsTUFBTztJQUNuQixTQUFTLEVBQUUsdUJBQVc7O0FBSTFCLFVBQVUsQ0FBQyxBQUFBLFFBQVE7RUFDakIsQUFBQSxJQUFJO0lBQ0YsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsdUJBQVc7RUFHeEIsQUFBQSxFQUFFO0lBQ0EsT0FBTyxFQUFFLENBQUU7SUFDWCxTQUFTLEVBQUUsSUFBSzs7QUFJcEIsVUFBVSxDQUFDLEFBQUEsV0FBVztFQUNwQixBQUFBLElBQUk7SUFDRixPQUFPLEVBQUUsQ0FBRTtFQUdiLEFBQUEsRUFBRTtJQUNBLE9BQU8sRUFBRSxDQUFFO0lBQ1gsU0FBUyxFQUFFLHVCQUFXOztBSXRDMUIsQUFBUSxLQUFILEdBQUcsY0FBYyxDQUFDO0VBQ3JCLFVBQVUsRUh3TnFCLElBQUk7RUd2Tm5DLE9BQU8sRUFBRSxLQUFNLEdBQ2hCOztDTEdELEFBQUEsQUFBVSxTQUFULEFBQUEsSUFBWSxBQUFBLEFBQVMsUUFBUixBQUFBLElBQVcsQUFBQSxBQUFjLGFBQWIsQUFBQSxJQUFnQixBQUFBLEFBQVcsVUFBVixBQUFBLEdBQWEsQUFBQSxTQUFTLEVBQUUsQUFBQSxXQUFXLENBQUM7RUFDN0UsT0FBTyxFQUFFLGVBQWdCLEdBQzFCOztBQUVELEFBQU8sSUFBSCxHQUFHLElBQUksQ0FBQztFQUNWLDBCQUEwQjtFQUMxQixVQUFVLEVFNk1xQixJQUFJLEdGNU1wQyIsIm5hbWVzIjpbXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0= */
diff --git a/xos/core/xoslib/methods/portforwarding.py b/xos/core/xoslib/methods/portforwarding.py
index 0d24423..c98b1bd 100644
--- a/xos/core/xoslib/methods/portforwarding.py
+++ b/xos/core/xoslib/methods/portforwarding.py
@@ -27,7 +27,7 @@
def get_queryset(self):
queryset = queryset=Port.objects.all()
- node_name = self.request.QUERY_PARAMS.get('node_name', None)
+ node_name = self.request.query_params.get('node_name', None)
if node_name is not None:
queryset = queryset.filter(instance__node__name = node_name)
@@ -51,7 +51,7 @@
def get_queryset(self):
queryset = queryset=Port.objects.all()
- node_name = self.request.QUERY_PARAMS.get('node_name', None)
+ node_name = self.request.query_params.get('node_name', None)
if node_name is not None:
queryset = queryset.filter(instance__node__name = node_name)
diff --git a/xos/core/xoslib/methods/sshkeys.py b/xos/core/xoslib/methods/sshkeys.py
index 6223540..705a968 100644
--- a/xos/core/xoslib/methods/sshkeys.py
+++ b/xos/core/xoslib/methods/sshkeys.py
@@ -26,7 +26,7 @@
def get_queryset(self):
queryset = queryset=Instance.objects.all()
- node_name = self.request.QUERY_PARAMS.get('node_name', None)
+ node_name = self.request.query_params.get('node_name', None)
if node_name is not None:
queryset = queryset.filter(node__name = node_name)
@@ -47,7 +47,7 @@
def get_queryset(self):
queryset = queryset=Instance.objects.all()
- node_name = self.request.QUERY_PARAMS.get('node_name', None)
+ node_name = self.request.query_params.get('node_name', None)
if node_name is not None:
queryset = queryset.filter(node__name = node_name)
diff --git a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
index fe5cc8f..7ff31fe 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
@@ -25,7 +25,7 @@
* - [xosValidation](/#/module/xos.uiComponents.directive:xosValidation)
**/
- angular.module('xos.uiComponents', []);
+ angular.module('xos.uiComponents', ['chart.js']);
})();
//# sourceMappingURL=../maps/ui_components/ui-components.module.js.map
@@ -135,7 +135,6 @@
label: 'delete',
icon: 'remove',
cb: function cb(item) {
- console.log(item);
_this.Resource.delete({ id: item.id }).$promise.then(function () {
_.remove(_this.data, function (d) {
return d.id === item.id;
@@ -259,6 +258,177 @@
*
* Visit http://guide.xosproject.org/devguide/addview/ for more information
*
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosSmartPie
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component,
+ * it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
+ * and a field name that is used to group the data.
+ * ```
+ * {
+ resource: 'Users',
+ groupBy: 'fieldName',
+ classes: 'my-custom-class',
+ labelFormatter: (labels) => {
+ // here you can format your label,
+ // you should return an array with the same order
+ return labels;
+ }
+ }
+ * ```
+ * @scope
+ * @example
+ <example module="sampleSmartPie">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPie', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ poll: 2,
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'Active' : 'Banned');
+ }
+ };
+ });
+ </file>
+ <file name="backend.js">
+ angular.module('sampleSmartPie')
+ .run(function($httpBackend, _){
+ let mock = [
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 1}
+ ],
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 2},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ],
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ]
+ ];
+ $httpBackend.whenGET('/test').respond(function(method, url, data, headers, params) {
+ return [200, mock[Math.round(Math.random() * 3)]];
+ });
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+ <example module="sampleSmartPiePoll">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPiePoll', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+ });
+ </file>
+ <file name="backendPoll.js">
+ angular.module('sampleSmartPiePoll')
+ .run(function($httpBackend, _){
+ let datas = [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1}
+ ];
+ $httpBackend.whenGET('/test').respond(200, datas)
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+ */
+ .directive('xosSmartPie', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '='
+ },
+ template: '\n <canvas\n class="chart chart-pie {{vm.config.classes}}"\n chart-data="vm.data" chart-labels="vm.labels"\n chart-legend="{{vm.config.legend}}">\n </canvas>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$injector", "$timeout", "$interval", "_", function controller($injector, $timeout, $interval, _) {
+ var _this = this;
+
+ this.Resource = $injector.get(this.config.resource);
+
+ var getData = function getData() {
+ _this.Resource.query().$promise.then(function (res) {
+
+ if (!res[0]) {
+ return;
+ }
+
+ // group data
+ var grouped = _.groupBy(res, _this.config.groupBy);
+ _this.data = _.reduce(Object.keys(grouped), function (data, group) {
+ return data.concat(grouped[group].length);
+ }, []);
+ // create labels
+ _this.labels = angular.isFunction(_this.config.labelFormatter) ? _this.config.labelFormatter(Object.keys(grouped)) : Object.keys(grouped);
+ });
+ };
+
+ getData();
+
+ if (this.config.poll) {
+ $interval(function () {
+ getData();
+ }, this.config.poll * 1000);
+ }
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartPie/smartPie.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
* Created by teone on 4/15/16.
*/
@@ -1154,6 +1324,31 @@
angular.module('xos.helpers')
/**
* @ngdoc service
+ * @name xos.helpers.Login
+ * @description Angular resource to fetch /api/utility/login/
+ **/
+ .service('Login', ["$resource", function ($resource) {
+ return $resource('/api/utility/login/');
+ }])
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Logout
+ * @description Angular resource to fetch /api/utility/logout/
+ **/
+ .service('Logout', ["$resource", function ($resource) {
+ return $resource('/api/utility/logout/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Utility.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
* @name xos.helpers.Users
* @description Angular resource to fetch /api/core/users/:id/
**/
@@ -1193,71 +1388,143 @@
/**
* @ngdoc service
* @name xos.helpers.Subscribers
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/
+ * @description Angular resource to fetch Subscribers
**/
.service('Subscribers', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/
- **/
- .service('Subscriber-features', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-uplink_speed
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/uplink_speed/
- **/
- .service('Subscriber-features-uplink_speed', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/uplink_speed/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-downlink_speed
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/downlink_speed/
- **/
- .service('Subscriber-features-downlink_speed', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/downlink_speed/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-cdn
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/cdn/
- **/
- .service('Subscriber-features-cdn', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/cdn/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-uverse
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/uverse/
- **/
- .service('Subscriber-features-uverse', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/uverse/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
- });
- }])
- /**
- * @ngdoc service
- * @name xos.helpers.Subscriber-features-status
- * @description Angular resource to fetch /api/tenant/cord/subscriber/:subscriber_id/features/status/
- **/
- .service('Subscriber-features-status', ["$resource", function ($resource) {
- return $resource('/api/tenant/cord/subscriber/:subscriber_id/features/status/', { subscriber_id: '@id' }, {
- update: { method: 'PUT' }
+ return $resource('/api/tenant/cord/subscriber/:id/', { id: '@id' }, {
+ update: { method: 'PUT' },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#View-a-Subscriber-Features-Detail
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * View-a-Subscriber-Features-Detail
+ **/
+ 'View-a-Subscriber-Features-Detail': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uplink_speed
+ **/
+ 'Read-Subscriber-uplink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uplink_speed
+ **/
+ 'Update-Subscriber-uplink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-downlink_speed
+ **/
+ 'Read-Subscriber-downlink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-downlink_speed
+ **/
+ 'Update-Subscriber-downlink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-cdn
+ **/
+ 'Read-Subscriber-cdn': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-cdn
+ **/
+ 'Update-Subscriber-cdn': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uverse
+ **/
+ 'Read-Subscriber-uverse': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uverse
+ **/
+ 'Update-Subscriber-uverse': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-status
+ **/
+ 'Read-Subscriber-status': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-status
+ **/
+ 'Update-Subscriber-status': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ }
});
}]);
})();
@@ -1271,6 +1538,44 @@
angular.module('xos.helpers')
/**
* @ngdoc service
+ * @name xos.helpers.Slices
+ * @description Angular resource to fetch /api/core/slices/:id/
+ **/
+ .service('Slices', ["$resource", function ($resource) {
+ return $resource('/api/core/slices/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Slices.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Sites
+ * @description Angular resource to fetch /api/core/sites/:id/
+ **/
+ .service('Sites', ["$resource", function ($resource) {
+ return $resource('/api/core/sites/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Sites.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
* @name xos.helpers.ONOS-Services-Collection
* @description Angular resource to fetch /api/service/onos/
**/
@@ -1305,6 +1610,25 @@
angular.module('xos.helpers')
/**
* @ngdoc service
+ * @name xos.helpers.Nodes
+ * @description Angular resource to fetch /api/core/nodes/:id/
+ **/
+ .service('Nodes', ["$resource", function ($resource) {
+ return $resource('/api/core/nodes/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Nodes.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
* @name xos.helpers.Instances
* @description Angular resource to fetch /api/core/instances/:id/
**/
@@ -1324,6 +1648,25 @@
angular.module('xos.helpers')
/**
* @ngdoc service
+ * @name xos.helpers.Flavors
+ * @description Angular resource to fetch /api/core/flavors/:id/
+ **/
+ .service('Flavors', ["$resource", function ($resource) {
+ return $resource('/api/core/flavors/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Flavors.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
* @name xos.helpers.Example-Services-Collection
* @description Angular resource to fetch /api/service/exampleservice/
**/
@@ -1338,6 +1681,25 @@
(function () {
'use strict';
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Deployments
+ * @description Angular resource to fetch /api/core/deployments/:id/
+ **/
+ .service('Deployments', ["$resource", function ($resource) {
+ return $resource('/api/core/deployments/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Deployments.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
/**
* @ngdoc service
* @name xos.helpers.NoHyperlinks
diff --git a/xos/core/xoslib/static/js/vendor/ngXosVendor.js b/xos/core/xoslib/static/js/vendor/ngXosVendor.js
index 534bb6b..b788f32 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosVendor.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosVendor.js
@@ -28745,10 +28745,12 @@
var minlength = 0;
attr.$observe('minlength', function(value) {
+ // console.log('minlength: ' + minlength);
minlength = toInt(value) || 0;
ctrl.$validate();
});
ctrl.$validators.minlength = function(modelValue, viewValue) {
+ // console.log('minlength: ' + minlength);
return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
};
}
@@ -65492,3 +65494,4116 @@
root._ = _;
}
}.call(this));
+
+/*!
+ * Chart.js
+ * http://chartjs.org/
+ * Version: 1.1.1
+ *
+ * Copyright 2015 Nick Downie
+ * Released under the MIT license
+ * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
+ */
+
+
+(function(){
+
+ "use strict";
+
+ //Declare root variable - window in the browser, global on the server
+ var root = this,
+ previous = root.Chart;
+
+ //Occupy the global variable of Chart, and create a simple base class
+ var Chart = function(context){
+ var chart = this;
+ this.canvas = context.canvas;
+
+ this.ctx = context;
+
+ //Variables global to the chart
+ var computeDimension = function(element,dimension)
+ {
+ if (element['offset'+dimension])
+ {
+ return element['offset'+dimension];
+ }
+ else
+ {
+ return document.defaultView.getComputedStyle(element).getPropertyValue(dimension);
+ }
+ };
+
+ var width = this.width = computeDimension(context.canvas,'Width') || context.canvas.width;
+ var height = this.height = computeDimension(context.canvas,'Height') || context.canvas.height;
+
+ this.aspectRatio = this.width / this.height;
+ //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
+ helpers.retinaScale(this);
+
+ return this;
+ };
+ //Globally expose the defaults to allow for user updating/changing
+ Chart.defaults = {
+ global: {
+ // Boolean - Whether to animate the chart
+ animation: true,
+
+ // Number - Number of animation steps
+ animationSteps: 60,
+
+ // String - Animation easing effect
+ animationEasing: "easeOutQuart",
+
+ // Boolean - If we should show the scale at all
+ showScale: true,
+
+ // Boolean - If we want to override with a hard coded scale
+ scaleOverride: false,
+
+ // ** Required if scaleOverride is true **
+ // Number - The number of steps in a hard coded scale
+ scaleSteps: null,
+ // Number - The value jump in the hard coded scale
+ scaleStepWidth: null,
+ // Number - The scale starting value
+ scaleStartValue: null,
+
+ // String - Colour of the scale line
+ scaleLineColor: "rgba(0,0,0,.1)",
+
+ // Number - Pixel width of the scale line
+ scaleLineWidth: 1,
+
+ // Boolean - Whether to show labels on the scale
+ scaleShowLabels: true,
+
+ // Interpolated JS string - can access value
+ scaleLabel: "<%=value%>",
+
+ // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there
+ scaleIntegersOnly: true,
+
+ // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
+ scaleBeginAtZero: false,
+
+ // String - Scale label font declaration for the scale label
+ scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+
+ // Number - Scale label font size in pixels
+ scaleFontSize: 12,
+
+ // String - Scale label font weight style
+ scaleFontStyle: "normal",
+
+ // String - Scale label font colour
+ scaleFontColor: "#666",
+
+ // Boolean - whether or not the chart should be responsive and resize when the browser does.
+ responsive: false,
+
+ // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
+ maintainAspectRatio: true,
+
+ // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
+ showTooltips: true,
+
+ // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function
+ customTooltips: false,
+
+ // Array - Array of string names to attach tooltip events
+ tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
+
+ // String - Tooltip background colour
+ tooltipFillColor: "rgba(0,0,0,0.8)",
+
+ // String - Tooltip label font declaration for the scale label
+ tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+
+ // Number - Tooltip label font size in pixels
+ tooltipFontSize: 14,
+
+ // String - Tooltip font weight style
+ tooltipFontStyle: "normal",
+
+ // String - Tooltip label font colour
+ tooltipFontColor: "#fff",
+
+ // String - Tooltip title font declaration for the scale label
+ tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+
+ // Number - Tooltip title font size in pixels
+ tooltipTitleFontSize: 14,
+
+ // String - Tooltip title font weight style
+ tooltipTitleFontStyle: "bold",
+
+ // String - Tooltip title font colour
+ tooltipTitleFontColor: "#fff",
+
+ // String - Tooltip title template
+ tooltipTitleTemplate: "<%= label%>",
+
+ // Number - pixel width of padding around tooltip text
+ tooltipYPadding: 6,
+
+ // Number - pixel width of padding around tooltip text
+ tooltipXPadding: 6,
+
+ // Number - Size of the caret on the tooltip
+ tooltipCaretSize: 8,
+
+ // Number - Pixel radius of the tooltip border
+ tooltipCornerRadius: 6,
+
+ // Number - Pixel offset from point x to tooltip edge
+ tooltipXOffset: 10,
+
+ // String - Template string for single tooltips
+ tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
+
+ // String - Template string for single tooltips
+ multiTooltipTemplate: "<%= datasetLabel %>: <%= value %>",
+
+ // String - Colour behind the legend colour block
+ multiTooltipKeyBackground: '#fff',
+
+ // Array - A list of colors to use as the defaults
+ segmentColorDefault: ["#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#B4B482", "#B15928" ],
+
+ // Array - A list of highlight colors to use as the defaults
+ segmentHighlightColorDefaults: [ "#CEF6FF", "#47A0DC", "#DAFFB2", "#5BC854", "#FFC2C1", "#FF4244", "#FFE797", "#FFA728", "#F2DAFE", "#9265C2", "#DCDCAA", "#D98150" ],
+
+ // Function - Will fire on animation progression.
+ onAnimationProgress: function(){},
+
+ // Function - Will fire on animation completion.
+ onAnimationComplete: function(){}
+
+ }
+ };
+
+ //Create a dictionary of chart types, to allow for extension of existing types
+ Chart.types = {};
+
+ //Global Chart helpers object for utility methods and classes
+ var helpers = Chart.helpers = {};
+
+ //-- Basic js utility methods
+ var each = helpers.each = function(loopable,callback,self){
+ var additionalArgs = Array.prototype.slice.call(arguments, 3);
+ // Check to see if null or undefined firstly.
+ if (loopable){
+ if (loopable.length === +loopable.length){
+ var i;
+ for (i=0; i<loopable.length; i++){
+ callback.apply(self,[loopable[i], i].concat(additionalArgs));
+ }
+ }
+ else{
+ for (var item in loopable){
+ callback.apply(self,[loopable[item],item].concat(additionalArgs));
+ }
+ }
+ }
+ },
+ clone = helpers.clone = function(obj){
+ var objClone = {};
+ each(obj,function(value,key){
+ if (obj.hasOwnProperty(key)){
+ objClone[key] = value;
+ }
+ });
+ return objClone;
+ },
+ extend = helpers.extend = function(base){
+ each(Array.prototype.slice.call(arguments,1), function(extensionObject) {
+ each(extensionObject,function(value,key){
+ if (extensionObject.hasOwnProperty(key)){
+ base[key] = value;
+ }
+ });
+ });
+ return base;
+ },
+ merge = helpers.merge = function(base,master){
+ //Merge properties in left object over to a shallow clone of object right.
+ var args = Array.prototype.slice.call(arguments,0);
+ args.unshift({});
+ return extend.apply(null, args);
+ },
+ indexOf = helpers.indexOf = function(arrayToSearch, item){
+ if (Array.prototype.indexOf) {
+ return arrayToSearch.indexOf(item);
+ }
+ else{
+ for (var i = 0; i < arrayToSearch.length; i++) {
+ if (arrayToSearch[i] === item) return i;
+ }
+ return -1;
+ }
+ },
+ where = helpers.where = function(collection, filterCallback){
+ var filtered = [];
+
+ helpers.each(collection, function(item){
+ if (filterCallback(item)){
+ filtered.push(item);
+ }
+ });
+
+ return filtered;
+ },
+ findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){
+ // Default to start of the array
+ if (!startIndex){
+ startIndex = -1;
+ }
+ for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
+ var currentItem = arrayToSearch[i];
+ if (filterCallback(currentItem)){
+ return currentItem;
+ }
+ }
+ },
+ findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){
+ // Default to end of the array
+ if (!startIndex){
+ startIndex = arrayToSearch.length;
+ }
+ for (var i = startIndex - 1; i >= 0; i--) {
+ var currentItem = arrayToSearch[i];
+ if (filterCallback(currentItem)){
+ return currentItem;
+ }
+ }
+ },
+ inherits = helpers.inherits = function(extensions){
+ //Basic javascript inheritance based on the model created in Backbone.js
+ var parent = this;
+ var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };
+
+ var Surrogate = function(){ this.constructor = ChartElement;};
+ Surrogate.prototype = parent.prototype;
+ ChartElement.prototype = new Surrogate();
+
+ ChartElement.extend = inherits;
+
+ if (extensions) extend(ChartElement.prototype, extensions);
+
+ ChartElement.__super__ = parent.prototype;
+
+ return ChartElement;
+ },
+ noop = helpers.noop = function(){},
+ uid = helpers.uid = (function(){
+ var id=0;
+ return function(){
+ return "chart-" + id++;
+ };
+ })(),
+ warn = helpers.warn = function(str){
+ //Method for warning of errors
+ if (window.console && typeof window.console.warn === "function") console.warn(str);
+ },
+ amd = helpers.amd = (typeof define === 'function' && define.amd),
+ //-- Math methods
+ isNumber = helpers.isNumber = function(n){
+ return !isNaN(parseFloat(n)) && isFinite(n);
+ },
+ max = helpers.max = function(array){
+ return Math.max.apply( Math, array );
+ },
+ min = helpers.min = function(array){
+ return Math.min.apply( Math, array );
+ },
+ cap = helpers.cap = function(valueToCap,maxValue,minValue){
+ if(isNumber(maxValue)) {
+ if( valueToCap > maxValue ) {
+ return maxValue;
+ }
+ }
+ else if(isNumber(minValue)){
+ if ( valueToCap < minValue ){
+ return minValue;
+ }
+ }
+ return valueToCap;
+ },
+ getDecimalPlaces = helpers.getDecimalPlaces = function(num){
+ if (num%1!==0 && isNumber(num)){
+ var s = num.toString();
+ if(s.indexOf("e-") < 0){
+ // no exponent, e.g. 0.01
+ return s.split(".")[1].length;
+ }
+ else if(s.indexOf(".") < 0) {
+ // no decimal point, e.g. 1e-9
+ return parseInt(s.split("e-")[1]);
+ }
+ else {
+ // exponent and decimal point, e.g. 1.23e-9
+ var parts = s.split(".")[1].split("e-");
+ return parts[0].length + parseInt(parts[1]);
+ }
+ }
+ else {
+ return 0;
+ }
+ },
+ toRadians = helpers.radians = function(degrees){
+ return degrees * (Math.PI/180);
+ },
+ // Gets the angle from vertical upright to the point about a centre.
+ getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){
+ var distanceFromXCenter = anglePoint.x - centrePoint.x,
+ distanceFromYCenter = anglePoint.y - centrePoint.y,
+ radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
+
+
+ var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter);
+
+ //If the segment is in the top left quadrant, we need to add another rotation to the angle
+ if (distanceFromXCenter < 0 && distanceFromYCenter < 0){
+ angle += Math.PI*2;
+ }
+
+ return {
+ angle: angle,
+ distance: radialDistanceFromCenter
+ };
+ },
+ aliasPixel = helpers.aliasPixel = function(pixelWidth){
+ return (pixelWidth % 2 === 0) ? 0 : 0.5;
+ },
+ splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){
+ //Props to Rob Spencer at scaled innovation for his post on splining between points
+ //http://scaledinnovation.com/analytics/splines/aboutSplines.html
+ var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)),
+ d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)),
+ fa=t*d01/(d01+d12),// scaling factor for triangle Ta
+ fb=t*d12/(d01+d12);
+ return {
+ inner : {
+ x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x),
+ y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y)
+ },
+ outer : {
+ x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x),
+ y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y)
+ }
+ };
+ },
+ calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){
+ return Math.floor(Math.log(val) / Math.LN10);
+ },
+ calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){
+
+ //Set a minimum step of two - a point at the top of the graph, and a point at the base
+ var minSteps = 2,
+ maxSteps = Math.floor(drawingSize/(textSize * 1.5)),
+ skipFitting = (minSteps >= maxSteps);
+
+ // Filter out null values since these would min() to zero
+ var values = [];
+ each(valuesArray, function( v ){
+ v == null || values.push( v );
+ });
+ var minValue = min(values),
+ maxValue = max(values);
+
+ // We need some degree of separation here to calculate the scales if all the values are the same
+ // Adding/minusing 0.5 will give us a range of 1.
+ if (maxValue === minValue){
+ maxValue += 0.5;
+ // So we don't end up with a graph with a negative start value if we've said always start from zero
+ if (minValue >= 0.5 && !startFromZero){
+ minValue -= 0.5;
+ }
+ else{
+ // Make up a whole number above the values
+ maxValue += 0.5;
+ }
+ }
+
+ var valueRange = Math.abs(maxValue - minValue),
+ rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
+ graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
+ graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
+ graphRange = graphMax - graphMin,
+ stepValue = Math.pow(10, rangeOrderOfMagnitude),
+ numberOfSteps = Math.round(graphRange / stepValue);
+
+ //If we have more space on the graph we'll use it to give more definition to the data
+ while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
+ if(numberOfSteps > maxSteps){
+ stepValue *=2;
+ numberOfSteps = Math.round(graphRange/stepValue);
+ // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
+ if (numberOfSteps % 1 !== 0){
+ skipFitting = true;
+ }
+ }
+ //We can fit in double the amount of scale points on the scale
+ else{
+ //If user has declared ints only, and the step value isn't a decimal
+ if (integersOnly && rangeOrderOfMagnitude >= 0){
+ //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
+ if(stepValue/2 % 1 === 0){
+ stepValue /=2;
+ numberOfSteps = Math.round(graphRange/stepValue);
+ }
+ //If it would make it a float break out of the loop
+ else{
+ break;
+ }
+ }
+ //If the scale doesn't have to be an int, make the scale more granular anyway.
+ else{
+ stepValue /=2;
+ numberOfSteps = Math.round(graphRange/stepValue);
+ }
+
+ }
+ }
+
+ if (skipFitting){
+ numberOfSteps = minSteps;
+ stepValue = graphRange / numberOfSteps;
+ }
+
+ return {
+ steps : numberOfSteps,
+ stepValue : stepValue,
+ min : graphMin,
+ max : graphMin + (numberOfSteps * stepValue)
+ };
+
+ },
+ /* jshint ignore:start */
+ // Blows up jshint errors based on the new Function constructor
+ //Templating methods
+ //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
+ template = helpers.template = function(templateString, valuesObject){
+
+ // If templateString is function rather than string-template - call the function for valuesObject
+
+ if(templateString instanceof Function){
+ return templateString(valuesObject);
+ }
+
+ var cache = {};
+ function tmpl(str, data){
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ var fn = !/\W/.test(str) ?
+ cache[str] = cache[str] :
+
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\t\n]/g, " ")
+ .split("<%").join("\t")
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)%>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("%>").join("p.push('")
+ .split("\r").join("\\'") +
+ "');}return p.join('');"
+ );
+
+ // Provide some basic currying to the user
+ return data ? fn( data ) : fn;
+ }
+ return tmpl(templateString,valuesObject);
+ },
+ /* jshint ignore:end */
+ generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){
+ var labelsArray = new Array(numberOfSteps);
+ if (templateString){
+ each(labelsArray,function(val,index){
+ labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});
+ });
+ }
+ return labelsArray;
+ },
+ //--Animation methods
+ //Easing functions adapted from Robert Penner's easing equations
+ //http://www.robertpenner.com/easing/
+ easingEffects = helpers.easingEffects = {
+ linear: function (t) {
+ return t;
+ },
+ easeInQuad: function (t) {
+ return t * t;
+ },
+ easeOutQuad: function (t) {
+ return -1 * t * (t - 2);
+ },
+ easeInOutQuad: function (t) {
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * t * t;
+ }
+ return -1 / 2 * ((--t) * (t - 2) - 1);
+ },
+ easeInCubic: function (t) {
+ return t * t * t;
+ },
+ easeOutCubic: function (t) {
+ return 1 * ((t = t / 1 - 1) * t * t + 1);
+ },
+ easeInOutCubic: function (t) {
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * t * t * t;
+ }
+ return 1 / 2 * ((t -= 2) * t * t + 2);
+ },
+ easeInQuart: function (t) {
+ return t * t * t * t;
+ },
+ easeOutQuart: function (t) {
+ return -1 * ((t = t / 1 - 1) * t * t * t - 1);
+ },
+ easeInOutQuart: function (t) {
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * t * t * t * t;
+ }
+ return -1 / 2 * ((t -= 2) * t * t * t - 2);
+ },
+ easeInQuint: function (t) {
+ return 1 * (t /= 1) * t * t * t * t;
+ },
+ easeOutQuint: function (t) {
+ return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
+ },
+ easeInOutQuint: function (t) {
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * t * t * t * t * t;
+ }
+ return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
+ },
+ easeInSine: function (t) {
+ return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
+ },
+ easeOutSine: function (t) {
+ return 1 * Math.sin(t / 1 * (Math.PI / 2));
+ },
+ easeInOutSine: function (t) {
+ return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
+ },
+ easeInExpo: function (t) {
+ return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
+ },
+ easeOutExpo: function (t) {
+ return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
+ },
+ easeInOutExpo: function (t) {
+ if (t === 0){
+ return 0;
+ }
+ if (t === 1){
+ return 1;
+ }
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * Math.pow(2, 10 * (t - 1));
+ }
+ return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
+ },
+ easeInCirc: function (t) {
+ if (t >= 1){
+ return t;
+ }
+ return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
+ },
+ easeOutCirc: function (t) {
+ return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
+ },
+ easeInOutCirc: function (t) {
+ if ((t /= 1 / 2) < 1){
+ return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
+ }
+ return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
+ },
+ easeInElastic: function (t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1) == 1){
+ return 1;
+ }
+ if (!p){
+ p = 1 * 0.3;
+ }
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else{
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
+ },
+ easeOutElastic: function (t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1) == 1){
+ return 1;
+ }
+ if (!p){
+ p = 1 * 0.3;
+ }
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else{
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
+ },
+ easeInOutElastic: function (t) {
+ var s = 1.70158;
+ var p = 0;
+ var a = 1;
+ if (t === 0){
+ return 0;
+ }
+ if ((t /= 1 / 2) == 2){
+ return 1;
+ }
+ if (!p){
+ p = 1 * (0.3 * 1.5);
+ }
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ } else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ if (t < 1){
+ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));}
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
+ },
+ easeInBack: function (t) {
+ var s = 1.70158;
+ return 1 * (t /= 1) * t * ((s + 1) * t - s);
+ },
+ easeOutBack: function (t) {
+ var s = 1.70158;
+ return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
+ },
+ easeInOutBack: function (t) {
+ var s = 1.70158;
+ if ((t /= 1 / 2) < 1){
+ return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
+ }
+ return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
+ },
+ easeInBounce: function (t) {
+ return 1 - easingEffects.easeOutBounce(1 - t);
+ },
+ easeOutBounce: function (t) {
+ if ((t /= 1) < (1 / 2.75)) {
+ return 1 * (7.5625 * t * t);
+ } else if (t < (2 / 2.75)) {
+ return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
+ } else if (t < (2.5 / 2.75)) {
+ return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
+ } else {
+ return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
+ }
+ },
+ easeInOutBounce: function (t) {
+ if (t < 1 / 2){
+ return easingEffects.easeInBounce(t * 2) * 0.5;
+ }
+ return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
+ }
+ },
+ //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
+ requestAnimFrame = helpers.requestAnimFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback) {
+ return window.setTimeout(callback, 1000 / 60);
+ };
+ })(),
+ cancelAnimFrame = helpers.cancelAnimFrame = (function(){
+ return window.cancelAnimationFrame ||
+ window.webkitCancelAnimationFrame ||
+ window.mozCancelAnimationFrame ||
+ window.oCancelAnimationFrame ||
+ window.msCancelAnimationFrame ||
+ function(callback) {
+ return window.clearTimeout(callback, 1000 / 60);
+ };
+ })(),
+ animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){
+
+ var currentStep = 0,
+ easingFunction = easingEffects[easingString] || easingEffects.linear;
+
+ var animationFrame = function(){
+ currentStep++;
+ var stepDecimal = currentStep/totalSteps;
+ var easeDecimal = easingFunction(stepDecimal);
+
+ callback.call(chartInstance,easeDecimal,stepDecimal, currentStep);
+ onProgress.call(chartInstance,easeDecimal,stepDecimal);
+ if (currentStep < totalSteps){
+ chartInstance.animationFrame = requestAnimFrame(animationFrame);
+ } else{
+ onComplete.apply(chartInstance);
+ }
+ };
+ requestAnimFrame(animationFrame);
+ },
+ //-- DOM methods
+ getRelativePosition = helpers.getRelativePosition = function(evt){
+ var mouseX, mouseY;
+ var e = evt.originalEvent || evt,
+ canvas = evt.currentTarget || evt.srcElement,
+ boundingRect = canvas.getBoundingClientRect();
+
+ if (e.touches){
+ mouseX = e.touches[0].clientX - boundingRect.left;
+ mouseY = e.touches[0].clientY - boundingRect.top;
+
+ }
+ else{
+ mouseX = e.clientX - boundingRect.left;
+ mouseY = e.clientY - boundingRect.top;
+ }
+
+ return {
+ x : mouseX,
+ y : mouseY
+ };
+
+ },
+ addEvent = helpers.addEvent = function(node,eventType,method){
+ if (node.addEventListener){
+ node.addEventListener(eventType,method);
+ } else if (node.attachEvent){
+ node.attachEvent("on"+eventType, method);
+ } else {
+ node["on"+eventType] = method;
+ }
+ },
+ removeEvent = helpers.removeEvent = function(node, eventType, handler){
+ if (node.removeEventListener){
+ node.removeEventListener(eventType, handler, false);
+ } else if (node.detachEvent){
+ node.detachEvent("on"+eventType,handler);
+ } else{
+ node["on" + eventType] = noop;
+ }
+ },
+ bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){
+ // Create the events object if it's not already present
+ if (!chartInstance.events) chartInstance.events = {};
+
+ each(arrayOfEvents,function(eventName){
+ chartInstance.events[eventName] = function(){
+ handler.apply(chartInstance, arguments);
+ };
+ addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]);
+ });
+ },
+ unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) {
+ each(arrayOfEvents, function(handler,eventName){
+ removeEvent(chartInstance.chart.canvas, eventName, handler);
+ });
+ },
+ getMaximumWidth = helpers.getMaximumWidth = function(domNode){
+ var container = domNode.parentNode,
+ padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
+ // TODO = check cross browser stuff with this.
+ return container ? container.clientWidth - padding : 0;
+ },
+ getMaximumHeight = helpers.getMaximumHeight = function(domNode){
+ var container = domNode.parentNode,
+ padding = parseInt(getStyle(container, 'padding-bottom')) + parseInt(getStyle(container, 'padding-top'));
+ // TODO = check cross browser stuff with this.
+ return container ? container.clientHeight - padding : 0;
+ },
+ getStyle = helpers.getStyle = function (el, property) {
+ return el.currentStyle ?
+ el.currentStyle[property] :
+ document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
+ },
+ getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
+ retinaScale = helpers.retinaScale = function(chart){
+ var ctx = chart.ctx,
+ width = chart.canvas.width,
+ height = chart.canvas.height;
+
+ if (window.devicePixelRatio) {
+ ctx.canvas.style.width = width + "px";
+ ctx.canvas.style.height = height + "px";
+ ctx.canvas.height = height * window.devicePixelRatio;
+ ctx.canvas.width = width * window.devicePixelRatio;
+ ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
+ }
+ },
+ //-- Canvas methods
+ clear = helpers.clear = function(chart){
+ chart.ctx.clearRect(0,0,chart.width,chart.height);
+ },
+ fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){
+ return fontStyle + " " + pixelSize+"px " + fontFamily;
+ },
+ longestText = helpers.longestText = function(ctx,font,arrayOfStrings){
+ ctx.font = font;
+ var longest = 0;
+ each(arrayOfStrings,function(string){
+ var textWidth = ctx.measureText(string).width;
+ longest = (textWidth > longest) ? textWidth : longest;
+ });
+ return longest;
+ },
+ drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+ ctx.lineTo(x + radius, y + height);
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+ ctx.lineTo(x, y + radius);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ ctx.closePath();
+ };
+
+
+ //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
+ //Destroy method on the chart will remove the instance of the chart from this reference.
+ Chart.instances = {};
+
+ Chart.Type = function(data,options,chart){
+ this.options = options;
+ this.chart = chart;
+ this.id = uid();
+ //Add the chart instance to the global namespace
+ Chart.instances[this.id] = this;
+
+ // Initialize is always called when a chart type is created
+ // By default it is a no op, but it should be extended
+ if (options.responsive){
+ this.resize();
+ }
+ this.initialize.call(this,data);
+ };
+
+ //Core methods that'll be a part of every chart type
+ extend(Chart.Type.prototype,{
+ initialize : function(){return this;},
+ clear : function(){
+ clear(this.chart);
+ return this;
+ },
+ stop : function(){
+ // Stops any current animation loop occuring
+ Chart.animationService.cancelAnimation(this);
+ return this;
+ },
+ resize : function(callback){
+ this.stop();
+ var canvas = this.chart.canvas,
+ newWidth = getMaximumWidth(this.chart.canvas),
+ newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);
+
+ canvas.width = this.chart.width = newWidth;
+ canvas.height = this.chart.height = newHeight;
+
+ retinaScale(this.chart);
+
+ if (typeof callback === "function"){
+ callback.apply(this, Array.prototype.slice.call(arguments, 1));
+ }
+ return this;
+ },
+ reflow : noop,
+ render : function(reflow){
+ if (reflow){
+ this.reflow();
+ }
+
+ if (this.options.animation && !reflow){
+ var animation = new Chart.Animation();
+ animation.numSteps = this.options.animationSteps;
+ animation.easing = this.options.animationEasing;
+
+ // render function
+ animation.render = function(chartInstance, animationObject) {
+ var easingFunction = helpers.easingEffects[animationObject.easing];
+ var stepDecimal = animationObject.currentStep / animationObject.numSteps;
+ var easeDecimal = easingFunction(stepDecimal);
+
+ chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
+ };
+
+ // user events
+ animation.onAnimationProgress = this.options.onAnimationProgress;
+ animation.onAnimationComplete = this.options.onAnimationComplete;
+
+ Chart.animationService.addAnimation(this, animation);
+ }
+ else{
+ this.draw();
+ this.options.onAnimationComplete.call(this);
+ }
+ return this;
+ },
+ generateLegend : function(){
+ return helpers.template(this.options.legendTemplate, this);
+ },
+ destroy : function(){
+ this.stop();
+ this.clear();
+ unbindEvents(this, this.events);
+ var canvas = this.chart.canvas;
+
+ // Reset canvas height/width attributes starts a fresh with the canvas context
+ canvas.width = this.chart.width;
+ canvas.height = this.chart.height;
+
+ // < IE9 doesn't support removeProperty
+ if (canvas.style.removeProperty) {
+ canvas.style.removeProperty('width');
+ canvas.style.removeProperty('height');
+ } else {
+ canvas.style.removeAttribute('width');
+ canvas.style.removeAttribute('height');
+ }
+
+ delete Chart.instances[this.id];
+ },
+ showTooltip : function(ChartElements, forceRedraw){
+ // Only redraw the chart if we've actually changed what we're hovering on.
+ if (typeof this.activeElements === 'undefined') this.activeElements = [];
+
+ var isChanged = (function(Elements){
+ var changed = false;
+
+ if (Elements.length !== this.activeElements.length){
+ changed = true;
+ return changed;
+ }
+
+ each(Elements, function(element, index){
+ if (element !== this.activeElements[index]){
+ changed = true;
+ }
+ }, this);
+ return changed;
+ }).call(this, ChartElements);
+
+ if (!isChanged && !forceRedraw){
+ return;
+ }
+ else{
+ this.activeElements = ChartElements;
+ }
+ this.draw();
+ if(this.options.customTooltips){
+ this.options.customTooltips(false);
+ }
+ if (ChartElements.length > 0){
+ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
+ if (this.datasets && this.datasets.length > 1) {
+ var dataArray,
+ dataIndex;
+
+ for (var i = this.datasets.length - 1; i >= 0; i--) {
+ dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
+ dataIndex = indexOf(dataArray, ChartElements[0]);
+ if (dataIndex !== -1){
+ break;
+ }
+ }
+ var tooltipLabels = [],
+ tooltipColors = [],
+ medianPosition = (function(index) {
+
+ // Get all the points at that particular index
+ var Elements = [],
+ dataCollection,
+ xPositions = [],
+ yPositions = [],
+ xMax,
+ yMax,
+ xMin,
+ yMin;
+ helpers.each(this.datasets, function(dataset){
+ dataCollection = dataset.points || dataset.bars || dataset.segments;
+ if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){
+ Elements.push(dataCollection[dataIndex]);
+ }
+ });
+
+ helpers.each(Elements, function(element) {
+ xPositions.push(element.x);
+ yPositions.push(element.y);
+
+
+ //Include any colour information about the element
+ tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
+ tooltipColors.push({
+ fill: element._saved.fillColor || element.fillColor,
+ stroke: element._saved.strokeColor || element.strokeColor
+ });
+
+ }, this);
+
+ yMin = min(yPositions);
+ yMax = max(yPositions);
+
+ xMin = min(xPositions);
+ xMax = max(xPositions);
+
+ return {
+ x: (xMin > this.chart.width/2) ? xMin : xMax,
+ y: (yMin + yMax)/2
+ };
+ }).call(this, dataIndex);
+
+ new Chart.MultiTooltip({
+ x: medianPosition.x,
+ y: medianPosition.y,
+ xPadding: this.options.tooltipXPadding,
+ yPadding: this.options.tooltipYPadding,
+ xOffset: this.options.tooltipXOffset,
+ fillColor: this.options.tooltipFillColor,
+ textColor: this.options.tooltipFontColor,
+ fontFamily: this.options.tooltipFontFamily,
+ fontStyle: this.options.tooltipFontStyle,
+ fontSize: this.options.tooltipFontSize,
+ titleTextColor: this.options.tooltipTitleFontColor,
+ titleFontFamily: this.options.tooltipTitleFontFamily,
+ titleFontStyle: this.options.tooltipTitleFontStyle,
+ titleFontSize: this.options.tooltipTitleFontSize,
+ cornerRadius: this.options.tooltipCornerRadius,
+ labels: tooltipLabels,
+ legendColors: tooltipColors,
+ legendColorBackground : this.options.multiTooltipKeyBackground,
+ title: template(this.options.tooltipTitleTemplate,ChartElements[0]),
+ chart: this.chart,
+ ctx: this.chart.ctx,
+ custom: this.options.customTooltips
+ }).draw();
+
+ } else {
+ each(ChartElements, function(Element) {
+ var tooltipPosition = Element.tooltipPosition();
+ new Chart.Tooltip({
+ x: Math.round(tooltipPosition.x),
+ y: Math.round(tooltipPosition.y),
+ xPadding: this.options.tooltipXPadding,
+ yPadding: this.options.tooltipYPadding,
+ fillColor: this.options.tooltipFillColor,
+ textColor: this.options.tooltipFontColor,
+ fontFamily: this.options.tooltipFontFamily,
+ fontStyle: this.options.tooltipFontStyle,
+ fontSize: this.options.tooltipFontSize,
+ caretHeight: this.options.tooltipCaretSize,
+ cornerRadius: this.options.tooltipCornerRadius,
+ text: template(this.options.tooltipTemplate, Element),
+ chart: this.chart,
+ custom: this.options.customTooltips
+ }).draw();
+ }, this);
+ }
+ }
+ return this;
+ },
+ toBase64Image : function(){
+ return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
+ }
+ });
+
+ Chart.Type.extend = function(extensions){
+
+ var parent = this;
+
+ var ChartType = function(){
+ return parent.apply(this,arguments);
+ };
+
+ //Copy the prototype object of the this class
+ ChartType.prototype = clone(parent.prototype);
+ //Now overwrite some of the properties in the base class with the new extensions
+ extend(ChartType.prototype, extensions);
+
+ ChartType.extend = Chart.Type.extend;
+
+ if (extensions.name || parent.prototype.name){
+
+ var chartName = extensions.name || parent.prototype.name;
+ //Assign any potential default values of the new chart type
+
+ //If none are defined, we'll use a clone of the chart type this is being extended from.
+ //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart
+ //doesn't define some defaults of their own.
+
+ var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};
+
+ Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults);
+
+ Chart.types[chartName] = ChartType;
+
+ //Register this new chart type in the Chart prototype
+ Chart.prototype[chartName] = function(data,options){
+ var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});
+ return new ChartType(data,config,this);
+ };
+ } else{
+ warn("Name not provided for this chart, so it hasn't been registered");
+ }
+ return parent;
+ };
+
+ Chart.Element = function(configuration){
+ extend(this,configuration);
+ this.initialize.apply(this,arguments);
+ this.save();
+ };
+ extend(Chart.Element.prototype,{
+ initialize : function(){},
+ restore : function(props){
+ if (!props){
+ extend(this,this._saved);
+ } else {
+ each(props,function(key){
+ this[key] = this._saved[key];
+ },this);
+ }
+ return this;
+ },
+ save : function(){
+ this._saved = clone(this);
+ delete this._saved._saved;
+ return this;
+ },
+ update : function(newProps){
+ each(newProps,function(value,key){
+ this._saved[key] = this[key];
+ this[key] = value;
+ },this);
+ return this;
+ },
+ transition : function(props,ease){
+ each(props,function(value,key){
+ this[key] = ((value - this._saved[key]) * ease) + this._saved[key];
+ },this);
+ return this;
+ },
+ tooltipPosition : function(){
+ return {
+ x : this.x,
+ y : this.y
+ };
+ },
+ hasValue: function(){
+ return isNumber(this.value);
+ }
+ });
+
+ Chart.Element.extend = inherits;
+
+
+ Chart.Point = Chart.Element.extend({
+ display: true,
+ inRange: function(chartX,chartY){
+ var hitDetectionRange = this.hitDetectionRadius + this.radius;
+ return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));
+ },
+ draw : function(){
+ if (this.display){
+ var ctx = this.ctx;
+ ctx.beginPath();
+
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
+ ctx.closePath();
+
+ ctx.strokeStyle = this.strokeColor;
+ ctx.lineWidth = this.strokeWidth;
+
+ ctx.fillStyle = this.fillColor;
+
+ ctx.fill();
+ ctx.stroke();
+ }
+
+
+ //Quick debug for bezier curve splining
+ //Highlights control points and the line between them.
+ //Handy for dev - stripped in the min version.
+
+ // ctx.save();
+ // ctx.fillStyle = "black";
+ // ctx.strokeStyle = "black"
+ // ctx.beginPath();
+ // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2);
+ // ctx.fill();
+
+ // ctx.beginPath();
+ // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);
+ // ctx.fill();
+
+ // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);
+ // ctx.lineTo(this.x, this.y);
+ // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);
+ // ctx.stroke();
+
+ // ctx.restore();
+
+
+
+ }
+ });
+
+ Chart.Arc = Chart.Element.extend({
+ inRange : function(chartX,chartY){
+
+ var pointRelativePosition = helpers.getAngleFromPoint(this, {
+ x: chartX,
+ y: chartY
+ });
+
+ // Normalize all angles to 0 - 2*PI (0 - 360°)
+ var pointRelativeAngle = pointRelativePosition.angle % (Math.PI * 2),
+ startAngle = (Math.PI * 2 + this.startAngle) % (Math.PI * 2),
+ endAngle = (Math.PI * 2 + this.endAngle) % (Math.PI * 2) || 360;
+
+ // Calculate wether the pointRelativeAngle is between the start and the end angle
+ var betweenAngles = (endAngle < startAngle) ?
+ pointRelativeAngle <= endAngle || pointRelativeAngle >= startAngle:
+ pointRelativeAngle >= startAngle && pointRelativeAngle <= endAngle;
+
+ //Check if within the range of the open/close angle
+ var withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
+
+ return (betweenAngles && withinRadius);
+ //Ensure within the outside of the arc centre, but inside arc outer
+ },
+ tooltipPosition : function(){
+ var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),
+ rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;
+ return {
+ x : this.x + (Math.cos(centreAngle) * rangeFromCentre),
+ y : this.y + (Math.sin(centreAngle) * rangeFromCentre)
+ };
+ },
+ draw : function(animationPercent){
+
+ var easingDecimal = animationPercent || 1;
+
+ var ctx = this.ctx;
+
+ ctx.beginPath();
+
+ ctx.arc(this.x, this.y, this.outerRadius < 0 ? 0 : this.outerRadius, this.startAngle, this.endAngle);
+
+ ctx.arc(this.x, this.y, this.innerRadius < 0 ? 0 : this.innerRadius, this.endAngle, this.startAngle, true);
+
+ ctx.closePath();
+ ctx.strokeStyle = this.strokeColor;
+ ctx.lineWidth = this.strokeWidth;
+
+ ctx.fillStyle = this.fillColor;
+
+ ctx.fill();
+ ctx.lineJoin = 'bevel';
+
+ if (this.showStroke){
+ ctx.stroke();
+ }
+ }
+ });
+
+ Chart.Rectangle = Chart.Element.extend({
+ draw : function(){
+ var ctx = this.ctx,
+ halfWidth = this.width/2,
+ leftX = this.x - halfWidth,
+ rightX = this.x + halfWidth,
+ top = this.base - (this.base - this.y),
+ halfStroke = this.strokeWidth / 2;
+
+ // Canvas doesn't allow us to stroke inside the width so we can
+ // adjust the sizes to fit if we're setting a stroke on the line
+ if (this.showStroke){
+ leftX += halfStroke;
+ rightX -= halfStroke;
+ top += halfStroke;
+ }
+
+ ctx.beginPath();
+
+ ctx.fillStyle = this.fillColor;
+ ctx.strokeStyle = this.strokeColor;
+ ctx.lineWidth = this.strokeWidth;
+
+ // It'd be nice to keep this class totally generic to any rectangle
+ // and simply specify which border to miss out.
+ ctx.moveTo(leftX, this.base);
+ ctx.lineTo(leftX, top);
+ ctx.lineTo(rightX, top);
+ ctx.lineTo(rightX, this.base);
+ ctx.fill();
+ if (this.showStroke){
+ ctx.stroke();
+ }
+ },
+ height : function(){
+ return this.base - this.y;
+ },
+ inRange : function(chartX,chartY){
+ return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);
+ }
+ });
+
+ Chart.Animation = Chart.Element.extend({
+ currentStep: null, // the current animation step
+ numSteps: 60, // default number of steps
+ easing: "", // the easing to use for this animation
+ render: null, // render function used by the animation service
+
+ onAnimationProgress: null, // user specified callback to fire on each step of the animation
+ onAnimationComplete: null, // user specified callback to fire when the animation finishes
+ });
+
+ Chart.Tooltip = Chart.Element.extend({
+ draw : function(){
+
+ var ctx = this.chart.ctx;
+
+ ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
+
+ this.xAlign = "center";
+ this.yAlign = "above";
+
+ //Distance between the actual element.y position and the start of the tooltip caret
+ var caretPadding = this.caretPadding = 2;
+
+ var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
+ tooltipRectHeight = this.fontSize + 2*this.yPadding,
+ tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;
+
+ if (this.x + tooltipWidth/2 >this.chart.width){
+ this.xAlign = "left";
+ } else if (this.x - tooltipWidth/2 < 0){
+ this.xAlign = "right";
+ }
+
+ if (this.y - tooltipHeight < 0){
+ this.yAlign = "below";
+ }
+
+
+ var tooltipX = this.x - tooltipWidth/2,
+ tooltipY = this.y - tooltipHeight;
+
+ ctx.fillStyle = this.fillColor;
+
+ // Custom Tooltips
+ if(this.custom){
+ this.custom(this);
+ }
+ else{
+ switch(this.yAlign)
+ {
+ case "above":
+ //Draw a caret above the x/y
+ ctx.beginPath();
+ ctx.moveTo(this.x,this.y - caretPadding);
+ ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
+ ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case "below":
+ tooltipY = this.y + caretPadding + this.caretHeight;
+ //Draw a caret below the x/y
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y + caretPadding);
+ ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
+ ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ }
+
+ switch(this.xAlign)
+ {
+ case "left":
+ tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
+ break;
+ case "right":
+ tooltipX = this.x - (this.cornerRadius + this.caretHeight);
+ break;
+ }
+
+ drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
+
+ ctx.fill();
+
+ ctx.fillStyle = this.textColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
+ }
+ }
+ });
+
+ Chart.MultiTooltip = Chart.Element.extend({
+ initialize : function(){
+ this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
+
+ this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);
+
+ this.titleHeight = this.title ? this.titleFontSize * 1.5 : 0;
+ this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleHeight;
+
+ this.ctx.font = this.titleFont;
+
+ var titleWidth = this.ctx.measureText(this.title).width,
+ //Label has a legend square as well so account for this.
+ labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3,
+ longestTextWidth = max([labelWidth,titleWidth]);
+
+ this.width = longestTextWidth + (this.xPadding*2);
+
+
+ var halfHeight = this.height/2;
+
+ //Check to ensure the height will fit on the canvas
+ if (this.y - halfHeight < 0 ){
+ this.y = halfHeight;
+ } else if (this.y + halfHeight > this.chart.height){
+ this.y = this.chart.height - halfHeight;
+ }
+
+ //Decide whether to align left or right based on position on canvas
+ if (this.x > this.chart.width/2){
+ this.x -= this.xOffset + this.width;
+ } else {
+ this.x += this.xOffset;
+ }
+
+
+ },
+ getLineHeight : function(index){
+ var baseLineHeight = this.y - (this.height/2) + this.yPadding,
+ afterTitleIndex = index-1;
+
+ //If the index is zero, we're getting the title
+ if (index === 0){
+ return baseLineHeight + this.titleHeight / 3;
+ } else{
+ return baseLineHeight + ((this.fontSize * 1.5 * afterTitleIndex) + this.fontSize / 2) + this.titleHeight;
+ }
+
+ },
+ draw : function(){
+ // Custom Tooltips
+ if(this.custom){
+ this.custom(this);
+ }
+ else{
+ drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
+ var ctx = this.ctx;
+ ctx.fillStyle = this.fillColor;
+ ctx.fill();
+ ctx.closePath();
+
+ ctx.textAlign = "left";
+ ctx.textBaseline = "middle";
+ ctx.fillStyle = this.titleTextColor;
+ ctx.font = this.titleFont;
+
+ ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
+
+ ctx.font = this.font;
+ helpers.each(this.labels,function(label,index){
+ ctx.fillStyle = this.textColor;
+ ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
+
+ //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
+ //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+ //Instead we'll make a white filled block to put the legendColour palette over.
+
+ ctx.fillStyle = this.legendColorBackground;
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+
+ ctx.fillStyle = this.legendColors[index].fill;
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
+
+
+ },this);
+ }
+ }
+ });
+
+ Chart.Scale = Chart.Element.extend({
+ initialize : function(){
+ this.fit();
+ },
+ buildYLabels : function(){
+ this.yLabels = [];
+
+ var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
+
+ for (var i=0; i<=this.steps; i++){
+ this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
+ }
+ this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) + 10 : 0;
+ },
+ addXLabel : function(label){
+ this.xLabels.push(label);
+ this.valuesCount++;
+ this.fit();
+ },
+ removeXLabel : function(){
+ this.xLabels.shift();
+ this.valuesCount--;
+ this.fit();
+ },
+ // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use
+ fit: function(){
+ // First we need the width of the yLabels, assuming the xLabels aren't rotated
+
+ // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation
+ this.startPoint = (this.display) ? this.fontSize : 0;
+ this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels
+
+ // Apply padding settings to the start and end point.
+ this.startPoint += this.padding;
+ this.endPoint -= this.padding;
+
+ // Cache the starting endpoint, excluding the space for x labels
+ var cachedEndPoint = this.endPoint;
+
+ // Cache the starting height, so can determine if we need to recalculate the scale yAxis
+ var cachedHeight = this.endPoint - this.startPoint,
+ cachedYLabelWidth;
+
+ // Build the current yLabels so we have an idea of what size they'll be to start
+ /*
+ * This sets what is returned from calculateScaleRange as static properties of this class:
+ *
+ this.steps;
+ this.stepValue;
+ this.min;
+ this.max;
+ *
+ */
+ this.calculateYRange(cachedHeight);
+
+ // With these properties set we can now build the array of yLabels
+ // and also the width of the largest yLabel
+ this.buildYLabels();
+
+ this.calculateXLabelRotation();
+
+ while((cachedHeight > this.endPoint - this.startPoint)){
+ cachedHeight = this.endPoint - this.startPoint;
+ cachedYLabelWidth = this.yLabelWidth;
+
+ this.calculateYRange(cachedHeight);
+ this.buildYLabels();
+
+ // Only go through the xLabel loop again if the yLabel width has changed
+ if (cachedYLabelWidth < this.yLabelWidth){
+ this.endPoint = cachedEndPoint;
+ this.calculateXLabelRotation();
+ }
+ }
+
+ },
+ calculateXLabelRotation : function(){
+ //Get the width of each grid by calculating the difference
+ //between x offsets between 0 and 1.
+
+ this.ctx.font = this.font;
+
+ var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
+ lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
+ firstRotated,
+ lastRotated;
+
+
+ this.xScalePaddingRight = lastWidth/2 + 3;
+ this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth) ? firstWidth/2 : this.yLabelWidth;
+
+ this.xLabelRotation = 0;
+ if (this.display){
+ var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),
+ cosRotation,
+ firstRotatedWidth;
+ this.xLabelWidth = originalLabelWidth;
+ //Allow 3 pixels x2 padding either side for label readability
+ var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
+
+ //Max label rotate should be 90 - also act as a loop counter
+ while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){
+ cosRotation = Math.cos(toRadians(this.xLabelRotation));
+
+ firstRotated = cosRotation * firstWidth;
+ lastRotated = cosRotation * lastWidth;
+
+ // We're right aligning the text now.
+ if (firstRotated + this.fontSize / 2 > this.yLabelWidth){
+ this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
+ }
+ this.xScalePaddingRight = this.fontSize/2;
+
+
+ this.xLabelRotation++;
+ this.xLabelWidth = cosRotation * originalLabelWidth;
+
+ }
+ if (this.xLabelRotation > 0){
+ this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3;
+ }
+ }
+ else{
+ this.xLabelWidth = 0;
+ this.xScalePaddingRight = this.padding;
+ this.xScalePaddingLeft = this.padding;
+ }
+
+ },
+ // Needs to be overidden in each Chart type
+ // Otherwise we need to pass all the data into the scale class
+ calculateYRange: noop,
+ drawingArea: function(){
+ return this.startPoint - this.endPoint;
+ },
+ calculateY : function(value){
+ var scalingFactor = this.drawingArea() / (this.min - this.max);
+ return this.endPoint - (scalingFactor * (value - this.min));
+ },
+ calculateX : function(index){
+ var isRotated = (this.xLabelRotation > 0),
+ // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
+ innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
+ valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
+ valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
+
+ if (this.offsetGridLines){
+ valueOffset += (valueWidth/2);
+ }
+
+ return Math.round(valueOffset);
+ },
+ update : function(newProps){
+ helpers.extend(this, newProps);
+ this.fit();
+ },
+ draw : function(){
+ var ctx = this.ctx,
+ yLabelGap = (this.endPoint - this.startPoint) / this.steps,
+ xStart = Math.round(this.xScalePaddingLeft);
+ if (this.display){
+ ctx.fillStyle = this.textColor;
+ ctx.font = this.font;
+ each(this.yLabels,function(labelString,index){
+ var yLabelCenter = this.endPoint - (yLabelGap * index),
+ linePositionY = Math.round(yLabelCenter),
+ drawHorizontalLine = this.showHorizontalLines;
+
+ ctx.textAlign = "right";
+ ctx.textBaseline = "middle";
+ if (this.showLabels){
+ ctx.fillText(labelString,xStart - 10,yLabelCenter);
+ }
+
+ // This is X axis, so draw it
+ if (index === 0 && !drawHorizontalLine){
+ drawHorizontalLine = true;
+ }
+
+ if (drawHorizontalLine){
+ ctx.beginPath();
+ }
+
+ if (index > 0){
+ // This is a grid line in the centre, so drop that
+ ctx.lineWidth = this.gridLineWidth;
+ ctx.strokeStyle = this.gridLineColor;
+ } else {
+ // This is the first line on the scale
+ ctx.lineWidth = this.lineWidth;
+ ctx.strokeStyle = this.lineColor;
+ }
+
+ linePositionY += helpers.aliasPixel(ctx.lineWidth);
+
+ if(drawHorizontalLine){
+ ctx.moveTo(xStart, linePositionY);
+ ctx.lineTo(this.width, linePositionY);
+ ctx.stroke();
+ ctx.closePath();
+ }
+
+ ctx.lineWidth = this.lineWidth;
+ ctx.strokeStyle = this.lineColor;
+ ctx.beginPath();
+ ctx.moveTo(xStart - 5, linePositionY);
+ ctx.lineTo(xStart, linePositionY);
+ ctx.stroke();
+ ctx.closePath();
+
+ },this);
+
+ each(this.xLabels,function(label,index){
+ var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
+ // Check to see if line/bar here and decide where to place the line
+ linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
+ isRotated = (this.xLabelRotation > 0),
+ drawVerticalLine = this.showVerticalLines;
+
+ // This is Y axis, so draw it
+ if (index === 0 && !drawVerticalLine){
+ drawVerticalLine = true;
+ }
+
+ if (drawVerticalLine){
+ ctx.beginPath();
+ }
+
+ if (index > 0){
+ // This is a grid line in the centre, so drop that
+ ctx.lineWidth = this.gridLineWidth;
+ ctx.strokeStyle = this.gridLineColor;
+ } else {
+ // This is the first line on the scale
+ ctx.lineWidth = this.lineWidth;
+ ctx.strokeStyle = this.lineColor;
+ }
+
+ if (drawVerticalLine){
+ ctx.moveTo(linePos,this.endPoint);
+ ctx.lineTo(linePos,this.startPoint - 3);
+ ctx.stroke();
+ ctx.closePath();
+ }
+
+
+ ctx.lineWidth = this.lineWidth;
+ ctx.strokeStyle = this.lineColor;
+
+
+ // Small lines at the bottom of the base grid line
+ ctx.beginPath();
+ ctx.moveTo(linePos,this.endPoint);
+ ctx.lineTo(linePos,this.endPoint + 5);
+ ctx.stroke();
+ ctx.closePath();
+
+ ctx.save();
+ ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);
+ ctx.rotate(toRadians(this.xLabelRotation)*-1);
+ ctx.font = this.font;
+ ctx.textAlign = (isRotated) ? "right" : "center";
+ ctx.textBaseline = (isRotated) ? "middle" : "top";
+ ctx.fillText(label, 0, 0);
+ ctx.restore();
+ },this);
+
+ }
+ }
+
+ });
+
+ Chart.RadialScale = Chart.Element.extend({
+ initialize: function(){
+ this.size = min([this.height, this.width]);
+ this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
+ },
+ calculateCenterOffset: function(value){
+ // Take into account half font size + the yPadding of the top value
+ var scalingFactor = this.drawingArea / (this.max - this.min);
+
+ return (value - this.min) * scalingFactor;
+ },
+ update : function(){
+ if (!this.lineArc){
+ this.setScaleSize();
+ } else {
+ this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
+ }
+ this.buildYLabels();
+ },
+ buildYLabels: function(){
+ this.yLabels = [];
+
+ var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
+
+ for (var i=0; i<=this.steps; i++){
+ this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
+ }
+ },
+ getCircumference : function(){
+ return ((Math.PI*2) / this.valuesCount);
+ },
+ setScaleSize: function(){
+ /*
+ * Right, this is really confusing and there is a lot of maths going on here
+ * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
+ *
+ * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
+ *
+ * Solution:
+ *
+ * We assume the radius of the polygon is half the size of the canvas at first
+ * at each index we check if the text overlaps.
+ *
+ * Where it does, we store that angle and that index.
+ *
+ * After finding the largest index and angle we calculate how much we need to remove
+ * from the shape radius to move the point inwards by that x.
+ *
+ * We average the left and right distances to get the maximum shape radius that can fit in the box
+ * along with labels.
+ *
+ * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
+ * on each side, removing that from the size, halving it and adding the left x protrusion width.
+ *
+ * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
+ * and position it in the most space efficient manner
+ *
+ * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
+ */
+
+
+ // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
+ // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
+ var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]),
+ pointPosition,
+ i,
+ textWidth,
+ halfTextWidth,
+ furthestRight = this.width,
+ furthestRightIndex,
+ furthestRightAngle,
+ furthestLeft = 0,
+ furthestLeftIndex,
+ furthestLeftAngle,
+ xProtrusionLeft,
+ xProtrusionRight,
+ radiusReductionRight,
+ radiusReductionLeft,
+ maxWidthRadius;
+ this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
+ for (i=0;i<this.valuesCount;i++){
+ // 5px to space the text slightly out - similar to what we do in the draw function.
+ pointPosition = this.getPointPosition(i, largestPossibleRadius);
+ textWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5;
+ if (i === 0 || i === this.valuesCount/2){
+ // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
+ // of the radar chart, so text will be aligned centrally, so we'll half it and compare
+ // w/left and right text sizes
+ halfTextWidth = textWidth/2;
+ if (pointPosition.x + halfTextWidth > furthestRight) {
+ furthestRight = pointPosition.x + halfTextWidth;
+ furthestRightIndex = i;
+ }
+ if (pointPosition.x - halfTextWidth < furthestLeft) {
+ furthestLeft = pointPosition.x - halfTextWidth;
+ furthestLeftIndex = i;
+ }
+ }
+ else if (i < this.valuesCount/2) {
+ // Less than half the values means we'll left align the text
+ if (pointPosition.x + textWidth > furthestRight) {
+ furthestRight = pointPosition.x + textWidth;
+ furthestRightIndex = i;
+ }
+ }
+ else if (i > this.valuesCount/2){
+ // More than half the values means we'll right align the text
+ if (pointPosition.x - textWidth < furthestLeft) {
+ furthestLeft = pointPosition.x - textWidth;
+ furthestLeftIndex = i;
+ }
+ }
+ }
+
+ xProtrusionLeft = furthestLeft;
+
+ xProtrusionRight = Math.ceil(furthestRight - this.width);
+
+ furthestRightAngle = this.getIndexAngle(furthestRightIndex);
+
+ furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
+
+ radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2);
+
+ radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2);
+
+ // Ensure we actually need to reduce the size of the chart
+ radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
+ radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
+
+ this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2;
+
+ //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
+ this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
+
+ },
+ setCenterPoint: function(leftMovement, rightMovement){
+
+ var maxRight = this.width - rightMovement - this.drawingArea,
+ maxLeft = leftMovement + this.drawingArea;
+
+ this.xCenter = (maxLeft + maxRight)/2;
+ // Always vertically in the centre as the text height doesn't change
+ this.yCenter = (this.height/2);
+ },
+
+ getIndexAngle : function(index){
+ var angleMultiplier = (Math.PI * 2) / this.valuesCount;
+ // Start from the top instead of right, so remove a quarter of the circle
+
+ return index * angleMultiplier - (Math.PI/2);
+ },
+ getPointPosition : function(index, distanceFromCenter){
+ var thisAngle = this.getIndexAngle(index);
+ return {
+ x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
+ y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
+ };
+ },
+ draw: function(){
+ if (this.display){
+ var ctx = this.ctx;
+ each(this.yLabels, function(label, index){
+ // Don't draw a centre value
+ if (index > 0){
+ var yCenterOffset = index * (this.drawingArea/this.steps),
+ yHeight = this.yCenter - yCenterOffset,
+ pointPosition;
+
+ // Draw circular lines around the scale
+ if (this.lineWidth > 0){
+ ctx.strokeStyle = this.lineColor;
+ ctx.lineWidth = this.lineWidth;
+
+ if(this.lineArc){
+ ctx.beginPath();
+ ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2);
+ ctx.closePath();
+ ctx.stroke();
+ } else{
+ ctx.beginPath();
+ for (var i=0;i<this.valuesCount;i++)
+ {
+ pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));
+ if (i === 0){
+ ctx.moveTo(pointPosition.x, pointPosition.y);
+ } else {
+ ctx.lineTo(pointPosition.x, pointPosition.y);
+ }
+ }
+ ctx.closePath();
+ ctx.stroke();
+ }
+ }
+ if(this.showLabels){
+ ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
+ if (this.showLabelBackdrop){
+ var labelWidth = ctx.measureText(label).width;
+ ctx.fillStyle = this.backdropColor;
+ ctx.fillRect(
+ this.xCenter - labelWidth/2 - this.backdropPaddingX,
+ yHeight - this.fontSize/2 - this.backdropPaddingY,
+ labelWidth + this.backdropPaddingX*2,
+ this.fontSize + this.backdropPaddingY*2
+ );
+ }
+ ctx.textAlign = 'center';
+ ctx.textBaseline = "middle";
+ ctx.fillStyle = this.fontColor;
+ ctx.fillText(label, this.xCenter, yHeight);
+ }
+ }
+ }, this);
+
+ if (!this.lineArc){
+ ctx.lineWidth = this.angleLineWidth;
+ ctx.strokeStyle = this.angleLineColor;
+ for (var i = this.valuesCount - 1; i >= 0; i--) {
+ var centerOffset = null, outerPosition = null;
+
+ if (this.angleLineWidth > 0 && (i % this.angleLineInterval === 0)){
+ centerOffset = this.calculateCenterOffset(this.max);
+ outerPosition = this.getPointPosition(i, centerOffset);
+ ctx.beginPath();
+ ctx.moveTo(this.xCenter, this.yCenter);
+ ctx.lineTo(outerPosition.x, outerPosition.y);
+ ctx.stroke();
+ ctx.closePath();
+ }
+
+ if (this.backgroundColors && this.backgroundColors.length == this.valuesCount) {
+ if (centerOffset == null)
+ centerOffset = this.calculateCenterOffset(this.max);
+
+ if (outerPosition == null)
+ outerPosition = this.getPointPosition(i, centerOffset);
+
+ var previousOuterPosition = this.getPointPosition(i === 0 ? this.valuesCount - 1 : i - 1, centerOffset);
+ var nextOuterPosition = this.getPointPosition(i === this.valuesCount - 1 ? 0 : i + 1, centerOffset);
+
+ var previousOuterHalfway = { x: (previousOuterPosition.x + outerPosition.x) / 2, y: (previousOuterPosition.y + outerPosition.y) / 2 };
+ var nextOuterHalfway = { x: (outerPosition.x + nextOuterPosition.x) / 2, y: (outerPosition.y + nextOuterPosition.y) / 2 };
+
+ ctx.beginPath();
+ ctx.moveTo(this.xCenter, this.yCenter);
+ ctx.lineTo(previousOuterHalfway.x, previousOuterHalfway.y);
+ ctx.lineTo(outerPosition.x, outerPosition.y);
+ ctx.lineTo(nextOuterHalfway.x, nextOuterHalfway.y);
+ ctx.fillStyle = this.backgroundColors[i];
+ ctx.fill();
+ ctx.closePath();
+ }
+ // Extra 3px out for some label spacing
+ var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
+ ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
+ ctx.fillStyle = this.pointLabelFontColor;
+
+ var labelsCount = this.labels.length,
+ halfLabelsCount = this.labels.length/2,
+ quarterLabelsCount = halfLabelsCount/2,
+ upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
+ exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
+ if (i === 0){
+ ctx.textAlign = 'center';
+ } else if(i === halfLabelsCount){
+ ctx.textAlign = 'center';
+ } else if (i < halfLabelsCount){
+ ctx.textAlign = 'left';
+ } else {
+ ctx.textAlign = 'right';
+ }
+
+ // Set the correct text baseline based on outer positioning
+ if (exactQuarter){
+ ctx.textBaseline = 'middle';
+ } else if (upperHalf){
+ ctx.textBaseline = 'bottom';
+ } else {
+ ctx.textBaseline = 'top';
+ }
+
+ ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
+ }
+ }
+ }
+ }
+ });
+
+ Chart.animationService = {
+ frameDuration: 17,
+ animations: [],
+ dropFrames: 0,
+ addAnimation: function(chartInstance, animationObject) {
+ for (var index = 0; index < this.animations.length; ++ index){
+ if (this.animations[index].chartInstance === chartInstance){
+ // replacing an in progress animation
+ this.animations[index].animationObject = animationObject;
+ return;
+ }
+ }
+
+ this.animations.push({
+ chartInstance: chartInstance,
+ animationObject: animationObject
+ });
+
+ // If there are no animations queued, manually kickstart a digest, for lack of a better word
+ if (this.animations.length == 1) {
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
+ }
+ },
+ // Cancel the animation for a given chart instance
+ cancelAnimation: function(chartInstance) {
+ var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
+ return animationWrapper.chartInstance === chartInstance;
+ });
+
+ if (index)
+ {
+ this.animations.splice(index, 1);
+ }
+ },
+ // calls startDigest with the proper context
+ digestWrapper: function() {
+ Chart.animationService.startDigest.call(Chart.animationService);
+ },
+ startDigest: function() {
+
+ var startTime = Date.now();
+ var framesToDrop = 0;
+
+ if(this.dropFrames > 1){
+ framesToDrop = Math.floor(this.dropFrames);
+ this.dropFrames -= framesToDrop;
+ }
+
+ for (var i = 0; i < this.animations.length; i++) {
+
+ if (this.animations[i].animationObject.currentStep === null){
+ this.animations[i].animationObject.currentStep = 0;
+ }
+
+ this.animations[i].animationObject.currentStep += 1 + framesToDrop;
+ if(this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps){
+ this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
+ }
+
+ this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
+
+ // Check if executed the last frame.
+ if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps){
+ // Call onAnimationComplete
+ this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance);
+ // Remove the animation.
+ this.animations.splice(i, 1);
+ // Keep the index in place to offset the splice
+ i--;
+ }
+ }
+
+ var endTime = Date.now();
+ var delay = endTime - startTime - this.frameDuration;
+ var frameDelay = delay / this.frameDuration;
+
+ if(frameDelay > 1){
+ this.dropFrames += frameDelay;
+ }
+
+ // Do we have more stuff to animate?
+ if (this.animations.length > 0){
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
+ }
+ }
+ };
+
+ // Attach global event to resize each chart instance when the browser resizes
+ helpers.addEvent(window, "resize", (function(){
+ // Basic debounce of resize function so it doesn't hurt performance when resizing browser.
+ var timeout;
+ return function(){
+ clearTimeout(timeout);
+ timeout = setTimeout(function(){
+ each(Chart.instances,function(instance){
+ // If the responsive flag is set in the chart instance config
+ // Cascade the resize event down to the chart.
+ if (instance.options.responsive){
+ instance.resize(instance.render, true);
+ }
+ });
+ }, 50);
+ };
+ })());
+
+
+ if (amd) {
+ define('Chart', [], function(){
+ return Chart;
+ });
+ } else if (typeof module === 'object' && module.exports) {
+ module.exports = Chart;
+ }
+
+ root.Chart = Chart;
+
+ Chart.noConflict = function(){
+ root.Chart = previous;
+ return Chart;
+ };
+
+}).call(this);
+
+(function(){
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ helpers = Chart.helpers;
+
+
+ var defaultConfig = {
+ //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
+ scaleBeginAtZero : true,
+
+ //Boolean - Whether grid lines are shown across the chart
+ scaleShowGridLines : true,
+
+ //String - Colour of the grid lines
+ scaleGridLineColor : "rgba(0,0,0,.05)",
+
+ //Number - Width of the grid lines
+ scaleGridLineWidth : 1,
+
+ //Boolean - Whether to show horizontal lines (except X axis)
+ scaleShowHorizontalLines: true,
+
+ //Boolean - Whether to show vertical lines (except Y axis)
+ scaleShowVerticalLines: true,
+
+ //Boolean - If there is a stroke on each bar
+ barShowStroke : true,
+
+ //Number - Pixel width of the bar stroke
+ barStrokeWidth : 2,
+
+ //Number - Spacing between each of the X value sets
+ barValueSpacing : 5,
+
+ //Number - Spacing between data sets within X values
+ barDatasetSpacing : 1,
+
+ //String - A legend template
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
+
+ };
+
+
+ Chart.Type.extend({
+ name: "Bar",
+ defaults : defaultConfig,
+ initialize: function(data){
+
+ //Expose options as a scope variable here so we can access it in the ScaleClass
+ var options = this.options;
+
+ this.ScaleClass = Chart.Scale.extend({
+ offsetGridLines : true,
+ calculateBarX : function(datasetCount, datasetIndex, barIndex){
+ //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
+ var xWidth = this.calculateBaseWidth(),
+ xAbsolute = this.calculateX(barIndex) - (xWidth/2),
+ barWidth = this.calculateBarWidth(datasetCount);
+
+ return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
+ },
+ calculateBaseWidth : function(){
+ return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
+ },
+ calculateBarWidth : function(datasetCount){
+ //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
+ var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
+
+ return (baseWidth / datasetCount);
+ }
+ });
+
+ this.datasets = [];
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
+
+ this.eachBars(function(bar){
+ bar.restore(['fillColor', 'strokeColor']);
+ });
+ helpers.each(activeBars, function(activeBar){
+ if (activeBar) {
+ activeBar.fillColor = activeBar.highlightFill;
+ activeBar.strokeColor = activeBar.highlightStroke;
+ }
+ });
+ this.showTooltip(activeBars);
+ });
+ }
+
+ //Declare the extension of the default point, to cater for the options passed in to the constructor
+ this.BarClass = Chart.Rectangle.extend({
+ strokeWidth : this.options.barStrokeWidth,
+ showStroke : this.options.barShowStroke,
+ ctx : this.chart.ctx
+ });
+
+ //Iterate through each of the datasets, and build this into a property of the chart
+ helpers.each(data.datasets,function(dataset,datasetIndex){
+
+ var datasetObject = {
+ label : dataset.label || null,
+ fillColor : dataset.fillColor,
+ strokeColor : dataset.strokeColor,
+ bars : []
+ };
+
+ this.datasets.push(datasetObject);
+
+ helpers.each(dataset.data,function(dataPoint,index){
+ //Add a new point for each piece of data, passing any required data to draw.
+ datasetObject.bars.push(new this.BarClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : (typeof dataset.strokeColor == 'object') ? dataset.strokeColor[index] : dataset.strokeColor,
+ fillColor : (typeof dataset.fillColor == 'object') ? dataset.fillColor[index] : dataset.fillColor,
+ highlightFill : (dataset.highlightFill) ? (typeof dataset.highlightFill == 'object') ? dataset.highlightFill[index] : dataset.highlightFill : (typeof dataset.fillColor == 'object') ? dataset.fillColor[index] : dataset.fillColor,
+ highlightStroke : (dataset.highlightStroke) ? (typeof dataset.highlightStroke == 'object') ? dataset.highlightStroke[index] : dataset.highlightStroke : (typeof dataset.strokeColor == 'object') ? dataset.strokeColor[index] : dataset.strokeColor
+ }));
+ },this);
+
+ },this);
+
+ this.buildScale(data.labels);
+
+ this.BarClass.prototype.base = this.scale.endPoint;
+
+ this.eachBars(function(bar, index, datasetIndex){
+ helpers.extend(bar, {
+ width : this.scale.calculateBarWidth(this.datasets.length),
+ x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
+ y: this.scale.endPoint
+ });
+ bar.save();
+ }, this);
+
+ this.render();
+ },
+ update : function(){
+ this.scale.update();
+ // Reset any highlight colours before updating.
+ helpers.each(this.activeElements, function(activeElement){
+ activeElement.restore(['fillColor', 'strokeColor']);
+ });
+
+ this.eachBars(function(bar){
+ bar.save();
+ });
+ this.render();
+ },
+ eachBars : function(callback){
+ helpers.each(this.datasets,function(dataset, datasetIndex){
+ helpers.each(dataset.bars, callback, this, datasetIndex);
+ },this);
+ },
+ getBarsAtEvent : function(e){
+ var barsArray = [],
+ eventPosition = helpers.getRelativePosition(e),
+ datasetIterator = function(dataset){
+ barsArray.push(dataset.bars[barIndex]);
+ },
+ barIndex;
+
+ for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
+ for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
+ if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
+ helpers.each(this.datasets, datasetIterator);
+ return barsArray;
+ }
+ }
+ }
+
+ return barsArray;
+ },
+ buildScale : function(labels){
+ var self = this;
+
+ var dataTotal = function(){
+ var values = [];
+ self.eachBars(function(bar){
+ values.push(bar.value);
+ });
+ return values;
+ };
+
+ var scaleOptions = {
+ templateString : this.options.scaleLabel,
+ height : this.chart.height,
+ width : this.chart.width,
+ ctx : this.chart.ctx,
+ textColor : this.options.scaleFontColor,
+ fontSize : this.options.scaleFontSize,
+ fontStyle : this.options.scaleFontStyle,
+ fontFamily : this.options.scaleFontFamily,
+ valuesCount : labels.length,
+ beginAtZero : this.options.scaleBeginAtZero,
+ integersOnly : this.options.scaleIntegersOnly,
+ calculateYRange: function(currentHeight){
+ var updatedRanges = helpers.calculateScaleRange(
+ dataTotal(),
+ currentHeight,
+ this.fontSize,
+ this.beginAtZero,
+ this.integersOnly
+ );
+ helpers.extend(this, updatedRanges);
+ },
+ xLabels : labels,
+ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
+ lineWidth : this.options.scaleLineWidth,
+ lineColor : this.options.scaleLineColor,
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
+ showVerticalLines : this.options.scaleShowVerticalLines,
+ gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
+ gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
+ padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
+ showLabels : this.options.scaleShowLabels,
+ display : this.options.showScale
+ };
+
+ if (this.options.scaleOverride){
+ helpers.extend(scaleOptions, {
+ calculateYRange: helpers.noop,
+ steps: this.options.scaleSteps,
+ stepValue: this.options.scaleStepWidth,
+ min: this.options.scaleStartValue,
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
+ });
+ }
+
+ this.scale = new this.ScaleClass(scaleOptions);
+ },
+ addData : function(valuesArray,label){
+ //Map the values array for each of the datasets
+ helpers.each(valuesArray,function(value,datasetIndex){
+ //Add a new point for each piece of data, passing any required data to draw.
+ this.datasets[datasetIndex].bars.push(new this.BarClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
+ y: this.scale.endPoint,
+ width : this.scale.calculateBarWidth(this.datasets.length),
+ base : this.scale.endPoint,
+ strokeColor : this.datasets[datasetIndex].strokeColor,
+ fillColor : this.datasets[datasetIndex].fillColor
+ }));
+ },this);
+
+ this.scale.addXLabel(label);
+ //Then re-render the chart.
+ this.update();
+ },
+ removeData : function(){
+ this.scale.removeXLabel();
+ //Then re-render the chart.
+ helpers.each(this.datasets,function(dataset){
+ dataset.bars.shift();
+ },this);
+ this.update();
+ },
+ reflow : function(){
+ helpers.extend(this.BarClass.prototype,{
+ y: this.scale.endPoint,
+ base : this.scale.endPoint
+ });
+ var newScaleProps = helpers.extend({
+ height : this.chart.height,
+ width : this.chart.width
+ });
+ this.scale.update(newScaleProps);
+ },
+ draw : function(ease){
+ var easingDecimal = ease || 1;
+ this.clear();
+
+ var ctx = this.chart.ctx;
+
+ this.scale.draw(easingDecimal);
+
+ //Draw all the bars for each dataset
+ helpers.each(this.datasets,function(dataset,datasetIndex){
+ helpers.each(dataset.bars,function(bar,index){
+ if (bar.hasValue()){
+ bar.base = this.scale.endPoint;
+ //Transition then draw
+ bar.transition({
+ x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
+ y : this.scale.calculateY(bar.value),
+ width : this.scale.calculateBarWidth(this.datasets.length)
+ }, easingDecimal).draw();
+ }
+ },this);
+
+ },this);
+ }
+ });
+
+
+}).call(this);
+
+(function(){
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ //Cache a local reference to Chart.helpers
+ helpers = Chart.helpers;
+
+ var defaultConfig = {
+ //Boolean - Whether we should show a stroke on each segment
+ segmentShowStroke : true,
+
+ //String - The colour of each segment stroke
+ segmentStrokeColor : "#fff",
+
+ //Number - The width of each segment stroke
+ segmentStrokeWidth : 2,
+
+ //The percentage of the chart that we cut out of the middle.
+ percentageInnerCutout : 50,
+
+ //Number - Amount of animation steps
+ animationSteps : 100,
+
+ //String - Animation easing effect
+ animationEasing : "easeOutBounce",
+
+ //Boolean - Whether we animate the rotation of the Doughnut
+ animateRotate : true,
+
+ //Boolean - Whether we animate scaling the Doughnut from the centre
+ animateScale : false,
+
+ //String - A legend template
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=segments[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
+
+ };
+
+ Chart.Type.extend({
+ //Passing in a name registers this chart in the Chart namespace
+ name: "Doughnut",
+ //Providing a defaults will also register the defaults in the chart namespace
+ defaults : defaultConfig,
+ //Initialize is fired when the chart is initialized - Data is passed in as a parameter
+ //Config is automatically merged by the core of Chart.js, and is available at this.options
+ initialize: function(data){
+
+ //Declare segments as a static property to prevent inheriting across the Chart type prototype
+ this.segments = [];
+ this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
+
+ this.SegmentArc = Chart.Arc.extend({
+ ctx : this.chart.ctx,
+ x : this.chart.width/2,
+ y : this.chart.height/2
+ });
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
+
+ helpers.each(this.segments,function(segment){
+ segment.restore(["fillColor"]);
+ });
+ helpers.each(activeSegments,function(activeSegment){
+ activeSegment.fillColor = activeSegment.highlightColor;
+ });
+ this.showTooltip(activeSegments);
+ });
+ }
+ this.calculateTotal(data);
+
+ helpers.each(data,function(datapoint, index){
+ if (!datapoint.color) {
+ datapoint.color = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)';
+ }
+ this.addData(datapoint, index, true);
+ },this);
+
+ this.render();
+ },
+ getSegmentsAtEvent : function(e){
+ var segmentsArray = [];
+
+ var location = helpers.getRelativePosition(e);
+
+ helpers.each(this.segments,function(segment){
+ if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
+ },this);
+ return segmentsArray;
+ },
+ addData : function(segment, atIndex, silent){
+ var index = atIndex !== undefined ? atIndex : this.segments.length;
+ if ( typeof(segment.color) === "undefined" ) {
+ segment.color = Chart.defaults.global.segmentColorDefault[index % Chart.defaults.global.segmentColorDefault.length];
+ segment.highlight = Chart.defaults.global.segmentHighlightColorDefaults[index % Chart.defaults.global.segmentHighlightColorDefaults.length];
+ }
+ this.segments.splice(index, 0, new this.SegmentArc({
+ value : segment.value,
+ outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
+ innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
+ fillColor : segment.color,
+ highlightColor : segment.highlight || segment.color,
+ showStroke : this.options.segmentShowStroke,
+ strokeWidth : this.options.segmentStrokeWidth,
+ strokeColor : this.options.segmentStrokeColor,
+ startAngle : Math.PI * 1.5,
+ circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
+ label : segment.label
+ }));
+ if (!silent){
+ this.reflow();
+ this.update();
+ }
+ },
+ calculateCircumference : function(value) {
+ if ( this.total > 0 ) {
+ return (Math.PI*2)*(value / this.total);
+ } else {
+ return 0;
+ }
+ },
+ calculateTotal : function(data){
+ this.total = 0;
+ helpers.each(data,function(segment){
+ this.total += Math.abs(segment.value);
+ },this);
+ },
+ update : function(){
+ this.calculateTotal(this.segments);
+
+ // Reset any highlight colours before updating.
+ helpers.each(this.activeElements, function(activeElement){
+ activeElement.restore(['fillColor']);
+ });
+
+ helpers.each(this.segments,function(segment){
+ segment.save();
+ });
+ this.render();
+ },
+
+ removeData: function(atIndex){
+ var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
+ this.segments.splice(indexToDelete, 1);
+ this.reflow();
+ this.update();
+ },
+
+ reflow : function(){
+ helpers.extend(this.SegmentArc.prototype,{
+ x : this.chart.width/2,
+ y : this.chart.height/2
+ });
+ this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
+ helpers.each(this.segments, function(segment){
+ segment.update({
+ outerRadius : this.outerRadius,
+ innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
+ });
+ }, this);
+ },
+ draw : function(easeDecimal){
+ var animDecimal = (easeDecimal) ? easeDecimal : 1;
+ this.clear();
+ helpers.each(this.segments,function(segment,index){
+ segment.transition({
+ circumference : this.calculateCircumference(segment.value),
+ outerRadius : this.outerRadius,
+ innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
+ },animDecimal);
+
+ segment.endAngle = segment.startAngle + segment.circumference;
+
+ segment.draw();
+ if (index === 0){
+ segment.startAngle = Math.PI * 1.5;
+ }
+ //Check to see if it's the last segment, if not get the next and update the start angle
+ if (index < this.segments.length-1){
+ this.segments[index+1].startAngle = segment.endAngle;
+ }
+ },this);
+
+ }
+ });
+
+ Chart.types.Doughnut.extend({
+ name : "Pie",
+ defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
+ });
+
+}).call(this);
+
+(function(){
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ helpers = Chart.helpers;
+
+ var defaultConfig = {
+
+ ///Boolean - Whether grid lines are shown across the chart
+ scaleShowGridLines : true,
+
+ //String - Colour of the grid lines
+ scaleGridLineColor : "rgba(0,0,0,.05)",
+
+ //Number - Width of the grid lines
+ scaleGridLineWidth : 1,
+
+ //Boolean - Whether to show horizontal lines (except X axis)
+ scaleShowHorizontalLines: true,
+
+ //Boolean - Whether to show vertical lines (except Y axis)
+ scaleShowVerticalLines: true,
+
+ //Boolean - Whether the line is curved between points
+ bezierCurve : true,
+
+ //Number - Tension of the bezier curve between points
+ bezierCurveTension : 0.4,
+
+ //Boolean - Whether to show a dot for each point
+ pointDot : true,
+
+ //Number - Radius of each point dot in pixels
+ pointDotRadius : 4,
+
+ //Number - Pixel width of point dot stroke
+ pointDotStrokeWidth : 1,
+
+ //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
+ pointHitDetectionRadius : 20,
+
+ //Boolean - Whether to show a stroke for datasets
+ datasetStroke : true,
+
+ //Number - Pixel width of dataset stroke
+ datasetStrokeWidth : 2,
+
+ //Boolean - Whether to fill the dataset with a colour
+ datasetFill : true,
+
+ //String - A legend template
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].strokeColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>",
+
+ //Boolean - Whether to horizontally center the label and point dot inside the grid
+ offsetGridLines : false
+
+ };
+
+
+ Chart.Type.extend({
+ name: "Line",
+ defaults : defaultConfig,
+ initialize: function(data){
+ //Declare the extension of the default point, to cater for the options passed in to the constructor
+ this.PointClass = Chart.Point.extend({
+ offsetGridLines : this.options.offsetGridLines,
+ strokeWidth : this.options.pointDotStrokeWidth,
+ radius : this.options.pointDotRadius,
+ display: this.options.pointDot,
+ hitDetectionRadius : this.options.pointHitDetectionRadius,
+ ctx : this.chart.ctx,
+ inRange : function(mouseX){
+ return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
+ }
+ });
+
+ this.datasets = [];
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
+ this.eachPoints(function(point){
+ point.restore(['fillColor', 'strokeColor']);
+ });
+ helpers.each(activePoints, function(activePoint){
+ activePoint.fillColor = activePoint.highlightFill;
+ activePoint.strokeColor = activePoint.highlightStroke;
+ });
+ this.showTooltip(activePoints);
+ });
+ }
+
+ //Iterate through each of the datasets, and build this into a property of the chart
+ helpers.each(data.datasets,function(dataset){
+
+ var datasetObject = {
+ label : dataset.label || null,
+ fillColor : dataset.fillColor,
+ strokeColor : dataset.strokeColor,
+ pointColor : dataset.pointColor,
+ pointStrokeColor : dataset.pointStrokeColor,
+ points : []
+ };
+
+ this.datasets.push(datasetObject);
+
+
+ helpers.each(dataset.data,function(dataPoint,index){
+ //Add a new point for each piece of data, passing any required data to draw.
+ datasetObject.points.push(new this.PointClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ strokeColor : dataset.pointStrokeColor,
+ fillColor : dataset.pointColor,
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
+ }));
+ },this);
+
+ this.buildScale(data.labels);
+
+
+ this.eachPoints(function(point, index){
+ helpers.extend(point, {
+ x: this.scale.calculateX(index),
+ y: this.scale.endPoint
+ });
+ point.save();
+ }, this);
+
+ },this);
+
+
+ this.render();
+ },
+ update : function(){
+ this.scale.update();
+ // Reset any highlight colours before updating.
+ helpers.each(this.activeElements, function(activeElement){
+ activeElement.restore(['fillColor', 'strokeColor']);
+ });
+ this.eachPoints(function(point){
+ point.save();
+ });
+ this.render();
+ },
+ eachPoints : function(callback){
+ helpers.each(this.datasets,function(dataset){
+ helpers.each(dataset.points,callback,this);
+ },this);
+ },
+ getPointsAtEvent : function(e){
+ var pointsArray = [],
+ eventPosition = helpers.getRelativePosition(e);
+ helpers.each(this.datasets,function(dataset){
+ helpers.each(dataset.points,function(point){
+ if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
+ });
+ },this);
+ return pointsArray;
+ },
+ buildScale : function(labels){
+ var self = this;
+
+ var dataTotal = function(){
+ var values = [];
+ self.eachPoints(function(point){
+ values.push(point.value);
+ });
+
+ return values;
+ };
+
+ var scaleOptions = {
+ templateString : this.options.scaleLabel,
+ height : this.chart.height,
+ width : this.chart.width,
+ ctx : this.chart.ctx,
+ textColor : this.options.scaleFontColor,
+ offsetGridLines : this.options.offsetGridLines,
+ fontSize : this.options.scaleFontSize,
+ fontStyle : this.options.scaleFontStyle,
+ fontFamily : this.options.scaleFontFamily,
+ valuesCount : labels.length,
+ beginAtZero : this.options.scaleBeginAtZero,
+ integersOnly : this.options.scaleIntegersOnly,
+ calculateYRange : function(currentHeight){
+ var updatedRanges = helpers.calculateScaleRange(
+ dataTotal(),
+ currentHeight,
+ this.fontSize,
+ this.beginAtZero,
+ this.integersOnly
+ );
+ helpers.extend(this, updatedRanges);
+ },
+ xLabels : labels,
+ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
+ lineWidth : this.options.scaleLineWidth,
+ lineColor : this.options.scaleLineColor,
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
+ showVerticalLines : this.options.scaleShowVerticalLines,
+ gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
+ gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
+ padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
+ showLabels : this.options.scaleShowLabels,
+ display : this.options.showScale
+ };
+
+ if (this.options.scaleOverride){
+ helpers.extend(scaleOptions, {
+ calculateYRange: helpers.noop,
+ steps: this.options.scaleSteps,
+ stepValue: this.options.scaleStepWidth,
+ min: this.options.scaleStartValue,
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
+ });
+ }
+
+
+ this.scale = new Chart.Scale(scaleOptions);
+ },
+ addData : function(valuesArray,label){
+ //Map the values array for each of the datasets
+
+ helpers.each(valuesArray,function(value,datasetIndex){
+ //Add a new point for each piece of data, passing any required data to draw.
+ this.datasets[datasetIndex].points.push(new this.PointClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: this.scale.calculateX(this.scale.valuesCount+1),
+ y: this.scale.endPoint,
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
+ fillColor : this.datasets[datasetIndex].pointColor
+ }));
+ },this);
+
+ this.scale.addXLabel(label);
+ //Then re-render the chart.
+ this.update();
+ },
+ removeData : function(){
+ this.scale.removeXLabel();
+ //Then re-render the chart.
+ helpers.each(this.datasets,function(dataset){
+ dataset.points.shift();
+ },this);
+ this.update();
+ },
+ reflow : function(){
+ var newScaleProps = helpers.extend({
+ height : this.chart.height,
+ width : this.chart.width
+ });
+ this.scale.update(newScaleProps);
+ },
+ draw : function(ease){
+ var easingDecimal = ease || 1;
+ this.clear();
+
+ var ctx = this.chart.ctx;
+
+ // Some helper methods for getting the next/prev points
+ var hasValue = function(item){
+ return item.value !== null;
+ },
+ nextPoint = function(point, collection, index){
+ return helpers.findNextWhere(collection, hasValue, index) || point;
+ },
+ previousPoint = function(point, collection, index){
+ return helpers.findPreviousWhere(collection, hasValue, index) || point;
+ };
+
+ if (!this.scale) return;
+ this.scale.draw(easingDecimal);
+
+
+ helpers.each(this.datasets,function(dataset){
+ var pointsWithValues = helpers.where(dataset.points, hasValue);
+
+ //Transition each point first so that the line and point drawing isn't out of sync
+ //We can use this extra loop to calculate the control points of this dataset also in this loop
+
+ helpers.each(dataset.points, function(point, index){
+ if (point.hasValue()){
+ point.transition({
+ y : this.scale.calculateY(point.value),
+ x : this.scale.calculateX(index)
+ }, easingDecimal);
+ }
+ },this);
+
+
+ // Control points need to be calculated in a separate loop, because we need to know the current x/y of the point
+ // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
+ if (this.options.bezierCurve){
+ helpers.each(pointsWithValues, function(point, index){
+ var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
+ point.controlPoints = helpers.splineCurve(
+ previousPoint(point, pointsWithValues, index),
+ point,
+ nextPoint(point, pointsWithValues, index),
+ tension
+ );
+
+ // Prevent the bezier going outside of the bounds of the graph
+
+ // Cap puter bezier handles to the upper/lower scale bounds
+ if (point.controlPoints.outer.y > this.scale.endPoint){
+ point.controlPoints.outer.y = this.scale.endPoint;
+ }
+ else if (point.controlPoints.outer.y < this.scale.startPoint){
+ point.controlPoints.outer.y = this.scale.startPoint;
+ }
+
+ // Cap inner bezier handles to the upper/lower scale bounds
+ if (point.controlPoints.inner.y > this.scale.endPoint){
+ point.controlPoints.inner.y = this.scale.endPoint;
+ }
+ else if (point.controlPoints.inner.y < this.scale.startPoint){
+ point.controlPoints.inner.y = this.scale.startPoint;
+ }
+ },this);
+ }
+
+
+ //Draw the line between all the points
+ ctx.lineWidth = this.options.datasetStrokeWidth;
+ ctx.strokeStyle = dataset.strokeColor;
+ ctx.beginPath();
+
+ helpers.each(pointsWithValues, function(point, index){
+ if (index === 0){
+ ctx.moveTo(point.x, point.y);
+ }
+ else{
+ if(this.options.bezierCurve){
+ var previous = previousPoint(point, pointsWithValues, index);
+
+ ctx.bezierCurveTo(
+ previous.controlPoints.outer.x,
+ previous.controlPoints.outer.y,
+ point.controlPoints.inner.x,
+ point.controlPoints.inner.y,
+ point.x,
+ point.y
+ );
+ }
+ else{
+ ctx.lineTo(point.x,point.y);
+ }
+ }
+ }, this);
+
+ if (this.options.datasetStroke) {
+ ctx.stroke();
+ }
+
+ if (this.options.datasetFill && pointsWithValues.length > 0){
+ //Round off the line by going to the base of the chart, back to the start, then fill.
+ ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
+ ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
+ ctx.fillStyle = dataset.fillColor;
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ //Now draw the points over the line
+ //A little inefficient double looping, but better than the line
+ //lagging behind the point positions
+ helpers.each(pointsWithValues,function(point){
+ point.draw();
+ });
+ },this);
+ }
+ });
+
+
+}).call(this);
+
+(function(){
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ //Cache a local reference to Chart.helpers
+ helpers = Chart.helpers;
+
+ var defaultConfig = {
+ //Boolean - Show a backdrop to the scale label
+ scaleShowLabelBackdrop : true,
+
+ //String - The colour of the label backdrop
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
+
+ // Boolean - Whether the scale should begin at zero
+ scaleBeginAtZero : true,
+
+ //Number - The backdrop padding above & below the label in pixels
+ scaleBackdropPaddingY : 2,
+
+ //Number - The backdrop padding to the side of the label in pixels
+ scaleBackdropPaddingX : 2,
+
+ //Boolean - Show line for each value in the scale
+ scaleShowLine : true,
+
+ //Boolean - Stroke a line around each segment in the chart
+ segmentShowStroke : true,
+
+ //String - The colour of the stroke on each segment.
+ segmentStrokeColor : "#fff",
+
+ //Number - The width of the stroke value in pixels
+ segmentStrokeWidth : 2,
+
+ //Number - Amount of animation steps
+ animationSteps : 100,
+
+ //String - Animation easing effect.
+ animationEasing : "easeOutBounce",
+
+ //Boolean - Whether to animate the rotation of the chart
+ animateRotate : true,
+
+ //Boolean - Whether to animate scaling the chart from the centre
+ animateScale : false,
+
+ //String - A legend template
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=segments[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
+ };
+
+
+ Chart.Type.extend({
+ //Passing in a name registers this chart in the Chart namespace
+ name: "PolarArea",
+ //Providing a defaults will also register the defaults in the chart namespace
+ defaults : defaultConfig,
+ //Initialize is fired when the chart is initialized - Data is passed in as a parameter
+ //Config is automatically merged by the core of Chart.js, and is available at this.options
+ initialize: function(data){
+ this.segments = [];
+ //Declare segment class as a chart instance specific class, so it can share props for this instance
+ this.SegmentArc = Chart.Arc.extend({
+ showStroke : this.options.segmentShowStroke,
+ strokeWidth : this.options.segmentStrokeWidth,
+ strokeColor : this.options.segmentStrokeColor,
+ ctx : this.chart.ctx,
+ innerRadius : 0,
+ x : this.chart.width/2,
+ y : this.chart.height/2
+ });
+ this.scale = new Chart.RadialScale({
+ display: this.options.showScale,
+ fontStyle: this.options.scaleFontStyle,
+ fontSize: this.options.scaleFontSize,
+ fontFamily: this.options.scaleFontFamily,
+ fontColor: this.options.scaleFontColor,
+ showLabels: this.options.scaleShowLabels,
+ showLabelBackdrop: this.options.scaleShowLabelBackdrop,
+ backdropColor: this.options.scaleBackdropColor,
+ backdropPaddingY : this.options.scaleBackdropPaddingY,
+ backdropPaddingX: this.options.scaleBackdropPaddingX,
+ lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
+ lineColor: this.options.scaleLineColor,
+ lineArc: true,
+ width: this.chart.width,
+ height: this.chart.height,
+ xCenter: this.chart.width/2,
+ yCenter: this.chart.height/2,
+ ctx : this.chart.ctx,
+ templateString: this.options.scaleLabel,
+ valuesCount: data.length
+ });
+
+ this.updateScaleRange(data);
+
+ this.scale.update();
+
+ helpers.each(data,function(segment,index){
+ this.addData(segment,index,true);
+ },this);
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
+ helpers.each(this.segments,function(segment){
+ segment.restore(["fillColor"]);
+ });
+ helpers.each(activeSegments,function(activeSegment){
+ activeSegment.fillColor = activeSegment.highlightColor;
+ });
+ this.showTooltip(activeSegments);
+ });
+ }
+
+ this.render();
+ },
+ getSegmentsAtEvent : function(e){
+ var segmentsArray = [];
+
+ var location = helpers.getRelativePosition(e);
+
+ helpers.each(this.segments,function(segment){
+ if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
+ },this);
+ return segmentsArray;
+ },
+ addData : function(segment, atIndex, silent){
+ var index = atIndex || this.segments.length;
+
+ this.segments.splice(index, 0, new this.SegmentArc({
+ fillColor: segment.color,
+ highlightColor: segment.highlight || segment.color,
+ label: segment.label,
+ value: segment.value,
+ outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
+ circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
+ startAngle: Math.PI * 1.5
+ }));
+ if (!silent){
+ this.reflow();
+ this.update();
+ }
+ },
+ removeData: function(atIndex){
+ var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
+ this.segments.splice(indexToDelete, 1);
+ this.reflow();
+ this.update();
+ },
+ calculateTotal: function(data){
+ this.total = 0;
+ helpers.each(data,function(segment){
+ this.total += segment.value;
+ },this);
+ this.scale.valuesCount = this.segments.length;
+ },
+ updateScaleRange: function(datapoints){
+ var valuesArray = [];
+ helpers.each(datapoints,function(segment){
+ valuesArray.push(segment.value);
+ });
+
+ var scaleSizes = (this.options.scaleOverride) ?
+ {
+ steps: this.options.scaleSteps,
+ stepValue: this.options.scaleStepWidth,
+ min: this.options.scaleStartValue,
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
+ } :
+ helpers.calculateScaleRange(
+ valuesArray,
+ helpers.min([this.chart.width, this.chart.height])/2,
+ this.options.scaleFontSize,
+ this.options.scaleBeginAtZero,
+ this.options.scaleIntegersOnly
+ );
+
+ helpers.extend(
+ this.scale,
+ scaleSizes,
+ {
+ size: helpers.min([this.chart.width, this.chart.height]),
+ xCenter: this.chart.width/2,
+ yCenter: this.chart.height/2
+ }
+ );
+
+ },
+ update : function(){
+ this.calculateTotal(this.segments);
+
+ helpers.each(this.segments,function(segment){
+ segment.save();
+ });
+
+ this.reflow();
+ this.render();
+ },
+ reflow : function(){
+ helpers.extend(this.SegmentArc.prototype,{
+ x : this.chart.width/2,
+ y : this.chart.height/2
+ });
+ this.updateScaleRange(this.segments);
+ this.scale.update();
+
+ helpers.extend(this.scale,{
+ xCenter: this.chart.width/2,
+ yCenter: this.chart.height/2
+ });
+
+ helpers.each(this.segments, function(segment){
+ segment.update({
+ outerRadius : this.scale.calculateCenterOffset(segment.value)
+ });
+ }, this);
+
+ },
+ draw : function(ease){
+ var easingDecimal = ease || 1;
+ //Clear & draw the canvas
+ this.clear();
+ helpers.each(this.segments,function(segment, index){
+ segment.transition({
+ circumference : this.scale.getCircumference(),
+ outerRadius : this.scale.calculateCenterOffset(segment.value)
+ },easingDecimal);
+
+ segment.endAngle = segment.startAngle + segment.circumference;
+
+ // If we've removed the first segment we need to set the first one to
+ // start at the top.
+ if (index === 0){
+ segment.startAngle = Math.PI * 1.5;
+ }
+
+ //Check to see if it's the last segment, if not get the next and update the start angle
+ if (index < this.segments.length - 1){
+ this.segments[index+1].startAngle = segment.endAngle;
+ }
+ segment.draw();
+ }, this);
+ this.scale.draw();
+ }
+ });
+
+}).call(this);
+
+(function(){
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ helpers = Chart.helpers;
+
+
+
+ Chart.Type.extend({
+ name: "Radar",
+ defaults:{
+ //Boolean - Whether to show lines for each scale point
+ scaleShowLine : true,
+
+ //Boolean - Whether we show the angle lines out of the radar
+ angleShowLineOut : true,
+
+ //Boolean - Whether to show labels on the scale
+ scaleShowLabels : false,
+
+ // Boolean - Whether the scale should begin at zero
+ scaleBeginAtZero : true,
+
+ //String - Colour of the angle line
+ angleLineColor : "rgba(0,0,0,.1)",
+
+ //Number - Pixel width of the angle line
+ angleLineWidth : 1,
+
+ //Number - Interval at which to draw angle lines ("every Nth point")
+ angleLineInterval: 1,
+
+ //String - Point label font declaration
+ pointLabelFontFamily : "'Arial'",
+
+ //String - Point label font weight
+ pointLabelFontStyle : "normal",
+
+ //Number - Point label font size in pixels
+ pointLabelFontSize : 10,
+
+ //String - Point label font colour
+ pointLabelFontColor : "#666",
+
+ //Boolean - Whether to show a dot for each point
+ pointDot : true,
+
+ //Number - Radius of each point dot in pixels
+ pointDotRadius : 3,
+
+ //Number - Pixel width of point dot stroke
+ pointDotStrokeWidth : 1,
+
+ //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
+ pointHitDetectionRadius : 20,
+
+ //Boolean - Whether to show a stroke for datasets
+ datasetStroke : true,
+
+ //Number - Pixel width of dataset stroke
+ datasetStrokeWidth : 2,
+
+ //Boolean - Whether to fill the dataset with a colour
+ datasetFill : true,
+
+ //String - A legend template
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].strokeColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
+
+ },
+
+ initialize: function(data){
+ this.PointClass = Chart.Point.extend({
+ strokeWidth : this.options.pointDotStrokeWidth,
+ radius : this.options.pointDotRadius,
+ display: this.options.pointDot,
+ hitDetectionRadius : this.options.pointHitDetectionRadius,
+ ctx : this.chart.ctx
+ });
+
+ this.datasets = [];
+
+ this.buildScale(data);
+
+ //Set up tooltip events on the chart
+ if (this.options.showTooltips){
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
+ var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
+
+ this.eachPoints(function(point){
+ point.restore(['fillColor', 'strokeColor']);
+ });
+ helpers.each(activePointsCollection, function(activePoint){
+ activePoint.fillColor = activePoint.highlightFill;
+ activePoint.strokeColor = activePoint.highlightStroke;
+ });
+
+ this.showTooltip(activePointsCollection);
+ });
+ }
+
+ //Iterate through each of the datasets, and build this into a property of the chart
+ helpers.each(data.datasets,function(dataset){
+
+ var datasetObject = {
+ label: dataset.label || null,
+ fillColor : dataset.fillColor,
+ strokeColor : dataset.strokeColor,
+ pointColor : dataset.pointColor,
+ pointStrokeColor : dataset.pointStrokeColor,
+ points : []
+ };
+
+ this.datasets.push(datasetObject);
+
+ helpers.each(dataset.data,function(dataPoint,index){
+ //Add a new point for each piece of data, passing any required data to draw.
+ var pointPosition;
+ if (!this.scale.animation){
+ pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
+ }
+ datasetObject.points.push(new this.PointClass({
+ value : dataPoint,
+ label : data.labels[index],
+ datasetLabel: dataset.label,
+ x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
+ y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
+ strokeColor : dataset.pointStrokeColor,
+ fillColor : dataset.pointColor,
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
+ }));
+ },this);
+
+ },this);
+
+ this.render();
+ },
+ eachPoints : function(callback){
+ helpers.each(this.datasets,function(dataset){
+ helpers.each(dataset.points,callback,this);
+ },this);
+ },
+
+ getPointsAtEvent : function(evt){
+ var mousePosition = helpers.getRelativePosition(evt),
+ fromCenter = helpers.getAngleFromPoint({
+ x: this.scale.xCenter,
+ y: this.scale.yCenter
+ }, mousePosition);
+
+ var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,
+ pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
+ activePointsCollection = [];
+
+ // If we're at the top, make the pointIndex 0 to get the first of the array.
+ if (pointIndex >= this.scale.valuesCount || pointIndex < 0){
+ pointIndex = 0;
+ }
+
+ if (fromCenter.distance <= this.scale.drawingArea){
+ helpers.each(this.datasets, function(dataset){
+ activePointsCollection.push(dataset.points[pointIndex]);
+ });
+ }
+
+ return activePointsCollection;
+ },
+
+ buildScale : function(data){
+ this.scale = new Chart.RadialScale({
+ display: this.options.showScale,
+ fontStyle: this.options.scaleFontStyle,
+ fontSize: this.options.scaleFontSize,
+ fontFamily: this.options.scaleFontFamily,
+ fontColor: this.options.scaleFontColor,
+ showLabels: this.options.scaleShowLabels,
+ showLabelBackdrop: this.options.scaleShowLabelBackdrop,
+ backdropColor: this.options.scaleBackdropColor,
+ backgroundColors: this.options.scaleBackgroundColors,
+ backdropPaddingY : this.options.scaleBackdropPaddingY,
+ backdropPaddingX: this.options.scaleBackdropPaddingX,
+ lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
+ lineColor: this.options.scaleLineColor,
+ angleLineColor : this.options.angleLineColor,
+ angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
+ angleLineInterval: (this.options.angleLineInterval) ? this.options.angleLineInterval : 1,
+ // Point labels at the edge of each line
+ pointLabelFontColor : this.options.pointLabelFontColor,
+ pointLabelFontSize : this.options.pointLabelFontSize,
+ pointLabelFontFamily : this.options.pointLabelFontFamily,
+ pointLabelFontStyle : this.options.pointLabelFontStyle,
+ height : this.chart.height,
+ width: this.chart.width,
+ xCenter: this.chart.width/2,
+ yCenter: this.chart.height/2,
+ ctx : this.chart.ctx,
+ templateString: this.options.scaleLabel,
+ labels: data.labels,
+ valuesCount: data.datasets[0].data.length
+ });
+
+ this.scale.setScaleSize();
+ this.updateScaleRange(data.datasets);
+ this.scale.buildYLabels();
+ },
+ updateScaleRange: function(datasets){
+ var valuesArray = (function(){
+ var totalDataArray = [];
+ helpers.each(datasets,function(dataset){
+ if (dataset.data){
+ totalDataArray = totalDataArray.concat(dataset.data);
+ }
+ else {
+ helpers.each(dataset.points, function(point){
+ totalDataArray.push(point.value);
+ });
+ }
+ });
+ return totalDataArray;
+ })();
+
+
+ var scaleSizes = (this.options.scaleOverride) ?
+ {
+ steps: this.options.scaleSteps,
+ stepValue: this.options.scaleStepWidth,
+ min: this.options.scaleStartValue,
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
+ } :
+ helpers.calculateScaleRange(
+ valuesArray,
+ helpers.min([this.chart.width, this.chart.height])/2,
+ this.options.scaleFontSize,
+ this.options.scaleBeginAtZero,
+ this.options.scaleIntegersOnly
+ );
+
+ helpers.extend(
+ this.scale,
+ scaleSizes
+ );
+
+ },
+ addData : function(valuesArray,label){
+ //Map the values array for each of the datasets
+ this.scale.valuesCount++;
+ helpers.each(valuesArray,function(value,datasetIndex){
+ var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
+ this.datasets[datasetIndex].points.push(new this.PointClass({
+ value : value,
+ label : label,
+ datasetLabel: this.datasets[datasetIndex].label,
+ x: pointPosition.x,
+ y: pointPosition.y,
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
+ fillColor : this.datasets[datasetIndex].pointColor
+ }));
+ },this);
+
+ this.scale.labels.push(label);
+
+ this.reflow();
+
+ this.update();
+ },
+ removeData : function(){
+ this.scale.valuesCount--;
+ this.scale.labels.shift();
+ helpers.each(this.datasets,function(dataset){
+ dataset.points.shift();
+ },this);
+ this.reflow();
+ this.update();
+ },
+ update : function(){
+ this.eachPoints(function(point){
+ point.save();
+ });
+ this.reflow();
+ this.render();
+ },
+ reflow: function(){
+ helpers.extend(this.scale, {
+ width : this.chart.width,
+ height: this.chart.height,
+ size : helpers.min([this.chart.width, this.chart.height]),
+ xCenter: this.chart.width/2,
+ yCenter: this.chart.height/2
+ });
+ this.updateScaleRange(this.datasets);
+ this.scale.setScaleSize();
+ this.scale.buildYLabels();
+ },
+ draw : function(ease){
+ var easeDecimal = ease || 1,
+ ctx = this.chart.ctx;
+ this.clear();
+ this.scale.draw();
+
+ helpers.each(this.datasets,function(dataset){
+
+ //Transition each point first so that the line and point drawing isn't out of sync
+ helpers.each(dataset.points,function(point,index){
+ if (point.hasValue()){
+ point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
+ }
+ },this);
+
+
+
+ //Draw the line between all the points
+ ctx.lineWidth = this.options.datasetStrokeWidth;
+ ctx.strokeStyle = dataset.strokeColor;
+ ctx.beginPath();
+ helpers.each(dataset.points,function(point,index){
+ if (index === 0){
+ ctx.moveTo(point.x,point.y);
+ }
+ else{
+ ctx.lineTo(point.x,point.y);
+ }
+ },this);
+ ctx.closePath();
+ ctx.stroke();
+
+ ctx.fillStyle = dataset.fillColor;
+ if(this.options.datasetFill){
+ ctx.fill();
+ }
+ //Now draw the points over the line
+ //A little inefficient double looping, but better than the line
+ //lagging behind the point positions
+ helpers.each(dataset.points,function(point){
+ if (point.hasValue()){
+ point.draw();
+ }
+ });
+
+ },this);
+
+ }
+
+ });
+
+
+
+
+
+}).call(this);
+
+(function (factory) {
+ 'use strict';
+ if (typeof exports === 'object') {
+ // Node/CommonJS
+ module.exports = factory(
+ typeof angular !== 'undefined' ? angular : require('angular'),
+ typeof Chart !== 'undefined' ? Chart : require('chart.js'));
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['angular', 'chart'], factory);
+ } else {
+ // Browser globals
+ factory(angular, Chart);
+ }
+}(function (angular, Chart) {
+ 'use strict';
+
+ Chart.defaults.global.responsive = true;
+ Chart.defaults.global.multiTooltipTemplate = '<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>';
+
+ Chart.defaults.global.colours = [
+ '#97BBCD', // blue
+ '#DCDCDC', // light grey
+ '#F7464A', // red
+ '#46BFBD', // green
+ '#FDB45C', // yellow
+ '#949FB1', // grey
+ '#4D5360' // dark grey
+ ];
+
+ var usingExcanvas = typeof window.G_vmlCanvasManager === 'object' &&
+ window.G_vmlCanvasManager !== null &&
+ typeof window.G_vmlCanvasManager.initElement === 'function';
+
+ if (usingExcanvas) Chart.defaults.global.animation = false;
+
+ return angular.module('chart.js', [])
+ .provider('ChartJs', ChartJsProvider)
+ .factory('ChartJsFactory', ['ChartJs', '$timeout', ChartJsFactory])
+ .directive('chartBase', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory(); }])
+ .directive('chartLine', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Line'); }])
+ .directive('chartBar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Bar'); }])
+ .directive('chartRadar', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Radar'); }])
+ .directive('chartDoughnut', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Doughnut'); }])
+ .directive('chartPie', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('Pie'); }])
+ .directive('chartPolarArea', ['ChartJsFactory', function (ChartJsFactory) { return new ChartJsFactory('PolarArea'); }]);
+
+ /**
+ * Wrapper for chart.js
+ * Allows configuring chart js using the provider
+ *
+ * angular.module('myModule', ['chart.js']).config(function(ChartJsProvider) {
+ * ChartJsProvider.setOptions({ responsive: true });
+ * ChartJsProvider.setOptions('Line', { responsive: false });
+ * })))
+ */
+ function ChartJsProvider () {
+ var options = {};
+ var ChartJs = {
+ Chart: Chart,
+ getOptions: function (type) {
+ var typeOptions = type && options[type] || {};
+ return angular.extend({}, options, typeOptions);
+ }
+ };
+
+ /**
+ * Allow to set global options during configuration
+ */
+ this.setOptions = function (type, customOptions) {
+ // If no type was specified set option for the global object
+ if (! customOptions) {
+ customOptions = type;
+ options = angular.extend(options, customOptions);
+ return;
+ }
+ // Set options for the specific chart
+ options[type] = angular.extend(options[type] || {}, customOptions);
+ };
+
+ this.$get = function () {
+ return ChartJs;
+ };
+ }
+
+ function ChartJsFactory (ChartJs, $timeout) {
+ return function chart (type) {
+ return {
+ restrict: 'CA',
+ scope: {
+ data: '=?',
+ labels: '=?',
+ options: '=?',
+ series: '=?',
+ colours: '=?',
+ getColour: '=?',
+ chartType: '=',
+ legend: '@',
+ click: '=?',
+ hover: '=?',
+
+ chartData: '=?',
+ chartLabels: '=?',
+ chartOptions: '=?',
+ chartSeries: '=?',
+ chartColours: '=?',
+ chartLegend: '@',
+ chartClick: '=?',
+ chartHover: '=?'
+ },
+ link: function (scope, elem/*, attrs */) {
+ var chart, container = document.createElement('div');
+ container.className = 'chart-container';
+ elem.replaceWith(container);
+ container.appendChild(elem[0]);
+
+ if (usingExcanvas) window.G_vmlCanvasManager.initElement(elem[0]);
+
+ ['data', 'labels', 'options', 'series', 'colours', 'legend', 'click', 'hover'].forEach(deprecated);
+ function aliasVar (fromName, toName) {
+ scope.$watch(fromName, function (newVal) {
+ if (typeof newVal === 'undefined') return;
+ scope[toName] = newVal;
+ });
+ }
+ /* provide backward compatibility to "old" directive names, by
+ * having an alias point from the new names to the old names. */
+ aliasVar('chartData', 'data');
+ aliasVar('chartLabels', 'labels');
+ aliasVar('chartOptions', 'options');
+ aliasVar('chartSeries', 'series');
+ aliasVar('chartColours', 'colours');
+ aliasVar('chartLegend', 'legend');
+ aliasVar('chartClick', 'click');
+ aliasVar('chartHover', 'hover');
+
+ // Order of setting "watch" matter
+
+ scope.$watch('data', function (newVal, oldVal) {
+ if (! newVal || ! newVal.length || (Array.isArray(newVal[0]) && ! newVal[0].length)) {
+ destroyChart(chart, scope);
+ return;
+ }
+ var chartType = type || scope.chartType;
+ if (! chartType) return;
+
+ if (chart && canUpdateChart(newVal, oldVal))
+ return updateChart(chart, newVal, scope, elem);
+
+ createChart(chartType);
+ }, true);
+
+ scope.$watch('series', resetChart, true);
+ scope.$watch('labels', resetChart, true);
+ scope.$watch('options', resetChart, true);
+ scope.$watch('colours', resetChart, true);
+
+ scope.$watch('chartType', function (newVal, oldVal) {
+ if (isEmpty(newVal)) return;
+ if (angular.equals(newVal, oldVal)) return;
+ createChart(newVal);
+ });
+
+ scope.$on('$destroy', function () {
+ destroyChart(chart, scope);
+ });
+
+ function resetChart (newVal, oldVal) {
+ if (isEmpty(newVal)) return;
+ if (angular.equals(newVal, oldVal)) return;
+ var chartType = type || scope.chartType;
+ if (! chartType) return;
+
+ // chart.update() doesn't work for series and labels
+ // so we have to re-create the chart entirely
+ createChart(chartType);
+ }
+
+ function createChart (type) {
+ if (isResponsive(type, scope) && elem[0].clientHeight === 0 && container.clientHeight === 0) {
+ return $timeout(function () {
+ createChart(type);
+ }, 50, false);
+ }
+ if (! scope.data || ! scope.data.length) return;
+ scope.getColour = typeof scope.getColour === 'function' ? scope.getColour : getRandomColour;
+ var colours = getColours(type, scope);
+ var cvs = elem[0], ctx = cvs.getContext('2d');
+ var data = Array.isArray(scope.data[0]) ?
+ getDataSets(scope.labels, scope.data, scope.series || [], colours) :
+ getData(scope.labels, scope.data, colours);
+ var options = angular.extend({}, ChartJs.getOptions(type), scope.options);
+
+ // Destroy old chart if it exists to avoid ghost charts issue
+ // https://github.com/jtblin/angular-chart.js/issues/187
+ destroyChart(chart, scope);
+ chart = new ChartJs.Chart(ctx)[type](data, options);
+ scope.$emit('create', chart);
+
+ // Bind events
+ cvs.onclick = scope.click ? getEventHandler(scope, chart, 'click', false) : angular.noop;
+ cvs.onmousemove = scope.hover ? getEventHandler(scope, chart, 'hover', true) : angular.noop;
+
+ if (scope.legend && scope.legend !== 'false') setLegend(elem, chart);
+ }
+
+ function deprecated (attr) {
+ if (typeof console !== 'undefined' && ChartJs.getOptions().env !== 'test') {
+ var warn = typeof console.warn === 'function' ? console.warn : console.log;
+ if (!! scope[attr]) {
+ warn.call(console, '"%s" is deprecated and will be removed in a future version. ' +
+ 'Please use "chart-%s" instead.', attr, attr);
+ }
+ }
+ }
+ }
+ };
+ };
+
+ function canUpdateChart (newVal, oldVal) {
+ if (newVal && oldVal && newVal.length && oldVal.length) {
+ return Array.isArray(newVal[0]) ?
+ newVal.length === oldVal.length && newVal.every(function (element, index) {
+ return element.length === oldVal[index].length; }) :
+ oldVal.reduce(sum, 0) > 0 ? newVal.length === oldVal.length : false;
+ }
+ return false;
+ }
+
+ function sum (carry, val) {
+ return carry + val;
+ }
+
+ function getEventHandler (scope, chart, action, triggerOnlyOnChange) {
+ var lastState = null;
+ return function (evt) {
+ var atEvent = chart.getPointsAtEvent || chart.getBarsAtEvent || chart.getSegmentsAtEvent;
+ if (atEvent) {
+ var activePoints = atEvent.call(chart, evt);
+ if (triggerOnlyOnChange === false || angular.equals(lastState, activePoints) === false) {
+ lastState = activePoints;
+ scope[action](activePoints, evt);
+ scope.$apply();
+ }
+ }
+ };
+ }
+
+ function getColours (type, scope) {
+ var notEnoughColours = false;
+ var colours = angular.copy(scope.colours ||
+ ChartJs.getOptions(type).colours ||
+ Chart.defaults.global.colours
+ );
+ while (colours.length < scope.data.length) {
+ colours.push(scope.getColour());
+ notEnoughColours = true;
+ }
+ // mutate colours in this case as we don't want
+ // the colours to change on each refresh
+ if (notEnoughColours) scope.colours = colours;
+ return colours.map(convertColour);
+ }
+
+ function convertColour (colour) {
+ if (typeof colour === 'object' && colour !== null) return colour;
+ if (typeof colour === 'string' && colour[0] === '#') return getColour(hexToRgb(colour.substr(1)));
+ return getRandomColour();
+ }
+
+ function getRandomColour () {
+ var colour = [getRandomInt(0, 255), getRandomInt(0, 255), getRandomInt(0, 255)];
+ return getColour(colour);
+ }
+
+ function getColour (colour) {
+ return {
+ fillColor: rgba(colour, 0.2),
+ strokeColor: rgba(colour, 1),
+ pointColor: rgba(colour, 1),
+ pointStrokeColor: '#fff',
+ pointHighlightFill: '#fff',
+ pointHighlightStroke: rgba(colour, 0.8)
+ };
+ }
+
+ function getRandomInt (min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+
+ function rgba (colour, alpha) {
+ if (usingExcanvas) {
+ // rgba not supported by IE8
+ return 'rgb(' + colour.join(',') + ')';
+ } else {
+ return 'rgba(' + colour.concat(alpha).join(',') + ')';
+ }
+ }
+
+ // Credit: http://stackoverflow.com/a/11508164/1190235
+ function hexToRgb (hex) {
+ var bigint = parseInt(hex, 16),
+ r = (bigint >> 16) & 255,
+ g = (bigint >> 8) & 255,
+ b = bigint & 255;
+
+ return [r, g, b];
+ }
+
+ function getDataSets (labels, data, series, colours) {
+ return {
+ labels: labels,
+ datasets: data.map(function (item, i) {
+ return angular.extend({}, colours[i], {
+ label: series[i],
+ data: item
+ });
+ })
+ };
+ }
+
+ function getData (labels, data, colours) {
+ return labels.map(function (label, i) {
+ return angular.extend({}, colours[i], {
+ label: label,
+ value: data[i],
+ color: colours[i].strokeColor,
+ highlight: colours[i].pointHighlightStroke
+ });
+ });
+ }
+
+ function setLegend (elem, chart) {
+ var $parent = elem.parent(),
+ $oldLegend = $parent.find('chart-legend'),
+ legend = '<chart-legend>' + chart.generateLegend() + '</chart-legend>';
+ if ($oldLegend.length) $oldLegend.replaceWith(legend);
+ else $parent.append(legend);
+ }
+
+ function updateChart (chart, values, scope, elem) {
+ if (Array.isArray(scope.data[0])) {
+ chart.datasets.forEach(function (dataset, i) {
+ (dataset.points || dataset.bars).forEach(function (dataItem, j) {
+ dataItem.value = values[i][j];
+ });
+ });
+ } else {
+ chart.segments.forEach(function (segment, i) {
+ segment.value = values[i];
+ });
+ }
+ chart.update();
+ scope.$emit('update', chart);
+ if (scope.legend && scope.legend !== 'false') setLegend(elem, chart);
+ }
+
+ function isEmpty (value) {
+ return ! value ||
+ (Array.isArray(value) && ! value.length) ||
+ (typeof value === 'object' && ! Object.keys(value).length);
+ }
+
+ function isResponsive (type, scope) {
+ var options = angular.extend({}, Chart.defaults.global, ChartJs.getOptions(type), scope.options);
+ return options.responsive;
+ }
+
+ function destroyChart(chart, scope) {
+ if(! chart) return;
+ chart.destroy();
+ scope.$emit('destroy', chart);
+ }
+ }
+}));
diff --git a/xos/synchronizers/openstack/ansible.py b/xos/synchronizers/openstack/ansible.py
deleted file mode 120000
index c20dc8b..0000000
--- a/xos/synchronizers/openstack/ansible.py
+++ /dev/null
@@ -1 +0,0 @@
-../base/ansible.py
\ No newline at end of file
diff --git a/xos/synchronizers/openstack/backend.py b/xos/synchronizers/openstack/backend.py
deleted file mode 100644
index 5f11d46..0000000
--- a/xos/synchronizers/openstack/backend.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os
-import sys
-import threading
-import time
-from synchronizers.base.event_loop import XOSObserver
-from synchronizers.base.event_manager import EventListener
-from xos.logger import Logger, logging
-from synchronizers.model_policy import run_policy
-from xos.config import Config
-
-logger = Logger(level=logging.INFO)
-
-class Backend:
-
- def run(self):
- # start the openstack observer
- observer = XOSObserver()
- observer_thread = threading.Thread(target=observer.run)
- observer_thread.start()
-
- # start model policies thread
- observer_name = getattr(Config(), "observer_name", "")
- if (not observer_name):
- model_policy_thread = threading.Thread(target=run_policy)
- model_policy_thread.start()
- else:
- model_policy_thread = None
- print "Skipping model policies thread for service observer."
-
-
- # start event listene
- #event_manager = EventListener(wake_up=observer.wake_up)
- #event_manager_thread = threading.Thread(target=event_manager.run)
- #event_manager_thread.start()
-
- while True:
- try:
- time.sleep(1000)
- except KeyboardInterrupt:
- print "exiting due to keyboard interrupt"
- # TODO: See about setting the threads as daemons
- observer_thread._Thread__stop()
- if model_policy_thread:
- model_policy_thread._Thread__stop()
- sys.exit(1)
-
diff --git a/xos/synchronizers/openstack/deleter.py b/xos/synchronizers/openstack/deleter.py
deleted file mode 100644
index 93fa572..0000000
--- a/xos/synchronizers/openstack/deleter.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import os
-import base64
-from xos.config import Config
-
-class Deleter:
- model=None # Must be overridden
-
- def __init__(self, *args, **kwargs):
- pass
-
- def call(self, pk, model_dict):
- # Fetch object from XOS db and delete it
- pass
-
- def __call__(self, *args, **kwargs):
- return self.call(*args, **kwargs)
diff --git a/xos/synchronizers/openstack/event_loop.py b/xos/synchronizers/openstack/event_loop.py
deleted file mode 100644
index db78f07..0000000
--- a/xos/synchronizers/openstack/event_loop.py
+++ /dev/null
@@ -1,527 +0,0 @@
-import os
-import imp
-import inspect
-import time
-import sys
-import traceback
-import commands
-import threading
-import json
-import pdb
-import pprint
-
-
-from datetime import datetime
-from collections import defaultdict
-from core.models import *
-from django.db.models import F, Q
-from django.db import connection
-from django.db import reset_queries
-#from openstack.manager import OpenStackManager
-from openstack.driver import OpenStackDriver
-from xos.logger import Logger, logging, logger
-#from timeout import timeout
-from xos.config import Config, XOS_DIR
-from synchronizers.base.steps import *
-from synchronizers.base.syncstep import SyncStep
-from synchronizers.base.toposort import toposort
-from synchronizers.base.error_mapper import *
-from synchronizers.openstack.openstacksyncstep import OpenStackSyncStep
-from synchronizers.base.steps.sync_object import SyncObject
-
-# Load app models
-
-try:
- app_module_names = Config().observer_applist.split(',')
-except AttributeError:
- app_module_names = []
-
-if (type(app_module_names)!=list):
- app_module_names=[app_module_names]
-
-app_modules = []
-
-for m in app_module_names:
- model_path = m+'.models'
- module = __import__(model_path,fromlist=[m])
- app_modules.append(module)
-
-
-debug_mode = False
-
-class bcolors:
- HEADER = '\033[95m'
- OKBLUE = '\033[94m'
- OKGREEN = '\033[92m'
- WARNING = '\033[93m'
- FAIL = '\033[91m'
- ENDC = '\033[0m'
- BOLD = '\033[1m'
- UNDERLINE = '\033[4m'
-
-logger = Logger(level=logging.INFO)
-
-class StepNotReady(Exception):
- pass
-
-class NoOpDriver:
- def __init__(self):
- self.enabled = True
- self.dependency_graph = None
-
-STEP_STATUS_WORKING=1
-STEP_STATUS_OK=2
-STEP_STATUS_KO=3
-
-def invert_graph(g):
- ig = {}
- for k,v in g.items():
- for v0 in v:
- try:
- ig[v0].append(k)
- except:
- ig[v0]=[k]
- return ig
-
-class XOSObserver:
- sync_steps = []
-
-
- def __init__(self):
- # The Condition object that gets signalled by Feefie events
- self.step_lookup = {}
- self.load_sync_step_modules()
- self.load_sync_steps()
- self.event_cond = threading.Condition()
-
- self.driver_kind = getattr(Config(), "observer_driver", "openstack")
- self.observer_name = getattr(Config(), "observer_name", "")
- if self.driver_kind=="openstack":
- self.driver = OpenStackDriver()
- else:
- self.driver = NoOpDriver()
-
- def consolePrint(self, what):
- if getattr(Config(), "observer_console_print", True):
- print what
-
- def wait_for_event(self, timeout):
- self.event_cond.acquire()
- self.event_cond.wait(timeout)
- self.event_cond.release()
-
- def wake_up(self):
- logger.info('Wake up routine called. Event cond %r'%self.event_cond)
- self.event_cond.acquire()
- self.event_cond.notify()
- self.event_cond.release()
-
- def load_sync_step_modules(self, step_dir=None):
- if step_dir is None:
- if hasattr(Config(), "observer_steps_dir"):
- step_dir = Config().observer_steps_dir
- else:
- step_dir = XOS_DIR + "/synchronizers/openstack/steps"
-
- for fn in os.listdir(step_dir):
- pathname = os.path.join(step_dir,fn)
- if os.path.isfile(pathname) and fn.endswith(".py") and (fn!="__init__.py"):
- module = imp.load_source(fn[:-3],pathname)
- for classname in dir(module):
- c = getattr(module, classname, None)
-
- # make sure 'c' is a descendent of SyncStep and has a
- # provides field (this eliminates the abstract base classes
- # since they don't have a provides)
-
- if inspect.isclass(c) and (issubclass(c, SyncStep) or issubclass(c,OpenStackSyncStep)) and hasattr(c,"provides") and (c not in self.sync_steps):
- self.sync_steps.append(c)
- logger.info('loaded sync steps: %s' % ",".join([x.__name__ for x in self.sync_steps]))
-
- def load_sync_steps(self):
- dep_path = Config().observer_dependency_graph
- logger.info('Loading model dependency graph from %s' % dep_path)
- try:
- # This contains dependencies between records, not sync steps
- self.model_dependency_graph = json.loads(open(dep_path).read())
- for left,lst in self.model_dependency_graph.items():
- new_lst = []
- for k in lst:
- try:
- tup = (k,k.lower())
- new_lst.append(tup)
- deps = self.model_dependency_graph[k]
- except:
- self.model_dependency_graph[k] = []
-
- self.model_dependency_graph[left] = new_lst
- except Exception,e:
- raise e
-
- try:
- backend_path = Config().observer_pl_dependency_graph
- logger.info('Loading backend dependency graph from %s' % backend_path)
- # This contains dependencies between backend records
- self.backend_dependency_graph = json.loads(open(backend_path).read())
- for k,v in self.backend_dependency_graph.items():
- try:
- self.model_dependency_graph[k].extend(v)
- except KeyError:
- self.model_dependency_graphp[k] = v
-
- except Exception,e:
- logger.info('Backend dependency graph not loaded')
- # We can work without a backend graph
- self.backend_dependency_graph = {}
-
- provides_dict = {}
- for s in self.sync_steps:
- self.step_lookup[s.__name__] = s
- for m in s.provides:
- try:
- provides_dict[m.__name__].append(s.__name__)
- except KeyError:
- provides_dict[m.__name__]=[s.__name__]
-
- step_graph = {}
- phantom_steps = []
- for k,v in self.model_dependency_graph.items():
- try:
- for source in provides_dict[k]:
- if (not v):
- step_graph[source] = []
-
- for m,_ in v:
- try:
- for dest in provides_dict[m]:
- # no deps, pass
- try:
- if (dest not in step_graph[source]):
- step_graph[source].append(dest)
- except:
- step_graph[source]=[dest]
- except KeyError:
- if (not provides_dict.has_key(m)):
- try:
- step_graph[source]+=['#%s'%m]
- except:
- step_graph[source]=['#%s'%m]
-
- phantom_steps+=['#%s'%m]
- pass
-
- except KeyError:
- pass
- # no dependencies, pass
-
-
- self.dependency_graph = step_graph
- self.deletion_dependency_graph = invert_graph(step_graph)
-
- pp = pprint.PrettyPrinter(indent=4)
- logger.info(pp.pformat(step_graph))
- self.ordered_steps = toposort(self.dependency_graph, phantom_steps+map(lambda s:s.__name__,self.sync_steps))
- self.ordered_steps = [i for i in self.ordered_steps if i!='SyncObject']
-
- logger.info("Order of steps=%s" % self.ordered_steps)
-
- self.load_run_times()
-
-
- def check_duration(self, step, duration):
- try:
- if (duration > step.deadline):
- logger.info('Sync step %s missed deadline, took %.2f seconds'%(step.name,duration))
- except AttributeError:
- # S doesn't have a deadline
- pass
-
- def update_run_time(self, step, deletion):
- if (not deletion):
- self.last_run_times[step.__name__]=time.time()
- else:
- self.last_deletion_run_times[step.__name__]=time.time()
-
-
- def check_schedule(self, step, deletion):
- last_run_times = self.last_run_times if not deletion else self.last_deletion_run_times
-
- time_since_last_run = time.time() - last_run_times.get(step.__name__, 0)
- try:
- if (time_since_last_run < step.requested_interval):
- raise StepNotReady
- except AttributeError:
- logger.info('Step %s does not have requested_interval set'%step.__name__)
- raise StepNotReady
-
- def load_run_times(self):
- try:
- jrun_times = open('/tmp/%sobserver_run_times'%self.observer_name).read()
- self.last_run_times = json.loads(jrun_times)
- except:
- self.last_run_times={}
- for e in self.ordered_steps:
- self.last_run_times[e]=0
- try:
- jrun_times = open('/tmp/%sobserver_deletion_run_times'%self.observer_name).read()
- self.last_deletion_run_times = json.loads(jrun_times)
- except:
- self.last_deletion_run_times={}
- for e in self.ordered_steps:
- self.last_deletion_run_times[e]=0
-
- def lookup_step_class(self,s):
- if ('#' in s):
- return SyncObject
- else:
- step = self.step_lookup[s]
- return step
-
- def lookup_step(self,s):
- if ('#' in s):
- objname = s[1:]
- so = SyncObject()
-
- try:
- obj = globals()[objname]
- except:
- for m in app_modules:
- if (hasattr(m,objname)):
- obj = getattr(m,objname)
-
- so.provides=[obj]
- so.observes=[obj]
- step = so
- else:
- step_class = self.step_lookup[s]
- step = step_class(driver=self.driver,error_map=self.error_mapper)
- return step
-
- def save_run_times(self):
- run_times = json.dumps(self.last_run_times)
- open('/tmp/%sobserver_run_times'%self.observer_name,'w').write(run_times)
-
- deletion_run_times = json.dumps(self.last_deletion_run_times)
- open('/tmp/%sobserver_deletion_run_times'%self.observer_name,'w').write(deletion_run_times)
-
- def check_class_dependency(self, step, failed_steps):
- step.dependenices = []
- for obj in step.provides:
- lst = self.model_dependency_graph.get(obj.__name__, [])
- nlst = map(lambda(a,b):b,lst)
- step.dependenices.extend(nlst)
- for failed_step in failed_steps:
- if (failed_step in step.dependencies):
- raise StepNotReady
-
- def sync(self, S, deletion):
- try:
- step = self.lookup_step_class(S)
- start_time=time.time()
-
- logger.info("Starting to work on step %s, deletion=%s" % (step.__name__, str(deletion)))
-
- dependency_graph = self.dependency_graph if not deletion else self.deletion_dependency_graph
- step_conditions = self.step_conditions# if not deletion else self.deletion_step_conditions
- step_status = self.step_status# if not deletion else self.deletion_step_status
-
- # Wait for step dependencies to be met
- try:
- deps = dependency_graph[S]
- has_deps = True
- except KeyError:
- has_deps = False
-
- go = True
-
- failed_dep = None
- if (has_deps):
- for d in deps:
- if d==step.__name__:
- logger.info(" step %s self-wait skipped" % step.__name__)
- go = True
- continue
-
- cond = step_conditions[d]
- cond.acquire()
- if (step_status[d] is STEP_STATUS_WORKING):
- logger.info(" step %s wait on dep %s" % (step.__name__, d))
- cond.wait()
- elif step_status[d] == STEP_STATUS_OK:
- go = True
- else:
- go = False
- failed_dep = d
- cond.release()
- if (not go):
- break
- else:
- go = True
-
- if (not go):
- self.consolePrint(bcolors.FAIL + "Step %r skipped on %r" % (step,failed_dep) + bcolors.ENDC)
- # SMBAKER: sync_step was not defined here, so I changed
- # this from 'sync_step' to 'step'. Verify.
- self.failed_steps.append(step)
- my_status = STEP_STATUS_KO
- else:
- sync_step = self.lookup_step(S)
- sync_step. __name__= step.__name__
- sync_step.dependencies = []
- try:
- mlist = sync_step.provides
-
- try:
- for m in mlist:
- lst = self.model_dependency_graph[m.__name__]
- nlst = map(lambda(a,b):b,lst)
- sync_step.dependencies.extend(nlst)
- except Exception,e:
- raise e
-
- except KeyError:
- pass
- sync_step.debug_mode = debug_mode
-
- should_run = False
- try:
- # Various checks that decide whether
- # this step runs or not
- self.check_class_dependency(sync_step, self.failed_steps) # dont run Slices if Sites failed
- self.check_schedule(sync_step, deletion) # dont run sync_network_routes if time since last run < 1 hour
- should_run = True
- except StepNotReady:
- logger.info('Step not ready: %s'%sync_step.__name__)
- self.failed_steps.append(sync_step)
- my_status = STEP_STATUS_KO
- except Exception,e:
- logger.error('%r' % e)
- logger.log_exc("sync step failed: %r. Deletion: %r"%(sync_step,deletion))
- self.failed_steps.append(sync_step)
- my_status = STEP_STATUS_KO
-
- if (should_run):
- try:
- duration=time.time() - start_time
-
- logger.info('Executing step %s, deletion=%s' % (sync_step.__name__, deletion))
-
- self.consolePrint(bcolors.OKBLUE + "Executing step %s" % sync_step.__name__ + bcolors.ENDC)
- failed_objects = sync_step(failed=list(self.failed_step_objects), deletion=deletion)
-
- self.check_duration(sync_step, duration)
-
- if failed_objects:
- self.failed_step_objects.update(failed_objects)
-
- logger.info("Step %r succeeded" % sync_step.__name__)
- self.consolePrint(bcolors.OKGREEN + "Step %r succeeded" % sync_step.__name__ + bcolors.ENDC)
- my_status = STEP_STATUS_OK
- self.update_run_time(sync_step,deletion)
- except Exception,e:
- self.consolePrint(bcolors.FAIL + "Model step %r failed" % (sync_step.__name__) + bcolors.ENDC)
- logger.error('Model step %r failed. This seems like a misconfiguration or bug: %r. This error will not be relayed to the user!' % (sync_step.__name__, e))
- logger.log_exc("Exception in sync step")
- self.failed_steps.append(S)
- my_status = STEP_STATUS_KO
- else:
- logger.info("Step %r succeeded due to non-run" % step)
- my_status = STEP_STATUS_OK
-
- try:
- my_cond = step_conditions[S]
- my_cond.acquire()
- step_status[S]=my_status
- my_cond.notify_all()
- my_cond.release()
- except KeyError,e:
- logger.info('Step %r is a leaf' % step)
- pass
- finally:
- try:
- reset_queries()
- except:
- # this shouldn't happen, but in case it does, catch it...
- logger.log_exc("exception in reset_queries")
-
- connection.close()
-
- def run(self):
- if not self.driver.enabled:
- return
-
- if (self.driver_kind=="openstack") and (not self.driver.has_openstack):
- return
-
- while True:
- logger.info('Waiting for event')
- self.wait_for_event(timeout=5)
- logger.info('Observer woke up')
-
- self.run_once()
-
- def run_once(self):
- try:
- loop_start = time.time()
- error_map_file = getattr(Config(), "error_map_path", XOS_DIR + "/error_map.txt")
- self.error_mapper = ErrorMapper(error_map_file)
-
- # Two passes. One for sync, the other for deletion.
- for deletion in [False,True]:
- # Set of individual objects within steps that failed
- self.failed_step_objects = set()
-
- # Set up conditions and step status
- # This is needed for steps to run in parallel
- # while obeying dependencies.
-
- providers = set()
- dependency_graph = self.dependency_graph if not deletion else self.deletion_dependency_graph
-
- for v in dependency_graph.values():
- if (v):
- providers.update(v)
-
- self.step_conditions = {}
- self.step_status = {}
-
- for p in list(providers):
- self.step_conditions[p] = threading.Condition()
-
- self.step_status[p] = STEP_STATUS_WORKING
-
- self.failed_steps = []
-
- threads = []
- logger.info('Deletion=%r...'%deletion)
- schedule = self.ordered_steps if not deletion else reversed(self.ordered_steps)
-
- for S in schedule:
- thread = threading.Thread(target=self.sync, args=(S, deletion))
-
- logger.info('Deletion=%r...'%deletion)
- threads.append(thread)
-
- # Start threads
- for t in threads:
- t.start()
-
- # another spot to clean up debug state
- try:
- reset_queries()
- except:
- # this shouldn't happen, but in case it does, catch it...
- logger.log_exc("exception in reset_queries")
-
- # Wait for all threads to finish before continuing with the run loop
- for t in threads:
- t.join()
-
- self.save_run_times()
-
- loop_end = time.time()
- open('/tmp/%sobserver_last_run'%self.observer_name,'w').write(json.dumps({'last_run': loop_end, 'last_duration':loop_end - loop_start}))
- except Exception, e:
- logger.error('Core error. This seems like a misconfiguration or bug: %r. This error will not be relayed to the user!' % e)
- logger.log_exc("Exception in observer run loop")
- traceback.print_exc()
diff --git a/xos/synchronizers/openstack/event_manager.py b/xos/synchronizers/openstack/event_manager.py
deleted file mode 100644
index fce2b68..0000000
--- a/xos/synchronizers/openstack/event_manager.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import threading
-import requests, json
-
-from xos.config import Config, XOS_DIR
-
-import uuid
-import os
-import imp
-import inspect
-import base64
-import json
-import traceback
-
-if getattr(Config(),"observer_fofum_disabled", False) != True:
- from fofum import Fofum
- fofum_enabled = True
-else:
- fofum_enabled = False
-
-random_client_id=None
-def get_random_client_id():
- global random_client_id
-
- if (random_client_id is None) and os.path.exists(XOS_DIR + "/random_client_id"):
- # try to use the last one we used, if we saved it
- try:
- random_client_id = open(XOS_DIR+"/random_client_id","r").readline().strip()
- print "get_random_client_id: loaded %s" % random_client_id
- except:
- print "get_random_client_id: failed to read " + XOS_DIR + "/random_client_id"
-
- if random_client_id is None:
- random_client_id = base64.urlsafe_b64encode(os.urandom(12))
- print "get_random_client_id: generated new id %s" % random_client_id
-
- # try to save it for later (XXX: could race with another client here)
- try:
- open(XOS_DIR + "/random_client_id","w").write("%s\n" % random_client_id)
- except:
- print "get_random_client_id: failed to write " + XOS_DIR + "/random_client_id"
-
- return random_client_id
-
-# decorator that marks dispatachable event methods
-def event(func):
- setattr(func, 'event', func.__name__)
- return func
-
-class EventHandler:
- # This code is currently not in use.
- def __init__(self):
- pass
-
- @staticmethod
- def get_events():
- events = []
- for name in dir(EventHandler):
- attribute = getattr(EventHandler, name)
- if hasattr(attribute, 'event'):
- events.append(getattr(attribute, 'event'))
- return events
-
- def dispatch(self, event, *args, **kwds):
- if hasattr(self, event):
- return getattr(self, event)(*args, **kwds)
-
-
-class EventSender:
- def __init__(self,user=None,clientid=None):
- try:
- user = Config().feefie_client_user
- except:
- user = 'pl'
-
- try:
- clid = Config().feefie_client_id
- except:
- clid = get_random_client_id()
- print "EventSender: no feefie_client_id configured. Using random id %s" % clid
-
- if fofum_enabled:
- self.fofum = Fofum(user=user)
- self.fofum.make(clid)
-
- def fire(self,**kwargs):
- kwargs["uuid"] = str(uuid.uuid1())
- if fofum_enabled:
- self.fofum.fire(json.dumps(kwargs))
-
-class EventListener:
- def __init__(self,wake_up=None):
- self.handler = EventHandler()
- self.wake_up = wake_up
-
- def handle_event(self, payload):
- payload_dict = json.loads(payload)
-
- if (self.wake_up):
- self.wake_up()
-
- def run(self):
- # This is our unique client id, to be used when firing and receiving events
- # It needs to be generated once and placed in the config file
-
- try:
- user = Config().feefie_client_user
- except:
- user = 'pl'
-
- try:
- clid = Config().feefie_client_id
- except:
- clid = get_random_client_id()
- print "EventListener: no feefie_client_id configured. Using random id %s" % clid
-
- if fofum_enabled:
- f = Fofum(user=user)
-
- listener_thread = threading.Thread(target=f.listen_for_event,args=(clid,self.handle_event))
- listener_thread.start()
diff --git a/xos/synchronizers/openstack/run_ansible b/xos/synchronizers/openstack/run_ansible
deleted file mode 100755
index a504ec3..0000000
--- a/xos/synchronizers/openstack/run_ansible
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-source /opt/ansible/hacking/env-setup >> /dev/null
-ansible-playbook -v "$@"
diff --git a/xos/synchronizers/openstack/run_ansible_verbose b/xos/synchronizers/openstack/run_ansible_verbose
deleted file mode 100755
index d17cad7..0000000
--- a/xos/synchronizers/openstack/run_ansible_verbose
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-source /opt/ansible/hacking/env-setup >> /dev/null
-ansible-playbook -vvv "$@"
diff --git a/xos/synchronizers/openstack/syncstep.py b/xos/synchronizers/openstack/syncstep.py
deleted file mode 100644
index 0a01356..0000000
--- a/xos/synchronizers/openstack/syncstep.py
+++ /dev/null
@@ -1,307 +0,0 @@
-import os
-import base64
-from datetime import datetime
-from xos.config import Config
-from xos.logger import Logger, logging
-from synchronizers.base.steps import *
-from django.db.models import F, Q
-from core.models import *
-from django.db import reset_queries
-from synchronizers.base.ansible import *
-from generate.dependency_walker import *
-
-from time import time
-import json
-import time
-import pdb
-
-logger = Logger(level=logging.INFO)
-
-def f7(seq):
- seen = set()
- seen_add = seen.add
- return [ x for x in seq if not (x in seen or seen_add(x))]
-
-def elim_dups(backend_str):
- strs = backend_str.split(' // ')
- strs2 = f7(strs)
- return ' // '.join(strs2)
-
-def deepgetattr(obj, attr):
- return reduce(getattr, attr.split('.'), obj)
-
-
-class InnocuousException(Exception):
- pass
-
-class DeferredException(Exception):
- pass
-
-class FailedDependency(Exception):
- pass
-
-class SyncStep(object):
- """ An XOS Sync step.
-
- Attributes:
- psmodel Model name the step synchronizes
- dependencies list of names of models that must be synchronized first if the current model depends on them
- """
-
- # map_sync_outputs can return this value to cause a step to be marked
- # successful without running ansible. Used for sync_network_controllers
- # on nat networks.
- SYNC_WITHOUT_RUNNING = "sync_without_running"
-
- slow=False
- def get_prop(self, prop):
- try:
- sync_config_dir = Config().sync_config_dir
- except:
- sync_config_dir = '/etc/xos/sync'
- prop_config_path = '/'.join(sync_config_dir,self.name,prop)
- return open(prop_config_path).read().rstrip()
-
- def __init__(self, **args):
- """Initialize a sync step
- Keyword arguments:
- name -- Name of the step
- provides -- XOS models sync'd by this step
- """
- dependencies = []
- self.driver = args.get('driver')
- self.error_map = args.get('error_map')
-
- try:
- self.soft_deadline = int(self.get_prop('soft_deadline_seconds'))
- except:
- self.soft_deadline = 5 # 5 seconds
-
- return
-
- def fetch_pending(self, deletion=False):
- # This is the most common implementation of fetch_pending
- # Steps should override it if they have their own logic
- # for figuring out what objects are outstanding.
-
- main_objs = self.observes
- if (type(main_objs) is not list):
- main_objs=[main_objs]
-
- objs = []
- for main_obj in main_objs:
- if (not deletion):
- lobjs = main_obj.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False),Q(no_sync=False))
- else:
- lobjs = main_obj.deleted_objects.all()
- objs.extend(lobjs)
-
- return objs
- #return Instance.objects.filter(ip=None)
-
- def check_dependencies(self, obj, failed):
- for dep in self.dependencies:
- peer_name = dep[0].lower() + dep[1:] # django names are camelCased with the first letter lower
-
- peer_objects=[]
- try:
- peer_names = plural(peer_name)
- peer_object_list=[]
-
- try:
- peer_object_list.append(deepgetattr(obj, peer_name))
- except:
- pass
-
- try:
- peer_object_list.append(deepgetattr(obj, peer_names))
- except:
- pass
-
- for peer_object in peer_object_list:
- try:
- peer_objects.extend(peer_object.all())
- except AttributeError:
- peer_objects.append(peer_object)
- except:
- peer_objects = []
-
- if (hasattr(obj,'controller')):
- try:
- peer_objects = filter(lambda o:o.controller==obj.controller, peer_objects)
- except AttributeError:
- pass
-
- if (failed in peer_objects):
- if (obj.backend_status!=failed.backend_status):
- obj.backend_status = failed.backend_status
- obj.save(update_fields=['backend_status'])
- raise FailedDependency("Failed dependency for %s:%s peer %s:%s failed %s:%s" % (obj.__class__.__name__, str(getattr(obj,"pk","no_pk")), peer_object.__class__.__name__, str(getattr(peer_object,"pk","no_pk")), failed.__class__.__name__, str(getattr(failed,"pk","no_pk"))))
-
-
- def sync_record(self, o):
- try:
- controller = o.get_controller()
- controller_register = json.loads(controller.backend_register)
-
- if (controller_register.get('disabled',False)):
- raise InnocuousException('Controller %s is disabled'%controller.name)
- except AttributeError:
- pass
-
- tenant_fields = self.map_sync_inputs(o)
- if tenant_fields == SyncStep.SYNC_WITHOUT_RUNNING:
- return
- main_objs=self.observes
- if (type(main_objs) is list):
- main_objs=main_objs[0]
-
- path = ''.join(main_objs.__name__).lower()
- res = run_template(self.playbook,tenant_fields,path=path)
-
- try:
- self.map_sync_outputs(o,res)
- except AttributeError:
- pass
-
- def delete_record(self, o):
- try:
- controller = o.get_controller()
- controller_register = json.loads(o.node.site_deployment.controller.backend_register)
-
- if (controller_register.get('disabled',False)):
- raise InnocuousException('Controller %s is disabled'%sliver.node.site_deployment.controller.name)
- except AttributeError:
- pass
-
- tenant_fields = self.map_delete_inputs(o)
-
- main_objs=self.observes
- if (type(main_objs) is list):
- main_objs=main_objs[0]
-
- path = ''.join(main_objs.__name__).lower()
-
- tenant_fields['delete']=True
- res = run_template(self.playbook,tenant_fields,path=path)
- try:
- self.map_delete_outputs(o,res)
- except AttributeError:
- pass
-
- def call(self, failed=[], deletion=False):
- #if ('Instance' in self.__class__.__name__):
- # pdb.set_trace()
-
- pending = self.fetch_pending(deletion)
-
- for o in pending:
- # another spot to clean up debug state
- try:
- reset_queries()
- except:
- # this shouldn't happen, but in case it does, catch it...
- logger.log_exc("exception in reset_queries",extra=o.tologdict())
-
- sync_failed = False
- try:
- backoff_disabled = Config().observer_backoff_disabled
- except:
- backoff_disabled = 0
-
- try:
- scratchpad = json.loads(o.backend_register)
- if (scratchpad):
- next_run = scratchpad['next_run']
- if (not backoff_disabled and next_run>time.time()):
- sync_failed = True
- except:
- logger.log_exc("Exception while loading scratchpad",extra=o.tologdict())
- pass
-
- if (not sync_failed):
- try:
- for f in failed:
- self.check_dependencies(o,f) # Raises exception if failed
- if (deletion):
- self.delete_record(o)
- o.delete(purge=True)
- else:
- self.sync_record(o)
- o.enacted = datetime.now() # Is this the same timezone? XXX
- scratchpad = {'next_run':0, 'exponent':0, 'last_success':time.time()}
- o.backend_register = json.dumps(scratchpad)
- o.backend_status = "1 - OK"
- o.save(update_fields=['enacted','backend_status','backend_register'])
- except (InnocuousException,Exception,DeferredException) as e:
- logger.log_exc("sync step failed!",extra=o.tologdict())
- try:
- if (o.backend_status.startswith('2 - ')):
- str_e = '%s // %r'%(o.backend_status[4:],e)
- str_e = elim_dups(str_e)
- else:
- str_e = '%r'%e
- except:
- str_e = '%r'%e
-
- try:
- error = self.error_map.map(str_e)
- except:
- error = '%s'%str_e
-
- if isinstance(e, InnocuousException) and not force_error:
- o.backend_status = '1 - %s'%error
- else:
- o.backend_status = '2 - %s'%error
-
- try:
- scratchpad = json.loads(o.backend_register)
- scratchpad['exponent']
- except:
- logger.log_exc("Exception while updating scratchpad",extra=o.tologdict())
- scratchpad = {'next_run':0, 'exponent':0, 'last_success':time.time(),'failures':0}
-
- # Second failure
- if (scratchpad['exponent']):
- if isinstance(e,DeferredException):
- delay = scratchpad['exponent'] * 60 # 1 minute
- else:
- delay = scratchpad['exponent'] * 600 # 10 minutes
- # cap delays at 8 hours
- if (delay>8*60*60):
- delay=8*60*60
- scratchpad['next_run'] = time.time() + delay
-
- try:
- scratchpad['exponent']+=1
- except:
- scratchpad['exponent']=1
-
- try:
- scratchpad['failures']+=1
- except KeyError:
- scratchpad['failures']=1
-
- scratchpad['last_failure']=time.time()
-
- o.backend_register = json.dumps(scratchpad)
-
- # TOFIX:
- # DatabaseError: value too long for type character varying(140)
- if (o.pk):
- try:
- o.backend_status = o.backend_status[:1024]
- o.save(update_fields=['backend_status','backend_register','updated'])
- except:
- print "Could not update backend status field!"
- pass
- sync_failed = True
-
-
- if (sync_failed):
- failed.append(o)
-
- return failed
-
- def __call__(self, **args):
- return self.call(**args)
diff --git a/xos/synchronizers/openstack/toposort.py b/xos/synchronizers/openstack/toposort.py
deleted file mode 100644
index 6839861..0000000
--- a/xos/synchronizers/openstack/toposort.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python
-
-import time
-import traceback
-import commands
-import threading
-import json
-import pdb
-
-from datetime import datetime
-from collections import defaultdict
-
-# Topological sort
-# Notes:
-# - Uses a stack instead of recursion
-# - Forfeits optimization involving tracking currently visited nodes
-def toposort(g, steps=None):
- # Get set of all nodes, including those without outgoing edges
- keys = set(g.keys())
- values = set({})
- for v in g.values():
- values=values | set(v)
-
- all_nodes=list(keys|values)
- if (not steps):
- steps = all_nodes
-
- # Final order
- order = []
-
- # DFS stack, not using recursion
- stack = []
-
- # Unmarked set
- unmarked = all_nodes
-
- # visiting = [] - skip, don't expect 1000s of nodes, |E|/|V| is small
-
- while unmarked:
- stack.insert(0,unmarked[0]) # push first unmarked
-
- while (stack):
- n = stack[0]
- add = True
- try:
- for m in g[n]:
- if (m in unmarked):
- add = False
- stack.insert(0,m)
- except KeyError:
- pass
- if (add):
- if (n in steps and n not in order):
- order.append(n)
- item = stack.pop(0)
- try:
- unmarked.remove(item)
- except ValueError:
- pass
-
- noorder = list(set(steps) - set(order))
- return order + noorder
-
-def main():
- graph_file=open('xos.deps').read()
- g = json.loads(graph_file)
- print toposort(g)
-
-if (__name__=='__main__'):
- main()
-
-#print toposort({'a':'b','b':'c','c':'d','d':'c'},['d','c','b','a'])
diff --git a/xos/tests/api/helpers/before_test.py b/xos/tests/api/helpers/before_test.py
new file mode 100644
index 0000000..e53c323
--- /dev/null
+++ b/xos/tests/api/helpers/before_test.py
@@ -0,0 +1,270 @@
+import dredd_hooks as hooks
+import sys
+
+# HELPERS
+# NOTE move in separated module
+import os
+import sys
+sys.path.append("/opt/xos")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+import django
+from core.models import *
+from services.cord.models import *
+from services.vtr.models import *
+import urllib2
+import json
+django.setup()
+
+
+def doLogin(username, password):
+ url = "http://127.0.0.1:8000/xoslib/login?username=%s&password=%s" % (username, password)
+ res = urllib2.urlopen(url).read()
+ parsed = json.loads(res)
+ return {'token': parsed['xoscsrftoken'], 'sessionid': parsed['xossessionid']}
+
+
+def cleanDB():
+ # deleting all subscribers
+ for s in CordSubscriberRoot.objects.all():
+ s.delete(purge=True)
+
+ # deleting all slices
+ for s in Slice.objects.all():
+ s.delete(purge=True)
+
+ # deleting all Services
+ for s in Service.objects.all():
+ s.delete(purge=True)
+
+ # deleting all Tenants
+ for s in Tenant.objects.all():
+ s.delete(purge=True)
+
+ # deleting all Networks
+ for s in Network.objects.all():
+ s.delete(purge=True)
+
+ # deleting all NetworkTemplates
+ for s in NetworkTemplate.objects.all():
+ s.delete(purge=True)
+
+ for s in NetworkSlice.objects.all():
+ s.delete(purge=True)
+
+ for s in AddressPool.objects.all():
+ s.delete(purge=True)
+
+ for s in Flavor.objects.all():
+ s.delete(purge=True)
+
+ for s in Image.objects.all():
+ s.delete(purge=True)
+
+ # print 'DB Cleaned'
+
+
+def createTestSubscriber():
+
+ cleanDB()
+ createFlavors()
+
+ # load user
+ user = User.objects.get(email="padmin@vicci.org")
+
+ # network template
+ private_template = NetworkTemplate()
+ private_template.name = 'Private Network'
+ private_template.save()
+
+ # creating the test subscriber
+ subscriber = CordSubscriberRoot(name='Test Subscriber 1', id=1)
+ subscriber.save()
+
+ # vRouter service
+ vrouter_service = VRouterService()
+ vrouter_service.name = 'service_vrouter'
+ vrouter_service.save()
+
+ # address pools
+ ap_vsg = AddressPool()
+ ap_vsg.service = vrouter_service
+ ap_vsg.name = 'addresses_vsg'
+ ap_vsg.addresses = '10.168.0.0'
+ ap_vsg.gateway_ip = '10.168.0.1'
+ ap_vsg.gateway_mac = '02:42:0a:a8:00:01'
+ ap_vsg.save()
+
+ # print 'vRouter created'
+
+ # Site
+ site = Site.objects.get(name='MySite')
+
+ # vSG service
+ vsg_service = VSGService()
+ vsg_service.name = 'service_vsg'
+
+ # vSG slice
+ vsg_slice = Slice(id=2)
+ vsg_slice.name = site.login_base + "_testVsg"
+ vsg_slice.service = vsg_service.id
+ vsg_slice.site = site
+ vsg_slice.caller = user
+
+ vsg_slice.save()
+
+ vsg_service.save()
+
+ # volt service
+ volt_service = VOLTService()
+ volt_service.name = 'service_volt'
+ volt_service.save()
+
+ # cvpe image
+ createImage('ubuntu-vcpe4')
+
+ # vcpe slice
+ vcpe_slice = Slice(id=3)
+ vcpe_slice.name = site.login_base + "_testVcpe"
+ vcpe_slice.service = Service.objects.get(kind='vCPE')
+ vcpe_slice.site = site
+ vcpe_slice.caller = user
+ vcpe_slice.save()
+
+ # print 'vcpe_slice created'
+
+ # create a lan network
+ lan_net = Network()
+ lan_net.name = 'lan_network'
+ lan_net.owner = vcpe_slice
+ lan_net.template = private_template
+ lan_net.save()
+
+ # print 'lan_network created'
+
+ # add relation between vcpe slice and lan network
+ vcpe_network = NetworkSlice()
+ vcpe_network.network = lan_net
+ vcpe_network.slice = vcpe_slice
+ vcpe_network.save()
+
+ # print 'vcpe network relation added'
+
+ # vbng service
+ vbng_service = VBNGService()
+ vbng_service.name = 'service_vbng'
+ vbng_service.save()
+
+ # print 'vbng_service creater'
+
+ # volt tenant
+ vt = VOLTTenant(subscriber=subscriber.id, id=1)
+ vt.s_tag = "222"
+ vt.c_tag = "432"
+ vt.provider_service_id = volt_service.id
+ vt.caller = user
+ vt.save()
+
+ # print "Subscriber Created"
+
+
+def deleteTruckrolls():
+ for s in VTRTenant.objects.all():
+ s.delete(purge=True)
+
+
+def setUpTruckroll():
+ service_vtr = VTRService()
+ service_vtr.name = 'service_vtr'
+ service_vtr.save()
+
+
+def createTruckroll():
+ setUpTruckroll()
+ tn = VTRTenant(id=1)
+ tn.save()
+
+
+def createFlavors():
+ small = Flavor(id=1)
+ small.name = "m1.small"
+ small.save()
+
+ medium = Flavor(id=2)
+ medium.name = "m1.medium"
+ medium.save()
+
+ large = Flavor(id=3)
+ large.name = "m1.large"
+ large.save()
+
+
+def createSlice():
+ site = Site.objects.get(name='MySite')
+ user = User.objects.get(email="padmin@vicci.org")
+
+ sl = Slice(id=1)
+ sl.name = site.login_base + "_testSlice"
+ sl.site = site
+ sl.caller = user
+ sl.save()
+ return sl
+
+
+def createDeployment():
+ deployment = Deployment(id=1)
+ deployment.name = 'MyTestDeployment'
+ deployment.save()
+ return deployment
+
+
+def createImage(name):
+ img = Image(id=1)
+ img.name = name
+ img.disk_format = 'QCOW2'
+ img.kind = 'vm'
+ img.save()
+ return img
+
+
+def createNode(deployment):
+ site = Site.objects.get(name='MySite')
+
+ site_deployment = SiteDeployment(id=1)
+ site_deployment.site = site
+ site_deployment.deployment = deployment
+ site_deployment.save()
+
+ node = Node(id=1)
+ node.name = 'test-node'
+ node.site = site
+ node.site_deployment = site_deployment
+ node.save()
+ return node
+
+
+def setupInstance():
+ deployment = createDeployment()
+ sl = createSlice()
+ node = createNode(deployment)
+ img = createImage('test-image')
+ print {'image': img.id, 'deployment': deployment.id, 'slice': sl.id}
+ return {'image': img, 'deployment': deployment, 'slice': sl}
+
+
+def createInstance():
+ requirements = setupInstance()
+ user = User.objects.get(email="padmin@vicci.org")
+
+ instance = Instance(id=1)
+ instance.name = 'test-instance'
+ instance.node = Node.objects.all()[0]
+ instance.image = requirements['image']
+ instance.slice = requirements['slice']
+ instance.deployment = requirements['deployment']
+ instance.caller = user
+ instance.save()
+
+setupInstance()
+# createTestSubscriber()
+# createInstance()
+# createNode()
diff --git a/xos/tests/api/hooks.py b/xos/tests/api/hooks.py
index 60440e0..1f4a0ad 100644
--- a/xos/tests/api/hooks.py
+++ b/xos/tests/api/hooks.py
@@ -57,17 +57,16 @@
for s in Flavor.objects.all():
s.delete(purge=True)
+ for s in Image.objects.all():
+ s.delete(purge=True)
+
# print 'DB Cleaned'
def createTestSubscriber():
cleanDB()
-
- # create flavors
- small = Flavor()
- small.name = "m1.small"
- small.save()
+ createFlavors()
# load user
user = User.objects.get(email="padmin@vicci.org")
@@ -105,7 +104,7 @@
vsg_service.name = 'service_vsg'
# vSG slice
- vsg_slice = Slice()
+ vsg_slice = Slice(id=2)
vsg_slice.name = site.login_base + "_testVsg"
vsg_slice.service = vsg_service.id
vsg_slice.site = site
@@ -120,8 +119,11 @@
volt_service.name = 'service_volt'
volt_service.save()
+ # cvpe image
+ createImage('ubuntu-vcpe4')
+
# vcpe slice
- vcpe_slice = Slice()
+ vcpe_slice = Slice(id=3)
vcpe_slice.name = site.login_base + "_testVcpe"
vcpe_slice.service = Service.objects.get(kind='vCPE')
vcpe_slice.site = site
@@ -131,7 +133,7 @@
# print 'vcpe_slice created'
# create a lan network
- lan_net = Network()
+ lan_net = Network(id=1)
lan_net.name = 'lan_network'
lan_net.owner = vcpe_slice
lan_net.template = private_template
@@ -182,11 +184,85 @@
tn.save()
-def createFlavor():
- fl = Flavor(id=1)
- fl.name = 'm1.large'
- fl.save()
- print(fl)
+def createFlavors():
+ small = Flavor(id=1)
+ small.name = "m1.small"
+ small.save()
+
+ medium = Flavor(id=2)
+ medium.name = "m1.medium"
+ medium.save()
+
+ large = Flavor(id=3)
+ large.name = "m1.large"
+ large.save()
+
+
+def createSlice():
+ site = Site.objects.get(name='MySite')
+ user = User.objects.get(email="padmin@vicci.org")
+
+ sl = Slice(id=1)
+ sl.name = site.login_base + "_testSlice"
+ sl.site = site
+ sl.caller = user
+ sl.save()
+ return sl
+
+
+def createDeployment():
+ deployment = Deployment(id=1)
+ deployment.name = 'MyTestDeployment'
+ deployment.save()
+ return deployment
+
+
+def createImage(name):
+ img = Image(id=1)
+ img.name = name
+ img.disk_format = 'QCOW2'
+ img.kind = 'vm'
+ img.save()
+ return img
+
+
+def createNode(deployment):
+ site = Site.objects.get(name='MySite')
+
+ site_deployment = SiteDeployment(id=1)
+ site_deployment.site = site
+ site_deployment.deployment = deployment
+ site_deployment.save()
+
+ node = Node(id=1)
+ node.name = 'test-node'
+ node.site = site
+ node.site_deployment = site_deployment
+ node.save()
+ return node
+
+
+def setupInstance():
+ deployment = createDeployment()
+ sl = createSlice()
+ node = createNode(deployment)
+ img = createImage('test-image')
+ # print {'image': img.id, 'deployment': deployment.id, 'slice': sl.id}
+ return {'image': img, 'deployment': deployment, 'slice': sl}
+
+
+def createInstance():
+ requirements = setupInstance()
+ user = User.objects.get(email="padmin@vicci.org")
+
+ instance = Instance(id=1)
+ instance.name = 'test-instance'
+ instance.node = Node.objects.all()[0]
+ instance.image = requirements['image']
+ instance.slice = requirements['slice']
+ instance.deployment = requirements['deployment']
+ instance.caller = user
+ instance.save()
@hooks.before_all
@@ -203,9 +279,15 @@
transaction['request']['headers']['X-CSRFToken'] = auth['token']
transaction['request']['headers']['Cookie'] = "xossessionid=%s; xoscsrftoken=%s" % (auth['sessionid'], auth['token'])
createTestSubscriber()
+ setupInstance()
sys.stdout.flush()
+# @hooks.after_each
+# def my_after_each(transaction):
+# print "-------------------------------- Test end --------------------------------"
+
+
@hooks.before("Truckroll > Truckroll Collection > Create a Truckroll")
def test1(transaction):
setUpTruckroll()
@@ -231,7 +313,33 @@
@hooks.before("Flavors > Flavors > View a Flavors Detail")
def test5(transaction):
- createFlavor()
+ createFlavors()
+
+
+@hooks.before("Deployments > Deployments > View a Deployment Detail")
+def get_deployments(transaction):
+ createDeployment()
+
+
+@hooks.before("Deployments > Deployments > Delete a Deployment")
+def delete_deployments(transaction):
+ createDeployment()
+
+
+@hooks.before("Instances > Instances Collection > Create an Instance")
+def create_instance(transaction):
+ setupInstance()
+ transaction['skip'] = True
+
+
+@hooks.before("Instances > Instances Detail > Get instance details")
+def get_instance(transaction):
+ createInstance()
+
+
+@hooks.before("Instances > Instances Detail > Delete instance")
+def delete_instance(transaction):
+ createInstance()
@hooks.before("Example > Example Services Collection > List all Example Services")
diff --git a/xos/tests/api/source/core/deployment.md b/xos/tests/api/source/core/deployment.md
index bcd1e0e..764a73f 100644
--- a/xos/tests/api/source/core/deployment.md
+++ b/xos/tests/api/source/core/deployment.md
@@ -8,6 +8,49 @@
+ Response 200 (application/json)
+ [
+ {
+ "humanReadableName": "MyDeployment",
+ "id": 1,
+ "created": "2016-04-29T16:19:03.549901Z",
+ "updated": "2016-04-29T16:19:05.624151Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "MyDeployment",
+ "accessControl": "allow all",
+ "images": [
+ "1"
+ ],
+ "sites": [
+ "1"
+ ],
+ "flavors": [
+ "1",
+ "2",
+ "3"
+ ],
+ "dashboardviews": [
+ "1"
+ ]
+ }
+ ]
+
+### Create a deployment [POST]
+
++ Request (application/json)
+
+ {
+ "humanReadableName": "MyDeployment",
+ }
+
++ Response 200 (application/json)
+
{
"humanReadableName": "MyDeployment",
"id": 1,
@@ -37,4 +80,47 @@
"dashboardviews": [
"1"
]
- }
\ No newline at end of file
+ }
+
+### View a Deployment Detail [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Deployment in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyDeployment",
+ "id": 1,
+ "created": "2016-04-27T21:46:57.354544Z",
+ "updated": "2016-04-27T21:47:05.221720Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "MyDeployment",
+ "accessControl": "allow all",
+ "images": [],
+ "sites": [
+ "1"
+ ],
+ "flavors": [
+ "3",
+ "2",
+ "1"
+ ],
+ "dashboardviews": [
+ "3"
+ ]
+ }
+
+### Delete a Deployment [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Deployment in the form of an integer
+
++ Response 204
\ No newline at end of file
diff --git a/xos/tests/api/source/core/flavors.md b/xos/tests/api/source/core/flavors.md
index cb63a57..aba5a6b 100644
--- a/xos/tests/api/source/core/flavors.md
+++ b/xos/tests/api/source/core/flavors.md
@@ -33,6 +33,39 @@
}
]
+### Create a Flavor [POST]
+
++ Request (application/json)
+
+ {
+ "humanReadableName": "mq.test",
+ }
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "m1.large",
+ "id": 1,
+ "created": "2016-04-29T16:19:01.979548Z",
+ "updated": "2016-04-29T16:19:03.568238Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "m1.large",
+ "description": null,
+ "flavor": "m1.large",
+ "order": 0,
+ "default": false,
+ "deployments": [
+ "1"
+ ]
+ }
+
### View a Flavors Detail [GET]
+ Parameters
@@ -61,4 +94,11 @@
"deployments": [
"1"
]
- }
\ No newline at end of file
+ }
+
+### Delete a Flavors Detail [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Flavors in the form of an integer
+
++ Response 204
\ No newline at end of file
diff --git a/xos/tests/api/source/core/instances.md b/xos/tests/api/source/core/instances.md
index 4b95259..9caf93e 100644
--- a/xos/tests/api/source/core/instances.md
+++ b/xos/tests/api/source/core/instances.md
@@ -2,7 +2,10 @@
List of the XOS instances
-## Instances [/api/core/instances/{id}/]
+## Instances Collection [/api/core/instances/{?no_hyperlinks}]
+
+ + no_hyperlinks (number, optional) - Wheter to return relation with links or ids
+ + Default: `0`
### List all Instances [GET]
@@ -43,4 +46,104 @@
]
}
]
-
\ No newline at end of file
+
+### Create an Instance [POST]
+
++ Parameters
+ + no_hyperlinks: 1
+
++ Request (application/json)
+
+ {
+ "name": "test-instance",
+ "image": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 1
+ }
+
++ Response 200 (application/json)
+
+ {
+ "id": 1,
+ "humanReadableName": "uninstantiated-1",
+ "created": "2016-04-26T00:36:22.465259Z",
+ "updated": "2016-04-26T00:36:22.465288Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": null,
+ "instance_uuid": null,
+ "name": "test-instance",
+ "instance_name": null,
+ "ip": null,
+ "image": "1",
+ "creator": "1",
+ "slice": "1",
+ "deployment": "1",
+ "node": "1",
+ "numberCores": 0,
+ "flavor": "1",
+ "userData": null,
+ "isolation": "vm",
+ "volumes": "/etc/dnsmasq.d,/etc/ufw",
+ "parent": null,
+ "networks": [
+ "1"
+ ]
+ }
+
+## Instances Detail [/api/core/instances/{id}/]
+
+### Get instance details [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Instance in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "id": 1,
+ "humanReadableName": "uninstantiated-1",
+ "created": "2016-04-26T00:36:22.465259Z",
+ "updated": "2016-04-26T00:36:22.465288Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": null,
+ "instance_uuid": null,
+ "name": "mysite_vcpe",
+ "instance_name": null,
+ "ip": null,
+ "image": "1",
+ "creator": "1",
+ "slice": "1",
+ "deployment": "1",
+ "node": "1",
+ "numberCores": 0,
+ "flavor": "1",
+ "userData": null,
+ "isolation": "vm",
+ "volumes": "/etc/dnsmasq.d,/etc/ufw",
+ "parent": null,
+ "networks": [
+ "1"
+ ]
+ }
+
+### Delete instance [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Instance in the form of an integer
+
++ Response 204
\ No newline at end of file
diff --git a/xos/tools/apigen/api.template.py b/xos/tools/apigen/api.template.py
index d0852db..9f5123c 100644
--- a/xos/tools/apigen/api.template.py
+++ b/xos/tools/apigen/api.template.py
@@ -148,7 +148,7 @@
id = IdField()
{% for ref in object.refs %}
{% if ref.multi %}
- {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, queryset = {{ ref.camel }}.objects.all())
+ {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = {{ ref.camel }}.objects.all())
{% else %}
{{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
{% endif %}
diff --git a/xos/tools/apigen/hpc-api.template.py b/xos/tools/apigen/hpc-api.template.py
index 2bb8b8a..ed252b8 100644
--- a/xos/tools/apigen/hpc-api.template.py
+++ b/xos/tools/apigen/hpc-api.template.py
@@ -146,7 +146,7 @@
id = IdField()
{% for ref in object.refs %}
{% if ref.multi %}
- {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, queryset = {{ ref.camel }}.objects.all())
+ {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = {{ ref.camel }}.objects.all())
{% else %}
{{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
{% endif %}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index e6d77f1..e83a22f 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -16,7 +16,11 @@
no-update:
type: boolean
default: false
- description: Do not allow Tosca to update this object)
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object)
# Service
define(xos_base_service_caps,
scalable:
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 2bca51e..b41a6c7 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -41,6 +41,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -117,6 +121,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -288,6 +296,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -352,6 +364,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -410,6 +426,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -464,6 +484,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -518,6 +542,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -603,6 +631,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -657,6 +689,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
kind:
type: string
default: generic
@@ -806,6 +842,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
password:
type: string
required: false
@@ -864,6 +904,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
network_parameter_type:
@@ -893,6 +937,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
visibility:
type: string
required: false
@@ -942,7 +990,11 @@
no-update:
type: boolean
default: false
- description: Do not allow Tosca to update this object
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
ip_version:
type: integer
required: no
@@ -1046,6 +1098,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
accessControl:
type: string
default: allow all
@@ -1075,6 +1131,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
addresses:
type: string
required: false
@@ -1138,6 +1198,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
backend_type:
type: string
required: false
@@ -1199,6 +1263,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
display_name:
type: string
required: false
@@ -1244,6 +1312,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
enabled:
type: boolean
default: true
@@ -1290,6 +1362,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
node:
type: tosca.capabilities.xos.Node
@@ -1311,6 +1387,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
node:
type: tosca.capabilities.xos.NodeLabel
@@ -1332,6 +1412,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
flavor:
type: string
required: false
@@ -1357,6 +1441,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
siterole:
type: tosca.capabilities.xos.SiteRole
@@ -1378,6 +1466,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
slicerole:
type: tosca.capabilities.xos.SliceRole
@@ -1399,6 +1491,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
tenantrole:
type: tosca.capabilities.xos.TenantRole
@@ -1420,6 +1516,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
capabilities:
deploymentrole:
type: tosca.capabilities.xos.DeploymentRole
@@ -1444,6 +1544,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
enabled:
type: boolean
default: true
@@ -1469,6 +1573,10 @@
type: boolean
default: false
description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
name:
type: string
required: true
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 82514c9..012f814 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -2,6 +2,7 @@
import pdb
import json
import subprocess
+import sys
from core.models import User
@@ -90,6 +91,13 @@
return None
return objs[0]
+ def get_replaces_objs(self):
+ replaces = self.get_property_default("replaces", None)
+ if replaces:
+ return self.xos_model.objects.filter(**{self.name_field: replaces})
+ else:
+ return []
+
def get_existing_objs(self):
return self.xos_model.objects.filter(**{self.name_field: self.obj_name})
@@ -97,7 +105,25 @@
return self.xos_model.__name__
def create_or_update(self):
+ replaces_objs = self.get_replaces_objs()
existing_objs = self.get_existing_objs()
+
+ if (replaces_objs and existing_objs):
+ ro = replaces_objs[0]
+ self.info("deleting %s:%s" % (self.get_model_class_name(), getattr(ro,self.name_field)))
+ ro.delete()
+
+ # in case we wanted to throw an error instead...
+ #self.error("CRITICAL ERROR: Both %s and %s exist!" % (getattr(ro,self.name_field), self.obj_name))
+ #sys.exit(-1)
+
+ if (replaces_objs and not existing_objs):
+ ro = replaces_objs[0]
+ self.info("renaming %s:%s to %s" % (self.get_model_class_name(), getattr(ro,self.name_field), self.obj_name))
+ setattr(ro, self.name_field, self.obj_name)
+ ro.save()
+ existing_objs = self.get_existing_objs()
+
if existing_objs:
if self.get_property_default("no-update", False):
self.info("%s:%s (%s) already exists. Skipping update due to 'no-update' property" % (self.get_model_class_name(), self.obj_name, self.full_name))
diff --git a/xos/xos/hpcapi.py b/xos/xos/hpcapi.py
index 5b97ab9..00b4ce5 100644
--- a/xos/xos/hpcapi.py
+++ b/xos/xos/hpcapi.py
@@ -123,8 +123,7 @@
# Based on serializers.py
class XOSModelSerializer(serializers.ModelSerializer):
- # TODO: Rest Framework 3.x doesn't support save_object()
- def NEED_TO_UPDATE_save_object(self, obj, **kwargs):
+ def save_object(self, obj, **kwargs):
""" rest_framework can't deal with ManyToMany relations that have a
through table. In xos, most of the through tables we have
@@ -191,7 +190,7 @@
return None
class Meta:
model = HpcHealthCheck
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
class HpcHealthCheckIdSerializer(XOSModelSerializer):
id = IdField()
@@ -207,7 +206,7 @@
return None
class Meta:
model = HpcHealthCheck
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
@@ -226,7 +225,7 @@
return None
class Meta:
model = HpcService
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
class HpcServiceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -242,7 +241,7 @@
return None
class Meta:
model = HpcService
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
@@ -261,7 +260,7 @@
return None
class Meta:
model = OriginServer
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
class OriginServerIdSerializer(XOSModelSerializer):
id = IdField()
@@ -277,7 +276,7 @@
return None
class Meta:
model = OriginServer
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
@@ -296,7 +295,7 @@
return None
class Meta:
model = CDNPrefix
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
class CDNPrefixIdSerializer(XOSModelSerializer):
id = IdField()
@@ -312,7 +311,7 @@
return None
class Meta:
model = CDNPrefix
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
@@ -331,7 +330,7 @@
return None
class Meta:
model = ServiceProvider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','service_provider_id','name','description','enabled',)
class ServiceProviderIdSerializer(XOSModelSerializer):
id = IdField()
@@ -347,7 +346,7 @@
return None
class Meta:
model = ServiceProvider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','service_provider_id','name','description','enabled',)
@@ -366,7 +365,7 @@
return None
class Meta:
model = ContentProvider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','content_provider_id','name','enabled','description','serviceProvider',)
class ContentProviderIdSerializer(XOSModelSerializer):
id = IdField()
@@ -382,7 +381,7 @@
return None
class Meta:
model = ContentProvider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','content_provider_id','name','enabled','description','serviceProvider',)
@@ -401,7 +400,7 @@
return None
class Meta:
model = AccessMap
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','name','description','map',)
class AccessMapIdSerializer(XOSModelSerializer):
id = IdField()
@@ -417,7 +416,7 @@
return None
class Meta:
model = AccessMap
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','name','description','map',)
@@ -436,7 +435,7 @@
return None
class Meta:
model = SiteMap
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
class SiteMapIdSerializer(XOSModelSerializer):
id = IdField()
@@ -452,7 +451,7 @@
return None
class Meta:
model = SiteMap
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
@@ -486,7 +485,7 @@
serializer_class = HpcHealthCheckSerializer
id_serializer_class = HpcHealthCheckIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -533,7 +532,7 @@
serializer_class = HpcServiceSerializer
id_serializer_class = HpcServiceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -580,7 +579,7 @@
serializer_class = OriginServerSerializer
id_serializer_class = OriginServerIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -627,7 +626,7 @@
serializer_class = CDNPrefixSerializer
id_serializer_class = CDNPrefixIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -674,7 +673,7 @@
serializer_class = ServiceProviderSerializer
id_serializer_class = ServiceProviderIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','hpcService','service_provider_id','name','description','enabled',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -721,7 +720,7 @@
serializer_class = ContentProviderSerializer
id_serializer_class = ContentProviderIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','content_provider_id','name','enabled','description','serviceProvider',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -768,7 +767,7 @@
serializer_class = AccessMapSerializer
id_serializer_class = AccessMapIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','name','description','map',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -815,7 +814,7 @@
serializer_class = SiteMapSerializer
id_serializer_class = SiteMapIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -854,3 +853,5 @@
# update() is handled by XOSRetrieveUpdateDestroyAPIView
# destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index 0343adc..dad7888 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -30,7 +30,7 @@
GEOIP_PATH = "/usr/share/GeoIP"
XOS_DIR = "/opt/xos"
-DEBUG = False
+DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
diff --git a/xos/xos/xosapi.py b/xos/xos/xosapi.py
index 7673f28..53de766 100644
--- a/xos/xos/xosapi.py
+++ b/xos/xos/xosapi.py
@@ -1,4 +1,3 @@
-
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@@ -67,6 +66,9 @@
url(r'xos/sitedeployments/$', SiteDeploymentList.as_view(), name='sitedeployment-list-legacy'),
url(r'xos/sitedeployments/(?P<pk>[a-zA-Z0-9\-]+)/$', SiteDeploymentDetail.as_view(), name ='sitedeployment-detail-legacy'),
+ url(r'xos/tenantprivileges/$', TenantPrivilegeList.as_view(), name='tenantprivilege-list-legacy'),
+ url(r'xos/tenantprivileges/(?P<pk>[a-zA-Z0-9\-]+)/$', TenantPrivilegeDetail.as_view(), name ='tenantprivilege-detail-legacy'),
+
url(r'xos/tags/$', TagList.as_view(), name='tag-list-legacy'),
url(r'xos/tags/(?P<pk>[a-zA-Z0-9\-]+)/$', TagDetail.as_view(), name ='tag-detail-legacy'),
@@ -94,6 +96,9 @@
url(r'xos/controllerslices/$', ControllerSliceList.as_view(), name='controllerslice-list-legacy'),
url(r'xos/controllerslices/(?P<pk>[a-zA-Z0-9\-]+)/$', ControllerSliceDetail.as_view(), name ='controllerslice-detail-legacy'),
+ url(r'xos/tenantroles/$', TenantRoleList.as_view(), name='tenantrole-list-legacy'),
+ url(r'xos/tenantroles/(?P<pk>[a-zA-Z0-9\-]+)/$', TenantRoleDetail.as_view(), name ='tenantrole-detail-legacy'),
+
url(r'xos/slices/$', SliceList.as_view(), name='slice-list-legacy'),
url(r'xos/slices/(?P<pk>[a-zA-Z0-9\-]+)/$', SliceDetail.as_view(), name ='slice-detail-legacy'),
@@ -275,6 +280,9 @@
url(r'api/core/sitedeployments/$', SiteDeploymentList.as_view(), name='sitedeployment-list'),
url(r'api/core/sitedeployments/(?P<pk>[a-zA-Z0-9\-]+)/$', SiteDeploymentDetail.as_view(), name ='sitedeployment-detail'),
+ url(r'api/core/tenantprivileges/$', TenantPrivilegeList.as_view(), name='tenantprivilege-list'),
+ url(r'api/core/tenantprivileges/(?P<pk>[a-zA-Z0-9\-]+)/$', TenantPrivilegeDetail.as_view(), name ='tenantprivilege-detail'),
+
url(r'api/core/tags/$', TagList.as_view(), name='tag-list'),
url(r'api/core/tags/(?P<pk>[a-zA-Z0-9\-]+)/$', TagDetail.as_view(), name ='tag-detail'),
@@ -302,6 +310,9 @@
url(r'api/core/controllerslices/$', ControllerSliceList.as_view(), name='controllerslice-list'),
url(r'api/core/controllerslices/(?P<pk>[a-zA-Z0-9\-]+)/$', ControllerSliceDetail.as_view(), name ='controllerslice-detail'),
+ url(r'api/core/tenantroles/$', TenantRoleList.as_view(), name='tenantrole-list'),
+ url(r'api/core/tenantroles/(?P<pk>[a-zA-Z0-9\-]+)/$', TenantRoleDetail.as_view(), name ='tenantrole-detail'),
+
url(r'api/core/slices/$', SliceList.as_view(), name='slice-list'),
url(r'api/core/slices/(?P<pk>[a-zA-Z0-9\-]+)/$', SliceDetail.as_view(), name ='slice-detail'),
@@ -466,6 +477,7 @@
'tenantrootroles': reverse('tenantrootrole-list-legacy', request=request, format=format),
'sliceroles': reverse('slicerole-list-legacy', request=request, format=format),
'sitedeployments': reverse('sitedeployment-list-legacy', request=request, format=format),
+ 'tenantprivileges': reverse('tenantprivilege-list-legacy', request=request, format=format),
'tags': reverse('tag-list-legacy', request=request, format=format),
'usercredentials': reverse('usercredential-list-legacy', request=request, format=format),
'invoices': reverse('invoice-list-legacy', request=request, format=format),
@@ -475,6 +487,7 @@
'serviceroles': reverse('servicerole-list-legacy', request=request, format=format),
'controllersites': reverse('controllersite-list-legacy', request=request, format=format),
'controllerslices': reverse('controllerslice-list-legacy', request=request, format=format),
+ 'tenantroles': reverse('tenantrole-list-legacy', request=request, format=format),
'slices': reverse('slice-list-legacy', request=request, format=format),
'networks': reverse('network-list-legacy', request=request, format=format),
'controllerroles': reverse('controllerrole-list-legacy', request=request, format=format),
@@ -540,6 +553,7 @@
'tenantrootroles': reverse('tenantrootrole-list', request=request, format=format),
'sliceroles': reverse('slicerole-list', request=request, format=format),
'sitedeployments': reverse('sitedeployment-list', request=request, format=format),
+ 'tenantprivileges': reverse('tenantprivilege-list', request=request, format=format),
'tags': reverse('tag-list', request=request, format=format),
'usercredentials': reverse('usercredential-list', request=request, format=format),
'invoices': reverse('invoice-list', request=request, format=format),
@@ -549,6 +563,7 @@
'serviceroles': reverse('servicerole-list', request=request, format=format),
'controllersites': reverse('controllersite-list', request=request, format=format),
'controllerslices': reverse('controllerslice-list', request=request, format=format),
+ 'tenantroles': reverse('tenantrole-list', request=request, format=format),
'slices': reverse('slice-list', request=request, format=format),
'networks': reverse('network-list', request=request, format=format),
'controllerroles': reverse('controllerrole-list', request=request, format=format),
@@ -673,7 +688,7 @@
return None
class Meta:
model = ServiceAttribute
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','service',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','service',)
class ServiceAttributeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -689,7 +704,7 @@
return None
class Meta:
model = ServiceAttribute
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','service',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','service',)
@@ -708,7 +723,7 @@
return None
class Meta:
model = ControllerImages
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','controller','glance_image_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','controller','glance_image_id',)
class ControllerImagesIdSerializer(XOSModelSerializer):
id = IdField()
@@ -724,7 +739,7 @@
return None
class Meta:
model = ControllerImages
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','controller','glance_image_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','controller','glance_image_id',)
@@ -743,7 +758,7 @@
return None
class Meta:
model = ControllerSitePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','site_privilege','role_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','site_privilege','role_id',)
class ControllerSitePrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -759,7 +774,7 @@
return None
class Meta:
model = ControllerSitePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','site_privilege','role_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','site_privilege','role_id',)
@@ -782,13 +797,13 @@
return None
class Meta:
model = Image
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','kind','disk_format','container_format','path','tag','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','kind','disk_format','container_format','path','tag','deployments',)
class ImageIdSerializer(XOSModelSerializer):
id = IdField()
- deployments = serializers.PrimaryKeyRelatedField(many=True, queryset = Deployment.objects.all())
+ deployments = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Deployment.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -802,7 +817,7 @@
return None
class Meta:
model = Image
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','kind','disk_format','container_format','path','tag','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','kind','disk_format','container_format','path','tag','deployments',)
@@ -821,7 +836,7 @@
return None
class Meta:
model = ControllerNetwork
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','controller','net_id','router_id','subnet_id','subnet',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','controller','net_id','router_id','subnet_id','subnet',)
class ControllerNetworkIdSerializer(XOSModelSerializer):
id = IdField()
@@ -837,7 +852,7 @@
return None
class Meta:
model = ControllerNetwork
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','controller','net_id','router_id','subnet_id','subnet',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','controller','net_id','router_id','subnet_id','subnet',)
@@ -860,13 +875,13 @@
return None
class Meta:
model = Site
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
class SiteIdSerializer(XOSModelSerializer):
id = IdField()
- deployments = serializers.PrimaryKeyRelatedField(many=True, queryset = Deployment.objects.all())
+ deployments = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Deployment.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -880,7 +895,7 @@
return None
class Meta:
model = Site
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
@@ -899,7 +914,7 @@
return None
class Meta:
model = TenantRootRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class TenantRootRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -915,7 +930,7 @@
return None
class Meta:
model = TenantRootRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -934,7 +949,7 @@
return None
class Meta:
model = SliceRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class SliceRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -950,7 +965,7 @@
return None
class Meta:
model = SliceRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -969,7 +984,7 @@
return None
class Meta:
model = SiteDeployment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','deployment','controller','availability_zone',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','deployment','controller','availability_zone',)
class SiteDeploymentIdSerializer(XOSModelSerializer):
id = IdField()
@@ -985,7 +1000,42 @@
return None
class Meta:
model = SiteDeployment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','deployment','controller','availability_zone',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','deployment','controller','availability_zone',)
+
+
+
+
+class TenantPrivilegeSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = TenantPrivilege
+ fields = ('humanReadableName', 'validators', 'created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','id','user','tenant','role',)
+
+class TenantPrivilegeIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = TenantPrivilege
+ fields = ('humanReadableName', 'validators', 'created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','id','user','tenant','role',)
@@ -1004,7 +1054,7 @@
return None
class Meta:
model = Tag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','service','name','value','content_type','object_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','service','name','value','content_type','object_id',)
class TagIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1020,7 +1070,7 @@
return None
class Meta:
model = Tag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','service','name','value','content_type','object_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','service','name','value','content_type','object_id',)
@@ -1039,7 +1089,7 @@
return None
class Meta:
model = UserCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','name','key_id','enc_value',)
class UserCredentialIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1055,7 +1105,7 @@
return None
class Meta:
model = UserCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','name','key_id','enc_value',)
@@ -1074,7 +1124,7 @@
return None
class Meta:
model = Invoice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','date','account',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','date','account',)
class InvoiceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1090,7 +1140,7 @@
return None
class Meta:
model = Invoice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','date','account',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','date','account',)
@@ -1109,7 +1159,7 @@
return None
class Meta:
model = SlicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','slice','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','slice','role',)
class SlicePrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1125,7 +1175,7 @@
return None
class Meta:
model = SlicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','slice','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','slice','role',)
@@ -1148,13 +1198,13 @@
return None
class Meta:
model = Flavor
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','flavor','order','default','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','flavor','order','default','deployments',)
class FlavorIdSerializer(XOSModelSerializer):
id = IdField()
- deployments = serializers.PrimaryKeyRelatedField(many=True, queryset = Deployment.objects.all())
+ deployments = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Deployment.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1168,7 +1218,7 @@
return None
class Meta:
model = Flavor
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','flavor','order','default','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','flavor','order','default','deployments',)
@@ -1187,7 +1237,7 @@
return None
class Meta:
model = Port
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','instance','ip','port_id','mac','xos_created',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','instance','ip','port_id','mac','xos_created',)
class PortIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1203,7 +1253,7 @@
return None
class Meta:
model = Port
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','instance','ip','port_id','mac','xos_created',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','instance','ip','port_id','mac','xos_created',)
@@ -1222,7 +1272,7 @@
return None
class Meta:
model = ServiceRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class ServiceRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1238,7 +1288,7 @@
return None
class Meta:
model = ServiceRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -1257,7 +1307,7 @@
return None
class Meta:
model = ControllerSite
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','controller','tenant_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','controller','tenant_id',)
class ControllerSiteIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1273,7 +1323,7 @@
return None
class Meta:
model = ControllerSite
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','controller','tenant_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','controller','tenant_id',)
@@ -1292,7 +1342,7 @@
return None
class Meta:
model = ControllerSlice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice','tenant_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice','tenant_id',)
class ControllerSliceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1308,7 +1358,42 @@
return None
class Meta:
model = ControllerSlice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice','tenant_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice','tenant_id',)
+
+
+
+
+class TenantRoleSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = TenantRole
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
+
+class TenantRoleIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = TenantRole
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -1335,17 +1420,17 @@
return None
class Meta:
model = Slice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
class SliceIdSerializer(XOSModelSerializer):
id = IdField()
- networks = serializers.PrimaryKeyRelatedField(many=True, queryset = Network.objects.all())
+ networks = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Network.objects.all())
- networks = serializers.PrimaryKeyRelatedField(many=True, queryset = Network.objects.all())
+ networks = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Network.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1359,7 +1444,7 @@
return None
class Meta:
model = Slice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
@@ -1398,29 +1483,29 @@
return None
class Meta:
model = Network
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
class NetworkIdSerializer(XOSModelSerializer):
id = IdField()
- slices = serializers.PrimaryKeyRelatedField(many=True, queryset = Slice.objects.all())
+ slices = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Slice.objects.all())
- slices = serializers.PrimaryKeyRelatedField(many=True, queryset = Slice.objects.all())
+ slices = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Slice.objects.all())
- instances = serializers.PrimaryKeyRelatedField(many=True, queryset = Instance.objects.all())
+ instances = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Instance.objects.all())
- routers = serializers.PrimaryKeyRelatedField(many=True, queryset = Router.objects.all())
+ routers = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Router.objects.all())
- routers = serializers.PrimaryKeyRelatedField(many=True, queryset = Router.objects.all())
+ routers = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Router.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1434,7 +1519,7 @@
return None
class Meta:
model = Network
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
@@ -1453,7 +1538,7 @@
return None
class Meta:
model = ControllerRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class ControllerRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1469,7 +1554,7 @@
return None
class Meta:
model = ControllerRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -1488,7 +1573,7 @@
return None
class Meta:
model = Diag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
class DiagIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1504,7 +1589,7 @@
return None
class Meta:
model = Diag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
@@ -1523,7 +1608,7 @@
return None
class Meta:
model = ServiceClass
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
class ServiceClassIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1539,7 +1624,7 @@
return None
class Meta:
model = ServiceClass
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
@@ -1558,7 +1643,7 @@
return None
class Meta:
model = TenantAttribute
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','tenant',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','tenant',)
class TenantAttributeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1574,7 +1659,7 @@
return None
class Meta:
model = TenantAttribute
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','tenant',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','tenant',)
@@ -1593,7 +1678,7 @@
return None
class Meta:
model = SiteRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class SiteRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1609,7 +1694,7 @@
return None
class Meta:
model = SiteRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -1628,7 +1713,7 @@
return None
class Meta:
model = Subscriber
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
class SubscriberIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1644,7 +1729,7 @@
return None
class Meta:
model = Subscriber
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
@@ -1667,13 +1752,13 @@
return None
class Meta:
model = Instance
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
class InstanceIdSerializer(XOSModelSerializer):
id = IdField()
- networks = serializers.PrimaryKeyRelatedField(many=True, queryset = Network.objects.all())
+ networks = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Network.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1687,7 +1772,7 @@
return None
class Meta:
model = Instance
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
@@ -1706,7 +1791,7 @@
return None
class Meta:
model = Charge
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','slice','kind','state','date','object','amount','coreHours','invoice',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','slice','kind','state','date','object','amount','coreHours','invoice',)
class ChargeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1722,7 +1807,7 @@
return None
class Meta:
model = Charge
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','slice','kind','state','date','object','amount','coreHours','invoice',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','slice','kind','state','date','object','amount','coreHours','invoice',)
@@ -1741,7 +1826,7 @@
return None
class Meta:
model = Program
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','kind','command','owner','contents','output','messages','status',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','kind','command','owner','contents','output','messages','status',)
class ProgramIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1757,7 +1842,7 @@
return None
class Meta:
model = Program
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','kind','command','owner','contents','output','messages','status',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','kind','command','owner','contents','output','messages','status',)
@@ -1776,7 +1861,7 @@
return None
class Meta:
model = Role
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role_type','role','description','content_type',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role_type','role','description','content_type',)
class RoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1792,7 +1877,7 @@
return None
class Meta:
model = Role
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role_type','role','description','content_type',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role_type','role','description','content_type',)
@@ -1811,7 +1896,7 @@
return None
class Meta:
model = UsableObject
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
class UsableObjectIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1827,7 +1912,7 @@
return None
class Meta:
model = UsableObject
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
@@ -1850,13 +1935,13 @@
return None
class Meta:
model = NodeLabel
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','nodes',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','nodes',)
class NodeLabelIdSerializer(XOSModelSerializer):
id = IdField()
- nodes = serializers.PrimaryKeyRelatedField(many=True, queryset = Node.objects.all())
+ nodes = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Node.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1870,7 +1955,7 @@
return None
class Meta:
model = NodeLabel
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','nodes',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','nodes',)
@@ -1889,7 +1974,7 @@
return None
class Meta:
model = SliceCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','key_id','enc_value',)
class SliceCredentialIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1905,7 +1990,7 @@
return None
class Meta:
model = SliceCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','key_id','enc_value',)
@@ -1928,13 +2013,13 @@
return None
class Meta:
model = Node
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_deployment','site','nodelabels',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_deployment','site','nodelabels',)
class NodeIdSerializer(XOSModelSerializer):
id = IdField()
- nodelabels = serializers.PrimaryKeyRelatedField(many=True, queryset = NodeLabel.objects.all())
+ nodelabels = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = NodeLabel.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -1948,7 +2033,7 @@
return None
class Meta:
model = Node
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_deployment','site','nodelabels',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_deployment','site','nodelabels',)
@@ -1967,7 +2052,7 @@
return None
class Meta:
model = AddressPool
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','addresses','inuse',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','addresses','gateway_ip','gateway_mac','cidr','inuse','service',)
class AddressPoolIdSerializer(XOSModelSerializer):
id = IdField()
@@ -1983,7 +2068,7 @@
return None
class Meta:
model = AddressPool
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','addresses','inuse',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','addresses','gateway_ip','gateway_mac','cidr','inuse','service',)
@@ -2010,17 +2095,17 @@
return None
class Meta:
model = DashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','url','enabled','controllers','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','url','enabled','controllers','deployments',)
class DashboardViewIdSerializer(XOSModelSerializer):
id = IdField()
- controllers = serializers.PrimaryKeyRelatedField(many=True, queryset = Controller.objects.all())
+ controllers = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Controller.objects.all())
- deployments = serializers.PrimaryKeyRelatedField(many=True, queryset = Deployment.objects.all())
+ deployments = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Deployment.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -2034,7 +2119,7 @@
return None
class Meta:
model = DashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','url','enabled','controllers','deployments',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','url','enabled','controllers','deployments',)
@@ -2053,7 +2138,7 @@
return None
class Meta:
model = NetworkParameter
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','parameter','value','content_type','object_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','parameter','value','content_type','object_id',)
class NetworkParameterIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2069,7 +2154,7 @@
return None
class Meta:
model = NetworkParameter
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','parameter','value','content_type','object_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','parameter','value','content_type','object_id',)
@@ -2088,7 +2173,7 @@
return None
class Meta:
model = ImageDeployments
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','deployment',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','deployment',)
class ImageDeploymentsIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2104,7 +2189,7 @@
return None
class Meta:
model = ImageDeployments
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','deployment',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','deployment',)
@@ -2123,7 +2208,7 @@
return None
class Meta:
model = ControllerUser
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','controller','kuser_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','controller','kuser_id',)
class ControllerUserIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2139,7 +2224,7 @@
return None
class Meta:
model = ControllerUser
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','controller','kuser_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','controller','kuser_id',)
@@ -2158,7 +2243,7 @@
return None
class Meta:
model = ReservedResource
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance','resource','quantity','reservationSet',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance','resource','quantity','reservationSet',)
class ReservedResourceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2174,7 +2259,7 @@
return None
class Meta:
model = ReservedResource
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance','resource','quantity','reservationSet',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance','resource','quantity','reservationSet',)
@@ -2193,7 +2278,7 @@
return None
class Meta:
model = NetworkTemplate
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
class NetworkTemplateIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2209,7 +2294,7 @@
return None
class Meta:
model = NetworkTemplate
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
@@ -2228,7 +2313,7 @@
return None
class Meta:
model = ControllerDashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','dashboardView','enabled','url',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','dashboardView','enabled','url',)
class ControllerDashboardViewIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2244,7 +2329,7 @@
return None
class Meta:
model = ControllerDashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','dashboardView','enabled','url',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','dashboardView','enabled','url',)
@@ -2263,7 +2348,7 @@
return None
class Meta:
model = UserDashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','dashboardView','order',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','dashboardView','order',)
class UserDashboardViewIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2279,7 +2364,7 @@
return None
class Meta:
model = UserDashboardView
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','dashboardView','order',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','dashboardView','order',)
@@ -2302,13 +2387,13 @@
return None
class Meta:
model = Controller
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
class ControllerIdSerializer(XOSModelSerializer):
id = IdField()
- dashboardviews = serializers.PrimaryKeyRelatedField(many=True, queryset = DashboardView.objects.all())
+ dashboardviews = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = DashboardView.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -2322,7 +2407,7 @@
return None
class Meta:
model = Controller
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
@@ -2341,7 +2426,7 @@
return None
class Meta:
model = User
- fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone',)
+ fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','timezone',)
class UserIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2357,7 +2442,7 @@
return None
class Meta:
model = User
- fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone',)
+ fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','timezone',)
@@ -2392,25 +2477,25 @@
return None
class Meta:
model = Deployment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','accessControl','images','sites','flavors','dashboardviews',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','accessControl','images','sites','flavors','dashboardviews',)
class DeploymentIdSerializer(XOSModelSerializer):
id = IdField()
- images = serializers.PrimaryKeyRelatedField(many=True, queryset = Image.objects.all())
+ images = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Image.objects.all())
- sites = serializers.PrimaryKeyRelatedField(many=True, queryset = Site.objects.all())
+ sites = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Site.objects.all())
- flavors = serializers.PrimaryKeyRelatedField(many=True, queryset = Flavor.objects.all())
+ flavors = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Flavor.objects.all())
- dashboardviews = serializers.PrimaryKeyRelatedField(many=True, queryset = DashboardView.objects.all())
+ dashboardviews = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = DashboardView.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -2424,7 +2509,7 @@
return None
class Meta:
model = Deployment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','accessControl','images','sites','flavors','dashboardviews',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','accessControl','images','sites','flavors','dashboardviews',)
@@ -2443,7 +2528,7 @@
return None
class Meta:
model = Reservation
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','startTime','slice','duration',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','startTime','slice','duration',)
class ReservationIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2459,7 +2544,7 @@
return None
class Meta:
model = Reservation
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','startTime','slice','duration',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','startTime','slice','duration',)
@@ -2478,7 +2563,7 @@
return None
class Meta:
model = SitePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','site','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','site','role',)
class SitePrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2494,7 +2579,7 @@
return None
class Meta:
model = SitePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','site','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','site','role',)
@@ -2513,7 +2598,7 @@
return None
class Meta:
model = Payment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','amount','date',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','amount','date',)
class PaymentIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2529,7 +2614,7 @@
return None
class Meta:
model = Payment
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','amount','date',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','amount','date',)
@@ -2548,7 +2633,7 @@
return None
class Meta:
model = Tenant
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
class TenantIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2564,7 +2649,7 @@
return None
class Meta:
model = Tenant
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
@@ -2583,7 +2668,7 @@
return None
class Meta:
model = NetworkSlice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','slice',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','slice',)
class NetworkSliceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2599,7 +2684,7 @@
return None
class Meta:
model = NetworkSlice
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','slice',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','slice',)
@@ -2618,7 +2703,7 @@
return None
class Meta:
model = Account
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site',)
class AccountIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2634,7 +2719,7 @@
return None
class Meta:
model = Account
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site',)
@@ -2653,7 +2738,7 @@
return None
class Meta:
model = TenantRoot
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
class TenantRootIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2669,7 +2754,7 @@
return None
class Meta:
model = TenantRoot
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
@@ -2688,7 +2773,7 @@
return None
class Meta:
model = Service
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
class ServiceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2704,7 +2789,7 @@
return None
class Meta:
model = Service
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
@@ -2723,7 +2808,7 @@
return None
class Meta:
model = ControllerSlicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice_privilege','role_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice_privilege','role_id',)
class ControllerSlicePrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2739,7 +2824,7 @@
return None
class Meta:
model = ControllerSlicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice_privilege','role_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice_privilege','role_id',)
@@ -2758,7 +2843,7 @@
return None
class Meta:
model = SiteCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','name','key_id','enc_value',)
class SiteCredentialIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2774,7 +2859,7 @@
return None
class Meta:
model = SiteCredential
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','name','key_id','enc_value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','name','key_id','enc_value',)
@@ -2793,7 +2878,7 @@
return None
class Meta:
model = DeploymentPrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','deployment','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','deployment','role',)
class DeploymentPrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2809,7 +2894,7 @@
return None
class Meta:
model = DeploymentPrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','deployment','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','deployment','role',)
@@ -2828,7 +2913,7 @@
return None
class Meta:
model = NetworkParameterType
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description',)
class NetworkParameterTypeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2844,7 +2929,7 @@
return None
class Meta:
model = NetworkParameterType
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description',)
@@ -2863,7 +2948,7 @@
return None
class Meta:
model = Provider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
class ProviderIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2879,7 +2964,7 @@
return None
class Meta:
model = Provider
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
@@ -2898,7 +2983,7 @@
return None
class Meta:
model = TenantWithContainer
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
class TenantWithContainerIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2914,7 +2999,7 @@
return None
class Meta:
model = TenantWithContainer
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
@@ -2933,7 +3018,7 @@
return None
class Meta:
model = DeploymentRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
class DeploymentRoleIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2949,7 +3034,7 @@
return None
class Meta:
model = DeploymentRole
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
@@ -2968,7 +3053,7 @@
return None
class Meta:
model = Project
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
class ProjectIdSerializer(XOSModelSerializer):
id = IdField()
@@ -2984,7 +3069,7 @@
return None
class Meta:
model = Project
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
@@ -3003,7 +3088,7 @@
return None
class Meta:
model = TenantRootPrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','tenant_root','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','tenant_root','role',)
class TenantRootPrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -3019,7 +3104,7 @@
return None
class Meta:
model = TenantRootPrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','tenant_root','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','tenant_root','role',)
@@ -3038,7 +3123,7 @@
return None
class Meta:
model = SliceTag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','value',)
class SliceTagIdSerializer(XOSModelSerializer):
id = IdField()
@@ -3054,7 +3139,7 @@
return None
class Meta:
model = SliceTag
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','value',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','value',)
@@ -3073,7 +3158,7 @@
return None
class Meta:
model = CoarseTenant
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
class CoarseTenantIdSerializer(XOSModelSerializer):
id = IdField()
@@ -3089,7 +3174,7 @@
return None
class Meta:
model = CoarseTenant
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
@@ -3116,17 +3201,17 @@
return None
class Meta:
model = Router
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','owner','networks','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','owner','networks','networks',)
class RouterIdSerializer(XOSModelSerializer):
id = IdField()
- networks = serializers.PrimaryKeyRelatedField(many=True, queryset = Network.objects.all())
+ networks = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Network.objects.all())
- networks = serializers.PrimaryKeyRelatedField(many=True, queryset = Network.objects.all())
+ networks = serializers.PrimaryKeyRelatedField(many=True, required=False, queryset = Network.objects.all())
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -3140,7 +3225,7 @@
return None
class Meta:
model = Router
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','owner','networks','networks',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','owner','networks','networks',)
@@ -3159,7 +3244,7 @@
return None
class Meta:
model = ServiceResource
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
class ServiceResourceIdSerializer(XOSModelSerializer):
id = IdField()
@@ -3175,7 +3260,7 @@
return None
class Meta:
model = ServiceResource
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
@@ -3194,7 +3279,7 @@
return None
class Meta:
model = ServicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','service','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','service','role',)
class ServicePrivilegeIdSerializer(XOSModelSerializer):
id = IdField()
@@ -3210,7 +3295,7 @@
return None
class Meta:
model = ServicePrivilege
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','service','role',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','service','role',)
@@ -3235,6 +3320,8 @@
SiteDeployment: SiteDeploymentSerializer,
+ TenantPrivilege: TenantPrivilegeSerializer,
+
Tag: TagSerializer,
UserCredential: UserCredentialSerializer,
@@ -3253,6 +3340,8 @@
ControllerSlice: ControllerSliceSerializer,
+ TenantRole: TenantRoleSerializer,
+
Slice: SliceSerializer,
Network: NetworkSerializer,
@@ -3364,7 +3453,7 @@
serializer_class = ServiceAttributeSerializer
id_serializer_class = ServiceAttributeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','service',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','service',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3411,7 +3500,7 @@
serializer_class = ControllerImagesSerializer
id_serializer_class = ControllerImagesIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','controller','glance_image_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','controller','glance_image_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3458,7 +3547,7 @@
serializer_class = ControllerSitePrivilegeSerializer
id_serializer_class = ControllerSitePrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','site_privilege','role_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','site_privilege','role_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3505,7 +3594,7 @@
serializer_class = ImageSerializer
id_serializer_class = ImageIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','kind','disk_format','container_format','path','tag','deployments',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','kind','disk_format','container_format','path','tag','deployments',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3552,7 +3641,7 @@
serializer_class = ControllerNetworkSerializer
id_serializer_class = ControllerNetworkIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','controller','net_id','router_id','subnet_id','subnet',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','controller','net_id','router_id','subnet_id','subnet',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3599,7 +3688,7 @@
serializer_class = SiteSerializer
id_serializer_class = SiteIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_url','enabled','hosts_nodes','hosts_users','location','longitude','latitude','login_base','is_public','abbreviated_name','deployments',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3646,7 +3735,7 @@
serializer_class = TenantRootRoleSerializer
id_serializer_class = TenantRootRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3693,7 +3782,7 @@
serializer_class = SliceRoleSerializer
id_serializer_class = SliceRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3740,7 +3829,7 @@
serializer_class = SiteDeploymentSerializer
id_serializer_class = SiteDeploymentIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','deployment','controller','availability_zone',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','deployment','controller','availability_zone',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3782,12 +3871,59 @@
+class TenantPrivilegeList(XOSListCreateAPIView):
+ queryset = TenantPrivilege.objects.select_related().all()
+ serializer_class = TenantPrivilegeSerializer
+ id_serializer_class = TenantPrivilegeIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','id','user','tenant','role',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"query_params"):
+ no_hyperlinks = self.request.query_params.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return TenantPrivilege.select_by_user(self.request.user)
+
+
+class TenantPrivilegeDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = TenantPrivilege.objects.select_related().all()
+ serializer_class = TenantPrivilegeSerializer
+ id_serializer_class = TenantPrivilegeIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"query_params"):
+ no_hyperlinks = self.request.query_params.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return TenantPrivilege.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
class TagList(XOSListCreateAPIView):
queryset = Tag.objects.select_related().all()
serializer_class = TagSerializer
id_serializer_class = TagIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','service','name','value','content_type','object_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','service','name','value','content_type','object_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3834,7 +3970,7 @@
serializer_class = UserCredentialSerializer
id_serializer_class = UserCredentialIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','name','key_id','enc_value',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','name','key_id','enc_value',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3881,7 +4017,7 @@
serializer_class = InvoiceSerializer
id_serializer_class = InvoiceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','date','account',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','date','account',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3928,7 +4064,7 @@
serializer_class = SlicePrivilegeSerializer
id_serializer_class = SlicePrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','slice','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','slice','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -3975,7 +4111,7 @@
serializer_class = FlavorSerializer
id_serializer_class = FlavorIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','flavor','order','default','deployments',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','flavor','order','default','deployments',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4022,7 +4158,7 @@
serializer_class = PortSerializer
id_serializer_class = PortIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','instance','ip','port_id','mac','xos_created',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','instance','ip','port_id','mac','xos_created',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4069,7 +4205,7 @@
serializer_class = ServiceRoleSerializer
id_serializer_class = ServiceRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4116,7 +4252,7 @@
serializer_class = ControllerSiteSerializer
id_serializer_class = ControllerSiteIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','controller','tenant_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','controller','tenant_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4163,7 +4299,7 @@
serializer_class = ControllerSliceSerializer
id_serializer_class = ControllerSliceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice','tenant_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice','tenant_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4205,12 +4341,59 @@
+class TenantRoleList(XOSListCreateAPIView):
+ queryset = TenantRole.objects.select_related().all()
+ serializer_class = TenantRoleSerializer
+ id_serializer_class = TenantRoleIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"query_params"):
+ no_hyperlinks = self.request.query_params.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return TenantRole.select_by_user(self.request.user)
+
+
+class TenantRoleDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = TenantRole.objects.select_related().all()
+ serializer_class = TenantRoleSerializer
+ id_serializer_class = TenantRoleIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"query_params"):
+ no_hyperlinks = self.request.query_params.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return TenantRole.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
class SliceList(XOSListCreateAPIView):
queryset = Slice.objects.select_related().all()
serializer_class = SliceSerializer
id_serializer_class = SliceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','enabled','omf_friendly','description','slice_url','site','max_instances','service','network','exposed_ports','serviceClass','creator','default_flavor','default_image','mount_data_sets','default_isolation','networks','networks',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4257,7 +4440,7 @@
serializer_class = NetworkSerializer
id_serializer_class = NetworkIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','template','subnet','ports','labels','owner','guaranteed_bandwidth','permit_all_slices','topology_parameters','controller_url','controller_parameters','network_id','router_id','subnet_id','autoconnect','slices','slices','instances','routers','routers',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4304,7 +4487,7 @@
serializer_class = ControllerRoleSerializer
id_serializer_class = ControllerRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4351,7 +4534,7 @@
serializer_class = DiagSerializer
id_serializer_class = DiagIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4398,7 +4581,7 @@
serializer_class = ServiceClassSerializer
id_serializer_class = ServiceClassIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','commitment','membershipFee','membershipFeeMonths','upgradeRequiresApproval',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4445,7 +4628,7 @@
serializer_class = TenantAttributeSerializer
id_serializer_class = TenantAttributeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','value','tenant',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','value','tenant',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4492,7 +4675,7 @@
serializer_class = SiteRoleSerializer
id_serializer_class = SiteRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4539,7 +4722,7 @@
serializer_class = SubscriberSerializer
id_serializer_class = SubscriberIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4586,7 +4769,7 @@
serializer_class = InstanceSerializer
id_serializer_class = InstanceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance_id','instance_uuid','name','instance_name','ip','image','creator','slice','deployment','node','numberCores','flavor','userData','isolation','volumes','parent','networks',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4633,7 +4816,7 @@
serializer_class = ChargeSerializer
id_serializer_class = ChargeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','slice','kind','state','date','object','amount','coreHours','invoice',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','slice','kind','state','date','object','amount','coreHours','invoice',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4680,7 +4863,7 @@
serializer_class = ProgramSerializer
id_serializer_class = ProgramIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','kind','command','owner','contents','output','messages','status',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','kind','command','owner','contents','output','messages','status',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4727,7 +4910,7 @@
serializer_class = RoleSerializer
id_serializer_class = RoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role_type','role','description','content_type',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role_type','role','description','content_type',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4774,7 +4957,7 @@
serializer_class = UsableObjectSerializer
id_serializer_class = UsableObjectIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4821,7 +5004,7 @@
serializer_class = NodeLabelSerializer
id_serializer_class = NodeLabelIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','nodes',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','nodes',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4868,7 +5051,7 @@
serializer_class = SliceCredentialSerializer
id_serializer_class = SliceCredentialIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','key_id','enc_value',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','key_id','enc_value',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4915,7 +5098,7 @@
serializer_class = NodeSerializer
id_serializer_class = NodeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','site_deployment','site','nodelabels',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','site_deployment','site','nodelabels',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -4962,7 +5145,7 @@
serializer_class = AddressPoolSerializer
id_serializer_class = AddressPoolIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','addresses','inuse',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','addresses','gateway_ip','gateway_mac','cidr','inuse','service',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5009,7 +5192,7 @@
serializer_class = DashboardViewSerializer
id_serializer_class = DashboardViewIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','url','enabled','controllers','deployments',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','url','enabled','controllers','deployments',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5056,7 +5239,7 @@
serializer_class = NetworkParameterSerializer
id_serializer_class = NetworkParameterIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','parameter','value','content_type','object_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','parameter','value','content_type','object_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5103,7 +5286,7 @@
serializer_class = ImageDeploymentsSerializer
id_serializer_class = ImageDeploymentsIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','image','deployment',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','image','deployment',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5150,7 +5333,7 @@
serializer_class = ControllerUserSerializer
id_serializer_class = ControllerUserIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','controller','kuser_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','controller','kuser_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5197,7 +5380,7 @@
serializer_class = ReservedResourceSerializer
id_serializer_class = ReservedResourceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','instance','resource','quantity','reservationSet',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','instance','resource','quantity','reservationSet',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5244,7 +5427,7 @@
serializer_class = NetworkTemplateSerializer
id_serializer_class = NetworkTemplateIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description','guaranteed_bandwidth','visibility','translation','access','shared_network_name','shared_network_id','topology_kind','controller_kind',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5291,7 +5474,7 @@
serializer_class = ControllerDashboardViewSerializer
id_serializer_class = ControllerDashboardViewIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','dashboardView','enabled','url',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','dashboardView','enabled','url',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5338,7 +5521,7 @@
serializer_class = UserDashboardViewSerializer
id_serializer_class = UserDashboardViewIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','dashboardView','order',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','dashboardView','order',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5385,7 +5568,7 @@
serializer_class = ControllerSerializer
id_serializer_class = ControllerIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5432,7 +5615,7 @@
serializer_class = UserSerializer
id_serializer_class = UserIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone',)
+ filter_fields = ('id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','timezone',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5479,7 +5662,7 @@
serializer_class = DeploymentSerializer
id_serializer_class = DeploymentIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','accessControl','images','sites','flavors','dashboardviews',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','accessControl','images','sites','flavors','dashboardviews',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5526,7 +5709,7 @@
serializer_class = ReservationSerializer
id_serializer_class = ReservationIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','startTime','slice','duration',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','startTime','slice','duration',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5573,7 +5756,7 @@
serializer_class = SitePrivilegeSerializer
id_serializer_class = SitePrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','site','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','site','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5620,7 +5803,7 @@
serializer_class = PaymentSerializer
id_serializer_class = PaymentIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','account','amount','date',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','account','amount','date',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5667,7 +5850,7 @@
serializer_class = TenantSerializer
id_serializer_class = TenantIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5714,7 +5897,7 @@
serializer_class = NetworkSliceSerializer
id_serializer_class = NetworkSliceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','network','slice',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','network','slice',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5761,7 +5944,7 @@
serializer_class = AccountSerializer
id_serializer_class = AccountIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5808,7 +5991,7 @@
serializer_class = TenantRootSerializer
id_serializer_class = TenantRootIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5855,7 +6038,7 @@
serializer_class = ServiceSerializer
id_serializer_class = ServiceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','private_key_fn','service_specific_id','service_specific_attribute',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5902,7 +6085,7 @@
serializer_class = ControllerSlicePrivilegeSerializer
id_serializer_class = ControllerSlicePrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','controller','slice_privilege','role_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','controller','slice_privilege','role_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5949,7 +6132,7 @@
serializer_class = SiteCredentialSerializer
id_serializer_class = SiteCredentialIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','site','name','key_id','enc_value',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','site','name','key_id','enc_value',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -5996,7 +6179,7 @@
serializer_class = DeploymentPrivilegeSerializer
id_serializer_class = DeploymentPrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','deployment','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','deployment','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6043,7 +6226,7 @@
serializer_class = NetworkParameterTypeSerializer
id_serializer_class = NetworkParameterTypeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','description',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','description',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6090,7 +6273,7 @@
serializer_class = ProviderSerializer
id_serializer_class = ProviderIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','name','service_specific_attribute','service_specific_id',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','name','service_specific_attribute','service_specific_id',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6137,7 +6320,7 @@
serializer_class = TenantWithContainerSerializer
id_serializer_class = TenantWithContainerIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6184,7 +6367,7 @@
serializer_class = DeploymentRoleSerializer
id_serializer_class = DeploymentRoleIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6231,7 +6414,7 @@
serializer_class = ProjectSerializer
id_serializer_class = ProjectIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6278,7 +6461,7 @@
serializer_class = TenantRootPrivilegeSerializer
id_serializer_class = TenantRootPrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','tenant_root','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','tenant_root','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6325,7 +6508,7 @@
serializer_class = SliceTagSerializer
id_serializer_class = SliceTagIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','slice','name','value',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','slice','name','value',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6372,7 +6555,7 @@
serializer_class = CoarseTenantSerializer
id_serializer_class = CoarseTenantIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','service_specific_id','service_specific_attribute','connect_method',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','kind','provider_service','subscriber_service','subscriber_tenant','subscriber_user','subscriber_root','subscriber_network','service_specific_id','service_specific_attribute','connect_method',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6419,7 +6602,7 @@
serializer_class = RouterSerializer
id_serializer_class = RouterIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','owner','networks','networks',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','name','owner','networks','networks',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6466,7 +6649,7 @@
serializer_class = ServiceResourceSerializer
id_serializer_class = ServiceResourceIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','serviceClass','name','maxUnitsDeployment','maxUnitsNode','maxDuration','bucketInRate','bucketMaxSize','cost','calendarReservable',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6513,7 +6696,7 @@
serializer_class = ServicePrivilegeSerializer
id_serializer_class = ServicePrivilegeIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','user','service','role',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','no_policy','user','service','role',)
def get_serializer_class(self):
no_hyperlinks=False
@@ -6552,3 +6735,5 @@
# update() is handled by XOSRetrieveUpdateDestroyAPIView
# destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+