Started serviceGrid view
diff --git a/apiary.apib b/apiary.apib
index 25915e9..0f9632d 100644
--- a/apiary.apib
+++ b/apiary.apib
@@ -421,6 +421,129 @@
+# Group Services
+
+List of the XOS Services
+
+## Services [/api/core/services/{id}/]
+
+### List all Services [GET]
+
++ Response 200 (application/json)
+
+ [
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+ ]
+
+### Create a Service [POST]
+
++ Request (application/json)
+
+ {
+ "name": "MyService",
+ "kind": "vROUTER"
+ }
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+
+### View a Service Detail [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Service in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+
+### Delete a Service [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Service in the form of an integer
+
++ Response 204
+
+
+
# Group Sites
List of the XOS sites
diff --git a/views/ngXosLib/xosHelpers/src/services/rest/Services.js b/views/ngXosLib/xosHelpers/src/services/rest/Services.js
new file mode 100644
index 0000000..a39098a
--- /dev/null
+++ b/views/ngXosLib/xosHelpers/src/services/rest/Services.js
@@ -0,0 +1,15 @@
+(function() {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Instances
+ * @description Angular resource to fetch /api/core/services/:id/
+ **/
+ .service('Services', function($resource){
+ return $resource('/api/core/services/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/.bowerrc b/views/ngXosViews/serviceGrid/.bowerrc
new file mode 100644
index 0000000..e491038
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "src/vendor/"
+}
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/.eslintrc b/views/ngXosViews/serviceGrid/.eslintrc
new file mode 100644
index 0000000..c852748
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/.eslintrc
@@ -0,0 +1,42 @@
+{
+ "ecmaFeatures": {
+ "blockBindings": true,
+ "forOf": true,
+ "destructuring": true,
+ "arrowFunctions": true,
+ "templateStrings": true
+ },
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ },
+ "plugins": [
+ //"angular"
+ ],
+ "rules": {
+ "quotes": [2, "single"],
+ "camelcase": [1, {"properties": "always"}],
+ "no-underscore-dangle": 1,
+ "eqeqeq": [2, "smart"],
+ "no-alert": 1,
+ "key-spacing": [1, { "beforeColon": false, "afterColon": true }],
+ "indent": [2, 2],
+ "no-irregular-whitespace": 1,
+ "eol-last": 0,
+ "max-nested-callbacks": [2, 4],
+ "comma-spacing": [1, {"before": false, "after": true}],
+ "no-trailing-spaces": [1, { skipBlankLines: true }],
+ "no-unused-vars": [1, {"vars": "all", "args": "after-used"}],
+ "new-cap": 0,
+
+ //"angular/ng_module_name": [2, '/^xos\.*[a-z]*$/'],
+ //"angular/ng_controller_name": [2, '/^[a-z].*Ctrl$/'],
+ //"angular/ng_service_name": [2, '/^[A-Z].*Service$/'],
+ //"angular/ng_directive_name": [2, '/^[a-z]+[[A-Z].*]*$/'],
+ //"angular/ng_di": [0, "function or array"]
+ },
+ "globals" :{
+ "angular": true
+ }
+}
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/.gitignore b/views/ngXosViews/serviceGrid/.gitignore
new file mode 100644
index 0000000..567aee4
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/.gitignore
@@ -0,0 +1,6 @@
+dist/
+src/vendor
+.tmp
+node_modules
+npm-debug.log
+dist/
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/bower.json b/views/ngXosViews/serviceGrid/bower.json
new file mode 100644
index 0000000..f99dc98
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/bower.json
@@ -0,0 +1,31 @@
+{
+ "name": "xos-serviceGrid",
+ "version": "0.0.0",
+ "authors": [
+ "Matteo Scandolo <teo@onlab.us>"
+ ],
+ "description": "The serviceGrid view",
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "static/js/vendor/",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ },
+ "devDependencies": {
+ "jquery": "2.1.4",
+ "angular-mocks": "1.4.7",
+ "angular": "1.4.7",
+ "angular-ui-router": "0.2.15",
+ "angular-cookies": "1.4.7",
+ "angular-animate": "1.4.7",
+ "angular-resource": "1.4.7",
+ "lodash": "~4.11.1",
+ "bootstrap-css": "3.3.6",
+ "angular-chart.js": "~0.10.2"
+ }
+}
diff --git a/views/ngXosViews/serviceGrid/env/default.js b/views/ngXosViews/serviceGrid/env/default.js
new file mode 100644
index 0000000..5a8b48c
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/env/default.js
@@ -0,0 +1,13 @@
+// This is a default configuration for your development environment.
+// You can duplicate this configuration for any of your Backend Environments.
+// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
+// `NODE_ENV=local npm start`
+//
+// If xoscsrftoken or xossessionid are not specified the browser value are used
+// (works only for local environment as both application are served on the same domain)
+
+module.exports = {
+ host: 'http://xos.dev:9999/',
+ xoscsrftoken: 'VlCJNJg79Gpf48CmElpfHiFOozppc0QZ',
+ xossessionid: 'qjbnydj4ujm5t07f2k22zpjwk9y5487c'
+};
diff --git a/views/ngXosViews/serviceGrid/gulp/build.js b/views/ngXosViews/serviceGrid/gulp/build.js
new file mode 100644
index 0000000..b582642
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/gulp/build.js
@@ -0,0 +1,163 @@
+'use strict';
+
+// BUILD
+//
+// The only purpose of this gulpfile is to build a XOS view and copy the correct files into
+// .html => dashboards
+// .js (minified and concat) => static/js
+//
+// The template are parsed and added to js with angular $templateCache
+
+var gulp = require('gulp');
+var ngAnnotate = require('gulp-ng-annotate');
+var uglify = require('gulp-uglify');
+var templateCache = require('gulp-angular-templatecache');
+var runSequence = require('run-sequence');
+var concat = require('gulp-concat-util');
+var del = require('del');
+var wiredep = require('wiredep');
+var angularFilesort = require('gulp-angular-filesort');
+var _ = require('lodash');
+var eslint = require('gulp-eslint');
+var inject = require('gulp-inject');
+var rename = require('gulp-rename');
+var replace = require('gulp-replace');
+var postcss = require('gulp-postcss');
+var autoprefixer = require('autoprefixer');
+var mqpacker = require('css-mqpacker');
+var csswring = require('csswring');
+
+const TEMPLATE_FOOTER = `
+angular.module('xos.serviceGrid')
+.run(['$location', function(a){
+ a.path('/');
+}])
+`
+
+module.exports = function(options){
+
+ // delete previous builded file
+ gulp.task('clean', function(){
+ return del(
+ [
+ options.dashboards + 'xosServiceGrid.html',
+ options.static + 'css/xosServiceGrid.css'
+ ],
+ {force: true}
+ );
+ });
+
+ // minify css
+ gulp.task('css', function () {
+ var processors = [
+ autoprefixer({browsers: ['last 1 version']}),
+ mqpacker,
+ csswring
+ ];
+
+ gulp.src([
+ `${options.css}**/*.css`,
+ `!${options.css}dev.css`
+ ])
+ .pipe(postcss(processors))
+ .pipe(gulp.dest(options.tmp + '/css/'));
+ });
+
+ // copy css in correct folder
+ gulp.task('copyCss', ['wait'], function(){
+ return gulp.src([`${options.tmp}/css/*.css`])
+ .pipe(concat('xosServiceGrid.css'))
+ .pipe(gulp.dest(options.static + 'css/'))
+ });
+
+ // compile and minify scripts
+ gulp.task('scripts', function() {
+ return gulp.src([
+ options.tmp + '**/*.js'
+ ])
+ .pipe(ngAnnotate())
+ .pipe(angularFilesort())
+ .pipe(concat('xosServiceGrid.js'))
+ .pipe(concat.header('//Autogenerated, do not edit!!!\n'))
+ .pipe(concat.footer(TEMPLATE_FOOTER))
+ .pipe(uglify())
+ .pipe(gulp.dest(options.static + 'js/'));
+ });
+
+ // set templates in cache
+ gulp.task('templates', function(){
+ return gulp.src('./src/templates/*.html')
+ .pipe(templateCache({
+ module: 'xos.serviceGrid',
+ root: 'templates/'
+ }))
+ .pipe(gulp.dest(options.tmp));
+ });
+
+ // copy html index to Django Folder
+ gulp.task('copyHtml', function(){
+ return gulp.src(options.src + 'index.html')
+ // remove dev dependencies from html
+ .pipe(replace(/<!-- bower:css -->(\n.*)*\n<!-- endbower --><!-- endcss -->/, ''))
+ .pipe(replace(/<!-- bower:js -->(\n.*)*\n<!-- endbower --><!-- endjs -->/, ''))
+ // injecting minified files
+ .pipe(
+ inject(
+ gulp.src([
+ options.static + 'js/vendor/xosServiceGridVendor.js',
+ options.static + 'js/xosServiceGrid.js',
+ options.static + 'css/xosServiceGrid.css'
+ ]),
+ {ignorePath: '/../../../xos/core/xoslib'}
+ )
+ )
+ .pipe(rename('xosServiceGrid.html'))
+ .pipe(gulp.dest(options.dashboards));
+ });
+
+ // minify vendor js files
+ gulp.task('wiredep', function(){
+ var bowerDeps = wiredep().js;
+ if(!bowerDeps){
+ return;
+ }
+
+ // remove angular (it's already loaded)
+ _.remove(bowerDeps, function(dep){
+ return dep.indexOf('angular/angular.js') !== -1;
+ });
+
+ return gulp.src(bowerDeps)
+ .pipe(concat('xosServiceGridVendor.js'))
+ .pipe(uglify())
+ .pipe(gulp.dest(options.static + 'js/vendor/'));
+ });
+
+ gulp.task('lint', function () {
+ return gulp.src(['src/js/**/*.js'])
+ .pipe(eslint())
+ .pipe(eslint.format())
+ .pipe(eslint.failAfterError());
+ });
+
+ gulp.task('wait', function (cb) {
+ // setTimeout could be any async task
+ setTimeout(function () {
+ cb();
+ }, 1000);
+ });
+
+ gulp.task('build', function() {
+ runSequence(
+ 'clean',
+ 'templates',
+ 'babel',
+ 'scripts',
+ 'wiredep',
+ 'css',
+ 'copyCss',
+ 'copyHtml',
+ 'cleanTmp'
+ );
+ });
+};
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/gulp/server.js b/views/ngXosViews/serviceGrid/gulp/server.js
new file mode 100644
index 0000000..c0678d9
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/gulp/server.js
@@ -0,0 +1,168 @@
+'use strict';
+
+var gulp = require('gulp');
+var browserSync = require('browser-sync').create();
+var inject = require('gulp-inject');
+var runSequence = require('run-sequence');
+var angularFilesort = require('gulp-angular-filesort');
+var babel = require('gulp-babel');
+var wiredep = require('wiredep').stream;
+var httpProxy = require('http-proxy');
+var del = require('del');
+var sass = require('gulp-sass');
+
+const environment = process.env.NODE_ENV;
+
+if (environment){
+ var conf = require(`../env/${environment}.js`);
+}
+else{
+ var conf = require('../env/default.js')
+}
+
+var proxy = httpProxy.createProxyServer({
+ target: conf.host || 'http://0.0.0.0:9999'
+});
+
+
+proxy.on('error', function(error, req, res) {
+ res.writeHead(500, {
+ 'Content-Type': 'text/plain'
+ });
+
+ console.error('[Proxy]', error);
+});
+
+module.exports = function(options){
+
+ gulp.task('browser', function() {
+ browserSync.init({
+ startPath: '#/',
+ snippetOptions: {
+ rule: {
+ match: /<!-- browserSync -->/i
+ }
+ },
+ server: {
+ baseDir: options.src,
+ routes: {
+ '/xos/core/xoslib/static/js/vendor': options.helpers,
+ '/xos/core/static': options.static + '../../static/'
+ },
+ middleware: function(req, res, next){
+ if(
+ // to be removed, deprecated API
+ // req.url.indexOf('/xos/') !== -1 ||
+ // req.url.indexOf('/xoslib/') !== -1 ||
+ // req.url.indexOf('/hpcapi/') !== -1 ||
+ req.url.indexOf('/api/') !== -1
+ ){
+ if(conf.xoscsrftoken && conf.xossessionid){
+ req.headers.cookie = `xoscsrftoken=${conf.xoscsrftoken}; xossessionid=${conf.xossessionid}`;
+ req.headers['x-csrftoken'] = conf.xoscsrftoken;
+ }
+ proxy.web(req, res);
+ }
+ else{
+ next();
+ }
+ }
+ }
+ });
+
+ gulp.watch(options.src + 'js/**/*.js', ['js-watch']);
+ gulp.watch(options.src + 'vendor/**/*.js', ['bower'], function(){
+ browserSync.reload();
+ });
+ gulp.watch(options.src + '**/*.html', function(){
+ browserSync.reload();
+ });
+ gulp.watch(options.css + '**/*.css', function(){
+ browserSync.reload();
+ });
+ gulp.watch(`${options.sass}/**/*.scss`, ['sass'], function(){
+ browserSync.reload();
+ });
+
+ gulp.watch([
+ options.helpers + 'ngXosHelpers.js',
+ options.static + '../../static/xosNgLib.css'
+ ], function(){
+ browserSync.reload();
+ });
+ });
+
+ // compile sass
+ gulp.task('sass', function () {
+ return gulp.src(`${options.sass}/**/*.scss`)
+ .pipe(sass().on('error', sass.logError))
+ .pipe(gulp.dest(options.css));
+ });
+
+ // transpile js with sourceMaps
+ gulp.task('babel', function(){
+ return gulp.src(options.scripts + '**/*.js')
+ .pipe(babel({sourceMaps: true}))
+ .pipe(gulp.dest(options.tmp));
+ });
+
+ // inject scripts
+ gulp.task('injectScript', ['cleanTmp', 'babel'], function(){
+ return gulp.src(options.src + 'index.html')
+ .pipe(
+ inject(
+ gulp.src([
+ options.tmp + '**/*.js',
+ options.helpers + 'ngXosHelpers.js'
+ ])
+ .pipe(angularFilesort()),
+ {
+ ignorePath: [options.src, '/../../ngXosLib']
+ }
+ )
+ )
+ .pipe(gulp.dest(options.src));
+ });
+
+ // inject CSS
+ gulp.task('injectCss', function(){
+ return gulp.src(options.src + 'index.html')
+ .pipe(
+ inject(
+ gulp.src([
+ options.src + 'css/*.css',
+ options.static + '../../static/xosNgLib.css'
+ ]),
+ {
+ ignorePath: [options.src]
+ }
+ )
+ )
+ .pipe(gulp.dest(options.src));
+ });
+
+ // inject bower dependencies with wiredep
+ gulp.task('bower', function () {
+ return gulp.src(options.src + 'index.html')
+ .pipe(wiredep({devDependencies: true}))
+ .pipe(gulp.dest(options.src));
+ });
+
+ gulp.task('js-watch', ['injectScript'], function(){
+ browserSync.reload();
+ });
+
+ gulp.task('cleanTmp', function(){
+ return del([options.tmp + '**/*']);
+ });
+
+ gulp.task('serve', function() {
+ runSequence(
+ 'sass',
+ 'bower',
+ 'injectScript',
+ 'injectCss',
+ ['browser']
+ );
+ });
+};
diff --git a/views/ngXosViews/serviceGrid/gulpfile.js b/views/ngXosViews/serviceGrid/gulpfile.js
new file mode 100644
index 0000000..08df554
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/gulpfile.js
@@ -0,0 +1,26 @@
+'use strict';
+
+var gulp = require('gulp');
+var wrench = require('wrench');
+
+var options = {
+ src: 'src/',
+ css: 'src/css/',
+ sass: 'src/sass/',
+ scripts: 'src/js/',
+ tmp: 'src/.tmp',
+ dist: 'dist/',
+ api: '../../ngXosLib/api/',
+ helpers: '../../../xos/core/xoslib/static/js/vendor/',
+ static: '../../../xos/core/xoslib/static/', // this is the django static folder
+ dashboards: '../../../xos/core/xoslib/dashboards/' // this is the django html folder
+};
+
+wrench.readdirSyncRecursive('./gulp')
+.map(function(file) {
+ require('./gulp/' + file)(options);
+});
+
+gulp.task('default', function () {
+ gulp.start('build');
+});
diff --git a/views/ngXosViews/serviceGrid/karma.conf.js b/views/ngXosViews/serviceGrid/karma.conf.js
new file mode 100644
index 0000000..4123be9
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/karma.conf.js
@@ -0,0 +1,88 @@
+// Karma configuration
+// Generated on Tue Oct 06 2015 09:27:10 GMT+0000 (UTC)
+
+/* eslint indent: [2,2], quotes: [2, "single"]*/
+
+/*eslint-disable*/
+var wiredep = require('wiredep');
+var path = require('path');
+
+var bowerComponents = wiredep( {devDependencies: true} )[ 'js' ].map(function( file ){
+ return path.relative(process.cwd(), file);
+});
+
+module.exports = function(config) {
+/*eslint-enable*/
+ config.set({
+
+ // base path that will be used to resolve all patterns (eg. files, exclude)
+ basePath: '',
+
+
+ // frameworks to use
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+ frameworks: ['jasmine'],
+
+
+ // list of files / patterns to load in the browser
+ files: bowerComponents.concat([
+ '../../../xos/core/xoslib/static/js/vendor/ngXosVendor.js',
+ '../../../xos/core/xoslib/static/js/vendor/ngXosHelpers.js',
+ 'src/js/**/*.js',
+ 'spec/**/*.mock.js',
+ 'spec/**/*.test.js',
+ 'src/**/*.html'
+ ]),
+
+
+ // list of files to exclude
+ exclude: [
+ ],
+
+
+ // preprocess matching files before serving them to the browser
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+ preprocessors: {
+ 'src/js/**/*.js': ['babel'],
+ 'spec/**/*.test.js': ['babel'],
+ 'src/**/*.html': ['ng-html2js']
+ },
+
+ ngHtml2JsPreprocessor: {
+ stripPrefix: 'src/', //strip the src path from template url (http://stackoverflow.com/questions/22869668/karma-unexpected-request-when-testing-angular-directive-even-with-ng-html2js)
+ moduleName: 'templates' // define the template module name
+ },
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress'
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+ reporters: ['mocha'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // start these browsers
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
+ browsers: ['PhantomJS'],
+
+
+ // Continuous Integration mode
+ // if true, Karma captures browsers, runs the tests and exits
+ singleRun: false
+ });
+};
diff --git a/views/ngXosViews/serviceGrid/package.json b/views/ngXosViews/serviceGrid/package.json
new file mode 100644
index 0000000..c33c615
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "xos-serviceGrid",
+ "version": "1.0.0",
+ "description": "Angular Application for XOS, created with generator-xos",
+ "scripts": {
+ "prestart": "npm install && bower install",
+ "start": "gulp serve",
+ "prebuild": "npm install && bower install",
+ "build": "gulp",
+ "test": "karma start",
+ "test:ci": "karma start --single-run",
+ "lint": "eslint src/js/"
+ },
+ "keywords": [
+ "XOS",
+ "Angular",
+ "XOSlib"
+ ],
+ "author": "Matteo Scandolo",
+ "license": "MIT",
+ "dependencies": {},
+ "devDependencies": {
+ "autoprefixer": "^6.3.3",
+ "browser-sync": "^2.9.11",
+ "css-mqpacker": "^4.0.0",
+ "csswring": "^4.2.1",
+ "del": "^2.0.2",
+ "easy-mocker": "^1.2.0",
+ "eslint": "^1.8.0",
+ "eslint-plugin-angular": "linkmesrl/eslint-plugin-angular",
+ "gulp": "^3.9.0",
+ "gulp-angular-filesort": "^1.1.1",
+ "gulp-angular-templatecache": "^1.8.0",
+ "gulp-babel": "^5.3.0",
+ "gulp-concat": "^2.6.0",
+ "gulp-concat-util": "^0.5.5",
+ "gulp-eslint": "^1.0.0",
+ "gulp-inject": "^3.0.0",
+ "gulp-minify-html": "^1.0.4",
+ "gulp-ng-annotate": "^1.1.0",
+ "gulp-postcss": "^6.0.1",
+ "gulp-rename": "^1.2.2",
+ "gulp-replace": "^0.5.4",
+ "gulp-sass": "^2.2.0",
+ "gulp-uglify": "^1.4.2",
+ "http-proxy": "^1.12.0",
+ "ink-docstrap": "^0.5.2",
+ "jasmine-core": "~2.3.4",
+ "karma": "^0.13.14",
+ "karma-babel-preprocessor": "~5.2.2",
+ "karma-coverage": "^0.5.3",
+ "karma-jasmine": "~0.3.6",
+ "karma-mocha-reporter": "~1.1.1",
+ "karma-ng-html2js-preprocessor": "^0.2.0",
+ "karma-phantomjs-launcher": "~0.2.1",
+ "lodash": "^3.10.1",
+ "phantomjs": "^1.9.19",
+ "proxy-middleware": "^0.15.0",
+ "run-sequence": "^1.1.4",
+ "wiredep": "^3.0.0-beta",
+ "wrench": "^1.5.8"
+ }
+}
diff --git a/views/ngXosViews/serviceGrid/spec/sample.test.js b/views/ngXosViews/serviceGrid/spec/sample.test.js
new file mode 100644
index 0000000..38958fd
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/spec/sample.test.js
@@ -0,0 +1,37 @@
+'use strict';
+
+describe('The User List', () => {
+
+ var scope, element, isolatedScope, httpBackend;
+
+ beforeEach(module('xos.serviceGrid'));
+ beforeEach(module('templates'));
+
+ beforeEach(inject(function($httpBackend, $compile, $rootScope){
+
+ httpBackend = $httpBackend;
+ // Setting up mock request
+ $httpBackend.expectGET('/api/core/users/?no_hyperlinks=1').respond([
+ {
+ email: 'teo@onlab.us',
+ firstname: 'Matteo',
+ lastname: 'Scandolo'
+ }
+ ]);
+
+ scope = $rootScope.$new();
+ element = angular.element('<users-list></users-list>');
+ $compile(element)(scope);
+ scope.$digest();
+ isolatedScope = element.isolateScope().vm;
+ }));
+
+ it('should load 1 users', () => {
+ httpBackend.flush();
+ expect(isolatedScope.users.length).toBe(1);
+ expect(isolatedScope.users[0].email).toEqual('teo@onlab.us');
+ expect(isolatedScope.users[0].firstname).toEqual('Matteo');
+ expect(isolatedScope.users[0].lastname).toEqual('Scandolo');
+ });
+
+});
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/css/main.css b/views/ngXosViews/serviceGrid/src/css/main.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/css/main.css
diff --git a/views/ngXosViews/serviceGrid/src/index.html b/views/ngXosViews/serviceGrid/src/index.html
new file mode 100644
index 0000000..2e7f92e
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/index.html
@@ -0,0 +1,34 @@
+<!-- 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 -->
+<link rel="stylesheet" href="/css/main.css">
+<link rel="stylesheet" href="/../../../xos/core/static/xosNgLib.css">
+<!-- endinject -->
+
+<div ng-app="xos.serviceGrid" id="xosServiceGrid" class="container-fluid">
+ <div ui-view></div>
+</div>
+
+<!-- bower:js -->
+<script src="vendor/jquery/dist/jquery.js"></script>
+<script src="vendor/angular/angular.js"></script>
+<script src="vendor/angular-mocks/angular-mocks.js"></script>
+<script src="vendor/angular-ui-router/release/angular-ui-router.js"></script>
+<script src="vendor/angular-cookies/angular-cookies.js"></script>
+<script src="vendor/angular-animate/angular-animate.js"></script>
+<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 -->
+<script src="/../../../xos/core/xoslib/static/js/vendor/ngXosHelpers.js"></script>
+<script src="/.tmp/main.js"></script>
+<script src="/.tmp/service-graph.js"></script>
+<!-- endinject -->
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/js/main.js b/views/ngXosViews/serviceGrid/src/js/main.js
new file mode 100644
index 0000000..5c41bd4
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/js/main.js
@@ -0,0 +1,72 @@
+'use strict';
+
+angular.module('xos.serviceGrid', [
+ 'ngResource',
+ 'ngCookies',
+ 'ui.router',
+ 'xos.helpers'
+])
+.config(($stateProvider) => {
+ $stateProvider
+ .state('serviceGrid', {
+ url: '/',
+ template: '<service-grid></service-grid>'
+ })
+ .state('serviceGraph', {
+ url: '/graph',
+ template: '<service-graph></service-graph>'
+ });
+})
+.config(function($httpProvider){
+ $httpProvider.interceptors.push('NoHyperlinks');
+})
+.directive('serviceGrid', function(){
+ return {
+ restrict: 'E',
+ scope: {},
+ bindToController: true,
+ controllerAs: 'vm',
+ templateUrl: 'templates/service-grid.tpl.html',
+ controller: function(Services, _){
+
+ this.tableConfig = {
+ columns: [
+ {
+ label: 'Status',
+ prop: 'status',
+ type: 'boolean',
+ },
+ {
+ label: 'Name',
+ prop: 'name',
+ link: item => `${item.view_url.replace(/\$[a-z]+\$/, item.id)}`
+ },
+ {
+ label: 'Kind',
+ prop: 'kind'
+ },
+ {
+ label: 'Enabled',
+ prop: 'enabled',
+ type: 'boolean'
+ }
+ ],
+ filter: 'field',
+ order: true
+ };
+
+ // retrieving user list
+ Services.query().$promise
+ .then((services) => {
+ this.services = _.map(services, s => {
+ // parse backend_status string in a boolean for display
+ s.status = parseInt(s.backend_status.match(/^[0-9]/)[0]) === 0 ? false : true;
+ return s;
+ })
+ })
+ .catch((e) => {
+ throw new Error(e);
+ });
+ }
+ };
+});
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/js/service-graph.js b/views/ngXosViews/serviceGrid/src/js/service-graph.js
new file mode 100644
index 0000000..0f9c05c
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/js/service-graph.js
@@ -0,0 +1,17 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.serviceGrid')
+ .directive('serviceGraph', function(){
+ return {
+ restrict: 'E',
+ scope: {},
+ bindToController: true,
+ controllerAs: 'vm',
+ templateUrl: 'templates/service-graph.tpl.html',
+ controller: function(){
+
+ }
+ };
+ })
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/sass/main.scss b/views/ngXosViews/serviceGrid/src/sass/main.scss
new file mode 100644
index 0000000..6f4bfe1
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/sass/main.scss
@@ -0,0 +1,5 @@
+@import '../../../../style/sass/lib/_variables.scss';
+
+#xosServiceGrid {
+
+}
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/templates/service-graph.tpl.html b/views/ngXosViews/serviceGrid/src/templates/service-graph.tpl.html
new file mode 100644
index 0000000..15be8a0
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/templates/service-graph.tpl.html
@@ -0,0 +1,14 @@
+<div class="row">
+ <div class="col-sm-10">
+ <h1>Graph</h1>
+ </div>
+ <div class="col-sm-2">
+ <a href="/admin/core/service/add" class="btn btn-success btn-block">
+ <i class="glyphicon glyphicon-plus"></i>
+ Add Service
+ </a>
+ <a href="#/" class="btn btn-default btn-block">
+ Service List
+ </a>
+ </div>
+</div>
\ No newline at end of file
diff --git a/views/ngXosViews/serviceGrid/src/templates/service-grid.tpl.html b/views/ngXosViews/serviceGrid/src/templates/service-grid.tpl.html
new file mode 100644
index 0000000..c371d37
--- /dev/null
+++ b/views/ngXosViews/serviceGrid/src/templates/service-grid.tpl.html
@@ -0,0 +1,14 @@
+<div class="row">
+ <div class="col-sm-10">
+ <xos-table config="vm.tableConfig" data="vm.services"></xos-table>
+ </div>
+ <div class="col-sm-2">
+ <a href="/admin/core/service/add" class="btn btn-success btn-block">
+ <i class="glyphicon glyphicon-plus"></i>
+ Add Service
+ </a>
+ <a href="#/graph" class="btn btn-default btn-block">
+ Tenancy Graph
+ </a>
+ </div>
+</div>
\ No newline at end of file
diff --git a/views/style/bs-config.js b/views/style/bs-config.js
index 2bc1416..288f1cc 100644
--- a/views/style/bs-config.js
+++ b/views/style/bs-config.js
@@ -6,7 +6,8 @@
'../../xos/core/xoslib/static/**/*.css',
'../../xos/core/dashboard/views/*.py',
'../../xos/templates/**/*.html',
- '../../xos/core/static/xos.css'
+ '../../xos/core/static/xos.css',
+ '../../xos/xos/**/*.py'
],
proxy: "xos.dev:9999",
open: true
diff --git a/xos/core/xoslib/dashboards/xosServiceGrid.html b/xos/core/xoslib/dashboards/xosServiceGrid.html
new file mode 100644
index 0000000..62bc24f
--- /dev/null
+++ b/xos/core/xoslib/dashboards/xosServiceGrid.html
@@ -0,0 +1,31 @@
+<!-- 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 -->
+<link rel="stylesheet" href="/static/css/xosServiceGrid.css">
+<!-- endinject -->
+
+<div ng-app="xos.serviceGrid" id="xosServiceGrid" class="container-fluid">
+ <div ui-view></div>
+</div>
+
+<!-- bower:js -->
+<script src="vendor/jquery/dist/jquery.js"></script>
+<script src="vendor/angular/angular.js"></script>
+<script src="vendor/angular-mocks/angular-mocks.js"></script>
+<script src="vendor/angular-ui-router/release/angular-ui-router.js"></script>
+<script src="vendor/angular-cookies/angular-cookies.js"></script>
+<script src="vendor/angular-animate/angular-animate.js"></script>
+<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 -->
+<script src="/static/js/xosServiceGrid.js"></script>
+<!-- endinject -->
\ No newline at end of file
diff --git a/xos/core/xoslib/static/css/xosServiceGrid.css b/xos/core/xoslib/static/css/xosServiceGrid.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xos/core/xoslib/static/css/xosServiceGrid.css
diff --git a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
index 9bb64a7..0c0446d 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
@@ -1 +1,2102 @@
-"use strict";!function(){angular.module("xos.uiComponents",["chart.js"])}(),function(){angular.module("xos.uiComponents").directive("xosSmartTable",function(){return{restrict:"E",scope:{config:"="},template:'\n <div class="row" ng-show="vm.data.length > 0">\n <div class="col-xs-12 text-right">\n <a href="" class="btn btn-success" ng-click="vm.createItem()">\n Add\n </a>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n </div>\n </div>\n <div class="panel panel-default" ng-show="vm.detailedItem">\n <div class="panel-heading">\n <div class="row">\n <div class="col-xs-11">\n <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n </div>\n <div class="col-xs-1">\n <a href="" ng-click="vm.cleanForm()">\n <i class="glyphicon glyphicon-remove pull-right"></i>\n </a>\n </div>\n </div>\n </div>\n <div class="panel-body">\n <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n </div>\n </div>\n <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n ',bindToController:!0,controllerAs:"vm",controller:["$injector","LabelFormatter","_","XosFormHelpers",function(e,n,o,r){var t=this;this.responseMsg=!1,this.responseErr=!1,this.tableConfig={columns:[],actions:[{label:"delete",icon:"remove",cb:function(e){t.Resource["delete"]({id:e.id}).$promise.then(function(){o.remove(t.data,function(n){return n.id===e.id}),t.responseMsg=t.config.resource+" with id "+e.id+" successfully deleted"})["catch"](function(n){t.responseErr=n.data.detail||"Error while deleting "+t.config.resource+" with id "+e.id})},color:"red"},{label:"details",icon:"search",cb:function(e){t.detailedItem=e}}],classes:"table table-striped table-bordered table-responsive",filter:"field",order:!0,pagination:{pageSize:10}},this.formConfig={exclude:this.config.hiddenFields,fields:{},formName:this.config.resource+"Form",actions:[{label:"Save",icon:"ok",cb:function(e){var n=void 0,o=!0;e.id?(n=e.$update(),o=!1):n=e.$save(),n.then(function(n){o&&t.data.push(angular.copy(n)),delete t.detailedItem,t.responseMsg=t.config.resource+" with id "+e.id+" successfully saved"})["catch"](function(n){t.responseErr=n.data.detail||"Error while saving "+t.config.resource+" with id "+e.id})},"class":"success"}]},this.cleanForm=function(){delete t.detailedItem},this.createItem=function(){t.detailedItem=new t.Resource},this.Resource=e.get(this.config.resource);var i=function(){t.Resource.query().$promise.then(function(e){if(e[0]){var i=e[0],a=Object.keys(i);o.remove(a,function(e){return"id"==e||"validators"==e}),angular.isArray(t.config.hiddenFields)&&(a=o.difference(a,t.config.hiddenFields));var s=a.map(function(e){return n.format(e)});a.forEach(function(e,n){t.tableConfig.columns.push({label:s[n],prop:e})}),a.forEach(function(e,o){t.formConfig.fields[e]={label:n.format(s[o]).replace(":",""),type:r._getFieldFormat(i[e])}}),t.data=e}})};i()}]}})}(),function(){angular.module("xos.uiComponents").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:!0,controllerAs:"vm",controller:["$injector","$timeout","$interval","$scope","_",function(e,n,o,r,t){var i=this;if(!this.config.resource&&!this.config.data)throw new Error("[xosSmartPie] Please provide a resource or an array of data in the configuration");var a=function(e){return t.groupBy(e,i.config.groupBy)},s=function(e){return t.reduce(Object.keys(e),function(n,o){return n.concat(e[o].length)},[])},c=function(e){return angular.isFunction(i.config.labelFormatter)?i.config.labelFormatter(Object.keys(e)):Object.keys(e)},l=function(e){var n=a(e);i.data=s(n),i.labels=c(n)};this.config.resource?!function(){i.Resource=e.get(i.config.resource);var n=function(){i.Resource.query().$promise.then(function(e){e[0]&&l(e)})};n(),i.config.poll&&o(function(){n()},1e3*i.config.poll)}():r.$watch(function(){return i.config.data},function(e){e&&l(i.config.data)},!0)}]}})}(),function(){angular.module("xos.uiComponents").directive("xosValidation",function(){return{restrict:"E",scope:{errors:"="},template:'\n <div ng-cloak>\n <!-- <pre>{{vm.errors.email | json}}</pre> -->\n <xos-alert config="vm.config" show="vm.errors.required !== undefined && vm.errors.required !== false">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.email !== undefined && vm.errors.email !== false">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.minlength !== undefined && vm.errors.minlength !== false">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.maxlength !== undefined && vm.errors.maxlength !== false">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.custom !== undefined && vm.errors.custom !== false">\n Field invalid\n </xos-alert>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:function(){this.config={type:"danger"}}}})}(),function(){angular.module("xos.uiComponents").directive("xosTable",function(){return{restrict:"E",scope:{data:"=",config:"="},template:'\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns" link-wrapper>\n <span ng-if="!col.type">{{item[col.prop]}}</span>\n <span ng-if="col.type === \'boolean\'">\n <i class="glyphicon"\n ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n </i>\n </span>\n <span ng-if="col.type === \'date\'">\n {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n </span>\n <span ng-if="col.type === \'array\'">\n {{item[col.prop] | arrayToList}}\n </span>\n <span ng-if="col.type === \'object\'">\n <dl class="dl-horizontal">\n <span ng-repeat="(k,v) in item[col.prop]">\n <dt>{{k}}</dt>\n <dd>{{v}}</dd>\n </span>\n </dl>\n </span>\n <span ng-if="col.type === \'custom\'">\n {{col.formatter(item[col.prop])}}\n </span>\n </td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',bindToController:!0,controllerAs:"vm",controller:["_",function(e){var n=this;if(!this.config)throw new Error('[xosTable] Please provide a configuration via the "config" attribute');if(!this.config.columns)throw new Error("[xosTable] Please provide a columns list in the configuration");var o=e.filter(this.config.columns,{type:"custom"});angular.isArray(o)&&o.length>0&&e.forEach(o,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided a custom field type, a formatter function should provided too.")});var r=e.filter(this.config.columns,function(e){return angular.isDefined(e.link)});angular.isArray(r)&&r.length>0&&e.forEach(r,function(e){if(!angular.isFunction(e.link))throw new Error("[xosTable] The link property should be a function.")}),this.columns=this.config.columns,this.classes=this.config.classes||"table table-striped table-bordered",this.config.actions,this.config.pagination&&(this.currentPage=0,this.goToPage=function(e){n.currentPage=e})}]}}).filter("arrayToList",function(){return function(e){return angular.isArray(e)?e.join(", "):e}}).directive("linkWrapper",function(){return{restrict:"A",transclude:!0,template:'\n <a ng-if="col.link" href="{{col.link(item)}}">\n <div ng-transclude></div>\n </a>\n <div ng-transclude ng-if="!col.link"></div>\n '}})}(),function(){angular.module("xos.uiComponents").directive("xosPagination",function(){return{restrict:"E",scope:{pageSize:"=",totalElements:"=",change:"="},template:'\n <div class="row" ng-if="vm.pageList.length > 1">\n <div class="col-xs-12 text-center">\n <ul class="pagination">\n <li\n ng-click="vm.goToPage(vm.currentPage - 1)"\n ng-class="{disabled: vm.currentPage == 0}">\n <a href="" aria-label="Previous">\n <span aria-hidden="true">«</span>\n </a>\n </li>\n <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n </li>\n <li\n ng-click="vm.goToPage(vm.currentPage + 1)"\n ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n <a href="" aria-label="Next">\n <span aria-hidden="true">»</span>\n </a>\n </li>\n </ul>\n </div>\n </div>\n ',bindToController:!0,controllerAs:"vm",controller:["$scope",function(e){var n=this;this.currentPage=0,this.goToPage=function(e){0>e||e===n.pages||(n.currentPage=e,n.change(e))},this.createPages=function(e){for(var n=[],o=0;e>o;o++)n.push(o);return n},e.$watch(function(){return n.totalElements},function(){n.totalElements&&(n.pages=Math.ceil(n.totalElements/n.pageSize),n.pageList=n.createPages(n.pages))})}]}}).filter("pagination",function(){return function(e,n){return e&&angular.isArray(e)?(n=parseInt(n,10),e.slice(n)):e}})}();var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(){angular.module("xos.uiComponents").directive("xosForm",function(){return{restrict:"E",scope:{config:"=",ngModel:"="},template:'\n <ng-form name="vm.{{vm.config.formName || \'form\'}}">\n <div class="form-group" ng-repeat="(name, field) in vm.formField">\n <label>{{field.label}}</label>\n <input\n ng-if="field.type !== \'boolean\'"\n type="{{field.type}}"\n name="{{name}}"\n class="form-control"\n ng-model="vm.ngModel[name]"\n ng-minlength="field.validators.minlength || 0"\n ng-maxlength="field.validators.maxlength || 2000"\n ng-required="field.validators.required || false" />\n <span class="boolean-field" ng-if="field.type === \'boolean\'">\n <button\n class="btn btn-success"\n ng-show="vm.ngModel[name]"\n ng-click="vm.ngModel[name] = false">\n <i class="glyphicon glyphicon-ok"></i>\n </button>\n <button\n class="btn btn-danger"\n ng-show="!vm.ngModel[name]"\n ng-click="vm.ngModel[name] = true">\n <i class="glyphicon glyphicon-remove"></i>\n </button>\n </span>\n <!-- <pre>{{vm[vm.config.formName][name].$error | json}}</pre> -->\n <xos-validation errors="vm[vm.config.formName || \'form\'][name].$error"></xos-validation>\n </div>\n <div class="form-group" ng-if="vm.config.actions">\n <button role="button" href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(vm.ngModel)"\n class="btn btn-{{action.class}}"\n title="{{action.label}}">\n <i class="glyphicon glyphicon-{{action.icon}}"></i>\n {{action.label}}\n </button>\n </div>\n </ng-form>\n ',bindToController:!0,controllerAs:"vm",controller:["$scope","$log","_","XosFormHelpers",function(e,n,o,r){var t=this;if(!this.config)throw new Error('[xosForm] Please provide a configuration via the "config" attribute');if(!this.config.actions)throw new Error("[xosForm] Please provide an action list in the configuration");this.excludedField=["id","validators","created","updated","deleted","backend_status"],this.config&&this.config.exclude&&(this.excludedField=this.excludedField.concat(this.config.exclude)),this.formField=[],e.$watch(function(){return t.ngModel},function(e){if(t.formField={},e){var n=o.difference(Object.keys(e),t.excludedField),i=r.parseModelField(n);t.formField=r.buildFormStructure(i,t.config.fields,e)}})}]}}).service("XosFormHelpers",["_","LabelFormatter",function(e,n){var o=this;this._isEmail=function(e){var n=/(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;return n.test(e)},this._getFieldFormat=function(n){return e.isDate(n)||!Number.isNaN(Date.parse(n))&&new Date(n).getTime()>6311808e5?"date":"boolean"==typeof n?"boolean":isNaN(n)||null===n?o._isEmail(n)?"email":null===n?"string":"undefined"==typeof n?"undefined":_typeof(n):"number"},this.buildFormStructure=function(r,t,i){return r=Object.keys(r).length>0?r:t,t=t||{},e.reduce(Object.keys(r),function(e,r){return e[r]={label:t[r]&&t[r].label?t[r].label+":":n.format(r),type:t[r]&&t[r].type?t[r].type:o._getFieldFormat(i[r]),validators:t[r]&&t[r].validators?t[r].validators:{}},"date"===e[r].type&&(i[r]=new Date(i[r])),"number"===e[r].type&&(i[r]=parseInt(i[r],10)),e},{})},this.parseModelField=function(n){return e.reduce(n,function(e,n){return e[n]={},e},{})}}])}(),function(){angular.module("xos.uiComponents").directive("xosAlert",function(){return{restrict:"E",scope:{config:"=",show:"=?"},template:'\n <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n <p ng-transclude></p>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$timeout",function(e){var n=this;if(!this.config)throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');this.show=this.show!==!1,this.dismiss=function(){n.show=!1},this.config.autoHide&&!function(){var o=e(function(){n.dismiss(),e.cancel(o)},n.config.autoHide)}()}]}})}(),function(){function e(e,n,o){e.interceptors.push("SetCSRFToken"),n.startSymbol("{$"),n.endSymbol("$}"),o.defaults.stripTrailingSlashes=!1}e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"],angular.module("bugSnag",[]).factory("$exceptionHandler",function(){return function(e,n){window.Bugsnag?Bugsnag.notifyException(e,{diagnostics:{cause:n}}):console.error(e,n,e.stack)}}),angular.module("xos.helpers",["ngCookies","ngResource","ngAnimate","bugSnag","xos.uiComponents"]).config(e).factory("_",["$window",function(e){return e._}])}(),function(){angular.module("xos.helpers").service("vSG-Collection",["$resource",function(e){return e("/api/service/vsg/")}])}(),function(){angular.module("xos.helpers").service("vOLT-Collection",["$resource",function(e){return e("/api/tenant/cord/volt/:volt_id/",{volt_id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Login",["$resource",function(e){return e("/api/utility/login/")}]).service("Logout",["$resource",function(e){return e("/api/utility/logout/")}])}(),function(){angular.module("xos.helpers").service("Users",["$resource",function(e){return e("/api/core/users/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Truckroll-Collection",["$resource",function(e){return e("/api/tenant/truckroll/:truckroll_id/",{truckroll_id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Subscribers",["$resource",function(e){return e("/api/tenant/cord/subscriber/:id/",{id:"@id"},{update:{method:"PUT"},"View-a-Subscriber-Features-Detail":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/"},"Read-Subscriber-uplink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Update-Subscriber-uplink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Read-Subscriber-downlink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Update-Subscriber-downlink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Read-Subscriber-cdn":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Update-Subscriber-cdn":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Read-Subscriber-uverse":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Update-Subscriber-uverse":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Read-Subscriber-status":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"},"Update-Subscriber-status":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"}})}])}(),function(){angular.module("xos.helpers").service("Slices",["$resource",function(e){return e("/api/core/slices/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Sites",["$resource",function(e){return e("/api/core/sites/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("ONOS-Services-Collection",["$resource",function(e){return e("/api/service/onos/")}])}(),function(){angular.module("xos.helpers").service("ONOS-App-Collection",["$resource",function(e){return e("/api/tenant/onos/app/")}])}(),function(){angular.module("xos.helpers").service("Nodes",["$resource",function(e){return e("/api/core/nodes/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Instances",["$resource",function(e){return e("/api/core/instances/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Flavors",["$resource",function(e){return e("/api/core/flavors/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Example-Services-Collection",["$resource",function(e){return e("/api/service/exampleservice/")}])}(),function(){angular.module("xos.helpers").service("Deployments",["$resource",function(e){return e("/api/core/deployments/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){function e(){return{request:function(e){return-1===e.url.indexOf(".html")&&(e.url+="?no_hyperlinks=1"),e}}}angular.module("xos.helpers").factory("NoHyperlinks",e)}(),angular.module("xos.helpers").config(["$provide",function(e){e.decorator("$log",["$delegate",function(e){var n=function(){return window.location.href.indexOf("debug=true")>=0},o=e.info,r=function(e){return function(){if(!n())return void console.log("logging is disabled");var o=[].slice.call(arguments),r=new Date;o[0]="["+r.getHours()+":"+r.getMinutes()+":"+r.getSeconds()+"] "+o[0],e.apply(null,o)}};return e.info=r(o),e}])}]),function(){function e(){var e=function(e){return e.split("_").join(" ").trim()},n=function(e){return e.split(/(?=[A-Z])/).map(function(e){return e.toLowerCase()}).join(" ")},o=function(e){return e.slice(0,1).toUpperCase()+e.slice(1)},r=function(r){return r=e(r),r=n(r),r=o(r).replace(/\s\s+/g," ")+":",r.replace("::",":")};return{_formatByUnderscore:e,_formatByUppercase:n,_capitalize:o,format:r}}angular.module("xos.uiComponents").factory("LabelFormatter",e)}(),function(){function e(e){return{request:function(n){return"GET"!==n.method&&(n.headers["X-CSRFToken"]=e.get("xoscsrftoken")),n}}}e.$inject=["$cookies"],angular.module("xos.helpers").factory("SetCSRFToken",e)}();
\ No newline at end of file
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc overview
+ * @name xos.uiComponents
+ * @description
+ * A collection of UI components useful for Dashboard development.
+ * Currently available components are:
+ * - [xosAlert](/#/module/xos.uiComponents.directive:xosAlert)
+ * - [xosForm](/#/module/xos.uiComponents.directive:xosForm)
+ * - [xosPagination](/#/module/xos.uiComponents.directive:xosPagination)
+ * - [xosSmartTable](/#/module/xos.uiComponents.directive:xosSmartTable)
+ * - [xosTable](/#/module/xos.uiComponents.directive:xosTable)
+ * - [xosValidation](/#/module/xos.uiComponents.directive:xosValidation)
+ **/
+
+ angular.module('xos.uiComponents', ['chart.js']);
+})();
+//# sourceMappingURL=../maps/ui_components/ui-components.module.js.map
+
+'use strict';
+
+/**
+ * © 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:xosSmartTable
+ * @link xos.uiComponents.directive:xosTable xosTable
+ * @link xos.uiComponents.directive:xosForm xosForm
+ * @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 an array of fields that shouldn't be printed.
+ * ```
+ * {
+ resource: 'Users',
+ hiddenFields: []
+ }
+ * ```
+ * @scope
+ * @example
+ <example module="sampleSmartTable">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-table config="vm.config"></xos-smart-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartTable', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ // This is only for documentation purpose
+ .run(function($httpBackend, _){
+ let datas = [{id: 1, name: 'Jhon', surname: 'Doe'}];
+ let count = 1;
+ let paramsUrl = new RegExp(/\/test\/(.+)/);
+ $httpBackend.whenDELETE(paramsUrl, undefined, ['id']).respond((method, url, data, headers, params) => {
+ data = angular.fromJson(data);
+ let id = url.match(paramsUrl)[1];
+ _.remove(datas, (d) => {
+ return d.id === parseInt(id);
+ })
+ return [204];
+ });
+ $httpBackend.whenGET('/test').respond(200, datas)
+ $httpBackend.whenPOST('/test').respond((method, url, data) => {
+ data = angular.fromJson(data);
+ data.id = ++count;
+ datas.push(data);
+ return [201, data, {}];
+ });
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ // End of documentation purpose, example start
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource'
+ };
+ });
+ </file>
+ </example>
+ */
+
+ .directive('xosSmartTable', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '='
+ },
+ template: '\n <div class="row" ng-show="vm.data.length > 0">\n <div class="col-xs-12 text-right">\n <a href="" class="btn btn-success" ng-click="vm.createItem()">\n Add\n </a>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n </div>\n </div>\n <div class="panel panel-default" ng-show="vm.detailedItem">\n <div class="panel-heading">\n <div class="row">\n <div class="col-xs-11">\n <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n </div>\n <div class="col-xs-1">\n <a href="" ng-click="vm.cleanForm()">\n <i class="glyphicon glyphicon-remove pull-right"></i>\n </a>\n </div>\n </div>\n </div>\n <div class="panel-body">\n <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n </div>\n </div>\n <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$injector", "LabelFormatter", "_", "XosFormHelpers", function controller($injector, LabelFormatter, _, XosFormHelpers) {
+ var _this = this;
+
+ // TODO
+ // - Validate the config (what if resource does not exist?)
+
+ // NOTE
+ // Corner case
+ // - if response is empty, how can we generate a form ?
+
+ this.responseMsg = false;
+ this.responseErr = false;
+
+ this.tableConfig = {
+ columns: [],
+ actions: [{
+ label: 'delete',
+ icon: 'remove',
+ cb: function cb(item) {
+ _this.Resource.delete({ id: item.id }).$promise.then(function () {
+ _.remove(_this.data, function (d) {
+ return d.id === item.id;
+ });
+ _this.responseMsg = _this.config.resource + ' with id ' + item.id + ' successfully deleted';
+ }).catch(function (err) {
+ _this.responseErr = err.data.detail || 'Error while deleting ' + _this.config.resource + ' with id ' + item.id;
+ });
+ },
+ color: 'red'
+ }, {
+ label: 'details',
+ icon: 'search',
+ cb: function cb(item) {
+ _this.detailedItem = item;
+ }
+ }],
+ classes: 'table table-striped table-bordered table-responsive',
+ filter: 'field',
+ order: true,
+ pagination: {
+ pageSize: 10
+ }
+ };
+
+ this.formConfig = {
+ exclude: this.config.hiddenFields,
+ fields: {},
+ formName: this.config.resource + 'Form',
+ actions: [{
+ label: 'Save',
+ icon: 'ok',
+ cb: function cb(item) {
+ var p = void 0;
+ var isNew = true;
+
+ if (item.id) {
+ p = item.$update();
+ isNew = false;
+ } else {
+ p = item.$save();
+ }
+
+ p.then(function (res) {
+ if (isNew) {
+ _this.data.push(angular.copy(res));
+ }
+ delete _this.detailedItem;
+ _this.responseMsg = _this.config.resource + ' with id ' + item.id + ' successfully saved';
+ }).catch(function (err) {
+ _this.responseErr = err.data.detail || 'Error while saving ' + _this.config.resource + ' with id ' + item.id;
+ });
+ },
+ class: 'success'
+ }]
+ };
+
+ this.cleanForm = function () {
+ delete _this.detailedItem;
+ };
+
+ this.createItem = function () {
+ _this.detailedItem = new _this.Resource();
+ };
+
+ this.Resource = $injector.get(this.config.resource);
+
+ var getData = function getData() {
+ _this.Resource.query().$promise.then(function (res) {
+
+ if (!res[0]) {
+ return;
+ }
+
+ var item = res[0];
+ var props = Object.keys(item);
+
+ _.remove(props, function (p) {
+ return p == 'id' || p == 'validators';
+ });
+
+ // TODO move out cb
+ if (angular.isArray(_this.config.hiddenFields)) {
+ props = _.difference(props, _this.config.hiddenFields);
+ }
+
+ var labels = props.map(function (p) {
+ return LabelFormatter.format(p);
+ });
+
+ props.forEach(function (p, i) {
+ _this.tableConfig.columns.push({
+ label: labels[i],
+ prop: p
+ });
+ });
+
+ // build form structure
+ props.forEach(function (p, i) {
+ _this.formConfig.fields[p] = {
+ label: LabelFormatter.format(labels[i]).replace(':', ''),
+ type: XosFormHelpers._getFieldFormat(item[p])
+ };
+ });
+
+ _this.data = res;
+ });
+ };
+
+ getData();
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartTable/smartTable.component.js.map
+
+'use strict';
+
+/**
+ * © 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
+
+ Displaying Local data
+ <example module="sampleSmartPieLocal">
+ <file name="index.html">
+ <div ng-controller="SampleCtrlLocal as vm">
+ <xos-smart-pie config="vm.configLocal"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPieLocal', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrlLocal', function($timeout){
+
+ this.datas = [
+ {id: 1, first_name: 'Jon', last_name: 'aaa', category: 2},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 1},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 2}
+ ];
+ this.configLocal = {
+ data: [],
+ groupBy: 'category',
+ classes: 'local',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+
+ $timeout(() => {
+ // this need to be triggered in this way just because of ngDoc,
+ // otherwise you can assign data directly in the config
+ this.configLocal.data = this.datas;
+ }, 1)
+ });
+ </file>
+ </example>
+ Fetching data from API
+ <example module="sampleSmartPieResource">
+ <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('sampleSmartPieResource', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ classes: 'resource',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+ });
+ </file>
+ <file name="backendPoll.js">
+ angular.module('sampleSmartPieResource')
+ .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>
+ Polling data from API
+ <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',
+ poll: 2,
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'Active' : 'Banned');
+ }
+ };
+ });
+ </file>
+ <file name="backend.js">
+ angular.module('sampleSmartPiePoll')
+ .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>
+ */
+ .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", "$scope", "_", function controller($injector, $timeout, $interval, $scope, _) {
+ var _this = this;
+
+ if (!this.config.resource && !this.config.data) {
+ throw new Error('[xosSmartPie] Please provide a resource or an array of data in the configuration');
+ }
+
+ var groupData = function groupData(data) {
+ return _.groupBy(data, _this.config.groupBy);
+ };
+ var formatData = function formatData(data) {
+ return _.reduce(Object.keys(data), function (list, group) {
+ return list.concat(data[group].length);
+ }, []);
+ };
+ var formatLabels = function formatLabels(data) {
+ return angular.isFunction(_this.config.labelFormatter) ? _this.config.labelFormatter(Object.keys(data)) : Object.keys(data);
+ };
+
+ var prepareData = function prepareData(data) {
+ // group data
+ var grouped = groupData(data);
+ _this.data = formatData(grouped);
+ // create labels
+ _this.labels = formatLabels(grouped);
+ };
+
+ if (this.config.resource) {
+ (function () {
+
+ _this.Resource = $injector.get(_this.config.resource);
+ var getData = function getData() {
+ _this.Resource.query().$promise.then(function (res) {
+
+ if (!res[0]) {
+ return;
+ }
+
+ prepareData(res);
+ });
+ };
+
+ getData();
+
+ if (_this.config.poll) {
+ $interval(function () {
+ getData();
+ }, _this.config.poll * 1000);
+ }
+ })();
+ } else {
+ $scope.$watch(function () {
+ return _this.config.data;
+ }, function (data) {
+ if (data) {
+ prepareData(_this.config.data);
+ }
+ }, true);
+ }
+ }]
+ };
+ });
+})();
+//# 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.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosAlert
+ * @restrict E
+ * @description The xos-alert directive
+ * @param {Object} config The configuration object
+ * ```
+ * {
+ * type: 'danger', //info, success, warning
+ * closeBtn: true, //default false
+ * autoHide: 3000 //delay to automatically hide the alert
+ * }
+ * ```
+ * @param {Boolean=} show Binding to show and hide the alert, default to true
+ * @element ANY
+ * @scope
+ * @example
+ <example module="sampleAlert1">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-alert config="vm.config1">
+ A sample alert message
+ </xos-alert>
+ <xos-alert config="vm.config2">
+ A sample alert message (with close button)
+ </xos-alert>
+ <xos-alert config="vm.config3">
+ A sample info message
+ </xos-alert>
+ <xos-alert config="vm.config4">
+ A sample success message
+ </xos-alert>
+ <xos-alert config="vm.config5">
+ A sample warning message
+ </xos-alert>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleAlert1', ['xos.uiComponents'])
+ .controller('SampleCtrl1', function(){
+ this.config1 = {
+ type: 'danger'
+ };
+ this.config2 = {
+ type: 'danger',
+ closeBtn: true
+ };
+ this.config3 = {
+ type: 'info'
+ };
+ this.config4 = {
+ type: 'success'
+ };
+ this.config5 = {
+ type: 'warning'
+ };
+ });
+ </file>
+ </example>
+ <example module="sampleAlert2" animations="true">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm" class="row">
+ <div class="col-sm-4">
+ <a class="btn btn-default btn-block" ng-show="!vm.show" ng-click="vm.show = true">Show Alert</a>
+ <a class="btn btn-default btn-block" ng-show="vm.show" ng-click="vm.show = false">Hide Alert</a>
+ </div>
+ <div class="col-sm-8">
+ <xos-alert config="vm.config1" show="vm.show">
+ A sample alert message, not displayed by default.
+ </xos-alert>
+ </div>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleAlert2', ['xos.uiComponents', 'ngAnimate'])
+ .controller('SampleCtrl', function(){
+ this.config1 = {
+ type: 'success'
+ };
+ this.show = false;
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosAlert', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '=',
+ show: '=?'
+ },
+ template: '\n <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n <p ng-transclude></p>\n </div>\n ',
+ transclude: true,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$timeout", function controller($timeout) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');
+ }
+
+ // default the value to true
+ this.show = this.show !== false;
+
+ this.dismiss = function () {
+ _this.show = false;
+ };
+
+ if (this.config.autoHide) {
+ (function () {
+ var to = $timeout(function () {
+ _this.dismiss();
+ $timeout.cancel(to);
+ }, _this.config.autoHide);
+ })();
+ }
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/alert/alert.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosValidation
+ * @restrict E
+ * @description The xos-validation directive
+ * @param {Object} errors The error object
+ * @element ANY
+ * @scope
+ * @example
+ <example module="sampleValidation">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <div class="row">
+ <div class="col-xs-12">
+ <label>Set an error type:</label>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.required = !vm.errors.required"
+ ng-class="{'btn-default': !vm.errors.required, 'btn-success': vm.errors.required}">
+ Required
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.email = !vm.errors.email"
+ ng-class="{'btn-default': !vm.errors.email, 'btn-success': vm.errors.email}">
+ Email
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.minlength = !vm.errors.minlength"
+ ng-class="{'btn-default': !vm.errors.minlength, 'btn-success': vm.errors.minlength}">
+ Min Length
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.maxlength = !vm.errors.maxlength"
+ ng-class="{'btn-default': !vm.errors.maxlength, 'btn-success': vm.errors.maxlength}">
+ Max Length
+ </a>
+ </div>
+ </div>
+ <xos-validation errors="vm.errors"></xos-validation>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleValidation', ['xos.uiComponents'])
+ .controller('SampleCtrl', function(){
+ this.errors = {
+ email: false
+ }
+ });
+ </file>
+ </example>
+ */
+
+ .directive('xosValidation', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ errors: '='
+ },
+ template: '\n <div ng-cloak>\n <!-- <pre>{{vm.errors.email | json}}</pre> -->\n <xos-alert config="vm.config" show="vm.errors.required !== undefined && vm.errors.required !== false">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.email !== undefined && vm.errors.email !== false">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.minlength !== undefined && vm.errors.minlength !== false">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.maxlength !== undefined && vm.errors.maxlength !== false">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.custom !== undefined && vm.errors.custom !== false">\n Field invalid\n </xos-alert>\n </div>\n ',
+ transclude: true,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: function controller() {
+ this.config = {
+ type: 'danger'
+ };
+ }
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/validation/validation.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosPagination
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Number} pageSize Number of elements per page
+ * @param {Number} totalElements Number of total elements in the collection
+ * @param {Function} change The callback to be triggered on page change.
+ * * @element ANY
+ * @scope
+ * @example
+ <example module="samplePagination">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-pagination
+ page-size="vm.pageSize"
+ total-elements="vm.totalElements"
+ change="vm.change">
+ </xos-pagination>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('samplePagination', ['xos.uiComponents'])
+ .controller('SampleCtrl1', function(){
+ this.pageSize = 10;
+ this.totalElements = 35;
+ this.change = (pageNumber) => {
+ console.log(pageNumber);
+ }
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosPagination', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ pageSize: '=',
+ totalElements: '=',
+ change: '='
+ },
+ template: '\n <div class="row" ng-if="vm.pageList.length > 1">\n <div class="col-xs-12 text-center">\n <ul class="pagination">\n <li\n ng-click="vm.goToPage(vm.currentPage - 1)"\n ng-class="{disabled: vm.currentPage == 0}">\n <a href="" aria-label="Previous">\n <span aria-hidden="true">«</span>\n </a>\n </li>\n <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n </li>\n <li\n ng-click="vm.goToPage(vm.currentPage + 1)"\n ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n <a href="" aria-label="Next">\n <span aria-hidden="true">»</span>\n </a>\n </li>\n </ul>\n </div>\n </div>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$scope", function controller($scope) {
+ var _this = this;
+
+ this.currentPage = 0;
+
+ this.goToPage = function (n) {
+ if (n < 0 || n === _this.pages) {
+ return;
+ }
+ _this.currentPage = n;
+ _this.change(n);
+ };
+
+ this.createPages = function (pages) {
+ var arr = [];
+ for (var i = 0; i < pages; i++) {
+ arr.push(i);
+ }
+ return arr;
+ };
+
+ // watch for data changes
+ $scope.$watch(function () {
+ return _this.totalElements;
+ }, function () {
+ if (_this.totalElements) {
+ _this.pages = Math.ceil(_this.totalElements / _this.pageSize);
+ _this.pageList = _this.createPages(_this.pages);
+ }
+ });
+ }]
+ };
+ }).filter('pagination', function () {
+ return function (input, start) {
+ if (!input || !angular.isArray(input)) {
+ return input;
+ }
+ start = parseInt(start, 10);
+ return input.slice(start);
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/pagination/pagination.component.js.map
+
+'use strict';
+
+/**
+ * © 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:xosTable
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component.
+ * ```
+ * {
+ * columns: [
+ * {
+ * label: 'Human readable name',
+ * prop: 'Property to read in the model object'
+ * }
+ * ],
+ * classes: 'table table-striped table-bordered',
+ * actions: [ // if defined add an action column
+ {
+ label: 'delete',
+ icon: 'remove', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ color: 'red'
+ }
+ ],
+ filter: 'field', // can be by `field` or `fulltext`
+ order: true // whether to show ordering arrows
+ * }
+ * ```
+ * @param {Array} data The data that should be rendered
+ * @element ANY
+ * @scope
+ * @example
+ # Basic usage
+ <example module="sampleTable1">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable1', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl1', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ]
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Filtering
+ <example module="sampleTable2" animations="true">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl2 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable2', ['xos.uiComponents', 'ngAnimate'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl2', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ],
+ classes: 'table table-striped table-condensed', // table classes, default to `table table-striped table-bordered`
+ actions: [ // if defined add an action column
+ {
+ label: 'delete', // label
+ icon: 'remove', // icons, refers to bootstraps glyphicon
+ cb: (user) => { // callback, get feeded with the full object
+ console.log(user);
+ },
+ color: 'red' // icon color
+ }
+ ],
+ filter: 'field', // can be by `field` or `fulltext`
+ order: true
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Pagination
+ <example module="sampleTable3">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl3 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable3', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl3', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ],
+ pagination: {
+ pageSize: 2
+ }
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ },
+ {
+ name: 'Lucky',
+ lastname: 'Clarkson'
+ },
+ {
+ name: 'Tate',
+ lastname: 'Spalding'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Field formatter
+ <example module="sampleTable4">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable4', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name',
+ prop: 'name',
+ link: item => `https://www.google.it/#q=${item.name}`
+ },
+ {
+ label: 'Enabled',
+ prop: 'enabled',
+ type: 'boolean'
+ },
+ {
+ label: 'Services',
+ prop: 'services',
+ type: 'array'
+ },
+ {
+ label: 'Details',
+ prop: 'details',
+ type: 'object'
+ }
+ ]
+ };
+ this.data = [
+ {
+ name: 'John',
+ enabled: true,
+ services: ['Cdn', 'IpTv'],
+ details: {
+ c_tag: '243',
+ s_tag: '444'
+ }
+ },
+ {
+ name: 'Gili',
+ enabled: false,
+ services: ['Cdn', 'IpTv', 'Cache'],
+ details: {
+ c_tag: '675',
+ s_tag: '893'
+ }
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Custom formatter
+ <example module="sampleTable5">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable5', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'Username',
+ prop: 'username'
+ },
+ {
+ label: 'Features',
+ prop: 'features',
+ type: 'custom',
+ formatter: (val) => {
+
+ let cdnEnabled = val.cdn ? 'enabled' : 'disabled';
+ return `
+ Cdn is ${cdnEnabled},
+ uplink speed is ${val.uplink_speed}
+ and downlink speed is ${val.downlink_speed}
+ `;
+ }
+ }
+ ]
+ };
+ this.data = [
+ {
+ username: 'John',
+ features: {
+ "cdn": false,
+ "uplink_speed": 1000000000,
+ "downlink_speed": 1000000000,
+ "uverse": true,
+ "status": "enabled"
+ }
+ },
+ {
+ username: 'Gili',
+ features: {
+ "cdn": true,
+ "uplink_speed": 3000000000,
+ "downlink_speed": 2000000000,
+ "uverse": true,
+ "status": "enabled"
+ }
+ }
+ ]
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosTable', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ data: '=',
+ config: '='
+ },
+ template: '\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns" link-wrapper>\n <span ng-if="!col.type">{{item[col.prop]}}</span>\n <span ng-if="col.type === \'boolean\'">\n <i class="glyphicon"\n ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n </i>\n </span>\n <span ng-if="col.type === \'date\'">\n {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n </span>\n <span ng-if="col.type === \'array\'">\n {{item[col.prop] | arrayToList}}\n </span>\n <span ng-if="col.type === \'object\'">\n <dl class="dl-horizontal">\n <span ng-repeat="(k,v) in item[col.prop]">\n <dt>{{k}}</dt>\n <dd>{{v}}</dd>\n </span>\n </dl>\n </span>\n <span ng-if="col.type === \'custom\'">\n {{col.formatter(item[col.prop])}}\n </span>\n </td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["_", function controller(_) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosTable] Please provide a configuration via the "config" attribute');
+ }
+
+ if (!this.config.columns) {
+ throw new Error('[xosTable] Please provide a columns list in the configuration');
+ }
+
+ // if columns with type 'custom' are provide
+ // check that a custom formatted is provided too
+ var customCols = _.filter(this.config.columns, { type: 'custom' });
+ if (angular.isArray(customCols) && customCols.length > 0) {
+ _.forEach(customCols, function (col) {
+ if (!col.formatter || !angular.isFunction(col.formatter)) {
+ throw new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.');
+ }
+ });
+ }
+
+ // if a link property is passed,
+ // it should be a function
+ var linkedColumns = _.filter(this.config.columns, function (col) {
+ return angular.isDefined(col.link);
+ });
+ if (angular.isArray(linkedColumns) && linkedColumns.length > 0) {
+ _.forEach(linkedColumns, function (col) {
+ if (!angular.isFunction(col.link)) {
+ throw new Error('[xosTable] The link property should be a function.');
+ }
+ });
+ }
+
+ this.columns = this.config.columns;
+ this.classes = this.config.classes || 'table table-striped table-bordered';
+
+ if (this.config.actions) {
+ // TODO validate action format
+ }
+ if (this.config.pagination) {
+ this.currentPage = 0;
+ this.goToPage = function (n) {
+ _this.currentPage = n;
+ };
+ }
+ }]
+ };
+ })
+ // TODO move in separate files
+ // TODO test
+ .filter('arrayToList', function () {
+ return function (input) {
+ if (!angular.isArray(input)) {
+ return input;
+ }
+ return input.join(', ');
+ };
+ })
+ // TODO test
+ .directive('linkWrapper', function () {
+ return {
+ restrict: 'A',
+ transclude: true,
+ template: '\n <a ng-if="col.link" href="{{col.link(item)}}">\n <div ng-transclude></div>\n </a>\n <div ng-transclude ng-if="!col.link"></div>\n '
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/table/table.component.js.map
+
+'use strict';
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/18/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosForm
+ * @restrict E
+ * @description The xos-form directive.
+ * This components have two usage, given a model it is able to autogenerate a form or it can be configured to create a custom form.
+ * @param {Object} config The configuration object
+ * ```
+ * {
+ * exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
+ * actions: [ // define the form buttons with related callback
+ * {
+ label: 'save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ * ],
+ * fields: {
+ * field_name: {
+ * label: 'Field Label',
+ * type: 'string' // options are: [date, boolean, number, email, string],
+ * validators: {
+ * minlength: number,
+ maxlength: number,
+ required: boolean,
+ min: number,
+ max: number
+ * }
+ * }
+ * }
+ * }
+ * ```
+ * @element ANY
+ * @scope
+ * @example
+
+ Autogenerated form
+ <example module="sampleForm">
+ <file name="script.js">
+ angular.module('sampleForm', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.model = {
+ first_name: 'Jhon',
+ last_name: 'Doe',
+ email: 'jhon.doe@sample.com',
+ active: true,
+ birthDate: '2015-02-17T22:06:38.059000Z'
+ }
+ this.config = {
+ exclude: ['password', 'last_login'],
+ formName: 'sampleForm',
+ actions: [
+ {
+ label: 'Save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ ]
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-form ng-model="vm.model" config="vm.config"></xos-form>
+ </div>
+ </file>
+ </example>
+ Configuration defined form
+ <example module="sampleForm1">
+ <file name="script.js">
+ angular.module('sampleForm1', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl1', function(){
+ this.model = {
+ };
+ this.config = {
+ exclude: ['password', 'last_login'],
+ formName: 'sampleForm1',
+ actions: [
+ {
+ label: 'Save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ ],
+ fields: {
+ first_name: {
+ type: 'string',
+ validators: {
+ required: true
+ }
+ },
+ last_name: {
+ label: 'Surname',
+ type: 'string',
+ validators: {
+ required: true,
+ minlength: 10
+ }
+ },
+ age: {
+ type: 'number',
+ validators: {
+ required: true,
+ min: 21
+ }
+ },
+ }
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-form ng-model="vm.model" config="vm.config"></xos-form>
+ </div>
+ </file>
+ </example>
+ **/
+
+ .directive('xosForm', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '=',
+ ngModel: '='
+ },
+ template: '\n <ng-form name="vm.{{vm.config.formName || \'form\'}}">\n <div class="form-group" ng-repeat="(name, field) in vm.formField">\n <label>{{field.label}}</label>\n <input\n ng-if="field.type !== \'boolean\'"\n type="{{field.type}}"\n name="{{name}}"\n class="form-control"\n ng-model="vm.ngModel[name]"\n ng-minlength="field.validators.minlength || 0"\n ng-maxlength="field.validators.maxlength || 2000"\n ng-required="field.validators.required || false" />\n <span class="boolean-field" ng-if="field.type === \'boolean\'">\n <button\n class="btn btn-success"\n ng-show="vm.ngModel[name]"\n ng-click="vm.ngModel[name] = false">\n <i class="glyphicon glyphicon-ok"></i>\n </button>\n <button\n class="btn btn-danger"\n ng-show="!vm.ngModel[name]"\n ng-click="vm.ngModel[name] = true">\n <i class="glyphicon glyphicon-remove"></i>\n </button>\n </span>\n <!-- <pre>{{vm[vm.config.formName][name].$error | json}}</pre> -->\n <xos-validation errors="vm[vm.config.formName || \'form\'][name].$error"></xos-validation>\n </div>\n <div class="form-group" ng-if="vm.config.actions">\n <button role="button" href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(vm.ngModel)"\n class="btn btn-{{action.class}}"\n title="{{action.label}}">\n <i class="glyphicon glyphicon-{{action.icon}}"></i>\n {{action.label}}\n </button>\n </div>\n </ng-form>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$scope", "$log", "_", "XosFormHelpers", function controller($scope, $log, _, XosFormHelpers) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
+ }
+
+ if (!this.config.actions) {
+ throw new Error('[xosForm] Please provide an action list in the configuration');
+ }
+
+ this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
+ if (this.config && this.config.exclude) {
+ this.excludedField = this.excludedField.concat(this.config.exclude);
+ }
+
+ this.formField = [];
+ $scope.$watch(function () {
+ return _this.ngModel;
+ }, function (model) {
+
+ // empty from old stuff
+ _this.formField = {};
+
+ if (!model) {
+ return;
+ }
+
+ var diff = _.difference(Object.keys(model), _this.excludedField);
+ var modelField = XosFormHelpers.parseModelField(diff);
+ _this.formField = XosFormHelpers.buildFormStructure(modelField, _this.config.fields, model);
+ });
+ }]
+ };
+ }).service('XosFormHelpers', ["_", "LabelFormatter", function (_, LabelFormatter) {
+ var _this2 = this;
+
+ this._isEmail = function (text) {
+ var re = /(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;
+ return re.test(text);
+ };
+
+ this._getFieldFormat = function (value) {
+
+ // check if is date
+ if (_.isDate(value) || !Number.isNaN(Date.parse(value)) && new Date(value).getTime() > 631180800000) {
+ return 'date';
+ }
+
+ // check if is boolean
+ // isNaN(false) = false, false is a number (0), true is a number (1)
+ if (typeof value === 'boolean') {
+ return 'boolean';
+ }
+
+ // check if a string is a number
+ if (!isNaN(value) && value !== null) {
+ return 'number';
+ }
+
+ // check if a string is an email
+ if (_this2._isEmail(value)) {
+ return 'email';
+ }
+
+ // if null return string
+ if (value === null) {
+ return 'string';
+ }
+
+ return typeof value === 'undefined' ? 'undefined' : _typeof(value);
+ };
+
+ this.buildFormStructure = function (modelField, customField, model) {
+
+ modelField = Object.keys(modelField).length > 0 ? modelField : customField; //if no model field are provided, check custom
+ customField = customField || {};
+
+ return _.reduce(Object.keys(modelField), function (form, f) {
+
+ form[f] = {
+ label: customField[f] && customField[f].label ? customField[f].label + ':' : LabelFormatter.format(f),
+ type: customField[f] && customField[f].type ? customField[f].type : _this2._getFieldFormat(model[f]),
+ validators: customField[f] && customField[f].validators ? customField[f].validators : {}
+ };
+
+ if (form[f].type === 'date') {
+ model[f] = new Date(model[f]);
+ }
+
+ if (form[f].type === 'number') {
+ model[f] = parseInt(model[f], 10);
+ }
+
+ return form;
+ }, {});
+ };
+
+ this.parseModelField = function (fields) {
+ return _.reduce(fields, function (form, f) {
+ form[f] = {};
+ return form;
+ }, {});
+ };
+ }]);
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/form/form.component.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ config.$inject = ["$httpProvider", "$interpolateProvider", "$resourceProvider"];
+ angular.module('bugSnag', []).factory('$exceptionHandler', function () {
+ return function (exception, cause) {
+ if (window.Bugsnag) {
+ Bugsnag.notifyException(exception, { diagnostics: { cause: cause } });
+ } else {
+ console.error(exception, cause, exception.stack);
+ }
+ };
+ });
+
+ /**
+ * @ngdoc overview
+ * @name xos.helpers
+ * @description this is the module that group all the helpers service and components for XOS
+ **/
+
+ angular.module('xos.helpers', ['ngCookies', 'ngResource', 'ngAnimate', 'bugSnag', 'xos.uiComponents']).config(config).factory('_', ["$window", function ($window) {
+ return $window._;
+ }]);
+
+ function config($httpProvider, $interpolateProvider, $resourceProvider) {
+ $httpProvider.interceptors.push('SetCSRFToken');
+
+ $interpolateProvider.startSymbol('{$');
+ $interpolateProvider.endSymbol('$}');
+
+ // NOTE http://www.masnun.com/2013/09/18/django-rest-framework-angularjs-resource-trailing-slash-problem.html
+ $resourceProvider.defaults.stripTrailingSlashes = false;
+ }
+})();
+//# sourceMappingURL=maps/xosHelpers.module.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.vSG-Collection
+ * @description Angular resource to fetch /api/service/vsg/
+ **/
+ .service('vSG-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/vsg/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/vSG.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.vOLT-Collection
+ * @description Angular resource to fetch /api/tenant/cord/volt/:volt_id/
+ **/
+ .service('vOLT-Collection', ["$resource", function ($resource) {
+ return $resource('/api/tenant/cord/volt/:volt_id/', { volt_id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/vOLT.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ 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/
+ **/
+ .service('Users', ["$resource", function ($resource) {
+ return $resource('/api/core/users/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Users.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Truckroll-Collection
+ * @description Angular resource to fetch /api/tenant/truckroll/:truckroll_id/
+ **/
+ .service('Truckroll-Collection', ["$resource", function ($resource) {
+ return $resource('/api/tenant/truckroll/:truckroll_id/', { truckroll_id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Truckroll.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Subscribers
+ * @description Angular resource to fetch Subscribers
+ **/
+ .service('Subscribers', ["$resource", function ($resource) {
+ 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/'
+ }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Subscribers.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ 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.Instances
+ * @description Angular resource to fetch /api/core/services/:id/
+ **/
+ .service('Services', ["$resource", function ($resource) {
+ return $resource('/api/core/services/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Services.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/
+ **/
+ .service('ONOS-Services-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/onos/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/ONOS-Services.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.ONOS-App-Collection
+ * @description Angular resource to fetch /api/tenant/onos/app/
+ **/
+ .service('ONOS-App-Collection', ["$resource", function ($resource) {
+ return $resource('/api/tenant/onos/app/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/ONOS-Apps.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ 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/
+ **/
+ .service('Instances', ["$resource", function ($resource) {
+ return $resource('/api/core/instances/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Instances.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ 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/
+ **/
+ .service('Example-Services-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/exampleservice/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Example.js.map
+
+'use strict';
+
+(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
+ * @description This factory is automatically loaded trough xos.helpers and will add an $http interceptor that will add ?no_hyperlinks=1 to your api request, that is required by django
+ **/
+
+ angular.module('xos.helpers').factory('NoHyperlinks', noHyperlinks);
+
+ function noHyperlinks() {
+ return {
+ request: function request(_request) {
+ if (_request.url.indexOf('.html') === -1) {
+ _request.url += '?no_hyperlinks=1';
+ }
+ return _request;
+ }
+ };
+ }
+})();
+//# sourceMappingURL=../maps/services/noHyperlinks.interceptor.js.map
+
+'use strict';
+
+// TODO write tests for log
+
+angular.module('xos.helpers').config(['$provide', function ($provide) {
+ // Use the `decorator` solution to substitute or attach behaviors to
+ // original service instance; @see angular-mocks for more examples....
+
+ $provide.decorator('$log', ['$delegate', function ($delegate) {
+
+ var isLogEnabled = function isLogEnabled() {
+ return window.location.href.indexOf('debug=true') >= 0;
+ };
+ // Save the original $log.debug()
+ var debugFn = $delegate.info;
+
+ // create the replacement function
+ var replacement = function replacement(fn) {
+ return function () {
+ if (!isLogEnabled()) {
+ console.log('logging is disabled');
+ return;
+ }
+ var args = [].slice.call(arguments);
+ var now = new Date();
+
+ // Prepend timestamp
+ args[0] = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] ' + args[0];
+
+ // Call the original with the output prepended with formatted timestamp
+ fn.apply(null, args);
+ };
+ };
+
+ $delegate.info = replacement(debugFn);
+
+ return $delegate;
+ }]);
+}]);
+//# sourceMappingURL=../maps/services/log.decorator.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.LabelFormatter
+ * @description This factory define a set of helper function to format label started from an object property
+ **/
+
+ angular.module('xos.uiComponents').factory('LabelFormatter', labelFormatter);
+
+ function labelFormatter() {
+
+ var _formatByUnderscore = function _formatByUnderscore(string) {
+ return string.split('_').join(' ').trim();
+ };
+
+ var _formatByUppercase = function _formatByUppercase(string) {
+ return string.split(/(?=[A-Z])/).map(function (w) {
+ return w.toLowerCase();
+ }).join(' ');
+ };
+
+ var _capitalize = function _capitalize(string) {
+ return string.slice(0, 1).toUpperCase() + string.slice(1);
+ };
+
+ var format = function format(string) {
+ string = _formatByUnderscore(string);
+ string = _formatByUppercase(string);
+
+ string = _capitalize(string).replace(/\s\s+/g, ' ') + ':';
+ return string.replace('::', ':');
+ };
+
+ return {
+ // test export
+ _formatByUnderscore: _formatByUnderscore,
+ _formatByUppercase: _formatByUppercase,
+ _capitalize: _capitalize,
+ // export to use
+ format: format
+ };
+ }
+})();
+//# sourceMappingURL=../maps/services/label_formatter.service.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.SetCSRFToken
+ * @description This factory is automatically loaded trough xos.helpers and will add an $http interceptor that will the CSRF-Token to your request headers
+ **/
+
+ setCSRFToken.$inject = ["$cookies"];
+ angular.module('xos.helpers').factory('SetCSRFToken', setCSRFToken);
+
+ function setCSRFToken($cookies) {
+ return {
+ request: function request(_request) {
+ if (_request.method !== 'GET') {
+ _request.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+ }
+ return _request;
+ }
+ };
+ }
+})();
+//# sourceMappingURL=../maps/services/csrfToken.interceptor.js.map
diff --git a/xos/core/xoslib/static/js/xosServiceGrid.js b/xos/core/xoslib/static/js/xosServiceGrid.js
new file mode 100644
index 0000000..4214f35
--- /dev/null
+++ b/xos/core/xoslib/static/js/xosServiceGrid.js
@@ -0,0 +1 @@
+"use strict";angular.module("xos.serviceGrid",["ngResource","ngCookies","ui.router","xos.helpers"]).config(["$stateProvider",function(e){e.state("serviceGrid",{url:"/",template:"<service-grid></service-grid>"})}]).config(["$httpProvider",function(e){e.interceptors.push("NoHyperlinks")}]).directive("serviceGrid",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/service-grid.tpl.html",controller:["Services","_",function(e,r){var t=this;this.tableConfig={columns:[{label:"Status",prop:"status",type:"boolean"},{label:"Name",prop:"name",link:function(e){return""+e.view_url.replace(/\$[a-z]+\$/,e.id)}},{label:"Kind",prop:"kind"},{label:"Enabled",prop:"enabled",type:"boolean"}],filter:"field",order:!0},e.query().$promise.then(function(e){t.services=r.map(e,function(e){return e.status=0!==parseInt(e.backend_status.match(/^[0-9]/)[0]),e})})["catch"](function(e){throw new Error(e)})}]}}),angular.module("xos.serviceGrid").run(["$templateCache",function(e){e.put("templates/service-grid.tpl.html",'<div class="row">\n <div class="col-sm-2">\n <a href="/admin/core/service/add" class="btn btn-default btn-block">\n <i class="glyphicon glyphicon-plus"></i>\n Add Service\n </a>\n <!-- <a href="" class="btn btn-default btn-block"></a> -->\n </div>\n <div class="col-sm-10">\n <xos-table config="vm.tableConfig" data="vm.services"></xos-table>\n </div>\n</div>')}]),angular.module("xos.serviceGrid").run(["$location",function(e){e.path("/")}]);
\ No newline at end of file
diff --git a/xos/tests/api/source/core/services.md b/xos/tests/api/source/core/services.md
new file mode 100644
index 0000000..3454bdb
--- /dev/null
+++ b/xos/tests/api/source/core/services.md
@@ -0,0 +1,120 @@
+# Group Services
+
+List of the XOS Services
+
+## Services [/api/core/services/{id}/]
+
+### List all Services [GET]
+
++ Response 200 (application/json)
+
+ [
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+ ]
+
+### Create a Service [POST]
+
++ Request (application/json)
+
+ {
+ "name": "MyService",
+ "kind": "vROUTER"
+ }
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+
+### View a Service Detail [GET]
+
++ Parameters
+ + id: 1 (number) - ID of the Service in the form of an integer
+
++ Response 200 (application/json)
+
+ {
+ "humanReadableName": "MyService",
+ "id": 1,
+ "created": "2016-05-05T23:06:33.835277Z",
+ "updated": "2016-05-05T23:06:33.835302Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "no_policy": false,
+ "description": null,
+ "enabled": true,
+ "kind": "vROUTER",
+ "name": "MyService",
+ "versionNumber": null,
+ "published": true,
+ "view_url": "/admin/vrouter/vrouterservice/$id$/",
+ "icon_url": null,
+ "public_key": null,
+ "private_key_fn": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null
+ }
+
+### Delete a Service [DELETE]
+
++ Parameters
+ + id: 1 (number) - ID of the Service in the form of an integer
+
++ Response 204
diff --git a/xos/xos/apps.py b/xos/xos/apps.py
index b38d753..b30f462 100644
--- a/xos/xos/apps.py
+++ b/xos/xos/apps.py
@@ -1,13 +1,14 @@
from suit.apps import DjangoSuitConfig
+
class MyDjangoSuitConfig(DjangoSuitConfig):
admin_name = 'XOS'
menu_position = 'vertical'
menu_open_first_child = False
menu = (
- {'label': 'Deployments', 'icon':'icon-deployment', 'url': '/admin/core/deployment/'},
- {'label': 'Sites', 'icon':'icon-site', 'url': '/admin/core/site/'},
- {'label': 'Slices', 'icon':'icon-slice', 'url': '/admin/core/slice/'},
- {'label': 'Users', 'icon':'icon-user', 'url': '/admin/core/user/'},
- {'label': 'Services', 'icon':'icon-cog', 'url': '/admin/core/service'},
- )
\ No newline at end of file
+ {'label': 'Deployments', 'icon': 'icon-deployment', 'url': '/admin/core/deployment/'},
+ {'label': 'Sites', 'icon': 'icon-site', 'url': '/admin/core/site/'},
+ {'label': 'Slices', 'icon': 'icon-slice', 'url': '/admin/core/slice/'},
+ {'label': 'Users', 'icon': 'icon-user', 'url': '/admin/core/user/'},
+ {'label': 'Services', 'icon': 'icon-cog', 'url': '/serviceGrid/'},
+ )