diff options
author | Nicolas Hart <contact@nclshart.net> | 2016-08-04 06:53:05 +0200 |
---|---|---|
committer | Nicolas Hart <contact@nclshart.net> | 2016-08-15 17:04:23 +0200 |
commit | 483fa29cfa9cec0ffa0adc746dfd097a9e1e7089 (patch) | |
tree | 8075b60c86447d04b17e571120f1f1a0f6593658 | |
parent | 215d19e8f996ded5c0f7148009b240cf4061062e (diff) |
[Web UI] dependency manager and build system
24 files changed, 566 insertions, 43075 deletions
@@ -43,3 +43,7 @@ _build # Tox .tox/ + +# web ui +node_modules/ +bower_components/ diff --git a/glances/outputs/glances_bottle.py b/glances/outputs/glances_bottle.py index d7d780bf..7fdcf8f9 100644 --- a/glances/outputs/glances_bottle.py +++ b/glances/outputs/glances_bottle.py @@ -57,7 +57,7 @@ class GlancesBottle(object): self._route() # Path where the statics files are stored - self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static') + self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/public') def app(self): return self._app() @@ -76,13 +76,7 @@ class GlancesBottle(object): self._app.route('/', method="GET", callback=self._index) self._app.route('/<refresh_time:int>', method=["GET"], callback=self._index) - self._app.route('/<filename:re:.*\.css>', method="GET", callback=self._css) - self._app.route('/<filename:re:.*\.js>', method="GET", callback=self._js) - self._app.route('/<filename:re:.*\.js.map>', method="GET", callback=self._js_map) - self._app.route('/<filename:re:.*\.html>', method="GET", callback=self._html) - - self._app.route('/<filename:re:.*\.png>', method="GET", callback=self._images) - self._app.route('/favicon.ico', method="GET", callback=self._favicon) + self._app.route('/<filepath:path>', method="GET", callback=self._resource) # REST API self._app.route('/api/2/args', method="GET", callback=self._api_args) @@ -126,37 +120,12 @@ class GlancesBottle(object): self.stats.update() # Display - return static_file("index.html", root=os.path.join(self.STATIC_PATH, 'html')) - - def _html(self, filename): - """Bottle callback for *.html files.""" - # Return the static file - return static_file(filename, root=os.path.join(self.STATIC_PATH, 'html')) - - def _css(self, filename): - """Bottle callback for *.css files.""" - # Return the static file - return static_file(filename, root=os.path.join(self.STATIC_PATH, 'css')) - - def _js(self, filename): - """Bottle callback for *.js files.""" - # Return the static file - return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js')) - - def _js_map(self, filename): - """Bottle callback for *.js.map files.""" - # Return the static file - return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js')) - - def _images(self, filename): - """Bottle callback for *.png files.""" - # Return the static file - return static_file(filename, root=os.path.join(self.STATIC_PATH, 'images')) + return static_file("index.html", root=self.STATIC_PATH) - def _favicon(self): - """Bottle callback for favicon.""" + def _resource(self, filepath): + """Bottle callback for resources files.""" # Return the static file - return static_file('favicon.ico', root=self.STATIC_PATH) + return static_file(filepath, root=self.STATIC_PATH) def _api_help(self): """Glances API RESTFul implementation. diff --git a/glances/outputs/static/README.md b/glances/outputs/static/README.md new file mode 100644 index 00000000..3e58fea0 --- /dev/null +++ b/glances/outputs/static/README.md @@ -0,0 +1,37 @@ +# How to contribute? + +In order to build the assets of the Web UI, you'll need [npm](https://docs.npmjs.com/getting-started/what-is-npm). + +## Install dependencies + +```bash +$ npm install +``` + +## Build assets + +```bash +$ npm run build +``` + +## Watch assets + +```bash +$ npm run watch +``` + +# Anatomy + +```bash +static +| +|--- css +| +|--- html +| +|--- images +| +|--- js +| +|--- public # path where builds are put +``` diff --git a/glances/outputs/static/bower.json b/glances/outputs/static/bower.json new file mode 100644 index 00000000..ee592e91 --- /dev/null +++ b/glances/outputs/static/bower.json @@ -0,0 +1,9 @@ +{ + "name": "Glances", + "private": true, + "dependencies": { + "angular": "^1.5.8", + "angular-route": "^1.5.8", + "lodash": "^4.13.1" + } +} diff --git a/glances/outputs/static/css/style.css b/glances/outputs/static/css/style.css index 44eaadcc..b54b310f 100644 --- a/glances/outputs/static/css/style.css +++ b/glances/outputs/static/css/style.css @@ -144,7 +144,7 @@ body { /* Loading page */ #loading-page .glances-logo { - background: url('glances.png') no-repeat center center; + background: url('../images/glances.png') no-repeat center center; background-size: contain; } diff --git a/glances/outputs/static/gulpfile.js b/glances/outputs/static/gulpfile.js new file mode 100644 index 00000000..fd2c9255 --- /dev/null +++ b/glances/outputs/static/gulpfile.js @@ -0,0 +1,56 @@ +var gulp = require('gulp'); +var uglify = require('gulp-uglify'); +var concat = require('gulp-concat'); +var mainBowerFiles = require('main-bower-files'); +var ngAnnotate = require('gulp-ng-annotate'); +var templateCache = require('gulp-angular-templatecache'); +var del = require('del'); + +gulp.task('clean', function() { + del('./public/*') +}); + +gulp.task('copy', function() { + gulp.src('./html/*.html') + .pipe(gulp.dest('./public')); + + gulp.src('./css/*.css') + .pipe(gulp.dest('./public/css')); + + gulp.src('./images/*.png') + .pipe(gulp.dest('./public/images')); + + gulp.src('favicon.ico') + .pipe(gulp.dest('./public')); +}); + +gulp.task('bower', function() { + return gulp.src(mainBowerFiles()) + .pipe(concat('vendor.js')) + .pipe(uglify()) + .pipe(gulp.dest('./public/js')) +}); + +gulp.task('build-js', function() { + return gulp.src('./js/**/*.js') + .pipe(ngAnnotate()) + .pipe(concat('main.js')) + .pipe(uglify()) + .pipe(gulp.dest('./public/js')) +}); + +gulp.task('template', function () { + return gulp.src('./html/plugins/*.html') + .pipe(templateCache('templates.js', {'root': 'plugins/', 'module': 'glancesApp'})) + .pipe(gulp.dest('./public/js')); +}); + +gulp.task('watch', function () { + gulp.watch(['./html/*.html','./css/*.css', './images/*.png'], ['copy']); + gulp.watch('bower.json', ['bower']); + gulp.watch('./js/**/*.js', ['build-js']); + gulp.watch('./html/plugins/*.html', ['template']); +}); + +gulp.task('build', ['clean', 'bower', 'build-js', 'template', 'copy']); +gulp.task('default', ['build']);
\ No newline at end of file diff --git a/glances/outputs/static/html/index.html b/glances/outputs/static/html/index.html index 31b515c2..d6c8e55e 100644 --- a/glances/outputs/static/html/index.html +++ b/glances/outputs/static/html/index.html @@ -8,42 +8,13 @@ <base href="/"> <link rel="icon" type="image/x-icon" href="favicon.ico" /> - <link rel="stylesheet" type="text/css" href="normalize.css" /> - <link rel="stylesheet" type="text/css" href="bootstrap.min.css" /> - <link rel="stylesheet" type="text/css" href="style.css" /> + <link rel="stylesheet" type="text/css" href="css/normalize.css" /> + <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" /> + <link rel="stylesheet" type="text/css" href="css/style.css" /> - <script type="text/javascript" src="vendors/angular.js"></script> - <script type="text/javascript" src="vendors/angular-route.js"></script> - <script type="text/javascript" src="vendors/lodash.js"></script> - <script type="text/javascript" src="app.js"></script> - <script type="text/javascript" src="filters.js"></script> - <script type="text/javascript" src="variables.js"></script> - <script type="text/javascript" src="directives.js"></script> - <script type="text/javascript" src="services/core/glances_stats.js"></script> - <script type="text/javascript" src="services/plugins/glances_plugin.js"></script> - <script type="text/javascript" src="services/plugins/glances_alert.js"></script> - <script type="text/javascript" src="services/plugins/glances_cpu.js"></script> - <script type="text/javascript" src="services/plugins/glances_diskio.js"></script> - <script type="text/javascript" src="services/plugins/glances_docker.js"></script> - <script type="text/javascript" src="services/plugins/glances_fs.js"></script> - <script type="text/javascript" src="services/plugins/glances_folders.js"></script> - <script type="text/javascript" src="services/plugins/glances_ip.js"></script> - <script type="text/javascript" src="services/plugins/glances_load.js"></script> - <script type="text/javascript" src="services/plugins/glances_mem.js"></script> - <script type="text/javascript" src="services/plugins/glances_memswap.js"></script> - <script type="text/javascript" src="services/plugins/glances_amps.js"></script> - <script type="text/javascript" src="services/plugins/glances_network.js"></script> - <script type="text/javascript" src="services/plugins/glances_percpu.js"></script> - <script type="text/javascript" src="services/plugins/glances_processcount.js"></script> - <script type="text/javascript" src="services/plugins/glances_processlist.js"></script> - <script type="text/javascript" src="services/plugins/glances_quicklook.js"></script> - <script type="text/javascript" src="services/plugins/glances_raid.js"></script> - <script type="text/javascript" src="services/plugins/glances_sensors.js"></script> - <script type="text/javascript" src="services/plugins/glances_system.js"></script> - <script type="text/javascript" src="services/plugins/glances_uptime.js"></script> - <script type="text/javascript" src="services/plugins/glances_ports.js"></script> - - <script type="text/javascript" src="stats_controller.js"></script> + <script type="text/javascript" src="js/vendor.js"></script> + <script type="text/javascript" src="js/main.js"></script> + <script type="text/javascript" src="js/templates.js"></script> </head> <body ng-view="" ng-keydown="onKeyDown($event)"> diff --git a/glances/outputs/static/js/filters.js b/glances/outputs/static/js/filters.js index 78f3d152..7457b026 100644 --- a/glances/outputs/static/js/filters.js +++ b/glances/outputs/static/js/filters.js @@ -81,7 +81,7 @@ glancesApp.filter('leftPad', function($filter) { return function (value, length, chars) { length = length || 0; chars = chars || ' '; - return _.padLeft(value, length, chars); + return _.padStart(value, length, chars); } }); diff --git a/glances/outputs/static/js/services/plugins/glances_quicklook.js b/glances/outputs/static/js/services/plugins/glances_quicklook.js index 75c70d96..37345b52 100644 --- a/glances/outputs/static/js/services/plugins/glances_quicklook.js +++ b/glances/outputs/static/js/services/plugins/glances_quicklook.js @@ -22,7 +22,7 @@ glancesApp.service('GlancesPluginQuicklook', function() { this.swap = data.swap; this.percpus = []; - _.forEach(data.percpu, function(cpu, key) { + angular.forEach(data.percpu, function(cpu) { this.percpus.push({ 'number': cpu.cpu_number, 'total': cpu.total diff --git a/glances/outputs/static/js/vendors/angular-route.js b/glances/outputs/static/js/vendors/angular-route.js deleted file mode 100644 index 281c74cf..00000000 --- a/glances/outputs/static/js/vendors/angular-route.js +++ /dev/null @@ -1,991 +0,0 @@ -/** - * @license AngularJS v1.4.9 - * (c) 2010-2015 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) {'use strict'; - -/** - * @ngdoc module - * @name ngRoute - * @description - * - * # ngRoute - * - * The `ngRoute` module provides routing and deeplinking services and directives for angular apps. - * - * ## Example - * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. - * - * - * <div doc-module-components="ngRoute"></div> - */ - /* global -ngRouteModule */ -var ngRouteModule = angular.module('ngRoute', ['ng']). - provider('$route', $RouteProvider), - $routeMinErr = angular.$$minErr('ngRoute'); - -/** - * @ngdoc provider - * @name $routeProvider - * - * @description - * - * Used for configuring routes. - * - * ## Example - * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. - * - * ## Dependencies - * Requires the {@link ngRoute `ngRoute`} module to be installed. - */ -function $RouteProvider() { - function inherit(parent, extra) { - return angular.extend(Object.create(parent), extra); - } - - var routes = {}; - - /** - * @ngdoc method - * @name $routeProvider#when - * - * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redundant trailing slash or is missing one, the route will still match and the - * `$location.path` will be updated to add or drop the trailing slash to exactly match the - * route definition. - * - * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up - * to the next slash are matched and stored in `$routeParams` under the given `name` - * when the route matches. - * * `path` can contain named groups starting with a colon and ending with a star: - * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` - * when the route matches. - * * `path` can contain optional named groups with a question mark: e.g.`:name?`. - * - * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match - * `/color/brown/largecode/code/with/slashes/edit` and extract: - * - * * `color: brown` - * * `largecode: code/with/slashes`. - * - * - * @param {Object} route Mapping information to be assigned to `$route.current` on route - * match. - * - * Object properties: - * - * - `controller` – `{(string|function()=}` – Controller fn that should be associated with - * newly created scope or the name of a {@link angular.Module#controller registered - * controller} if passed as a string. - * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller. - * If present, the controller will be published to scope under the `controllerAs` name. - * - `template` – `{string=|function()=}` – html template as a string or a function that - * returns an html template as a string which should be used by {@link - * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. - * This property takes precedence over `templateUrl`. - * - * If `template` is a function, it will be called with the following parameters: - * - * - `{Array.<Object>}` - route parameters extracted from the current - * `$location.path()` by applying the current route - * - * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html - * template that should be used by {@link ngRoute.directive:ngView ngView}. - * - * If `templateUrl` is a function, it will be called with the following parameters: - * - * - `{Array.<Object>}` - route parameters extracted from the current - * `$location.path()` by applying the current route - * - * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should - * be injected into the controller. If any of these dependencies are promises, the router - * will wait for them all to be resolved or one to be rejected before the controller is - * instantiated. - * If all the promises are resolved successfully, the values of the resolved promises are - * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is - * fired. If any of the promises are rejected the - * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object - * is: - * - * - `key` – `{string}`: a name of a dependency to be injected into the controller. - * - `factory` - `{string|function}`: If `string` then it is an alias for a service. - * Otherwise if function, then it is {@link auto.$injector#invoke injected} - * and the return value is treated as the dependency. If the result is a promise, it is - * resolved before its value is injected into the controller. Be aware that - * `ngRoute.$routeParams` will still refer to the previous route within these resolve - * functions. Use `$route.current.params` to access the new route parameters, instead. - * - * - `redirectTo` – {(string|function())=} – value to update - * {@link ng.$location $location} path with and trigger route redirection. - * - * If `redirectTo` is a function, it will be called with the following parameters: - * - * - `{Object.<string>}` - route parameters extracted from the current - * `$location.path()` by applying the current route templateUrl. - * - `{string}` - current `$location.path()` - * - `{Object}` - current `$location.search()` - * - * The custom `redirectTo` function is expected to return a string which will be used - * to update `$location.path()` and `$location.search()`. - * - * - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()` - * or `$location.hash()` changes. - * - * If the option is set to `false` and url in the browser changes, then - * `$routeUpdate` event is broadcasted on the root scope. - * - * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive - * - * If the option is set to `true`, then the particular route can be matched without being - * case sensitive - * - * @returns {Object} self - * - * @description - * Adds a new route definition to the `$route` service. - */ - this.when = function(path, route) { - //copy original route object to preserve params inherited from proto chain - var routeCopy = angular.copy(route); - if (angular.isUndefined(routeCopy.reloadOnSearch)) { - routeCopy.reloadOnSearch = true; - } - if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { - routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; - } - routes[path] = angular.extend( - routeCopy, - path && pathRegExp(path, routeCopy) - ); - - // create redirection for trailing slashes - if (path) { - var redirectPath = (path[path.length - 1] == '/') - ? path.substr(0, path.length - 1) - : path + '/'; - - routes[redirectPath] = angular.extend( - {redirectTo: path}, - pathRegExp(redirectPath, routeCopy) - ); - } - - return this; - }; - - /** - * @ngdoc property - * @name $routeProvider#caseInsensitiveMatch - * @description - * - * A boolean property indicating if routes defined - * using this provider should be matched using a case insensitive - * algorithm. Defaults to `false`. - */ - this.caseInsensitiveMatch = false; - - /** - * @param path {string} path - * @param opts {Object} options - * @return {?Object} - * - * @description - * Normalizes the given path, returning a regular expression - * and the original path. - * - * Inspired by pathRexp in visionmedia/express/lib/utils.js. - */ - function pathRegExp(path, opts) { - var insensitive = opts.caseInsensitiveMatch, - ret = { - originalPath: path, - regexp: path - }, - keys = ret.keys = []; - - path = path - .replace(/([().])/g, '\\$1') - .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) { - var optional = option === '?' ? option : null; - var star = option === '*' ? option : null; - keys.push({ name: key, optional: !!optional }); - slash = slash || ''; - return '' - + (optional ? '' : slash) - + '(?:' - + (optional ? slash : '') - + (star && '(.+?)' || '([^/]+)') - + (optional || '') - + ')' - + (optional || ''); - }) - .replace(/([\/$\*])/g, '\\$1'); - - ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); - return ret; - } - - /** - * @ngdoc method - * @name $routeProvider#otherwise - * - * @description - * Sets route definition that will be used on route change when no other route definition - * is matched. - * - * @param {Object|string} params Mapping information to be assigned to `$route.current`. - * If called with a string, the value maps to `redirectTo`. - * @returns {Object} self - */ - this.otherwise = function(params) { - if (typeof params === 'string') { - params = {redirectTo: params}; - } - this.when(null, params); - return this; - }; - - - this.$get = ['$rootScope', - '$location', - '$routeParams', - '$q', - '$injector', - '$templateRequest', - '$sce', - function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) { - - /** - * @ngdoc service - * @name $route - * @requires $location - * @requires $routeParams - * - * @property {Object} current Reference to the current route definition. - * The route definition contains: - * - * - `controller`: The controller constructor as define in route definition. - * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for - * controller instantiation. The `locals` contain - * the resolved values of the `resolve` map. Additionally the `locals` also contain: - * - * - `$scope` - The current route scope. - * - `$template` - The current route template HTML. - * - * @property {Object} routes Object with all route configuration Objects as its properties. - * - * @description - * `$route` is used for deep-linking URLs to controllers and views (HTML partials). - * It watches `$location.url()` and tries to map the path to an existing route definition. - * - * Requires the {@link ngRoute `ngRoute`} module to be installed. - * - * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. - * - * The `$route` service is typically used in conjunction with the - * {@link ngRoute.directive:ngView `ngView`} directive and the - * {@link ngRoute.$routeParams `$routeParams`} service. - * - * @example - * This example shows how changing the URL hash causes the `$route` to match a route against the - * URL, and the `ngView` pulls in the partial. - * - * <example name="$route-service" module="ngRouteExample" - * deps="angular-route.js" fixBase="true"> - * <file name="index.html"> - * <div ng-controller="MainController"> - * Choose: - * <a href="Book/Moby">Moby</a> | - * <a href="Book/Moby/ch/1">Moby: Ch1</a> | - * <a href="Book/Gatsby">Gatsby</a> | - * <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | - * <a href="Book/Scarlet">Scarlet Letter</a><br/> - * - * <div ng-view></div> - * - * <hr /> - * - * <pre>$location.path() = {{$location.path()}}</pre> - * <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre> - * <pre>$route.current.params = {{$route.current.params}}</pre> - * <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre> - * <pre>$routeParams = {{$routeParams}}</pre> - * </div> - * </file> - * - * <file name="book.html"> - * controller: {{name}}<br /> - * Book Id: {{params.bookId}}<br /> - * </file> - * - * <file name="chapter.html"> - * controller: {{name}}<br /> - * Book Id: {{params.bookId}}<br /> - * Chapter Id: {{params.chapterId}} - * </file> - * - * <file name="script.js"> - * angular.module('ngRouteExample', ['ngRoute']) - * - * .controller('MainController', function($scope, $route, $routeParams, $location) { - * $scope.$route = $route; - * $scope.$location = $location; - * $scope.$routeParams = $routeParams; - * }) - * - * .controller('BookController', function($scope, $routeParams) { - * $scope.name = "BookController"; - * $scope.params = $routeParams; - * }) - * - * .controller('ChapterController', function($scope, $routeParams) { - * $scope.name = "ChapterController"; - * $scope.params = $routeParams; - * }) - * - * .config(function($routeProvider, $locationProvider) { - * $routeProvider - * .when('/Book/:bookId', { - * templateUrl: 'book.html', - * controller: 'BookController', - * resolve: { - * // I will cause a 1 second delay - * delay: function($q, $timeout) { - * var delay = $q.defer(); - * $timeout(delay.resolve, 1000); - * return delay.promise; - * } - * } - * }) - * .when('/Book/:bookId/ch/:chapterId', { - * templateUrl: 'chapter.html', - * controller: 'ChapterController' - * }); - * - * // configure html5 to get links working on jsfiddle - * $locationProvider.html5Mode(true); - * }); - * - * </file> - * - * <file name="protractor.js" type="protractor"> - * it('should load and compile correct template', function() { - * element(by.linkText('Moby: Ch1')).click(); - * var content = element(by.css('[ng-view]')).getText(); - * expect(content).toMatch(/controller\: ChapterController/); - * expect(content).toMatch(/Book Id\: Moby/); - * expect(content).toMatch(/Chapter Id\: 1/); - * - * element(by.partialLinkText('Scarlet')).click(); - * - * content = element(by.css('[ng-view]')).getText(); - * expect(content).toMatch(/controller\: BookController/); - * expect(content).toMatch(/Book Id\: Scarlet/); - * }); - * </file> - * </example> - */ - - /** - * @ngdoc event - * @name $route#$routeChangeStart - * @eventType broadcast on root scope - * @description - * Broadcasted before a route change. At this point the route services starts - * resolving all of the dependencies needed for the route change to occur. - * Typically this involves fetching the view template as well as any dependencies - * defined in `resolve` route property. Once all of the dependencies are resolved - * `$routeChangeSuccess` is fired. - * - * The route change (and the `$location` change that triggered it) can be prevented - * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} - * for more details about event object. - * - * @param {Object} angularEvent Synthetic event object. - * @param {Route} next Future route information. - * @param {Route} current Current route information. - */ - - /** - * @ngdoc event - * @name $route#$routeChangeSuccess - * @eventType broadcast on root scope - * @description - * Broadcasted after a route change has happened |