diff --git a/glances/main.py b/glances/main.py index 3ecd1c74..9322a16c 100644 --- a/glances/main.py +++ b/glances/main.py @@ -296,8 +296,9 @@ Examples of use: if WINDOWS: args.webserver = True - # In web server mode + # In web server mode, defaul refresh time: 5 sec if args.webserver: + args.time = 5 args.process_short_name = True # Server or client login/password diff --git a/glances/outputs/glances_bottle.py b/glances/outputs/glances_bottle.py index 3afd2526..ee3cacf7 100644 --- a/glances/outputs/glances_bottle.py +++ b/glances/outputs/glances_bottle.py @@ -30,7 +30,7 @@ from glances.timer import Timer from glances.logger import logger try: - from bottle import Bottle, static_file, abort, response, request, auth_basic + from bottle import Bottle, static_file, abort, response, request, auth_basic, template, TEMPLATE_PATH except ImportError: logger.critical('Bottle module not found. Glances cannot start in web server mode.') sys.exit(2) @@ -72,6 +72,9 @@ class GlancesBottle(object): # Path where the statics files are stored self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/public') + # Paths for templates + TEMPLATE_PATH.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/templates')) + def load_config(self, config): """Load the outputs section of the configuration file.""" # Limit the number of processes to display in the WebUI @@ -153,11 +156,15 @@ class GlancesBottle(object): def _index(self, refresh_time=None): """Bottle callback for index.html (/) file.""" + + if refresh_time is None: + refresh_time = self.args.time + # Update the stat self.__update__() # Display - return static_file("index.html", root=self.STATIC_PATH) + return template("index.html", refresh_time=refresh_time) def _resource(self, filepath): """Bottle callback for resources files.""" diff --git a/glances/outputs/static/README.md b/glances/outputs/static/README.md index 32c3950e..8bfb845c 100644 --- a/glances/outputs/static/README.md +++ b/glances/outputs/static/README.md @@ -31,11 +31,22 @@ static | |--- css | -|--- html -| |--- images | |--- js | |--- public # path where builds are put +| +|--- templates (bottle) ``` + +## Data + +Each plugin receives the data in the following format: + +* stats +* views +* isBsd +* isLinux +* isMac +* isWindows diff --git a/glances/outputs/static/bower.json b/glances/outputs/static/bower.json index 93c7c550..451aa73f 100644 --- a/glances/outputs/static/bower.json +++ b/glances/outputs/static/bower.json @@ -3,8 +3,15 @@ "private": true, "dependencies": { "angular": "^1.5.8", - "angular-route": "^1.5.8", "lodash": "^4.13.1", - "favico.js": "^0.3.10" + "favico.js": "^0.3.10", + "angular-hotkeys": "chieffancypants/angular-hotkeys#^1.7.0" + }, + "overrides": { + "angular-hotkeys": { + "main": [ + "build/hotkeys.js" + ] + } } } diff --git a/glances/outputs/static/css/style.css b/glances/outputs/static/css/style.css index 16dda3de..92494a45 100644 --- a/glances/outputs/static/css/style.css +++ b/glances/outputs/static/css/style.css @@ -106,16 +106,16 @@ body { } /* Plugins */ -#processlist .table-cell { +#processlist-plugin .table-cell { padding: 0px 5px 0px 5px; white-space: nowrap; } -#containers .table-cell { +#containers-plugin .table-cell { padding: 0px 10px 0px 10px; white-space: nowrap; } -#quicklook .progress { +#quicklook-plugin .progress { margin-bottom: 0px; min-width: 100px; background-color: #000; @@ -123,25 +123,25 @@ body { border-radius: 0px; text-align: right; } -#quicklook .progress-bar-ok { +#quicklook-plugin .progress-bar-ok { background-color: #3E7B04; } -#quicklook .progress-bar-careful { +#quicklook-plugin .progress-bar-careful { background-color: #295183; } -#quicklook .progress-bar-warning { +#quicklook-plugin .progress-bar-warning { background-color: #5D4062; } -#quicklook .progress-bar-critical { +#quicklook-plugin .progress-bar-critical { background-color: #A30000; } -#quicklook .cpu-name { +#quicklook-plugin .cpu-name { white-space: nowrap; overflow: hidden; width: 100%; text-overflow: ellipsis; } -#amps .process-result { +#amps-plugin .process-result { max-width: 300px; overflow: hidden; white-space: pre-wrap; @@ -149,7 +149,7 @@ body { text-overflow: ellipsis; } -#gpu .gpu-name { +#gpu-plugin .gpu-name { white-space: nowrap; overflow: hidden; width: 100%; diff --git a/glances/outputs/static/gulpfile.js b/glances/outputs/static/gulpfile.js index 615a9c8c..ec342736 100644 --- a/glances/outputs/static/gulpfile.js +++ b/glances/outputs/static/gulpfile.js @@ -21,7 +21,7 @@ gulp.task('copy', function() { gulp.src('./images/*.png') .pipe(gulp.dest('./public/images')); - gulp.src('favicon.ico') + gulp.src('./images/favicon.ico') .pipe(gulp.dest('./public')); }); @@ -41,8 +41,8 @@ gulp.task('build-js', function() { }); gulp.task('template', function () { - return gulp.src('./html/plugins/*.html') - .pipe(templateCache('templates.js', {'root': 'plugins/', 'module': 'glancesApp'})) + return gulp.src('./js/components/**/*.html') + .pipe(templateCache('templates.js', {'root': 'components/', 'module': 'glancesApp'})) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('./public/js')); }); diff --git a/glances/outputs/static/html/help.html b/glances/outputs/static/html/help.html deleted file mode 100644 index f42a36ed..00000000 --- a/glances/outputs/static/html/help.html +++ /dev/null @@ -1,76 +0,0 @@ -
-
{{help.version}} {{help.psutil_version}}
-
-
 
-
-
{{help.configuration_file}}
-
-
 
-
-
{{help.sort_auto}}
-
{{help.sort_network}}
-
-
-
{{help.sort_cpu}}
-
{{help.show_hide_alert}}
-
-
-
{{help.sort_mem}}
-
{{help.percpu}}
-
-
-
{{help.sort_user}}
-
{{help.show_hide_ip}}
-
-
-
{{help.sort_proc}}
-
{{help.enable_disable_docker}}
-
-
-
{{help.sort_io}}
-
{{help.view_network_io_combination}}
-
-
-
{{help.sort_cpu_times}}
-
{{help.view_cumulative_network}}
-
-
-
{{help.show_hide_diskio}}
-
{{help.show_hide_filesytem_freespace}}
-
-
-
{{help.show_hide_filesystem}}
-
{{help.show_hide_help}}
-
-
-
{{help.show_hide_network}}
-
{{help.diskio_iops}}
-
-
-
{{help.show_hide_sensors}}
-
{{help.show_hide_top_menu}}
-
-
-
{{help.show_hide_left_sidebar}}
-
{{help.show_hide_amp}}
-
-
-
{{help.enable_disable_process_stats}}
-
{{help.show_hide_irq}}
-
-
-
{{help.enable_disable_gpu}}
-
{{help.enable_disable_mean_gpu}}
-
-
-
{{help.enable_disable_quick_look}}
-
-
-
-
{{help.enable_disable_short_processname}}
-
-
-
-
{{help.enable_disable_ports}}
-
-
diff --git a/glances/outputs/static/html/plugins/alert.html b/glances/outputs/static/html/plugins/alert.html deleted file mode 100644 index f4fe2f2f..00000000 --- a/glances/outputs/static/html/plugins/alert.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
-{{alert.begin | date : 'yyyy-MM-dd H:mm:ss'}} ({{ alert.ongoing ? 'ongoing' : alert.duration }}) - {{alert.level}} on {{alert.name}} ({{alert.max}}) -
-
-
diff --git a/glances/outputs/static/html/plugins/alerts.html b/glances/outputs/static/html/plugins/alerts.html deleted file mode 100644 index 1c0a1b42..00000000 --- a/glances/outputs/static/html/plugins/alerts.html +++ /dev/null @@ -1,2 +0,0 @@ -No warning or critical alert detected -Warning or critical alerts (lasts {{statsAlert.count()}} entries) diff --git a/glances/outputs/static/html/plugins/amps.html b/glances/outputs/static/html/plugins/amps.html deleted file mode 100644 index bd149629..00000000 --- a/glances/outputs/static/html/plugins/amps.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
{{ process.name }}
-
{{ process.count }}
-
{{ process.result }}
-
-
diff --git a/glances/outputs/static/html/plugins/cloud.html b/glances/outputs/static/html/plugins/cloud.html deleted file mode 100644 index 781cc814..00000000 --- a/glances/outputs/static/html/plugins/cloud.html +++ /dev/null @@ -1 +0,0 @@ -{{ statsCloud.getProvider() }} {{ statsCloud.getInstance() }} diff --git a/glances/outputs/static/html/plugins/cpu.html b/glances/outputs/static/html/plugins/cpu.html deleted file mode 100644 index 53175592..00000000 --- a/glances/outputs/static/html/plugins/cpu.html +++ /dev/null @@ -1,82 +0,0 @@ -
-
-
-
-
CPU
-
{{ statsCpu.total }}%
-
-
-
user:
-
- {{ statsCpu.user }}% -
-
-
-
system:
-
- {{ statsCpu.system }}% -
-
-
-
idle:
-
{{ statsCpu.idle }}%
-
-
-
- - -
diff --git a/glances/outputs/static/html/plugins/diskio.html b/glances/outputs/static/html/plugins/diskio.html deleted file mode 100644 index a136c5d6..00000000 --- a/glances/outputs/static/html/plugins/diskio.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
DISK I/O
-
R/s
-
W/s
- -
IOR/s
-
IOW/s
-
-
-
{{(disk.alias ? disk.alias : disk.name) | min_size}}
-
{{disk.bitrate.txps }}
-
{{disk.bitrate.rxps }}
- -
{{disk.count.txps }}
-
{{disk.count.rxps }}
-
diff --git a/glances/outputs/static/html/plugins/docker.html b/glances/outputs/static/html/plugins/docker.html deleted file mode 100644 index b0ae92ae..00000000 --- a/glances/outputs/static/html/plugins/docker.html +++ /dev/null @@ -1,26 +0,0 @@ -CONTAINERS {{ statsDocker.containers.length }} (served by Docker {{ statsDocker.version }}) - -
-
-
Name
-
Status
-
CPU%
-
MEM
-
IOR/s
-
IOW/s
-
RX/s
-
TX/s
-
Command
-
-
-
{{ container.name }}
-
{{ container.status }}
-
{{ container.cpu | number:1 }}
-
{{ container.memory | bytes }}
-
{{ container.ior / container.io_time_since_update | bits }}
-
{{ container.iow / container.io_time_since_update | bits }}
-
{{ container.rx / container.net_time_since_update | bits }}
-
{{ container.tx / container.net_time_since_update | bits }}
-
{{ container.command }}
-
-
diff --git a/glances/outputs/static/html/plugins/folders.html b/glances/outputs/static/html/plugins/folders.html deleted file mode 100644 index b43db8ad..00000000 --- a/glances/outputs/static/html/plugins/folders.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
FOLDERS
-
Size
-
-
-
{{ folder.path }}
-
{{ folder.size | bytes }}
-
diff --git a/glances/outputs/static/html/plugins/fs.html b/glances/outputs/static/html/plugins/fs.html deleted file mode 100644 index 4c0ed16f..00000000 --- a/glances/outputs/static/html/plugins/fs.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
FILE SYS
-
- Used - Free -
-
Total
-
-
-
{{ fs.shortMountPoint }} ({{ fs.name }})
-
- {{ fs.used | bytes }} - {{ fs.free | bytes }} -
-
{{ fs.size | bytes }}
-
diff --git a/glances/outputs/static/html/plugins/gpu.html b/glances/outputs/static/html/plugins/gpu.html deleted file mode 100644 index 731ad73a..00000000 --- a/glances/outputs/static/html/plugins/gpu.html +++ /dev/null @@ -1,25 +0,0 @@ -
- {{ statsGpu.name }} -
-
-
-
proc:
-
{{ statsGpu.mean.proc | number : 0 }}%
-
N/A
-
-
-
mem:
-
{{ statsGpu.mean.mem | number : 0 }}%
-
N/A
-
-
-
- {{ gpu.gpu_id }}: - {{ gpu.proc | number : 0 }}% - N/A - mem: - {{ gpu.mem | number : 0 }}% - N/A -
-
-
diff --git a/glances/outputs/static/html/plugins/ip.html b/glances/outputs/static/html/plugins/ip.html deleted file mode 100644 index bfcbf643..00000000 --- a/glances/outputs/static/html/plugins/ip.html +++ /dev/null @@ -1 +0,0 @@ - - IP {{ statsIp.address }}/{{ statsIp.maskCidr }} Pub {{ statsIp.publicAddress }} diff --git a/glances/outputs/static/html/plugins/load.html b/glances/outputs/static/html/plugins/load.html deleted file mode 100644 index 1d90b6e3..00000000 --- a/glances/outputs/static/html/plugins/load.html +++ /dev/null @@ -1,24 +0,0 @@ -
-
-
LOAD
-
{{ statsLoad.cpucore }}-core
-
-
-
1 min:
-
- {{ statsLoad.min1 | number : 2}} -
-
-
-
5 min:
-
- {{ statsLoad.min5 | number : 2}} -
-
-
-
15 min:
-
- {{ statsLoad.min15 | number : 2}} -
-
-
diff --git a/glances/outputs/static/html/plugins/mem.html b/glances/outputs/static/html/plugins/mem.html deleted file mode 100644 index 9f6f869d..00000000 --- a/glances/outputs/static/html/plugins/mem.html +++ /dev/null @@ -1,20 +0,0 @@ -
-
-
MEM
-
{{ statsMem.percent }}%
-
-
-
total:
-
{{ statsMem.total | bytes }}
-
-
-
used:
-
- {{ statsMem.used | bytes:2 }} -
-
-
-
free:
-
{{ statsMem.free | bytes }}
-
-
diff --git a/glances/outputs/static/html/plugins/mem_more.html b/glances/outputs/static/html/plugins/mem_more.html deleted file mode 100644 index 49d831dd..00000000 --- a/glances/outputs/static/html/plugins/mem_more.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
-
active:
-
{{ statsMem.active | bytes }}
-
-
-
inactive:
-
{{ statsMem.inactive | bytes }}
-
-
-
buffers:
-
{{ statsMem.buffers | bytes }}
-
-
-
cached:
-
{{ statsMem.cached | bytes }}
-
-
diff --git a/glances/outputs/static/html/plugins/memswap.html b/glances/outputs/static/html/plugins/memswap.html deleted file mode 100644 index 3d566a51..00000000 --- a/glances/outputs/static/html/plugins/memswap.html +++ /dev/null @@ -1,20 +0,0 @@ -
-
-
SWAP
-
{{ statsMemSwap.percent }}%
-
-
-
total:
-
{{ statsMemSwap.total | bytes }}
-
-
-
used:
-
- {{ statsMemSwap.used | bytes }} -
-
-
-
free:
-
{{ statsMemSwap.free | bytes }}
-
-
diff --git a/glances/outputs/static/html/plugins/network.html b/glances/outputs/static/html/plugins/network.html deleted file mode 100644 index 6030f263..00000000 --- a/glances/outputs/static/html/plugins/network.html +++ /dev/null @@ -1,28 +0,0 @@ -
-
NETWORK
-
Rx/s
-
Tx/s
- -
-
Rx+Tx/s
- -
Rx
-
Tx
- -
-
Rx+Tx
-
-
-
{{ network.interfaceName | min_size }}
-
{{ arguments.byte ? (network.rx / network.time_since_update | bytes) : (network.rx / network.time_since_update | bits) }}
-
{{ arguments.byte ? (network.tx / network.time_since_update | bytes) : (network.tx / network.time_since_update | bits) }}
- -
-
{{ arguments.byte ? (network.cx / network.time_since_update | bytes) : (network.cx / network.time_since_update | bits) }}
- -
{{ arguments.byte ? (network.cumulativeRx | bytes) : (network.cumulativeRx | bits) }}
-
{{ arguments.byte ? (network.cumulativeTx | bytes) : (network.cumulativeTx | bits) }}
- -
-
{{ arguments.byte ? (network.cumulativeCx | bytes) : (network.cumulativeCx | bits) }}
-
diff --git a/glances/outputs/static/html/plugins/per_cpu.html b/glances/outputs/static/html/plugins/per_cpu.html deleted file mode 100644 index 552fc017..00000000 --- a/glances/outputs/static/html/plugins/per_cpu.html +++ /dev/null @@ -1,34 +0,0 @@ -
-
-
PER CPU
-
{{ percpu.total }}%
-
-
-
user:
-
- {{ percpu.user }}% -
-
-
-
system:
-
- {{ percpu.system }}% -
-
-
-
idle:
-
{{ percpu.idle }}%
-
-
-
iowait:
-
- {{ percpu.iowait }}% -
-
-
-
steal:
-
- {{ percpu.steal }}% -
-
-
diff --git a/glances/outputs/static/html/plugins/ports.html b/glances/outputs/static/html/plugins/ports.html deleted file mode 100644 index 3d29cb9e..00000000 --- a/glances/outputs/static/html/plugins/ports.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
{{(port.description ? port.description : port.host + ' ' + port.port) | min_size: 20}}
-
-
- Scanning - Timeout - Open - {{port.status * 1000.0 | number:0}}ms -
-
diff --git a/glances/outputs/static/html/plugins/processcount.html b/glances/outputs/static/html/plugins/processcount.html deleted file mode 100644 index 82e29ae6..00000000 --- a/glances/outputs/static/html/plugins/processcount.html +++ /dev/null @@ -1,6 +0,0 @@ -TASKS -{{ statsProcessCount.total }} ({{ statsProcessCount.thread }} thr), -{{ statsProcessCount.running }} run, -{{ statsProcessCount.sleeping }} slp, -{{ statsProcessCount.stopped }} oth - sorted {{ sorter.auto ? 'automatically' : '' }} by {{ sorter.getColumnLabel(sorter.column) }}, flat view diff --git a/glances/outputs/static/html/plugins/processlist.html b/glances/outputs/static/html/plugins/processlist.html deleted file mode 100644 index 8dba824f..00000000 --- a/glances/outputs/static/html/plugins/processlist.html +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
CPU%
-
MEM%
- - -
PID
-
USER
-
NI
-
S
- - - -
Command
-
-
-
{{process.cpu_percent | number:1}}
-
{{process.memory_percent | number:1}}
- - -
{{process.pid}}
-
{{process.username}}
-
{{process.nice | exclamation}}
-
{{process.status}}
- - - -
{{process.name}}
-
{{process.cmdline}}
-
-
diff --git a/glances/outputs/static/html/plugins/quicklook.html b/glances/outputs/static/html/plugins/quicklook.html deleted file mode 100644 index a64523e0..00000000 --- a/glances/outputs/static/html/plugins/quicklook.html +++ /dev/null @@ -1,57 +0,0 @@ -
- {{ statsQuicklook.cpu_name }} -
-
-
-
CPU
-
-
-
-   -
-
-
-
- {{ statsQuicklook.cpu }}% -
-
-
-
CPU{{ percpu.number }}
-
-
-
-   -
-
-
-
- {{ percpu.total }}% -
-
-
-
MEM
-
-
-
-   -
-
-
-
- {{ statsQuicklook.mem }}% -
-
-
-
SWAP
-
-
-
-   -
-
-
-
- {{ statsQuicklook.swap }}% -
-
-
diff --git a/glances/outputs/static/html/plugins/sensors.html b/glances/outputs/static/html/plugins/sensors.html deleted file mode 100644 index 5288f879..00000000 --- a/glances/outputs/static/html/plugins/sensors.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
SENSORS
-
- -
-
{{ sensor.label }}
-
{{ sensor.unit }}
-
{{ sensor.value }}
-
diff --git a/glances/outputs/static/html/plugins/system.html b/glances/outputs/static/html/plugins/system.html deleted file mode 100644 index 5e700b91..00000000 --- a/glances/outputs/static/html/plugins/system.html +++ /dev/null @@ -1,4 +0,0 @@ -Disconnected from -{{ statsSystem.hostname }} - - \ No newline at end of file diff --git a/glances/outputs/static/html/plugins/uptime.html b/glances/outputs/static/html/plugins/uptime.html deleted file mode 100644 index 1fc9238c..00000000 --- a/glances/outputs/static/html/plugins/uptime.html +++ /dev/null @@ -1 +0,0 @@ -Uptime: {{ statsUptime.uptime }} diff --git a/glances/outputs/static/html/plugins/wifi.html b/glances/outputs/static/html/plugins/wifi.html deleted file mode 100644 index 6c46c938..00000000 --- a/glances/outputs/static/html/plugins/wifi.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
WIFI
-
-
dBm
-
-
-
{{ hotspot.ssid|limitTo:20 }} {{ hotspot.encryption_type }}
-
-
{{ hotspot.signal }}
-
diff --git a/glances/outputs/static/html/stats.html b/glances/outputs/static/html/stats.html deleted file mode 100644 index 7982ad15..00000000 --- a/glances/outputs/static/html/stats.html +++ /dev/null @@ -1,88 +0,0 @@ -
- -
Loading...
-
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
PROCESSES DISABLED (press 'z' to display)
-
-
-
diff --git a/glances/outputs/static/favicon.ico b/glances/outputs/static/images/favicon.ico similarity index 100% rename from glances/outputs/static/favicon.ico rename to glances/outputs/static/images/favicon.ico diff --git a/glances/outputs/static/js/app.js b/glances/outputs/static/js/app.js index 0730b378..6bf44656 100644 --- a/glances/outputs/static/js/app.js +++ b/glances/outputs/static/js/app.js @@ -1,31 +1,19 @@ -var glancesApp = angular.module('glancesApp', ['ngRoute']) +var glancesApp = angular.module('glancesApp', ['glances.config', 'cfp.hotkeys']) -.config(function($routeProvider, $locationProvider) { - $routeProvider.when('/:refresh_time?', { - templateUrl : 'stats.html', - controller : 'statsController', - resolve: { - help: function(GlancesStats) { - return GlancesStats.getHelp(); - }, - config: function(GlancesStats) { - return GlancesStats.getConfig(); - }, - arguments: function(GlancesStats, $route) { - return GlancesStats.getArguments().then(function(arguments) { - var refreshTimeRoute = parseInt($route.current.params.refresh_time); - if (!isNaN(refreshTimeRoute) && refreshTimeRoute > 1) { - arguments.time = refreshTimeRoute; - } +.value('CONFIG', {}) +.value('ARGUMENTS', {}) - return arguments; - }); - } - } +.config(function (hotkeysProvider) { + hotkeysProvider.useNgRoute = false; + hotkeysProvider.includeCheatSheet = false; +}) + +.run(function ($rootScope, GlancesStats) { + $rootScope.title = "Glances"; + + $rootScope.$on('data_refreshed', function (event, data) { + $rootScope.title = data.stats.system.hostname + ' - Glances'; }); - $locationProvider.html5Mode(true); -}) -.run(function($rootScope) { - $rootScope.title = "Glances"; + GlancesStats.init(); }); diff --git a/glances/outputs/static/js/components/glances/component.js b/glances/outputs/static/js/components/glances/component.js new file mode 100644 index 00000000..0fd23c76 --- /dev/null +++ b/glances/outputs/static/js/components/glances/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glances', { + controller: GlancesController, + controllerAs: 'vm', + templateUrl: 'components/glances/view.html' +}); diff --git a/glances/outputs/static/js/components/glances/controller.js b/glances/outputs/static/js/components/glances/controller.js new file mode 100644 index 00000000..f77ee1ee --- /dev/null +++ b/glances/outputs/static/js/components/glances/controller.js @@ -0,0 +1,216 @@ +'use strict'; + +function GlancesController($scope, GlancesStats, hotkeys, ARGUMENTS) { + var vm = this; + vm.dataLoaded = false; + vm.arguments = ARGUMENTS; + + $scope.$on('data_refreshed', function (event, data) { + vm.hasGpu = data.stats.gpu.length > 0; + vm.dataLoaded = true; + }); + + // A => Enable/disable AMPs + hotkeys.add({ + combo: 'A', + callback: function () { + ARGUMENTS.disable_amps = !ARGUMENTS.disable_amps; + } + }); + + // d => Show/hide disk I/O stats + hotkeys.add({ + combo: 'd', + callback: function () { + ARGUMENTS.disable_diskio = !ARGUMENTS.disable_diskio; + } + }); + + // Q => Show/hide IRQ + hotkeys.add({ + combo: 'Q', + callback: function () { + ARGUMENTS.enable_irq = !ARGUMENTS.enable_irq; + } + }); + + // f => Show/hide filesystem stats + hotkeys.add({ + combo: 'f', + callback: function () { + ARGUMENTS.disable_fs = !ARGUMENTS.disable_fs; + } + }); + + // n => Show/hide network stats + hotkeys.add({ + combo: 'n', + callback: function () { + ARGUMENTS.disable_network = !ARGUMENTS.disable_network; + } + }); + + // s => Show/hide sensors stats + hotkeys.add({ + combo: 's', + callback: function () { + ARGUMENTS.disable_sensors = !ARGUMENTS.disable_sensors; + } + }); + + // 2 => Show/hide left sidebar + hotkeys.add({ + combo: '2', + callback: function () { + ARGUMENTS.disable_left_sidebar = !ARGUMENTS.disable_left_sidebar; + } + }); + + // z => Enable/disable processes stats + hotkeys.add({ + combo: 'z', + callback: function () { + ARGUMENTS.disable_process = !ARGUMENTS.disable_process; + } + }); + + // SLASH => Enable/disable short processes name + hotkeys.add({ + combo: '/', + callback: function () { + ARGUMENTS.process_short_name = !ARGUMENTS.process_short_name; + } + }); + + // D => Enable/disable Docker stats + hotkeys.add({ + combo: 'D', + callback: function () { + ARGUMENTS.disable_docker = !ARGUMENTS.disable_docker; + } + }); + + // b => Bytes or bits for network I/O + hotkeys.add({ + combo: 'b', + callback: function () { + ARGUMENTS.byte = !ARGUMENTS.byte; + } + }); + + // 'B' => Switch between bit/s and IO/s for Disk IO + hotkeys.add({ + combo: 'B', + callback: function () { + ARGUMENTS.diskio_iops = !ARGUMENTS.diskio_iops; + } + }); + + // l => Show/hide alert logs + hotkeys.add({ + combo: 'l', + callback: function () { + ARGUMENTS.disable_alert = !ARGUMENTS.disable_alert; + } + }); + + // 1 => Global CPU or per-CPU stats + hotkeys.add({ + combo: '1', + callback: function () { + ARGUMENTS.percpu = !ARGUMENTS.percpu; + } + }); + + // h => Show/hide this help screen + hotkeys.add({ + combo: 'h', + callback: function () { + ARGUMENTS.help_tag = !ARGUMENTS.help_tag; + } + }); + + // T => View network I/O as combination + hotkeys.add({ + combo: 'T', + callback: function () { + ARGUMENTS.network_sum = !ARGUMENTS.network_sum; + } + }); + + // U => View cumulative network I/O + hotkeys.add({ + combo: 'U', + callback: function () { + ARGUMENTS.network_cumul = !ARGUMENTS.network_cumul; + } + }); + + // F => Show filesystem free space + hotkeys.add({ + combo: 'F', + callback: function () { + ARGUMENTS.fs_free_space = !ARGUMENTS.fs_free_space; + } + }); + + // 3 => Enable/disable quick look plugin + hotkeys.add({ + combo: '3', + callback: function () { + ARGUMENTS.disable_quicklook = !ARGUMENTS.disable_quicklook; + } + }); + + // 6 => Enable/disable mean gpu + hotkeys.add({ + combo: '6', + callback: function () { + ARGUMENTS.meangpu = !ARGUMENTS.meangpu; + } + }); + + // G => Enable/disable gpu + hotkeys.add({ + combo: 'G', + callback: function () { + ARGUMENTS.disable_gpu = !ARGUMENTS.disable_gpu; + } + }); + + hotkeys.add({ + combo: '5', + callback: function () { + ARGUMENTS.disable_quicklook = !ARGUMENTS.disable_quicklook; + ARGUMENTS.disable_cpu = !ARGUMENTS.disable_cpu; + ARGUMENTS.disable_mem = !ARGUMENTS.disable_mem; + ARGUMENTS.disable_memswap = !ARGUMENTS.disable_memswap; + ARGUMENTS.disable_load = !ARGUMENTS.disable_load; + ARGUMENTS.disable_gpu = !ARGUMENTS.disable_gpu; + } + }); + + // I => Show/hide IP module + hotkeys.add({ + combo: 'I', + callback: function () { + ARGUMENTS.disable_ip = !ARGUMENTS.disable_ip; + } + }); + + // P => Enable/disable ports module + hotkeys.add({ + combo: 'P', + callback: function () { + ARGUMENTS.disable_ports = !ARGUMENTS.disable_ports; + } + }); + + // 'W' > Enable/Disable Wifi plugin + hotkeys.add({ + combo: 'W', + callback: function () { + ARGUMENTS.disable_wifi = !ARGUMENTS.disable_wifi; + } + }); +} diff --git a/glances/outputs/static/js/components/glances/view.html b/glances/outputs/static/js/components/glances/view.html new file mode 100644 index 00000000..dec86499 --- /dev/null +++ b/glances/outputs/static/js/components/glances/view.html @@ -0,0 +1,89 @@ +
+
+ +
Loading...
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+
+ +
+ + + +
+
+
+
+ diff --git a/glances/outputs/static/js/components/help/component.js b/glances/outputs/static/js/components/help/component.js new file mode 100644 index 00000000..e773c38f --- /dev/null +++ b/glances/outputs/static/js/components/help/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesHelp', { + controller: GlancesHelpController, + controllerAs: 'vm', + templateUrl: 'components/help/view.html' +}); diff --git a/glances/outputs/static/js/components/help/controller.js b/glances/outputs/static/js/components/help/controller.js new file mode 100644 index 00000000..231b8c37 --- /dev/null +++ b/glances/outputs/static/js/components/help/controller.js @@ -0,0 +1,9 @@ +'use strict'; + +function GlancesHelpController($http) { + var vm = this; + + $http.get('api/2/help').then(function (response) { + vm.help = response.data; + }); +} diff --git a/glances/outputs/static/js/components/help/view.html b/glances/outputs/static/js/components/help/view.html new file mode 100644 index 00000000..cc238f9f --- /dev/null +++ b/glances/outputs/static/js/components/help/view.html @@ -0,0 +1,79 @@ +
+
+
{{vm.help.version}} {{vm.help.psutil_version}}
+
+
 
+
+
{{vm.help.configuration_file}}
+
+
 
+
+
{{vm.help.sort_auto}}
+
{{vm.help.sort_network}}
+
+
+
{{vm.help.sort_cpu}}
+
{{vm.help.show_hide_alert}}
+
+
+
{{vm.help.sort_mem}}
+
{{vm.help.percpu}}
+
+
+
{{vm.help.sort_user}}
+
{{vm.help.show_hide_ip}}
+
+
+
{{vm.help.sort_proc}}
+
{{vm.help.enable_disable_docker}}
+
+
+
{{vm.help.sort_io}}
+
{{vm.help.view_network_io_combination}}
+
+
+
{{vm.help.sort_cpu_times}}
+
{{vm.help.view_cumulative_network}}
+
+
+
{{vm.help.show_hide_diskio}}
+
{{vm.help.show_hide_filesytem_freespace}}
+
+
+
{{vm.help.show_hide_filesystem}}
+
{{vm.help.show_hide_vm.help}}
+
+
+
{{vm.help.show_hide_network}}
+
{{vm.help.diskio_iops}}
+
+
+
{{vm.help.show_hide_sensors}}
+
{{vm.help.show_hide_top_menu}}
+
+
+
{{vm.help.show_hide_left_sidebar}}
+
{{vm.help.show_hide_amp}}
+
+
+
{{vm.help.enable_disable_process_stats}}
+
{{vm.help.show_hide_irq}}
+
+
+
{{vm.help.enable_disable_gpu}}
+
{{vm.help.enable_disable_mean_gpu}}
+
+
+
{{vm.help.enable_disable_quick_look}}
+
+
+
+
{{vm.help.enable_disable_short_processname}}
+
+
+
+
{{vm.help.enable_disable_ports}}
+
+
+ +
diff --git a/glances/outputs/static/js/components/plugin-alert/component.js b/glances/outputs/static/js/components/plugin-alert/component.js new file mode 100644 index 00000000..cc02883e --- /dev/null +++ b/glances/outputs/static/js/components/plugin-alert/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginAlert', { + controller: GlancesPluginAlertController, + controllerAs: 'vm', + templateUrl: 'components/plugin-alert/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-alert/controller.js b/glances/outputs/static/js/components/plugin-alert/controller.js new file mode 100644 index 00000000..90c91e1a --- /dev/null +++ b/glances/outputs/static/js/components/plugin-alert/controller.js @@ -0,0 +1,65 @@ +'use strict'; + +function GlancesPluginAlertController($scope, favicoService) { + var vm = this; + var _alerts = []; + + $scope.$on('data_refreshed', function (event, data) { + var alertStats = data.stats['alert']; + if (!_.isArray(alertStats)) { + alertStats = []; + } + + _alerts = []; + for (var i = 0; i < alertStats.length; i++) { + var alertalertStats = alertStats[i]; + var alert = {}; + + alert.name = alertalertStats[3]; + alert.level = alertalertStats[2]; + alert.begin = alertalertStats[0] * 1000; + alert.end = alertalertStats[1] * 1000; + alert.ongoing = alertalertStats[1] == -1; + alert.min = alertalertStats[6]; + alert.mean = alertalertStats[5]; + alert.max = alertalertStats[4]; + + if (!alert.ongoing) { + var duration = alert.end - alert.begin; + var seconds = parseInt((duration / 1000) % 60) + , minutes = parseInt((duration / (1000 * 60)) % 60) + , hours = parseInt((duration / (1000 * 60 * 60)) % 24); + + alert.duration = _.padStart(hours, 2, '0') + ":" + _.padStart(minutes, 2, '0') + ":" + _.padStart(seconds, 2, '0'); + } + + _alerts.push(alert); + } + + if (vm.hasOngoingAlerts()) { + favicoService.badge(vm.countOngoingAlerts()); + } else { + favicoService.reset(); + } + }); + + vm.hasAlerts = function () { + return _alerts.length > 0; + }; + + vm.getAlerts = function () { + return _alerts; + }; + + vm.count = function () { + return _alerts.length; + }; + + vm.hasOngoingAlerts = function () { + return _.filter(_alerts, {'ongoing': true}).length > 0; + }; + + vm.countOngoingAlerts = function () { + return _.filter(_alerts, {'ongoing': true}).length; + } +} diff --git a/glances/outputs/static/js/components/plugin-alert/view.html b/glances/outputs/static/js/components/plugin-alert/view.html new file mode 100644 index 00000000..4f62b69c --- /dev/null +++ b/glances/outputs/static/js/components/plugin-alert/view.html @@ -0,0 +1,15 @@ +
+ No warning or critical alert detected + Warning or critical alerts (lasts {{vm.count()}} entries) +
+
+
+
+
+ {{alert.begin | date : 'yyyy-MM-dd H:mm:ss'}} ({{ alert.ongoing ? 'ongoing' : alert.duration }}) - {{alert.level}} on {{alert.name}} + ({{alert.max}}) +
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-amps/component.js b/glances/outputs/static/js/components/plugin-amps/component.js new file mode 100644 index 00000000..ae0d1733 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-amps/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginAmps', { + controller: GlancesPluginAmpsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-amps/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-amps/controller.js b/glances/outputs/static/js/components/plugin-amps/controller.js new file mode 100644 index 00000000..cea16ba4 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-amps/controller.js @@ -0,0 +1,44 @@ +'use strict'; + +function GlancesPluginAmpsController($scope, GlancesStats, favicoService) { + var vm = this; + vm.processes = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processes = data.stats['amps']; + + this.processes = []; + angular.forEach(processes, function (process) { + if (process.result !== null) { + this.processes.push(process); + } + }, this); + } + + vm.getDescriptionDecoration = function (process) { + var count = process.count; + var countMin = process.countmin; + var countMax = process.countmax; + var decoration = "ok"; + + if (count > 0) { + if ((countMin == null || count >= countMin) && (countMax == null || count <= countMax)) { + decoration = 'ok'; + } else { + decoration = 'careful'; + } + } else { + decoration = countMin == null ? 'ok' : 'critical'; + } + + return decoration; + } +} diff --git a/glances/outputs/static/js/components/plugin-amps/view.html b/glances/outputs/static/js/components/plugin-amps/view.html new file mode 100644 index 00000000..d5ec8e0e --- /dev/null +++ b/glances/outputs/static/js/components/plugin-amps/view.html @@ -0,0 +1,9 @@ +
+
+
+
{{ process.name }}
+
{{ process.count }}
+
{{ process.result }}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-cloud/component.js b/glances/outputs/static/js/components/plugin-cloud/component.js new file mode 100644 index 00000000..ef1af9ee --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cloud/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginCloud', { + controller: GlancesPluginCloudController, + controllerAs: 'vm', + templateUrl: 'components/plugin-cloud/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-cloud/controller.js b/glances/outputs/static/js/components/plugin-cloud/controller.js new file mode 100644 index 00000000..4dbadd51 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cloud/controller.js @@ -0,0 +1,25 @@ +'use strict'; + +function GlancesPluginCloudController($scope, GlancesStats) { + var vm = this; + + vm.provider = null; + vm.instance = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['cloud']; + + if (stats['ami-id'] !== undefined) { + vm.provider = 'AWS EC2'; + vm.instance = stats['instance-type'] + ' instance ' + stats['instance-id'] + ' (' + stats['region'] + ')'; + } + } +} diff --git a/glances/outputs/static/js/components/plugin-cloud/view.html b/glances/outputs/static/js/components/plugin-cloud/view.html new file mode 100644 index 00000000..5a5c6441 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cloud/view.html @@ -0,0 +1,3 @@ +
+ {{ vm.provider }} {{ vm.instance }} +
diff --git a/glances/outputs/static/js/components/plugin-cpu/component.js b/glances/outputs/static/js/components/plugin-cpu/component.js new file mode 100644 index 00000000..4590f7ec --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cpu/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginCpu', { + controller: GlancesPluginCpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-cpu/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-cpu/controller.js b/glances/outputs/static/js/components/plugin-cpu/controller.js new file mode 100644 index 00000000..92cdfa65 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cpu/controller.js @@ -0,0 +1,65 @@ +'use strict'; + +function GlancesPluginCpuController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.total = null; + vm.user = null; + vm.system = null; + vm.idle = null; + vm.nice = null; + vm.irq = null; + vm.iowait = null; + vm.steal = null; + vm.ctx_switches = null; + vm.interrupts = null; + vm.soft_interrupts = null; + vm.syscalls = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['cpu']; + _view = data.views['cpu']; + + vm.total = stats.total; + vm.user = stats.user; + vm.system = stats.system; + vm.idle = stats.idle; + vm.nice = stats.nice; + vm.irq = stats.irq; + vm.iowait = stats.iowait; + vm.steal = stats.steal; + + if (stats.ctx_switches) { + vm.ctx_switches = Math.floor(stats.ctx_switches / stats.time_since_update); + } + + if (stats.interrupts) { + vm.interrupts = Math.floor(stats.interrupts / stats.time_since_update); + } + + if (stats.soft_interrupts) { + vm.soft_interrupts = Math.floor(stats.soft_interrupts / stats.time_since_update); + } + + if (stats.syscalls) { + vm.syscalls = Math.floor(stats.syscalls / stats.time_since_update); + } + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-cpu/view.html b/glances/outputs/static/js/components/plugin-cpu/view.html new file mode 100644 index 00000000..ac6fd4d8 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-cpu/view.html @@ -0,0 +1,84 @@ +
+
+
+
+
+
CPU
+
{{ vm.total }}%
+
+
+
user:
+
+ {{ vm.user }}% +
+
+
+
system:
+
+ {{ vm.system }}% +
+
+
+
idle:
+
{{ vm.idle }}%
+
+
+
+ + +
+
diff --git a/glances/outputs/static/js/components/plugin-diskio/component.js b/glances/outputs/static/js/components/plugin-diskio/component.js new file mode 100644 index 00000000..c6321545 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-diskio/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginDiskio', { + controller: GlancesPluginDiskioController, + controllerAs: 'vm', + templateUrl: 'components/plugin-diskio/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-diskio/controller.js b/glances/outputs/static/js/components/plugin-diskio/controller.js new file mode 100644 index 00000000..66536f12 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-diskio/controller.js @@ -0,0 +1,39 @@ +'use strict'; + +function GlancesPluginDiskioController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.disks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['diskio']; + stats = $filter('orderBy')(stats, 'disk_name'); + + vm.disks = []; + for (var i = 0; i < stats.length; i++) { + var diskioData = stats[i]; + var timeSinceUpdate = diskioData['time_since_update']; + + vm.disks.push({ + 'name': diskioData['disk_name'], + 'bitrate': { + 'txps': $filter('bytes')(diskioData['read_bytes'] / timeSinceUpdate), + 'rxps': $filter('bytes')(diskioData['write_bytes'] / timeSinceUpdate) + }, + 'count': { + 'txps': $filter('bytes')(diskioData['read_count'] / timeSinceUpdate), + 'rxps': $filter('bytes')(diskioData['write_count'] / timeSinceUpdate) + }, + 'alias': diskioData['alias'] !== undefined ? diskioData['alias'] : null + }); + } + } +} diff --git a/glances/outputs/static/js/components/plugin-diskio/view.html b/glances/outputs/static/js/components/plugin-diskio/view.html new file mode 100644 index 00000000..5559fac0 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-diskio/view.html @@ -0,0 +1,16 @@ +
+
DISK I/O
+
R/s
+
W/s
+ +
IOR/s
+
IOW/s
+
+
+
{{(disk.alias ? disk.alias : disk.name) | min_size}}
+
{{disk.bitrate.txps }}
+
{{disk.bitrate.rxps }}
+ +
{{disk.count.txps }}
+
{{disk.count.rxps }}
+
diff --git a/glances/outputs/static/js/components/plugin-docker/component.js b/glances/outputs/static/js/components/plugin-docker/component.js new file mode 100644 index 00000000..8ca79a72 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-docker/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginDocker', { + controller: GlancesPluginDockerController, + controllerAs: 'vm', + templateUrl: 'components/plugin-docker/view.html' +}); diff --git a/glances/outputs/static/js/services/plugins/docker.js b/glances/outputs/static/js/components/plugin-docker/controller.js similarity index 62% rename from glances/outputs/static/js/services/plugins/docker.js rename to glances/outputs/static/js/components/plugin-docker/controller.js index 06895e68..70b090de 100644 --- a/glances/outputs/static/js/services/plugins/docker.js +++ b/glances/outputs/static/js/components/plugin-docker/controller.js @@ -1,19 +1,28 @@ -glancesApp.service('GlancesPluginDocker', function(GlancesPlugin) { +'use strict'; - var _pluginName = "docker"; - this.containers = []; - this.version = null; +function GlancesPluginDockerController($scope, GlancesStats) { + var vm = this; + vm.containers = []; + vm.version = null; - this.setData = function(data, views) { - data = data[_pluginName]; + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['docker']; this.containers = []; - if(_.isEmpty(data)) { + if (_.isEmpty(stats)) { return; } - for (var i = 0; i < data['containers'].length; i++) { - var containerData = data['containers'][i]; + for (var i = 0; i < stats['containers'].length; i++) { + var containerData = stats['containers'][i]; var container = { 'id': containerData.Id, @@ -31,9 +40,9 @@ glancesApp.service('GlancesPluginDocker', function(GlancesPlugin) { 'image': containerData.Image }; - this.containers.push(container); + vm.containers.push(container); } - this.version = data['version']['Version']; - }; -}); + vm.version = stats['version']['Version']; + } +} diff --git a/glances/outputs/static/js/components/plugin-docker/view.html b/glances/outputs/static/js/components/plugin-docker/view.html new file mode 100644 index 00000000..63e9a217 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-docker/view.html @@ -0,0 +1,29 @@ +
+ CONTAINERS {{ vm.containers.length }} (served by Docker {{ vm.version }}) + +
+
+
Name
+
Status
+
CPU%
+
MEM
+
IOR/s
+
IOW/s
+
RX/s
+
TX/s
+
Command
+
+
+
{{ container.name }}
+
{{ container.status }} +
+
{{ container.cpu | number:1 }}
+
{{ container.memory | bytes }}
+
{{ container.ior / container.io_time_since_update | bits }}
+
{{ container.iow / container.io_time_since_update | bits }}
+
{{ container.rx / container.net_time_since_update | bits }}
+
{{ container.tx / container.net_time_since_update | bits }}
+
{{ container.command }}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-folders/component.js b/glances/outputs/static/js/components/plugin-folders/component.js new file mode 100644 index 00000000..c3f99837 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-folders/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginFolders', { + controller: GlancesPluginFsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-folders/view.html' +}); diff --git a/glances/outputs/static/js/services/plugins/folders.js b/glances/outputs/static/js/components/plugin-folders/controller.js similarity index 57% rename from glances/outputs/static/js/services/plugins/folders.js rename to glances/outputs/static/js/components/plugin-folders/controller.js index 6b3660c5..bedae76a 100644 --- a/glances/outputs/static/js/services/plugins/folders.js +++ b/glances/outputs/static/js/components/plugin-folders/controller.js @@ -1,13 +1,23 @@ -glancesApp.service('GlancesPluginFolders', function() { - var _pluginName = "folders"; - this.folders = []; +'use strict'; - this.setData = function(data, views) { - data = data[_pluginName]; - this.folders = []; +function GlancesPluginFoldersController($scope, GlancesStats) { + var vm = this; + vm.folders = []; - for (var i = 0; i < data.length; i++) { - var folderData = data[i]; + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['folders']; + vm.folders = []; + + for (var i = 0; i < stats.length; i++) { + var folderData = stats[i]; var folder = { 'path': folderData['path'], @@ -17,11 +27,11 @@ glancesApp.service('GlancesPluginFolders', function() { 'critical': folderData['critical'] }; - this.folders.push(folder); + vm.folders.push(folder); } - }; + } - this.getDecoration = function(folder) { + vm.getDecoration = function (folder) { if (!Number.isInteger(folder.size)) { return; @@ -37,4 +47,4 @@ glancesApp.service('GlancesPluginFolders', function() { return 'ok'; }; -}); +} diff --git a/glances/outputs/static/js/components/plugin-folders/view.html b/glances/outputs/static/js/components/plugin-folders/view.html new file mode 100644 index 00000000..4308ff58 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-folders/view.html @@ -0,0 +1,8 @@ +
+
FOLDERS
+
Size
+
+
+
{{ folder.path }}
+
{{ folder.size | bytes }}
+
diff --git a/glances/outputs/static/js/components/plugin-fs/component.js b/glances/outputs/static/js/components/plugin-fs/component.js new file mode 100644 index 00000000..424ffe1d --- /dev/null +++ b/glances/outputs/static/js/components/plugin-fs/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginFs', { + controller: GlancesPluginFsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-fs/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-fs/controller.js b/glances/outputs/static/js/components/plugin-fs/controller.js new file mode 100644 index 00000000..218b28ab --- /dev/null +++ b/glances/outputs/static/js/components/plugin-fs/controller.js @@ -0,0 +1,51 @@ +'use strict'; + +function GlancesPluginFsController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + var _view = {}; + vm.arguments = ARGUMENTS; + vm.fileSystems = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['fs']; + _view = data.views['fs']; + + vm.fileSystems = []; + for (var i = 0; i < stats.length; i++) { + var fsData = stats[i]; + + var shortMountPoint = fsData['mnt_point']; + if (shortMountPoint.length > 9) { + shortMountPoint = '_' + fsData['mnt_point'].slice(-8); + } + + vm.fileSystems.push(fs = { + 'name': fsData['device_name'], + 'mountPoint': fsData['mnt_point'], + 'shortMountPoint': shortMountPoint, + 'percent': fsData['percent'], + 'size': fsData['size'], + 'used': fsData['used'], + 'free': fsData['free'] + }); + } + + vm.fileSystems = $filter('orderBy')(vm.fileSystems, 'mnt_point'); + }; + + vm.getDecoration = function (mountPoint, field) { + if (_view[mountPoint][field] == undefined) { + return; + } + + return _view[mountPoint][field].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-fs/view.html b/glances/outputs/static/js/components/plugin-fs/view.html new file mode 100644 index 00000000..46ce1cfa --- /dev/null +++ b/glances/outputs/static/js/components/plugin-fs/view.html @@ -0,0 +1,18 @@ +
+
FILE SYS
+
+ Used + Free +
+
Total
+
+
+
{{ fs.shortMountPoint }} ({{ fs.name }}) +
+
+ {{ fs.used | bytes }} + {{ fs.free | bytes }} +
+
{{ fs.size | bytes }}
+
diff --git a/glances/outputs/static/js/components/plugin-gpu/component.js b/glances/outputs/static/js/components/plugin-gpu/component.js new file mode 100644 index 00000000..5eddcd64 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-gpu/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginGpu', { + controller: GlancesPluginGpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-gpu/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-gpu/controller.js b/glances/outputs/static/js/components/plugin-gpu/controller.js new file mode 100644 index 00000000..8857b958 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-gpu/controller.js @@ -0,0 +1,67 @@ +'use strict'; + +function GlancesPluginGpuController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + var _view = {}; + vm.gpus = []; + vm.name = "GPU"; + vm.mean = {}; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['gpu']; + _view = data.views['gpu']; + + if (stats.length === 0) { + return; + } + + vm.gpus = []; + vm.name = "GPU"; + vm.mean = { + proc: null, + mem: null + }; + var sameName = true; + + for (var i = 0; i < stats.length; i++) { + var gpuData = stats[i]; + + var gpu = gpuData; + + vm.mean.proc += gpu.proc; + vm.mean.mem += gpu.mem; + + vm.gpus.push(gpu); + } + + if (stats.length === 1) { + vm.name = stats[0].name; + } else if (sameName) { + vm.name = stats.length + ' GPU ' + stats[0].name; + } + + vm.mean.proc = vm.mean.proc / stats.length; + vm.mean.mem = vm.mean.mem / stats.length; + } + + vm.getDecoration = function (gpuId, value) { + if (_view[gpuId][value] == undefined) { + return; + } + + return _view[gpuId][value].decoration.toLowerCase(); + }; + + vm.getMeanDecoration = function (value) { + return vm.getDecoration(0, value); + }; +} diff --git a/glances/outputs/static/js/components/plugin-gpu/view.html b/glances/outputs/static/js/components/plugin-gpu/view.html new file mode 100644 index 00000000..bdbf1695 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-gpu/view.html @@ -0,0 +1,32 @@ +
+
+ {{ vm.name }} +
+
+
+
proc:
+
{{ vm.mean.proc | + number : 0 }}% +
+
N/A
+
+
+
mem:
+
{{ vm.mean.mem | number : + 0 }}% +
+
N/A
+
+
+
+ {{ gpu.gpu_id }}: + {{ gpu.proc | number : 0 }}% + N/A + mem: + {{ gpu.mem | number : 0 }}% + N/A +
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-ip/component.js b/glances/outputs/static/js/components/plugin-ip/component.js new file mode 100644 index 00000000..552c9461 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ip/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginIp', { + controller: GlancesPluginIpController, + controllerAs: 'vm', + templateUrl: 'components/plugin-ip/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-ip/controller.js b/glances/outputs/static/js/components/plugin-ip/controller.js new file mode 100644 index 00000000..31193178 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ip/controller.js @@ -0,0 +1,30 @@ +'use strict'; + +function GlancesPluginIpController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + + vm.address = null; + vm.gateway = null; + vm.mask = null; + vm.maskCidr = null; + vm.publicAddress = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var ipStats = data.stats['ip']; + + vm.address = ipStats.address; + vm.gateway = ipStats.gateway; + vm.mask = ipStats.mask; + vm.maskCidr = ipStats.mask_cidr; + vm.publicAddress = ipStats.public_address + } +} diff --git a/glances/outputs/static/js/components/plugin-ip/view.html b/glances/outputs/static/js/components/plugin-ip/view.html new file mode 100644 index 00000000..d96387c0 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ip/view.html @@ -0,0 +1,4 @@ +
+  - IP {{ vm.address }}/{{ vm.maskCidr }} Pub {{ vm.publicAddress }} +
diff --git a/glances/outputs/static/js/components/plugin-irq/component.js b/glances/outputs/static/js/components/plugin-irq/component.js new file mode 100644 index 00000000..52ed284c --- /dev/null +++ b/glances/outputs/static/js/components/plugin-irq/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginIrq', { + controller: GlancesPluginIrqController, + controllerAs: 'vm', + templateUrl: 'components/plugin-irq/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-irq/controller.js b/glances/outputs/static/js/components/plugin-irq/controller.js new file mode 100644 index 00000000..d6de65ef --- /dev/null +++ b/glances/outputs/static/js/components/plugin-irq/controller.js @@ -0,0 +1,30 @@ +'use strict'; + +function GlancesPluginIrqController($scope, GlancesStats) { + var vm = this; + vm.irqs = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['irq']; + vm.irqs = []; + + for (var i = 0; i < stats.length; i++) { + var IrqData = stats[i]; + + var irq = { + 'irq_line': IrqData['irq_line'], + 'irq_rate': IrqData['irq_rate'] + }; + + vm.irqs.push(irq); + } + } +} diff --git a/glances/outputs/static/html/plugins/irq.html b/glances/outputs/static/js/components/plugin-irq/view.html similarity index 74% rename from glances/outputs/static/html/plugins/irq.html rename to glances/outputs/static/js/components/plugin-irq/view.html index 8b2aa606..a9399aa9 100644 --- a/glances/outputs/static/html/plugins/irq.html +++ b/glances/outputs/static/js/components/plugin-irq/view.html @@ -1,9 +1,9 @@ -
+
IRQ
Rate/s
-
+
{{irq.irq_line}}
{{irq.irq_rate}}
diff --git a/glances/outputs/static/js/components/plugin-load/component.js b/glances/outputs/static/js/components/plugin-load/component.js new file mode 100644 index 00000000..ed1b4e51 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-load/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginLoad', { + controller: GlancesPluginLoadController, + controllerAs: 'vm', + templateUrl: 'components/plugin-load/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-load/controller.js b/glances/outputs/static/js/components/plugin-load/controller.js new file mode 100644 index 00000000..14233660 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-load/controller.js @@ -0,0 +1,37 @@ +'use strict'; + +function GlancesPluginLoadController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.cpucore = null; + vm.min1 = null; + vm.min5 = null; + vm.min15 = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['load']; + _view = data.views['load']; + + vm.cpucore = stats['cpucore']; + vm.min1 = stats['min1']; + vm.min5 = stats['min5']; + vm.min15 = stats['min15']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-load/view.html b/glances/outputs/static/js/components/plugin-load/view.html new file mode 100644 index 00000000..3aeff5b0 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-load/view.html @@ -0,0 +1,26 @@ +
+
+
+
LOAD
+
{{ vm.cpucore }}-core
+
+
+
1 min:
+
+ {{ vm.min1 | number : 2}} +
+
+
+
5 min:
+
+ {{ vm.min5 | number : 2}} +
+
+
+
15 min:
+
+ {{ vm.min15 | number : 2}} +
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-mem-more/component.js b/glances/outputs/static/js/components/plugin-mem-more/component.js new file mode 100644 index 00000000..6c5efeb5 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem-more/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginMemMore', { + controller: GlancesPluginMemMoreController, + controllerAs: 'vm', + templateUrl: 'components/plugin-mem-more/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-mem-more/controller.js b/glances/outputs/static/js/components/plugin-mem-more/controller.js new file mode 100644 index 00000000..482f4b70 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem-more/controller.js @@ -0,0 +1,27 @@ +'use strict'; + +function GlancesPluginMemMoreController($scope, GlancesStats) { + var vm = this; + + vm.active = null; + vm.inactive = null; + vm.buffers = null; + vm.cached = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['mem']; + + vm.active = stats['active']; + vm.inactive = stats['inactive']; + vm.buffers = stats['buffers']; + vm.cached = stats['cached']; + } +} diff --git a/glances/outputs/static/js/components/plugin-mem-more/view.html b/glances/outputs/static/js/components/plugin-mem-more/view.html new file mode 100644 index 00000000..4531a8e6 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem-more/view.html @@ -0,0 +1,20 @@ +
+
+
+
active:
+
{{ vm.active | bytes }}
+
+
+
inactive:
+
{{ vm.inactive | bytes }}
+
+
+
buffers:
+
{{ vm.buffers | bytes }}
+
+
+
cached:
+
{{ vm.cached | bytes }}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-mem/component.js b/glances/outputs/static/js/components/plugin-mem/component.js new file mode 100644 index 00000000..7e78e1f4 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginMem', { + controller: GlancesPluginMemController, + controllerAs: 'vm', + templateUrl: 'components/plugin-mem/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-mem/controller.js b/glances/outputs/static/js/components/plugin-mem/controller.js new file mode 100644 index 00000000..daaeb38e --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem/controller.js @@ -0,0 +1,37 @@ +'use strict'; + +function GlancesPluginMemController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.percent = null; + vm.total = null; + vm.used = null; + vm.free = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['mem']; + _view = data.views['mem']; + + vm.percent = stats['percent']; + vm.total = stats['total']; + vm.used = stats['used']; + vm.free = stats['free']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-mem/view.html b/glances/outputs/static/js/components/plugin-mem/view.html new file mode 100644 index 00000000..f7f88c4a --- /dev/null +++ b/glances/outputs/static/js/components/plugin-mem/view.html @@ -0,0 +1,22 @@ +
+
+
+
MEM
+
{{ vm.percent }}%
+
+
+
total:
+
{{ vm.total | bytes }}
+
+
+
used:
+
+ {{ vm.used | bytes:2 }} +
+
+
+
free:
+
{{ vm.free | bytes }}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-memswap/component.js b/glances/outputs/static/js/components/plugin-memswap/component.js new file mode 100644 index 00000000..c573e8fb --- /dev/null +++ b/glances/outputs/static/js/components/plugin-memswap/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginMemswap', { + controller: GlancesPluginMemswapController, + controllerAs: 'vm', + templateUrl: 'components/plugin-memswap/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-memswap/controller.js b/glances/outputs/static/js/components/plugin-memswap/controller.js new file mode 100644 index 00000000..c4458c19 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-memswap/controller.js @@ -0,0 +1,37 @@ +'use strict'; + +function GlancesPluginMemswapController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.percent = null; + vm.total = null; + vm.used = null; + vm.free = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['memswap']; + _view = data.views['memswap']; + + vm.percent = stats['percent']; + vm.total = stats['total']; + vm.used = stats['used']; + vm.free = stats['free']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-memswap/view.html b/glances/outputs/static/js/components/plugin-memswap/view.html new file mode 100644 index 00000000..590b2484 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-memswap/view.html @@ -0,0 +1,22 @@ +
+
+
+
SWAP
+
{{ vm.percent }}%
+
+
+
total:
+
{{ vm.total | bytes }}
+
+
+
used:
+
+ {{ vm.used | bytes }} +
+
+
+
free:
+
{{ vm.free | bytes }}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-network/component.js b/glances/outputs/static/js/components/plugin-network/component.js new file mode 100644 index 00000000..7eb87184 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-network/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginNetwork', { + controller: GlancesPluginNetworkController, + controllerAs: 'vm', + templateUrl: 'components/plugin-network/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-network/controller.js b/glances/outputs/static/js/components/plugin-network/controller.js new file mode 100644 index 00000000..f09f3b00 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-network/controller.js @@ -0,0 +1,39 @@ +'use strict'; + +function GlancesPluginNetworkController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.networks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var networkStats = data.stats['network']; + + vm.networks = []; + for (var i = 0; i < networkStats.length; i++) { + var networkData = networkStats[i]; + + var network = { + 'interfaceName': networkData['interface_name'], + 'rx': networkData['rx'], + 'tx': networkData['tx'], + 'cx': networkData['cx'], + 'time_since_update': networkData['time_since_update'], + 'cumulativeRx': networkData['cumulative_rx'], + 'cumulativeTx': networkData['cumulative_tx'], + 'cumulativeCx': networkData['cumulative_cx'] + }; + + vm.networks.push(network); + } + + vm.networks = $filter('orderBy')(vm.networks, 'interfaceName'); + } +} diff --git a/glances/outputs/static/js/components/plugin-network/view.html b/glances/outputs/static/js/components/plugin-network/view.html new file mode 100644 index 00000000..1ad5081a --- /dev/null +++ b/glances/outputs/static/js/components/plugin-network/view.html @@ -0,0 +1,40 @@ +
+
NETWORK
+
Rx/s
+
Tx/s
+ +
+
Rx+Tx/s
+ +
Rx
+
Tx
+ +
+
Rx+Tx
+
+
+
{{ network.interfaceName | min_size }}
+
{{ vm.arguments.byte ? + (network.rx / network.time_since_update | bytes) : (network.rx / network.time_since_update | bits) }} +
+
{{ vm.arguments.byte ? + (network.tx / network.time_since_update | bytes) : (network.tx / network.time_since_update | bits) }} +
+ +
+
{{ vm.arguments.byte ? + (network.cx / network.time_since_update | bytes) : (network.cx / network.time_since_update | bits) }} +
+ +
{{ vm.arguments.byte ? + (network.cumulativeRx | bytes) : (network.cumulativeRx | bits) }} +
+
{{ vm.arguments.byte ? + (network.cumulativeTx | bytes) : (network.cumulativeTx | bits) }} +
+ +
+
{{ vm.arguments.byte ? + (network.cumulativeCx | bytes) : (network.cumulativeCx | bits) }} +
+
diff --git a/glances/outputs/static/js/components/plugin-percpu/component.js b/glances/outputs/static/js/components/plugin-percpu/component.js new file mode 100644 index 00000000..c4a3773b --- /dev/null +++ b/glances/outputs/static/js/components/plugin-percpu/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginPercpu', { + controller: GlancesPluginPercpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-percpu/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-percpu/controller.js b/glances/outputs/static/js/components/plugin-percpu/controller.js new file mode 100644 index 00000000..0bb7c5fe --- /dev/null +++ b/glances/outputs/static/js/components/plugin-percpu/controller.js @@ -0,0 +1,42 @@ +'use strict'; + +function GlancesPluginPercpuController($scope, GlancesStats, GlancesPluginHelper) { + var vm = this; + vm.cpus = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var percpuStats = data.stats['percpu']; + + vm.cpus = []; + + for (var i = 0; i < percpuStats.length; i++) { + var cpuData = percpuStats[i]; + + vm.cpus.push({ + 'number': cpuData.cpu_number, + 'total': cpuData.total, + 'user': cpuData.user, + 'system': cpuData.system, + 'idle': cpuData.idle, + 'iowait': cpuData.iowait, + 'steal': cpuData.steal + }); + } + } + + vm.getUserAlert = function (cpu) { + return GlancesPluginHelper.getAlert('percpu', 'percpu_user_', cpu.user) + }; + + vm.getSystemAlert = function (cpu) { + return GlancesPluginHelper.getAlert('percpu', 'percpu_system_', cpu.system); + }; +} diff --git a/glances/outputs/static/js/components/plugin-percpu/view.html b/glances/outputs/static/js/components/plugin-percpu/view.html new file mode 100644 index 00000000..a79228b6 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-percpu/view.html @@ -0,0 +1,40 @@ +
+
+
+
PER CPU
+
{{ percpu.total }}%
+
+
+
user:
+
+ {{ percpu.user }}% +
+
+
+
system:
+
+ {{ percpu.system }}% +
+
+
+
idle:
+
{{ percpu.idle }}%
+
+
+
iowait:
+
+ {{ percpu.iowait }}% +
+
+
+
steal:
+
+ {{ percpu.steal }}% +
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-ports/component.js b/glances/outputs/static/js/components/plugin-ports/component.js new file mode 100644 index 00000000..f1f68fb4 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ports/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginPorts', { + controller: GlancesPluginPortsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-ports/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-ports/controller.js b/glances/outputs/static/js/components/plugin-ports/controller.js new file mode 100644 index 00000000..e75adfe2 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ports/controller.js @@ -0,0 +1,39 @@ +'use strict'; + +function GlancesPluginPortsController($scope, GlancesStats) { + var vm = this; + vm.ports = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['ports']; + + vm.ports = []; + angular.forEach(stats, function (port) { + vm.ports.push(port); + }, this); + } + + vm.getDecoration = function (port) { + if (port.status === null) { + return 'careful'; + } + + if (port.status === false) { + return 'critical'; + } + + if (port.rtt_warning !== null && port.status > port.rtt_warning) { + return 'warning'; + } + + return 'ok'; + }; +} diff --git a/glances/outputs/static/js/components/plugin-ports/view.html b/glances/outputs/static/js/components/plugin-ports/view.html new file mode 100644 index 00000000..560886a2 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-ports/view.html @@ -0,0 +1,12 @@ +
+
{{(port.description ? port.description : port.host + ' ' + port.port) | min_size: + 20}} +
+
+
+ Scanning + Timeout + Open + {{port.status * 1000.0 | number:0}}ms +
+
\ No newline at end of file diff --git a/glances/outputs/static/js/components/plugin-process/component.js b/glances/outputs/static/js/components/plugin-process/component.js new file mode 100644 index 00000000..90606d70 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-process/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginProcess', { + controller: GlancesPluginProcessController, + controllerAs: 'vm', + templateUrl: 'components/plugin-process/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-process/controller.js b/glances/outputs/static/js/components/plugin-process/controller.js new file mode 100644 index 00000000..fc3353c2 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-process/controller.js @@ -0,0 +1,84 @@ +'use strict'; + +function GlancesPluginProcessController(ARGUMENTS, hotkeys) { + var vm = this; + vm.arguments = ARGUMENTS; + + vm.sorter = { + column: "cpu_percent", + auto: true, + isReverseColumn: function (column) { + return !(column === 'username' || column === 'name'); + }, + getColumnLabel: function (column) { + if (_.isEqual(column, ['io_read', 'io_write'])) { + return 'io_counters'; + } else { + return column; + } + } + }; + + // a => Sort processes automatically + hotkeys.add({ + combo: 'a', + callback: function () { + vm.sorter.column = "cpu_percent"; + vm.sorter.auto = true; + } + }); + + // c => Sort processes by CPU% + hotkeys.add({ + combo: 'c', + callback: function () { + vm.sorter.column = "cpu_percent"; + vm.sorter.auto = false; + } + }); + + // m => Sort processes by MEM% + hotkeys.add({ + combo: 'm', + callback: function () { + vm.sorter.column = "memory_percent"; + vm.sorter.auto = false; + } + }); + + // u => Sort processes by user + hotkeys.add({ + combo: 'u', + callback: function () { + vm.sorter.column = "username"; + vm.sorter.auto = false; + } + }); + + // p => Sort processes by name + hotkeys.add({ + combo: 'p', + callback: function () { + vm.sorter.column = "name"; + vm.sorter.auto = false; + } + }); + + // i => Sort processes by I/O rate + hotkeys.add({ + combo: 'i', + callback: function () { + vm.sorter.column = ['io_read', 'io_write']; + vm.sorter.auto = false; + } + }); + + // t => Sort processes by time + hotkeys.add({ + combo: 't', + callback: function () { + vm.sorter.column = "timemillis"; + vm.sorter.auto = false; + } + }); +} diff --git a/glances/outputs/static/js/components/plugin-process/view.html b/glances/outputs/static/js/components/plugin-process/view.html new file mode 100644 index 00000000..75bb90d6 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-process/view.html @@ -0,0 +1,10 @@ +
+ +
+
+ +
+
+ +
+
PROCESSES DISABLED (press 'z' to display)
diff --git a/glances/outputs/static/js/components/plugin-processcount/component.js b/glances/outputs/static/js/components/plugin-processcount/component.js new file mode 100644 index 00000000..5418d6b8 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processcount/component.js @@ -0,0 +1,10 @@ +'use strict'; + +glancesApp.component('glancesPluginProcesscount', { + controller: GlancesPluginProcesscountController, + controllerAs: 'vm', + bindings: { + sorter: '<' + }, + templateUrl: 'components/plugin-processcount/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-processcount/controller.js b/glances/outputs/static/js/components/plugin-processcount/controller.js new file mode 100644 index 00000000..f2e739da --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processcount/controller.js @@ -0,0 +1,29 @@ +'use strict'; + +function GlancesPluginProcesscountController($scope, GlancesStats) { + var vm = this; + + vm.total = null; + vm.running = null; + vm.sleeping = null; + vm.stopped = null; + vm.thread = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processcountStats = data.stats['processcount']; + + vm.total = processcountStats['total'] || 0; + vm.running = processcountStats['running'] || 0; + vm.sleeping = processcountStats['sleeping'] || 0; + vm.stopped = processcountStats['stopped'] || 0; + vm.thread = processcountStats['thread'] || 0; + } +} diff --git a/glances/outputs/static/js/components/plugin-processcount/view.html b/glances/outputs/static/js/components/plugin-processcount/view.html new file mode 100644 index 00000000..bdbc71cb --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processcount/view.html @@ -0,0 +1,8 @@ +
+ TASKS + {{ vm.total }} ({{ vm.thread }} thr), + {{ vm.running }} run, + {{ vm.sleeping }} slp, + {{ vm.stopped }} oth + sorted {{ vm.sorter.auto ? 'automatically' : '' }} by {{ vm.sorter.getColumnLabel(vm.sorter.column) }}, flat view +
\ No newline at end of file diff --git a/glances/outputs/static/js/components/plugin-processlist/component.js b/glances/outputs/static/js/components/plugin-processlist/component.js new file mode 100644 index 00000000..e633659f --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processlist/component.js @@ -0,0 +1,10 @@ +'use strict'; + +glancesApp.component('glancesPluginProcesslist', { + controller: GlancesPluginProcesslistController, + controllerAs: 'vm', + bindings: { + sorter: '<' + }, + templateUrl: 'components/plugin-processlist/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-processlist/controller.js b/glances/outputs/static/js/components/plugin-processlist/controller.js new file mode 100644 index 00000000..17442d6f --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processlist/controller.js @@ -0,0 +1,75 @@ +'use strict'; + +function GlancesPluginProcesslistController($scope, GlancesStats, GlancesPluginHelper, $filter, CONFIG, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.processes = []; + vm.ioReadWritePresent = false; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processlistStats = data.stats['processlist'] || []; + + vm.processes = []; + vm.ioReadWritePresent = false; + + for (var i = 0; i < processlistStats.length; i++) { + var process = processlistStats[i]; + + process.memvirt = process.memory_info[1]; + process.memres = process.memory_info[0]; + process.timeplus = $filter('timedelta')(process.cpu_times); + process.timemillis = $filter('timemillis')(process.cpu_times); + + process.ioRead = null; + process.ioWrite = null; + + if (process.io_counters) { + vm.ioReadWritePresent = true; + + process.ioRead = (process.io_counters[0] - process.io_counters[2]) / process.time_since_update; + + if (process.ioRead != 0) { + process.ioRead = $filter('bytes')(process.ioRead); + } + + process.ioWrite = (process.io_counters[1] - process.io_counters[3]) / process.time_since_update; + + if (process.ioWrite != 0) { + process.ioWrite = $filter('bytes')(process.ioWrite); + } + } + + process.isNice = process.nice !== undefined && ((data.stats.isWindows && process.nice != 32) || (!data.stats.isWindows && process.nice != 0)); + + if (Array.isArray(process.cmdline)) { + process.cmdline = process.cmdline.join(' '); + } + + if (data.isWindows) { + process.username = _.last(process.username.split('\\')); + } + + vm.processes.push(process); + } + } + + vm.getCpuPercentAlert = function (process) { + return GlancesPluginHelper.getAlert('processlist', 'processlist_cpu_', process.cpu_percent); + }; + + vm.getMemoryPercentAlert = function (process) { + return GlancesPluginHelper.getAlert('processlist', 'processlist_mem_', process.cpu_percent); + }; + + vm.getLimit = function () { + return CONFIG.outputs !== undefined ? CONFIG.outputs.max_processes_display : undefined; + }; +} diff --git a/glances/outputs/static/js/components/plugin-processlist/view.html b/glances/outputs/static/js/components/plugin-processlist/view.html new file mode 100644 index 00000000..fc1f7a63 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-processlist/view.html @@ -0,0 +1,43 @@ +
+
+
+
CPU%
+
MEM%
+ + +
PID
+
USER
+
NI
+
S
+ + + +
Command
+
+
+
{{process.cpu_percent | number:1}}
+
{{process.memory_percent | number:1}} +
+ + +
{{process.pid}}
+
{{process.username}}
+
{{process.nice | exclamation}}
+
{{process.status}}
+ + + +
{{process.name}}
+
{{process.cmdline}}
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-quicklook/component.js b/glances/outputs/static/js/components/plugin-quicklook/component.js new file mode 100644 index 00000000..7962a959 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-quicklook/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginQuicklook', { + controller: GlancesPluginQuicklookController, + controllerAs: 'vm', + templateUrl: 'components/plugin-quicklook/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-quicklook/controller.js b/glances/outputs/static/js/components/plugin-quicklook/controller.js new file mode 100644 index 00000000..612690b6 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-quicklook/controller.js @@ -0,0 +1,51 @@ +'use strict'; + +function GlancesPluginQuicklookController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + var _view = {}; + + vm.mem = null; + vm.cpu = null; + vm.cpu_name = null; + vm.cpu_hz_current = null; + vm.cpu_hz = null; + vm.swap = null; + vm.percpus = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['quicklook']; + _view = data.views['quicklook']; + + vm.mem = stats.mem; + vm.cpu = stats.cpu; + vm.cpu_name = stats.cpu_name; + vm.cpu_hz_current = stats.cpu_hz_current; + vm.cpu_hz = stats.cpu_hz; + vm.swap = stats.swap; + vm.percpus = []; + + angular.forEach(stats.percpu, function (cpu) { + vm.percpus.push({ + 'number': cpu.cpu_number, + 'total': cpu.total + }); + }, this); + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-quicklook/view.html b/glances/outputs/static/js/components/plugin-quicklook/view.html new file mode 100644 index 00000000..daf9cfbe --- /dev/null +++ b/glances/outputs/static/js/components/plugin-quicklook/view.html @@ -0,0 +1,67 @@ +
+
+ {{ vm.cpu_name }} +
+
+
+
CPU
+
+
+
+   +
+
+
+
+ {{ vm.cpu }}% +
+
+
+
CPU{{ percpu.number }}
+
+
+
+   +
+
+
+
+ {{ percpu.total }}% +
+
+
+
MEM
+
+
+
+   +
+
+
+
+ {{ vm.mem }}% +
+
+
+
SWAP
+
+
+
+   +
+
+
+
+ {{ vm.swap }}% +
+
+
+
diff --git a/glances/outputs/static/js/components/plugin-raid/component.js b/glances/outputs/static/js/components/plugin-raid/component.js new file mode 100644 index 00000000..74d5f62b --- /dev/null +++ b/glances/outputs/static/js/components/plugin-raid/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginRaid', { + controller: GlancesPluginRaidController, + controllerAs: 'vm', + templateUrl: 'components/plugin-raid/view.html' +}); diff --git a/glances/outputs/static/js/services/plugins/raid.js b/glances/outputs/static/js/components/plugin-raid/controller.js similarity index 62% rename from glances/outputs/static/js/services/plugins/raid.js rename to glances/outputs/static/js/components/plugin-raid/controller.js index 900b2a5d..8a63320b 100644 --- a/glances/outputs/static/js/services/plugins/raid.js +++ b/glances/outputs/static/js/components/plugin-raid/controller.js @@ -1,12 +1,22 @@ -glancesApp.service('GlancesPluginRaid', function () { - var _pluginName = "raid"; - this.disks = []; +'use strict'; - this.setData = function (data, views) { +function GlancesPluginRaidController($scope, GlancesStats) { + var vm = this; + vm.disks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { var disks = []; - data = data[_pluginName]; + var stats = data.stats['raid']; - _.forIn(data, function(diskData, diskKey) { + _.forIn(stats, function (diskData, diskKey) { var disk = { 'name': diskKey, 'type': diskData.type == null ? 'UNKNOWN' : diskData.type, @@ -19,7 +29,7 @@ glancesApp.service('GlancesPluginRaid', function () { 'components': [] }; - _.forEach(diskData.components, function(number, name) { + _.forEach(diskData.components, function (number, name) { disk.components.push({ 'number': number, 'name': name @@ -29,14 +39,14 @@ glancesApp.service('GlancesPluginRaid', function () { disks.push(disk); }); - this.disks = disks; - }; + vm.disks = disks; + } - this.hasDisks = function() { + vm.hasDisks = function () { return this.disks.length > 0; } - this.getAlert = function(disk) { + vm.getAlert = function (disk) { if (disk.inactive) { return 'critical'; } @@ -47,4 +57,4 @@ glancesApp.service('GlancesPluginRaid', function () { return 'ok' } -}); +} diff --git a/glances/outputs/static/html/plugins/raid.html b/glances/outputs/static/js/components/plugin-raid/view.html similarity index 68% rename from glances/outputs/static/html/plugins/raid.html rename to glances/outputs/static/js/components/plugin-raid/view.html index a6e0ab30..bc6038ee 100644 --- a/glances/outputs/static/html/plugins/raid.html +++ b/glances/outputs/static/js/components/plugin-raid/view.html @@ -1,9 +1,9 @@ -
+
RAID disks
Used
Total
-
+
{{ disk.type | uppercase }} {{ disk.name }}
└─ Degraded mode
@@ -14,6 +14,6 @@    {{ $last ? '└─' : '├─' }} disk {{ component.number }}: {{ component.name }}
-
{{ disk.used }}
-
{{ disk.available }}
-
\ No newline at end of file +
{{ disk.used }}
+
{{ disk.available }}
+
diff --git a/glances/outputs/static/js/components/plugin-sensors/component.js b/glances/outputs/static/js/components/plugin-sensors/component.js new file mode 100644 index 00000000..b94ae90d --- /dev/null +++ b/glances/outputs/static/js/components/plugin-sensors/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginSensors', { + controller: GlancesPluginSensorsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-sensors/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-sensors/controller.js b/glances/outputs/static/js/components/plugin-sensors/controller.js new file mode 100644 index 00000000..46c82d50 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-sensors/controller.js @@ -0,0 +1,30 @@ +'use strict'; + +function GlancesPluginSensorsController($scope, GlancesStats, GlancesPluginHelper) { + var vm = this; + vm.sensors = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['sensors']; + + _.remove(stats, function (sensor) { + return (_.isArray(sensor.value) && _.isEmpty(sensor.value)) || sensor.value === 0; + }); + + vm.sensors = data; + } + + vm.getAlert = function (sensor) { + var current = sensor.type == 'battery' ? 100 - sensor.value : sensor.value; + + return GlancesPluginHelper.getAlert('sensors', 'sensors_' + sensor.type + '_', current); + }; +} diff --git a/glances/outputs/static/js/components/plugin-sensors/view.html b/glances/outputs/static/js/components/plugin-sensors/view.html new file mode 100644 index 00000000..8fa4c52a --- /dev/null +++ b/glances/outputs/static/js/components/plugin-sensors/view.html @@ -0,0 +1,9 @@ +
+
SENSORS
+
+ +
+
{{ sensor.label }}
+
{{ sensor.unit }}
+
{{ sensor.value }}
+
diff --git a/glances/outputs/static/js/components/plugin-system/component.js b/glances/outputs/static/js/components/plugin-system/component.js new file mode 100644 index 00000000..75209120 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-system/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginSystem', { + controller: GlancesPluginSystemController, + controllerAs: 'vm', + templateUrl: 'components/plugin-system/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-system/controller.js b/glances/outputs/static/js/components/plugin-system/controller.js new file mode 100644 index 00000000..03490765 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-system/controller.js @@ -0,0 +1,36 @@ +'use strict'; + +function GlancesPluginSystemController($scope, GlancesStats) { + var vm = this; + + vm.hostname = null; + vm.platform = null; + vm.humanReadableName = null; + vm.os = { + 'name': null, + 'version': null + }; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + $scope.$on('is_disconnected', function () { + vm.isDisconnected = true; + }); + + var loadData = function (data) { + var stats = data.stats['system']; + + vm.hostname = stats['hostname']; + vm.platform = stats['platform']; + vm.os.name = stats['os_name']; + vm.os.version = stats['os_version']; + vm.humanReadableName = stats['hr_name']; + vm.isDisconnected = false; + } +} diff --git a/glances/outputs/static/js/components/plugin-system/view.html b/glances/outputs/static/js/components/plugin-system/view.html new file mode 100644 index 00000000..2e1e0824 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-system/view.html @@ -0,0 +1,7 @@ +
+ Disconnected from + {{ vm.hostname }} + + +
diff --git a/glances/outputs/static/js/components/plugin-uptime/component.js b/glances/outputs/static/js/components/plugin-uptime/component.js new file mode 100644 index 00000000..6d8ecd2b --- /dev/null +++ b/glances/outputs/static/js/components/plugin-uptime/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginUptime', { + controller: GlancesPluginUptimeController, + controllerAs: 'vm', + templateUrl: 'components/plugin-uptime/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-uptime/controller.js b/glances/outputs/static/js/components/plugin-uptime/controller.js new file mode 100644 index 00000000..b42be7be --- /dev/null +++ b/glances/outputs/static/js/components/plugin-uptime/controller.js @@ -0,0 +1,18 @@ +'use strict'; + +function GlancesPluginUptimeController($scope, GlancesStats) { + var vm = this; + vm.value = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + vm.value = data.stats['uptime']; + } +} diff --git a/glances/outputs/static/js/components/plugin-uptime/view.html b/glances/outputs/static/js/components/plugin-uptime/view.html new file mode 100644 index 00000000..2c8b4015 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-uptime/view.html @@ -0,0 +1,3 @@ +
+ Uptime: {{ vm.value }} +
diff --git a/glances/outputs/static/js/components/plugin-wifi/component.js b/glances/outputs/static/js/components/plugin-wifi/component.js new file mode 100644 index 00000000..4d89f1cd --- /dev/null +++ b/glances/outputs/static/js/components/plugin-wifi/component.js @@ -0,0 +1,7 @@ +'use strict'; + +glancesApp.component('glancesPluginWifi', { + controller: GlancesPluginWifiController, + controllerAs: 'vm', + templateUrl: 'components/plugin-wifi/view.html' +}); diff --git a/glances/outputs/static/js/components/plugin-wifi/controller.js b/glances/outputs/static/js/components/plugin-wifi/controller.js new file mode 100644 index 00000000..3da8ab66 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-wifi/controller.js @@ -0,0 +1,47 @@ +'use strict'; + +function GlancesPluginWifiController($scope, $filter, GlancesStats) { + var vm = this; + var _view = {}; + + vm.hotspots = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['wifi']; + _view = data.views['wifi']; + + vm.hotspots = []; + for (var i = 0; i < stats.length; i++) { + var hotspotData = stats[i]; + + if (hotspotData['ssid'] === '') { + continue; + } + + vm.hotspots.push({ + 'ssid': hotspotData['ssid'], + 'encrypted': hotspotData['encrypted'], + 'signal': hotspotData['signal'], + 'encryption_type': hotspotData['encryption_type'] + }); + } + + vm.hotspots = $filter('orderBy')(vm.hotspots, 'ssid'); + } + + vm.getDecoration = function (hotpost, field) { + if (_view[hotpost.ssid][field] == undefined) { + return; + } + + return _view[hotpost.ssid][field].decoration.toLowerCase(); + }; +} diff --git a/glances/outputs/static/js/components/plugin-wifi/view.html b/glances/outputs/static/js/components/plugin-wifi/view.html new file mode 100644 index 00000000..86432cb2 --- /dev/null +++ b/glances/outputs/static/js/components/plugin-wifi/view.html @@ -0,0 +1,13 @@ +
+
+
WIFI
+
+
dBm
+
+
+
{{ hotspot.ssid|limitTo:20 }} {{ hotspot.encryption_type }} +
+
+
{{ hotspot.signal }}
+
+
diff --git a/glances/outputs/static/js/controllers.js b/glances/outputs/static/js/controllers.js deleted file mode 100644 index a369c3ba..00000000 --- a/glances/outputs/static/js/controllers.js +++ /dev/null @@ -1,214 +0,0 @@ -glancesApp.controller('statsController', function ($scope, $rootScope, $interval, GlancesStats, help, config, arguments, favicoService) { - $scope.help = help; - $scope.arguments = arguments; - - $scope.sorter = { - column: "cpu_percent", - auto: true, - isReverseColumn: function (column) { - return !(column == 'username' || column == 'name'); - }, - getColumnLabel: function (column) { - if (_.isEqual(column, ['io_read', 'io_write'])) { - return 'io_counters'; - } else { - return column; - } - } - }; - - $scope.dataLoaded = false; - $scope.refreshData = function () { - GlancesStats.getData().then(function (data) { - - $scope.statsAlert = GlancesStats.getPlugin('alert'); - $scope.statsCloud = GlancesStats.getPlugin('cloud'); - $scope.statsCpu = GlancesStats.getPlugin('cpu'); - $scope.statsDiskio = GlancesStats.getPlugin('diskio'); - $scope.statsIrq = GlancesStats.getPlugin('irq'); - $scope.statsDocker = GlancesStats.getPlugin('docker'); - $scope.statsFs = GlancesStats.getPlugin('fs'); - $scope.statsFolders = GlancesStats.getPlugin('folders'); - $scope.statsGpu = GlancesStats.getPlugin('gpu'); - $scope.statsIp = GlancesStats.getPlugin('ip'); - $scope.statsLoad = GlancesStats.getPlugin('load'); - $scope.statsMem = GlancesStats.getPlugin('mem'); - $scope.statsMemSwap = GlancesStats.getPlugin('memswap'); - $scope.statsAmps = GlancesStats.getPlugin('amps'); - $scope.statsNetwork = GlancesStats.getPlugin('network'); - $scope.statsPerCpu = GlancesStats.getPlugin('percpu'); - $scope.statsProcessCount = GlancesStats.getPlugin('processcount'); - $scope.statsProcessList = GlancesStats.getPlugin('processlist'); - $scope.statsQuicklook = GlancesStats.getPlugin('quicklook'); - $scope.statsRaid = GlancesStats.getPlugin('raid'); - $scope.statsSensors = GlancesStats.getPlugin('sensors'); - $scope.statsSystem = GlancesStats.getPlugin('system'); - $scope.statsUptime = GlancesStats.getPlugin('uptime'); - $scope.statsPorts = GlancesStats.getPlugin('ports'); - $scope.statsWifi = GlancesStats.getPlugin('wifi'); - - $rootScope.title = $scope.statsSystem.hostname + ' - Glances'; - - if ($scope.statsAlert.hasOngoingAlerts()) { - favicoService.badge($scope.statsAlert.countOngoingAlerts()); - } else { - favicoService.reset(); - } - - $scope.is_disconnected = false; - $scope.dataLoaded = true; - }, function() { - $scope.is_disconnected = true; - }); - }; - - $scope.refreshData(); - $interval(function () { - $scope.refreshData(); - }, arguments.time * 1000); // in milliseconds - - $scope.onKeyDown = function ($event) { - - switch (true) { - case !$event.shiftKey && $event.keyCode == keycodes.a: - // a => Sort processes automatically - $scope.sorter.column = "cpu_percent"; - $scope.sorter.auto = true; - break; - case $event.shiftKey && $event.keyCode == keycodes.A: - // A => Enable/disable AMPs - $scope.arguments.disable_amps = !$scope.arguments.disable_amps; - break; - case !$event.shiftKey && $event.keyCode == keycodes.c: - // c => Sort processes by CPU% - $scope.sorter.column = "cpu_percent"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.m: - // m => Sort processes by MEM% - $scope.sorter.column = "memory_percent"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.u: - // u => Sort processes by user - $scope.sorter.column = "username"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.p: - // p => Sort processes by name - $scope.sorter.column = "name"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.i: - // i => Sort processes by I/O rate - $scope.sorter.column = ['io_read', 'io_write']; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.t: - // t => Sort processes by time - $scope.sorter.column = "timemillis"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.d: - // d => Show/hide disk I/O stats - $scope.arguments.disable_diskio = !$scope.arguments.disable_diskio; - break; - case $event.shiftKey && $event.keyCode == keycodes.Q: - // Q => Show/hide IRQ - $scope.arguments.enable_irq = !$scope.arguments.enable_irq; - break; - case !$event.shiftKey && $event.keyCode == keycodes.f: - // f => Show/hide filesystem stats - $scope.arguments.disable_fs = !$scope.arguments.disable_fs; - break; - case !$event.shiftKey && $event.keyCode == keycodes.n: - // n => Show/hide network stats - $scope.arguments.disable_network = !$scope.arguments.disable_network; - break; - case !$event.shiftKey && $event.keyCode == keycodes.s: - // s => Show/hide sensors stats - $scope.arguments.disable_sensors = !$scope.arguments.disable_sensors; - break; - case $event.shiftKey && $event.keyCode == keycodes.TWO: - // 2 => Show/hide left sidebar - $scope.arguments.disable_left_sidebar = !$scope.arguments.disable_left_sidebar; - break; - case !$event.shiftKey && $event.keyCode == keycodes.z: - // z => Enable/disable processes stats - $scope.arguments.disable_process = !$scope.arguments.disable_process; - break; - case $event.keyCode == keycodes.SLASH: - // SLASH => Enable/disable short processes name - $scope.arguments.process_short_name = !$scope.arguments.process_short_name; - break; - case $event.shiftKey && $event.keyCode == keycodes.D: - // D => Enable/disable Docker stats - $scope.arguments.disable_docker = !$scope.arguments.disable_docker; - break; - case !$event.shiftKey && $event.keyCode == keycodes.b: - // b => Bytes or bits for network I/O - $scope.arguments.byte = !$scope.arguments.byte; - break; - case $event.shiftKey && $event.keyCode == keycodes.b: - // 'B' => Switch between bit/s and IO/s for Disk IO - $scope.arguments.diskio_iops = !$scope.arguments.diskio_iops; - break; - case !$event.shiftKey && $event.keyCode == keycodes.l: - // l => Show/hide alert logs - $scope.arguments.disable_alert = !$scope.arguments.disable_alert; - break; - case $event.shiftKey && $event.keyCode == keycodes.ONE: - // 1 => Global CPU or per-CPU stats - $scope.arguments.percpu = !$scope.arguments.percpu; - break; - case !$event.shiftKey && $event.keyCode == keycodes.h: - // h => Show/hide this help screen - $scope.arguments.help_tag = !$scope.arguments.help_tag; - break; - case $event.shiftKey && $event.keyCode == keycodes.T: - // T => View network I/O as combination - $scope.arguments.network_sum = !$scope.arguments.network_sum; - break; - case $event.shiftKey && $event.keyCode == keycodes.u: - // U => View cumulative network I/O - $scope.arguments.network_cumul = !$scope.arguments.network_cumul; - break; - case $event.shiftKey && $event.keyCode == keycodes.f: - // F => Show filesystem free space - $scope.arguments.fs_free_space = !$scope.arguments.fs_free_space; - break; - case $event.shiftKey && $event.keyCode == keycodes.THREE: - // 3 => Enable/disable quick look plugin - $scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook; - break; - case $event.shiftKey && $event.keyCode == keycodes.SIX: - // 6 => Enable/disable mean gpu - $scope.arguments.meangpu = !$scope.arguments.meangpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.g: - // G => Enable/disable gpu - $scope.arguments.disable_gpu = !$scope.arguments.disable_gpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.FIVE: - $scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook; - $scope.arguments.disable_cpu = !$scope.arguments.disable_cpu; - $scope.arguments.disable_mem = !$scope.arguments.disable_mem; - $scope.arguments.disable_memswap = !$scope.arguments.disable_memswap; - $scope.arguments.disable_load = !$scope.arguments.disable_load; - $scope.arguments.disable_gpu = !$scope.arguments.disable_gpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.i: - // I => Show/hide IP module - $scope.arguments.disable_ip = !$scope.arguments.disable_ip; - break; - case $event.shiftKey && $event.keyCode == keycodes.p: - // I => Enable/disable ports module - $scope.arguments.disable_ports = !$scope.arguments.disable_ports; - break; - case $event.shiftKey && $event.keyCode == keycodes.w: - // 'W' > Enable/Disable Wifi plugin - $scope.arguments.disable_wifi = !$scope.arguments.disable_wifi; - break; - } - }; -}); diff --git a/glances/outputs/static/js/directives.js b/glances/outputs/static/js/directives.js index 8bdc893e..a50cb54d 100644 --- a/glances/outputs/static/js/directives.js +++ b/glances/outputs/static/js/directives.js @@ -1,4 +1,4 @@ -glancesApp.directive("sortableTh", function() { +glancesApp.directive("sortableTh", function () { return { restrict: 'A', scope: { @@ -8,9 +8,9 @@ glancesApp.directive("sortableTh", function() { element.addClass('sortable'); - scope.$watch(function() { + scope.$watch(function () { return scope.sorter.column; - }, function(newValue, oldValue) { + }, function (newValue, oldValue) { if (angular.isArray(newValue)) { if (newValue.indexOf(attrs.column) !== -1) { @@ -28,7 +28,7 @@ glancesApp.directive("sortableTh", function() { }); - element.on('click', function() { + element.on('click', function () { scope.sorter.column = attrs.column; diff --git a/glances/outputs/static/js/filters.js b/glances/outputs/static/js/filters.js index 7457b026..3041c15c 100644 --- a/glances/outputs/static/js/filters.js +++ b/glances/outputs/static/js/filters.js @@ -1,5 +1,5 @@ -glancesApp.filter('min_size', function() { - return function(input, max) { +glancesApp.filter('min_size', function () { + return function (input, max) { var max = max || 8; if (input.length > max) { return "_" + input.substring(input.length - max) @@ -7,8 +7,8 @@ glancesApp.filter('min_size', function() { return input }; }); -glancesApp.filter('exclamation', function() { - return function(input) { +glancesApp.filter('exclamation', function () { + return function (input) { if (input === undefined || input === '') { return '?'; } @@ -16,95 +16,95 @@ glancesApp.filter('exclamation', function() { }; }); -glancesApp.filter('bytes', function() { +glancesApp.filter('bytes', function () { return function (bytes, low_precision) { low_precision = low_precision || false; - if (isNaN(parseFloat(bytes)) || !isFinite(bytes) || bytes == 0){ + if (isNaN(parseFloat(bytes)) || !isFinite(bytes) || bytes == 0) { return bytes; } var symbols = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; var prefix = { - 'Y': 1208925819614629174706176, - 'Z': 1180591620717411303424, - 'E': 1152921504606846976, - 'P': 1125899906842624, - 'T': 1099511627776, - 'G': 1073741824, - 'M': 1048576, - 'K': 1024 + 'Y': 1208925819614629174706176, + 'Z': 1180591620717411303424, + 'E': 1152921504606846976, + 'P': 1125899906842624, + 'T': 1099511627776, + 'G': 1073741824, + 'M': 1048576, + 'K': 1024 }; var reverseSymbols = _(symbols).reverse().value(); for (var i = 0; i < reverseSymbols.length; i++) { - var symbol = reverseSymbols[i]; - var value = bytes / prefix[symbol]; + var symbol = reverseSymbols[i]; + var value = bytes / prefix[symbol]; - if(value > 1) { - var decimal_precision = 0; + if (value > 1) { + var decimal_precision = 0; - if(value < 10) { - decimal_precision = 2; - } - else if(value < 100) { - decimal_precision = 1; - } + if (value < 10) { + decimal_precision = 2; + } + else if (value < 100) { + decimal_precision = 1; + } - if(low_precision) { - if(symbol == 'MK') { - decimal_precision = 0; - } - else { - decimal_precision = _.min([1, decimal_precision]); - } - } - else if(symbol == 'K') { - decimal_precision = 0; - } + if (low_precision) { + if (symbol == 'MK') { + decimal_precision = 0; + } + else { + decimal_precision = _.min([1, decimal_precision]); + } + } + else if (symbol == 'K') { + decimal_precision = 0; + } - return parseFloat(value).toFixed(decimal_precision) + symbol; - } + return parseFloat(value).toFixed(decimal_precision) + symbol; + } } return bytes.toFixed(0); } }); -glancesApp.filter('bits', function($filter) { +glancesApp.filter('bits', function ($filter) { return function (bits, low_precision) { - bits = Math.round(bits) * 8; - return $filter('bytes')(bits, low_precision) + 'b'; + bits = Math.round(bits) * 8; + return $filter('bytes')(bits, low_precision) + 'b'; } }); -glancesApp.filter('leftPad', function($filter) { +glancesApp.filter('leftPad', function () { return function (value, length, chars) { - length = length || 0; - chars = chars || ' '; - return _.padStart(value, length, chars); + length = length || 0; + chars = chars || ' '; + return _.padStart(value, length, chars); } }); -glancesApp.filter('timemillis', function() { +glancesApp.filter('timemillis', function () { return function (array) { - var sum = 0.0; - for (var i = 0; i < array.length; i++) { - sum += array[i] * 1000.0; - } - return sum; + var sum = 0.0; + for (var i = 0; i < array.length; i++) { + sum += array[i] * 1000.0; + } + return sum; } }); -glancesApp.filter('timedelta', function($filter) { +glancesApp.filter('timedelta', function ($filter) { return function (value) { - var sum = $filter('timemillis')(value); - var d = new Date(sum); + var sum = $filter('timemillis')(value); + var d = new Date(sum); - return { - hours: d.getUTCHours(), // TODO : multiple days ( * (d.getDay() * 24))) - minutes: d.getUTCMinutes(), - seconds: d.getUTCSeconds(), - milliseconds: parseInt("" + d.getUTCMilliseconds() / 10) - }; + return { + hours: d.getUTCHours(), // TODO : multiple days ( * (d.getDay() * 24))) + minutes: d.getUTCMinutes(), + seconds: d.getUTCSeconds(), + milliseconds: parseInt("" + d.getUTCMilliseconds() / 10) + }; } }); diff --git a/glances/outputs/static/js/services/core/favicon.js b/glances/outputs/static/js/services/core/favicon.js deleted file mode 100644 index 2ed96d38..00000000 --- a/glances/outputs/static/js/services/core/favicon.js +++ /dev/null @@ -1,14 +0,0 @@ -glancesApp.service('favicoService', function() { - - var favico = new Favico({ - animation : 'none' - }); - - this.badge = function(nb) { - favico.badge(nb); - }; - - this.reset = function() { - favico.reset(); - }; -}); diff --git a/glances/outputs/static/js/services/core/stats.js b/glances/outputs/static/js/services/core/stats.js deleted file mode 100644 index 1b63525b..00000000 --- a/glances/outputs/static/js/services/core/stats.js +++ /dev/null @@ -1,106 +0,0 @@ -glancesApp.service('GlancesStats', function($http, $injector, $q, GlancesPlugin) { - var _stats = [], _views = [], _limits = [], _config = {}; - - var _plugins = { - 'alert': 'GlancesPluginAlert', - 'cloud': 'GlancesPluginCloud', - 'cpu': 'GlancesPluginCpu', - 'diskio': 'GlancesPluginDiskio', - 'irq' : 'GlancesPluginIrq', - 'docker': 'GlancesPluginDocker', - 'ip': 'GlancesPluginIp', - 'fs': 'GlancesPluginFs', - 'folders': 'GlancesPluginFolders', - 'gpu': 'GlancesPluginGpu', - 'load': 'GlancesPluginLoad', - 'mem': 'GlancesPluginMem', - 'memswap': 'GlancesPluginMemSwap', - 'amps': 'GlancesPluginAmps', - 'network': 'GlancesPluginNetwork', - 'percpu': 'GlancesPluginPerCpu', - 'processcount': 'GlancesPluginProcessCount', - 'processlist': 'GlancesPluginProcessList', - 'quicklook': 'GlancesPluginQuicklook', - 'raid': 'GlancesPluginRaid', - 'sensors': 'GlancesPluginSensors', - 'system': 'GlancesPluginSystem', - 'uptime': 'GlancesPluginUptime', - 'ports': 'GlancesPluginPorts', - 'wifi': 'GlancesPluginWifi' - }; - - this.getData = function() { - return $q.all([ - this.getAllStats(), - this.getAllViews() - ]).then(function(results) { - return { - 'stats': results[0], - 'view': results[1] - }; - }); - }; - - this.getAllStats = function() { - return $http.get('/api/2/all').then(function (response) { - _stats = response.data; - - return response.data; - }); - }; - - this.getAllLimits = function() { - return $http.get('/api/2/all/limits').then(function (response) { - _limits = response.data; - - return response.data; - }); - }; - - this.getAllViews = function() { - return $http.get('/api/2/all/views').then(function (response) { - _views = response.data; - - return response.data; - }); - }; - - this.getHelp = function() { - return $http.get('/api/2/help').then(function (response) { - return response.data; - }); - }; - - this.getConfig = function() { - return $http.get('/api/2/config').then(function (response) { - _config = response.data; - - return _config; - }); - }; - - this.getArguments = function() { - return $http.get('/api/2/args').then(function (response) { - return response.data; - }); - }; - - this.getPlugin = function(name) { - var plugin = _plugins[name]; - - if (plugin === undefined) { - throw "Plugin '" + name + "' not found"; - } - - plugin = $injector.get(plugin); - plugin.setData(_stats, _views, _config); - - return plugin; - }; - - // load limits to init GlancePlugin helper - this.getAllLimits().then(function(limits) { - GlancesPlugin.setLimits(limits); - }); - -}); diff --git a/glances/outputs/static/js/services/favicon.js b/glances/outputs/static/js/services/favicon.js new file mode 100644 index 00000000..6a2432eb --- /dev/null +++ b/glances/outputs/static/js/services/favicon.js @@ -0,0 +1,14 @@ +glancesApp.service('favicoService', function () { + + var favico = new Favico({ + animation: 'none' + }); + + this.badge = function (nb) { + favico.badge(nb); + }; + + this.reset = function () { + favico.reset(); + }; +}); diff --git a/glances/outputs/static/js/services/plugins/plugin.js b/glances/outputs/static/js/services/plugin_helper.js similarity index 92% rename from glances/outputs/static/js/services/plugins/plugin.js rename to glances/outputs/static/js/services/plugin_helper.js index a2fbc91c..c80e69aa 100644 --- a/glances/outputs/static/js/services/plugins/plugin.js +++ b/glances/outputs/static/js/services/plugin_helper.js @@ -1,11 +1,11 @@ -glancesApp.service('GlancesPlugin', function () { +glancesApp.service('GlancesPluginHelper', function () { var plugin = { 'limits': {}, 'limitSuffix': ['critical', 'careful', 'warning'] }; - plugin.setLimits = function(limits){ + plugin.setLimits = function (limits) { this.limits = limits; }; diff --git a/glances/outputs/static/js/services/plugins/alert.js b/glances/outputs/static/js/services/plugins/alert.js deleted file mode 100644 index 9df6db89..00000000 --- a/glances/outputs/static/js/services/plugins/alert.js +++ /dev/null @@ -1,58 +0,0 @@ -glancesApp.service('GlancesPluginAlert', function () { - var _pluginName = "alert"; - var _alerts = []; - - this.setData = function (data, views) { - data = data[_pluginName]; - _alerts = []; - - if(!_.isArray(data)) { - data = []; - } - - for (var i = 0; i < data.length; i++) { - var alertData = data[i]; - var alert = {}; - - alert.name = alertData[3]; - alert.level = alertData[2]; - alert.begin = alertData[0] * 1000; - alert.end = alertData[1] * 1000; - alert.ongoing = alertData[1] == -1; - alert.min = alertData[6]; - alert.mean = alertData[5]; - alert.max = alertData[4]; - - if (!alert.ongoing) { - var duration = alert.end - alert.begin; - var seconds = parseInt((duration / 1000) % 60) - , minutes = parseInt((duration / (1000 * 60)) % 60) - , hours = parseInt((duration / (1000 * 60 * 60)) % 24); - - alert.duration = _.padStart(hours, 2, '0') + ":" + _.padStart(minutes, 2, '0') + ":" + _.padStart(seconds, 2, '0'); - } - - _alerts.push(alert); - } - }; - - this.hasAlerts = function () { - return _alerts.length > 0; - }; - - this.getAlerts = function () { - return _alerts; - }; - - this.count = function () { - return _alerts.length; - }; - - this.hasOngoingAlerts = function () { - return _.filter(_alerts, { 'ongoing': true }).length > 0; - }; - - this.countOngoingAlerts = function () { - return _.filter(_alerts, { 'ongoing': true }).length; - } -}); diff --git a/glances/outputs/static/js/services/plugins/amps.js b/glances/outputs/static/js/services/plugins/amps.js deleted file mode 100644 index 3d37814e..00000000 --- a/glances/outputs/static/js/services/plugins/amps.js +++ /dev/null @@ -1,34 +0,0 @@ -glancesApp.service('GlancesPluginAmps', function() { - var _pluginName = "amps"; - this.processes = []; - - this.setData = function(data, views) { - var processes = data[_pluginName]; - - this.processes = []; - angular.forEach(processes, function(process) { - if (process.result !== null) { - this.processes.push(process); - } - }, this); - }; - - this.getDescriptionDecoration = function(process) { - var count = process.count; - var countMin = process.countmin; - var countMax = process.countmax; - var decoration = "ok"; - - if (count > 0) { - if ((countMin == null || count >= countMin) && (countMax == null || count <= countMax)) { - decoration = 'ok'; - } else { - decoration = 'careful'; - } - } else { - decoration = countMin == null ? 'ok' : 'critical'; - } - - return decoration; - } -}); diff --git a/glances/outputs/static/js/services/plugins/cloud.js b/glances/outputs/static/js/services/plugins/cloud.js deleted file mode 100644 index ae6957c3..00000000 --- a/glances/outputs/static/js/services/plugins/cloud.js +++ /dev/null @@ -1,22 +0,0 @@ -glancesApp.service('GlancesPluginCloud', function() { - var _pluginName = "cloud"; - var _provider = null; - var _instance = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - if (data['ami-id'] !== undefined) { - _provider = 'AWS EC2'; - _instance = data['instance-type'] + ' instance ' + data['instance-id'] + ' (' + data['region'] + ')'; - } - } - - this.getProvider = function() { - return _provider; - } - - this.getInstance = function() { - return _instance; - } -}); diff --git a/glances/outputs/static/js/services/plugins/cpu.js b/glances/outputs/static/js/services/plugins/cpu.js deleted file mode 100644 index 5353a311..00000000 --- a/glances/outputs/static/js/services/plugins/cpu.js +++ /dev/null @@ -1,55 +0,0 @@ -glancesApp.service('GlancesPluginCpu', function() { - var _pluginName = "cpu"; - var _view = {}; - - this.total = null; - this.user = null; - this.system = null; - this.idle = null; - this.nice = null; - this.irq = null; - this.iowait = null; - this.steal = null; - this.ctx_switches = null; - this.interrupts = null; - this.soft_interrupts = null; - this.syscalls = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - this.total = data.total; - this.user = data.user; - this.system = data.system; - this.idle = data.idle; - this.nice = data.nice; - this.irq = data.irq; - this.iowait = data.iowait; - this.steal = data.steal; - - if (data.ctx_switches) { - this.ctx_switches = Math.floor(data.ctx_switches / data.time_since_update); - } - - if (data.interrupts) { - this.interrupts = Math.floor(data.interrupts / data.time_since_update); - } - - if (data.soft_interrupts) { - this.soft_interrupts = Math.floor(data.soft_interrupts / data.time_since_update); - } - - if (data.syscalls) { - this.syscalls = Math.floor(data.syscalls / data.time_since_update); - } - } - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - } -}); diff --git a/glances/outputs/static/js/services/plugins/diskio.js b/glances/outputs/static/js/services/plugins/diskio.js deleted file mode 100644 index 5e0aacf2..00000000 --- a/glances/outputs/static/js/services/plugins/diskio.js +++ /dev/null @@ -1,30 +0,0 @@ -glancesApp.service('GlancesPluginDiskio', function($filter) { - var _pluginName = "diskio"; - this.disks = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - data = $filter('orderBy')(data,'disk_name'); - this.disks = []; - - for (var i = 0; i < data.length; i++) { - var diskioData = data[i]; - var timeSinceUpdate = diskioData['time_since_update']; - - var diskio = { - 'name': diskioData['disk_name'], - 'bitrate': { - 'txps': $filter('bytes')(diskioData['read_bytes'] / timeSinceUpdate), - 'rxps': $filter('bytes')(diskioData['write_bytes'] / timeSinceUpdate) - }, - 'count': { - 'txps': $filter('bytes')(diskioData['read_count'] / timeSinceUpdate), - 'rxps': $filter('bytes')(diskioData['write_count'] / timeSinceUpdate) - }, - 'alias': diskioData['alias'] !== undefined ? diskioData['alias'] : null - }; - - this.disks.push(diskio); - } - }; -}); diff --git a/glances/outputs/static/js/services/plugins/fs.js b/glances/outputs/static/js/services/plugins/fs.js deleted file mode 100644 index 07f9b9e2..00000000 --- a/glances/outputs/static/js/services/plugins/fs.js +++ /dev/null @@ -1,40 +0,0 @@ -glancesApp.service('GlancesPluginFs', function() { - var _pluginName = "fs"; - var _view = {}; - this.fileSystems = []; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - this.fileSystems = []; - - for (var i = 0; i < data.length; i++) { - var fsData = data[i]; - - var shortMountPoint = fsData['mnt_point']; - if (shortMountPoint.length > 9) { - shortMountPoint = '_' + fsData['mnt_point'].slice(-8); - } - - var fs = { - 'name': fsData['device_name'], - 'mountPoint': fsData['mnt_point'], - 'shortMountPoint': shortMountPoint, - 'percent': fsData['percent'], - 'size': fsData['size'], - 'used': fsData['used'], - 'free': fsData['free'] - }; - - this.fileSystems.push(fs); - } - }; - - this.getDecoration = function(mountPoint, field) { - if(_view[mountPoint][field] == undefined) { - return; - } - - return _view[mountPoint][field].decoration.toLowerCase(); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/gpu.js b/glances/outputs/static/js/services/plugins/gpu.js deleted file mode 100644 index f84fc953..00000000 --- a/glances/outputs/static/js/services/plugins/gpu.js +++ /dev/null @@ -1,56 +0,0 @@ -glancesApp.service('GlancesPluginGpu', function() { - var _pluginName = "gpu"; - var _view = {}; - this.gpus = []; - this.name = "GPU"; - this.mean = {}; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - if (data.length === 0) { - return; - } - - this.gpus = []; - this.name = "GPU"; - this.mean = { - proc: null, - mem: null - }; - var sameName = true; - - for (var i = 0; i < data.length; i++) { - var gpuData = data[i]; - - var gpu = gpuData; - - this.mean.proc += gpu.proc; - this.mean.mem += gpu.mem; - - this.gpus.push(gpu); - } - - if (data.length === 1 ) { - this.name = data[0].name; - } else if (sameName) { - this.name = data.length + ' GPU ' + data[0].name; - } - - this.mean.proc = this.mean.proc / data.length; - this.mean.mem = this.mean.mem / data.length; - }; - - this.getDecoration = function(gpuId, value) { - if(_view[gpuId][value] == undefined) { - return; - } - - return _view[gpuId][value].decoration.toLowerCase(); - }; - - this.getMeanDecoration = function(value) { - return this.getDecoration(0, value); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/ip.js b/glances/outputs/static/js/services/plugins/ip.js deleted file mode 100644 index a13add6c..00000000 --- a/glances/outputs/static/js/services/plugins/ip.js +++ /dev/null @@ -1,19 +0,0 @@ -glancesApp.service('GlancesPluginIp', function() { - var _pluginName = "ip"; - - this.address = null; - this.gateway = null; - this.mask = null; - this.maskCidr = null; - this.publicAddress = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - this.address = data.address; - this.gateway = data.gateway; - this.mask = data.mask; - this.maskCidr = data.mask_cidr; - this.publicAddress = data.public_address - }; -}); diff --git a/glances/outputs/static/js/services/plugins/irq.js b/glances/outputs/static/js/services/plugins/irq.js deleted file mode 100644 index 8e71c8ed..00000000 --- a/glances/outputs/static/js/services/plugins/irq.js +++ /dev/null @@ -1,21 +0,0 @@ -glancesApp.service('GlancesPluginIrq', function() { - var _pluginName = "irq"; - this.irqs = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.irqs = []; - - for (var i = 0; i < data.length; i++) { - var IrqData = data[i]; - var timeSinceUpdate = IrqData['time_since_update']; - - var irq = { - 'irq_line': IrqData['irq_line'], - 'irq_rate': IrqData['irq_rate'] - }; - - this.irqs.push(irq); - } - }; -}); diff --git a/glances/outputs/static/js/services/plugins/load.js b/glances/outputs/static/js/services/plugins/load.js deleted file mode 100644 index 7a9993f1..00000000 --- a/glances/outputs/static/js/services/plugins/load.js +++ /dev/null @@ -1,27 +0,0 @@ -glancesApp.service('GlancesPluginLoad', function() { - var _pluginName = "load"; - var _view = {}; - - this.cpucore = null; - this.min1 = null; - this.min5 = null; - this.min15 = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.cpucore = data['cpucore']; - this.min1 = data['min1']; - this.min5 = data['min5']; - this.min15 = data['min15']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/mem.js b/glances/outputs/static/js/services/plugins/mem.js deleted file mode 100644 index 93b0d599..00000000 --- a/glances/outputs/static/js/services/plugins/mem.js +++ /dev/null @@ -1,36 +0,0 @@ -glancesApp.service('GlancesPluginMem', function() { - var _pluginName = "mem"; - var _view = {}; - - this.percent = null; - this.total = null; - this.used = null; - this.free = null; - this.version = null; - this.active = null; - this.inactive = null; - this.buffers = null; - this.cached = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.percent = data['percent']; - this.total = data['total']; - this.used = data['used']; - this.free = data['free']; - this.active = data['active']; - this.inactive = data['inactive']; - this.buffers = data['buffers']; - this.cached = data['cached']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/memswap.js b/glances/outputs/static/js/services/plugins/memswap.js deleted file mode 100644 index 7ac1684b..00000000 --- a/glances/outputs/static/js/services/plugins/memswap.js +++ /dev/null @@ -1,27 +0,0 @@ -glancesApp.service('GlancesPluginMemSwap', function() { - var _pluginName = "memswap"; - var _view = {}; - - this.percent = null; - this.total = null; - this.used = null; - this.free = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.percent = data['percent']; - this.total = data['total']; - this.used = data['used']; - this.free = data['free']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/network.js b/glances/outputs/static/js/services/plugins/network.js deleted file mode 100644 index d52d3c01..00000000 --- a/glances/outputs/static/js/services/plugins/network.js +++ /dev/null @@ -1,25 +0,0 @@ -glancesApp.service('GlancesPluginNetwork', function() { - var _pluginName = "network"; - this.networks = []; - - this.setData = function(data, views) { - this.networks = []; - - for (var i = 0; i < data[_pluginName].length; i++) { - var networkData = data[_pluginName][i]; - - var network = { - 'interfaceName': networkData['interface_name'], - 'rx': networkData['rx'], - 'tx': networkData['tx'], - 'cx': networkData['cx'], - 'time_since_update': networkData['time_since_update'], - 'cumulativeRx': networkData['cumulative_rx'], - 'cumulativeTx': networkData['cumulative_tx'], - 'cumulativeCx': networkData['cumulative_cx'] - }; - - this.networks.push(network); - } - }; -}); diff --git a/glances/outputs/static/js/services/plugins/percpu.js b/glances/outputs/static/js/services/plugins/percpu.js deleted file mode 100644 index 83b28f6a..00000000 --- a/glances/outputs/static/js/services/plugins/percpu.js +++ /dev/null @@ -1,30 +0,0 @@ -glancesApp.service('GlancesPluginPerCpu', function($filter, GlancesPlugin) { - var _pluginName = "percpu"; - this.cpus = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.cpus = []; - - for (var i = 0; i < data.length; i++) { - var cpuData = data[i]; - - this.cpus.push({ - 'total': cpuData.total, - 'user': cpuData.user, - 'system': cpuData.system, - 'idle': cpuData.idle, - 'iowait': cpuData.iowait, - 'steal': cpuData.steal - }); - } - }; - - this.getUserAlert = function(cpu) { - return GlancesPlugin.getAlert(_pluginName, 'percpu_user_', cpu.user) - }; - - this.getSystemAlert = function(cpu) { - return GlancesPlugin.getAlert(_pluginName, 'percpu_system_', cpu.system); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/ports.js b/glances/outputs/static/js/services/plugins/ports.js deleted file mode 100644 index 96fd57e5..00000000 --- a/glances/outputs/static/js/services/plugins/ports.js +++ /dev/null @@ -1,29 +0,0 @@ -glancesApp.service('GlancesPluginPorts', function() { - var _pluginName = "ports"; - this.ports = []; - - this.setData = function(data, views) { - var ports = data[_pluginName]; - this.ports = []; - - angular.forEach(ports, function(port) { - this.ports.push(port); - }, this); - }; - - this.getDecoration = function(port) { - if (port.status === null) { - return 'careful'; - } - - if (port.status === false) { - return 'critical'; - } - - if (port.rtt_warning !== null && port.status > port.rtt_warning) { - return 'warning'; - } - - return 'ok'; - }; -}); diff --git a/glances/outputs/static/js/services/plugins/processcount.js b/glances/outputs/static/js/services/plugins/processcount.js deleted file mode 100644 index 163f6b49..00000000 --- a/glances/outputs/static/js/services/plugins/processcount.js +++ /dev/null @@ -1,19 +0,0 @@ -glancesApp.service('GlancesPluginProcessCount', function() { - var _pluginName = "processcount"; - - this.total = null; - this.running = null; - this.sleeping = null; - this.stopped = null; - this.thread = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - this.total = data['total'] || 0; - this.running = data['running'] || 0; - this.sleeping = data['sleeping'] || 0; - this.stopped = data['stopped'] || 0; - this.thread = data['thread'] || 0; - }; -}); diff --git a/glances/outputs/static/js/services/plugins/processlist.js b/glances/outputs/static/js/services/plugins/processlist.js deleted file mode 100644 index 24cb5069..00000000 --- a/glances/outputs/static/js/services/plugins/processlist.js +++ /dev/null @@ -1,64 +0,0 @@ -glancesApp.service('GlancesPluginProcessList', function($filter, GlancesPlugin) { - var _pluginName = "processlist"; - var _maxProcessesToDisplay = undefined; - this.processes = []; - this.ioReadWritePresent = false; - - this.setData = function(data, views, config) { - this.processes = []; - this.ioReadWritePresent = false; - _maxProcessesToDisplay = config.outputs !== undefined ? config.outputs.max_processes_display : undefined;; - - for (var i = 0; i < data[_pluginName].length; i++) { - var process = data[_pluginName][i]; - - process.memvirt = process.memory_info[1]; - process.memres = process.memory_info[0]; - process.timeplus = $filter('timedelta')(process.cpu_times); - process.timemillis = $filter('timemillis')(process.cpu_times); - - process.ioRead = null; - process.ioWrite = null; - - if (process.io_counters) { - this.ioReadWritePresent = true; - - process.ioRead = (process.io_counters[0] - process.io_counters[2]) / process.time_since_update; - - if (process.ioRead != 0) { - process.ioRead = $filter('bytes')(process.ioRead); - } - - process.ioWrite = (process.io_counters[1] - process.io_counters[3]) / process.time_since_update; - - if (process.ioWrite != 0) { - process.ioWrite = $filter('bytes')(process.ioWrite); - } - } - - process.isNice = process.nice !== undefined && ((data['system'].os_name === 'Windows' && process.nice != 32) || (data['system'].os_name !== 'Windows' && process.nice != 0)); - - if (Array.isArray(process.cmdline)) { - process.cmdline = process.cmdline.join(' '); - } - - if (data['system'].os_name === 'Windows') { - process.username = _.last(process.username.split('\\')); - } - - this.processes.push(process); - } - }; - - this.getCpuPercentAlert = function(process) { - return GlancesPlugin.getAlert(_pluginName, 'processlist_cpu_', process.cpu_percent); - }; - - this.getMemoryPercentAlert = function(process) { - return GlancesPlugin.getAlert(_pluginName, 'processlist_mem_', process.cpu_percent); - }; - - this.getLimit = function() { - return _maxProcessesToDisplay; - }; -}); diff --git a/glances/outputs/static/js/services/plugins/quicklook.js b/glances/outputs/static/js/services/plugins/quicklook.js deleted file mode 100644 index 37345b52..00000000 --- a/glances/outputs/static/js/services/plugins/quicklook.js +++ /dev/null @@ -1,40 +0,0 @@ -glancesApp.service('GlancesPluginQuicklook', function() { - var _pluginName = "quicklook"; - var _view = {}; - - this.mem = null; - this.cpu = null; - this.cpu_name = null; - this.cpu_hz_current = null; - this.cpu_hz = null; - this.swap = null; - this.percpus = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - this.mem = data.mem; - this.cpu = data.cpu; - this.cpu_name = data.cpu_name; - this.cpu_hz_current = data.cpu_hz_current; - this.cpu_hz = data.cpu_hz; - this.swap = data.swap; - this.percpus = []; - - angular.forEach(data.percpu, function(cpu) { - this.percpus.push({ - 'number': cpu.cpu_number, - 'total': cpu.total - }); - }, this); - } - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - } -}); diff --git a/glances/outputs/static/js/services/plugins/sensors.js b/glances/outputs/static/js/services/plugins/sensors.js deleted file mode 100644 index e553505a..00000000 --- a/glances/outputs/static/js/services/plugins/sensors.js +++ /dev/null @@ -1,21 +0,0 @@ -glancesApp.service('GlancesPluginSensors', function(GlancesPlugin) { - - var _pluginName = "sensors"; - this.sensors = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - - _.remove(data, function(sensor) { - return (_.isArray(sensor.value) && _.isEmpty(sensor.value)) || sensor.value === 0; - }); - - this.sensors = data; - }; - - this.getAlert = function(sensor) { - var current = sensor.type == 'battery' ? 100 - sensor.value : sensor.value; - - return GlancesPlugin.getAlert(_pluginName, 'sensors_' + sensor.type + '_', current); - }; -}); diff --git a/glances/outputs/static/js/services/plugins/system.js b/glances/outputs/static/js/services/plugins/system.js deleted file mode 100644 index ecf4f0f8..00000000 --- a/glances/outputs/static/js/services/plugins/system.js +++ /dev/null @@ -1,37 +0,0 @@ -glancesApp.service('GlancesPluginSystem', function() { - var _pluginName = "system"; - - this.hostname = null; - this.platform = null; - this.humanReadableName = null; - this.os = { - 'name': null, - 'version': null - }; - - this.setData = function(data, views) { - data = data[_pluginName]; - - this.hostname = data['hostname']; - this.platform = data['platform']; - this.os.name = data['os_name']; - this.os.version = data['os_version']; - this.humanReadableName = data['hr_name']; - }; - - this.isBsd = function() { - return this.os.name === 'FreeBSD'; - }; - - this.isLinux = function() { - return this.os.name === 'Linux'; - }; - - this.isMac = function() { - return this.os.name === 'Darwin'; - }; - - this.isWindows = function() { - return this.os.name === 'Windows'; - }; -}); diff --git a/glances/outputs/static/js/services/plugins/uptime.js b/glances/outputs/static/js/services/plugins/uptime.js deleted file mode 100644 index 4a008bde..00000000 --- a/glances/outputs/static/js/services/plugins/uptime.js +++ /dev/null @@ -1,7 +0,0 @@ -glancesApp.service('GlancesPluginUptime', function() { - this.uptime = null; - - this.setData = function(data, views) { - this.uptime = data['uptime']; - }; -}); diff --git a/glances/outputs/static/js/services/plugins/wifi.js b/glances/outputs/static/js/services/plugins/wifi.js deleted file mode 100644 index 2defd931..00000000 --- a/glances/outputs/static/js/services/plugins/wifi.js +++ /dev/null @@ -1,36 +0,0 @@ -glancesApp.service('GlancesPluginWifi', function() { - var _pluginName = "wifi"; - var _view = {}; - this.hotspots = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - this.hotspots = []; - for (var i = 0; i < data.length; i++) { - var hotspotData = data[i]; - - if (hotspotData['ssid'] === '') { - continue; - } - - var hotspot = { - 'ssid': hotspotData['ssid'], - 'encrypted': hotspotData['encrypted'], - 'signal': hotspotData['signal'], - 'encryption_type': hotspotData['encryption_type'], - }; - - this.hotspots.push(hotspot); - } - }; - - this.getDecoration = function(hotpost, field) { - if(_view[hotpost.ssid][field] == undefined) { - return; - } - - return _view[hotpost.ssid][field].decoration.toLowerCase(); - }; -}); diff --git a/glances/outputs/static/js/services/stats.js b/glances/outputs/static/js/services/stats.js new file mode 100644 index 00000000..03f39590 --- /dev/null +++ b/glances/outputs/static/js/services/stats.js @@ -0,0 +1,68 @@ +glancesApp.service('GlancesStats', function ($http, $q, $rootScope, $timeout, GlancesPluginHelper, REFRESH_TIME, CONFIG, ARGUMENTS) { + + var _data = false; + + this.getData = function () { + return _data; + } + + // load config/limit/arguments and execute stats/views auto refresh + this.init = function () { + var refreshData = function () { + return $q.all([ + getAllStats(), + getAllViews() + ]).then(function (results) { + _data = { + 'stats': results[0], + 'views': results[1], + 'isBsd': results[0]['system']['os_name'] === 'FreeBSD', + 'isLinux': results[0]['system']['os_name'] === 'Linux', + 'isMac': results[0]['system']['os_name'] === 'Darwin', + 'isWindows': results[0]['system']['os_name'] === 'Windows' + }; + + $rootScope.$broadcast('data_refreshed', _data); + nextLoad(); + }, function () { + $rootScope.$broadcast('is_disconnected'); + nextLoad(); + }); + }; + + // load limits to init GlancePlugin helper + $http.get('api/2/all/limits').then(function (response) { + GlancesPluginHelper.setLimits(response.data); + }); + $http.get('api/2/config').then(function (response) { + angular.extend(CONFIG, response.data); + }); + $http.get('api/2/args').then(function (response) { + angular.extend(ARGUMENTS, response.data); + }); + + var loadPromise; + var cancelNextLoad = function () { + $timeout.cancel(loadPromise); + }; + + var nextLoad = function () { + cancelNextLoad(); + loadPromise = $timeout(refreshData, REFRESH_TIME * 1000); // in milliseconds + }; + + refreshData(); + } + + var getAllStats = function () { + return $http.get('api/2/all').then(function (response) { + return response.data; + }); + }; + + var getAllViews = function () { + return $http.get('api/2/all/views').then(function (response) { + return response.data; + }); + }; +}); diff --git a/glances/outputs/static/js/variables.js b/glances/outputs/static/js/variables.js deleted file mode 100644 index 3517d844..00000000 --- a/glances/outputs/static/js/variables.js +++ /dev/null @@ -1,35 +0,0 @@ -var keycodes = { - 'a' : '65', - 'c' : '67', - 'm' : '77', - 'p' : '80', - 'i' : '73', - 't' : '84', - 'u' : '85', - 'd' : '68', - 'f' : '70', - 'n' : '78', - 's' : '83', - 'z' : '90', - 'e' : '69', - 'SLASH': '191', - 'D' : '68', - 'b' : '66', - 'l' : '76', - 'w' : '87', - 'x' : '88', - 'ONE': '49', - 'TWO': '50', - 'THREE': '51', - 'FOUR': '52', - 'FIVE': '53', - 'SIX': '54', - 'h' : '72', - 'T' : '84', - 'F' : '70', - 'g' : '71', - 'r' : '82', - 'q' : '81', - 'A' : '65', - 'Q' : '81' -} diff --git a/glances/outputs/static/public/css/style.min.css b/glances/outputs/static/public/css/style.min.css index 16dda3de..92494a45 100644 --- a/glances/outputs/static/public/css/style.min.css +++ b/glances/outputs/static/public/css/style.min.css @@ -106,16 +106,16 @@ body { } /* Plugins */ -#processlist .table-cell { +#processlist-plugin .table-cell { padding: 0px 5px 0px 5px; white-space: nowrap; } -#containers .table-cell { +#containers-plugin .table-cell { padding: 0px 10px 0px 10px; white-space: nowrap; } -#quicklook .progress { +#quicklook-plugin .progress { margin-bottom: 0px; min-width: 100px; background-color: #000; @@ -123,25 +123,25 @@ body { border-radius: 0px; text-align: right; } -#quicklook .progress-bar-ok { +#quicklook-plugin .progress-bar-ok { background-color: #3E7B04; } -#quicklook .progress-bar-careful { +#quicklook-plugin .progress-bar-careful { background-color: #295183; } -#quicklook .progress-bar-warning { +#quicklook-plugin .progress-bar-warning { background-color: #5D4062; } -#quicklook .progress-bar-critical { +#quicklook-plugin .progress-bar-critical { background-color: #A30000; } -#quicklook .cpu-name { +#quicklook-plugin .cpu-name { white-space: nowrap; overflow: hidden; width: 100%; text-overflow: ellipsis; } -#amps .process-result { +#amps-plugin .process-result { max-width: 300px; overflow: hidden; white-space: pre-wrap; @@ -149,7 +149,7 @@ body { text-overflow: ellipsis; } -#gpu .gpu-name { +#gpu-plugin .gpu-name { white-space: nowrap; overflow: hidden; width: 100%; diff --git a/glances/outputs/static/public/help.html b/glances/outputs/static/public/help.html deleted file mode 100644 index f42a36ed..00000000 --- a/glances/outputs/static/public/help.html +++ /dev/null @@ -1,76 +0,0 @@ -
-
{{help.version}} {{help.psutil_version}}
-
-
 
-
-
{{help.configuration_file}}
-
-
 
-
-
{{help.sort_auto}}
-
{{help.sort_network}}
-
-
-
{{help.sort_cpu}}
-
{{help.show_hide_alert}}
-
-
-
{{help.sort_mem}}
-
{{help.percpu}}
-
-
-
{{help.sort_user}}
-
{{help.show_hide_ip}}
-
-
-
{{help.sort_proc}}
-
{{help.enable_disable_docker}}
-
-
-
{{help.sort_io}}
-
{{help.view_network_io_combination}}
-
-
-
{{help.sort_cpu_times}}
-
{{help.view_cumulative_network}}
-
-
-
{{help.show_hide_diskio}}
-
{{help.show_hide_filesytem_freespace}}
-
-
-
{{help.show_hide_filesystem}}
-
{{help.show_hide_help}}
-
-
-
{{help.show_hide_network}}
-
{{help.diskio_iops}}
-
-
-
{{help.show_hide_sensors}}
-
{{help.show_hide_top_menu}}
-
-
-
{{help.show_hide_left_sidebar}}
-
{{help.show_hide_amp}}
-
-
-
{{help.enable_disable_process_stats}}
-
{{help.show_hide_irq}}
-
-
-
{{help.enable_disable_gpu}}
-
{{help.enable_disable_mean_gpu}}
-
-
-
{{help.enable_disable_quick_look}}
-
-
-
-
{{help.enable_disable_short_processname}}
-
-
-
-
{{help.enable_disable_ports}}
-
-
diff --git a/glances/outputs/static/public/index.html b/glances/outputs/static/public/index.html deleted file mode 100644 index eec4fb70..00000000 --- a/glances/outputs/static/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Glances - - - - - - - - - - - - - - - - diff --git a/glances/outputs/static/public/js/main.min.js b/glances/outputs/static/public/js/main.min.js index 03555ecd..01ccc367 100644 --- a/glances/outputs/static/public/js/main.min.js +++ b/glances/outputs/static/public/js/main.min.js @@ -1,251 +1,24 @@ -var glancesApp = angular.module('glancesApp', ['ngRoute']) +var glancesApp = angular.module('glancesApp', ['glances.config', 'cfp.hotkeys']) -.config(["$routeProvider", "$locationProvider", function($routeProvider, $locationProvider) { - $routeProvider.when('/:refresh_time?', { - templateUrl : 'stats.html', - controller : 'statsController', - resolve: { - help: ["GlancesStats", function(GlancesStats) { - return GlancesStats.getHelp(); - }], - config: ["GlancesStats", function(GlancesStats) { - return GlancesStats.getConfig(); - }], - arguments: ["GlancesStats", "$route", function(GlancesStats, $route) { - return GlancesStats.getArguments().then(function(arguments) { - var refreshTimeRoute = parseInt($route.current.params.refresh_time); - if (!isNaN(refreshTimeRoute) && refreshTimeRoute > 1) { - arguments.time = refreshTimeRoute; - } +.value('CONFIG', {}) +.value('ARGUMENTS', {}) - return arguments; - }); - }] - } +.config(["hotkeysProvider", function (hotkeysProvider) { + hotkeysProvider.useNgRoute = false; + hotkeysProvider.includeCheatSheet = false; +}]) + +.run(["$rootScope", "GlancesStats", function ($rootScope, GlancesStats) { + $rootScope.title = "Glances"; + + $rootScope.$on('data_refreshed', function (event, data) { + $rootScope.title = data.stats.system.hostname + ' - Glances'; }); - $locationProvider.html5Mode(true); -}]) -.run(["$rootScope", function($rootScope) { - $rootScope.title = "Glances"; + GlancesStats.init(); }]); -glancesApp.controller('statsController', ["$scope", "$rootScope", "$interval", "GlancesStats", "help", "config", "arguments", "favicoService", function ($scope, $rootScope, $interval, GlancesStats, help, config, arguments, favicoService) { - $scope.help = help; - $scope.arguments = arguments; - - $scope.sorter = { - column: "cpu_percent", - auto: true, - isReverseColumn: function (column) { - return !(column == 'username' || column == 'name'); - }, - getColumnLabel: function (column) { - if (_.isEqual(column, ['io_read', 'io_write'])) { - return 'io_counters'; - } else { - return column; - } - } - }; - - $scope.dataLoaded = false; - $scope.refreshData = function () { - GlancesStats.getData().then(function (data) { - - $scope.statsAlert = GlancesStats.getPlugin('alert'); - $scope.statsCloud = GlancesStats.getPlugin('cloud'); - $scope.statsCpu = GlancesStats.getPlugin('cpu'); - $scope.statsDiskio = GlancesStats.getPlugin('diskio'); - $scope.statsIrq = GlancesStats.getPlugin('irq'); - $scope.statsDocker = GlancesStats.getPlugin('docker'); - $scope.statsFs = GlancesStats.getPlugin('fs'); - $scope.statsFolders = GlancesStats.getPlugin('folders'); - $scope.statsGpu = GlancesStats.getPlugin('gpu'); - $scope.statsIp = GlancesStats.getPlugin('ip'); - $scope.statsLoad = GlancesStats.getPlugin('load'); - $scope.statsMem = GlancesStats.getPlugin('mem'); - $scope.statsMemSwap = GlancesStats.getPlugin('memswap'); - $scope.statsAmps = GlancesStats.getPlugin('amps'); - $scope.statsNetwork = GlancesStats.getPlugin('network'); - $scope.statsPerCpu = GlancesStats.getPlugin('percpu'); - $scope.statsProcessCount = GlancesStats.getPlugin('processcount'); - $scope.statsProcessList = GlancesStats.getPlugin('processlist'); - $scope.statsQuicklook = GlancesStats.getPlugin('quicklook'); - $scope.statsRaid = GlancesStats.getPlugin('raid'); - $scope.statsSensors = GlancesStats.getPlugin('sensors'); - $scope.statsSystem = GlancesStats.getPlugin('system'); - $scope.statsUptime = GlancesStats.getPlugin('uptime'); - $scope.statsPorts = GlancesStats.getPlugin('ports'); - $scope.statsWifi = GlancesStats.getPlugin('wifi'); - - $rootScope.title = $scope.statsSystem.hostname + ' - Glances'; - - if ($scope.statsAlert.hasOngoingAlerts()) { - favicoService.badge($scope.statsAlert.countOngoingAlerts()); - } else { - favicoService.reset(); - } - - $scope.is_disconnected = false; - $scope.dataLoaded = true; - }, function() { - $scope.is_disconnected = true; - }); - }; - - $scope.refreshData(); - $interval(function () { - $scope.refreshData(); - }, arguments.time * 1000); // in milliseconds - - $scope.onKeyDown = function ($event) { - - switch (true) { - case !$event.shiftKey && $event.keyCode == keycodes.a: - // a => Sort processes automatically - $scope.sorter.column = "cpu_percent"; - $scope.sorter.auto = true; - break; - case $event.shiftKey && $event.keyCode == keycodes.A: - // A => Enable/disable AMPs - $scope.arguments.disable_amps = !$scope.arguments.disable_amps; - break; - case !$event.shiftKey && $event.keyCode == keycodes.c: - // c => Sort processes by CPU% - $scope.sorter.column = "cpu_percent"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.m: - // m => Sort processes by MEM% - $scope.sorter.column = "memory_percent"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.u: - // u => Sort processes by user - $scope.sorter.column = "username"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.p: - // p => Sort processes by name - $scope.sorter.column = "name"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.i: - // i => Sort processes by I/O rate - $scope.sorter.column = ['io_read', 'io_write']; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.t: - // t => Sort processes by time - $scope.sorter.column = "timemillis"; - $scope.sorter.auto = false; - break; - case !$event.shiftKey && $event.keyCode == keycodes.d: - // d => Show/hide disk I/O stats - $scope.arguments.disable_diskio = !$scope.arguments.disable_diskio; - break; - case $event.shiftKey && $event.keyCode == keycodes.Q: - // Q => Show/hide IRQ - $scope.arguments.enable_irq = !$scope.arguments.enable_irq; - break; - case !$event.shiftKey && $event.keyCode == keycodes.f: - // f => Show/hide filesystem stats - $scope.arguments.disable_fs = !$scope.arguments.disable_fs; - break; - case !$event.shiftKey && $event.keyCode == keycodes.n: - // n => Show/hide network stats - $scope.arguments.disable_network = !$scope.arguments.disable_network; - break; - case !$event.shiftKey && $event.keyCode == keycodes.s: - // s => Show/hide sensors stats - $scope.arguments.disable_sensors = !$scope.arguments.disable_sensors; - break; - case $event.shiftKey && $event.keyCode == keycodes.TWO: - // 2 => Show/hide left sidebar - $scope.arguments.disable_left_sidebar = !$scope.arguments.disable_left_sidebar; - break; - case !$event.shiftKey && $event.keyCode == keycodes.z: - // z => Enable/disable processes stats - $scope.arguments.disable_process = !$scope.arguments.disable_process; - break; - case $event.keyCode == keycodes.SLASH: - // SLASH => Enable/disable short processes name - $scope.arguments.process_short_name = !$scope.arguments.process_short_name; - break; - case $event.shiftKey && $event.keyCode == keycodes.D: - // D => Enable/disable Docker stats - $scope.arguments.disable_docker = !$scope.arguments.disable_docker; - break; - case !$event.shiftKey && $event.keyCode == keycodes.b: - // b => Bytes or bits for network I/O - $scope.arguments.byte = !$scope.arguments.byte; - break; - case $event.shiftKey && $event.keyCode == keycodes.b: - // 'B' => Switch between bit/s and IO/s for Disk IO - $scope.arguments.diskio_iops = !$scope.arguments.diskio_iops; - break; - case !$event.shiftKey && $event.keyCode == keycodes.l: - // l => Show/hide alert logs - $scope.arguments.disable_alert = !$scope.arguments.disable_alert; - break; - case $event.shiftKey && $event.keyCode == keycodes.ONE: - // 1 => Global CPU or per-CPU stats - $scope.arguments.percpu = !$scope.arguments.percpu; - break; - case !$event.shiftKey && $event.keyCode == keycodes.h: - // h => Show/hide this help screen - $scope.arguments.help_tag = !$scope.arguments.help_tag; - break; - case $event.shiftKey && $event.keyCode == keycodes.T: - // T => View network I/O as combination - $scope.arguments.network_sum = !$scope.arguments.network_sum; - break; - case $event.shiftKey && $event.keyCode == keycodes.u: - // U => View cumulative network I/O - $scope.arguments.network_cumul = !$scope.arguments.network_cumul; - break; - case $event.shiftKey && $event.keyCode == keycodes.f: - // F => Show filesystem free space - $scope.arguments.fs_free_space = !$scope.arguments.fs_free_space; - break; - case $event.shiftKey && $event.keyCode == keycodes.THREE: - // 3 => Enable/disable quick look plugin - $scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook; - break; - case $event.shiftKey && $event.keyCode == keycodes.SIX: - // 6 => Enable/disable mean gpu - $scope.arguments.meangpu = !$scope.arguments.meangpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.g: - // G => Enable/disable gpu - $scope.arguments.disable_gpu = !$scope.arguments.disable_gpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.FIVE: - $scope.arguments.disable_quicklook = !$scope.arguments.disable_quicklook; - $scope.arguments.disable_cpu = !$scope.arguments.disable_cpu; - $scope.arguments.disable_mem = !$scope.arguments.disable_mem; - $scope.arguments.disable_memswap = !$scope.arguments.disable_memswap; - $scope.arguments.disable_load = !$scope.arguments.disable_load; - $scope.arguments.disable_gpu = !$scope.arguments.disable_gpu; - break; - case $event.shiftKey && $event.keyCode == keycodes.i: - // I => Show/hide IP module - $scope.arguments.disable_ip = !$scope.arguments.disable_ip; - break; - case $event.shiftKey && $event.keyCode == keycodes.p: - // I => Enable/disable ports module - $scope.arguments.disable_ports = !$scope.arguments.disable_ports; - break; - case $event.shiftKey && $event.keyCode == keycodes.w: - // 'W' > Enable/Disable Wifi plugin - $scope.arguments.disable_wifi = !$scope.arguments.disable_wifi; - break; - } - }; -}]); - -glancesApp.directive("sortableTh", function() { +glancesApp.directive("sortableTh", function () { return { restrict: 'A', scope: { @@ -255,9 +28,9 @@ glancesApp.directive("sortableTh", function() { element.addClass('sortable'); - scope.$watch(function() { + scope.$watch(function () { return scope.sorter.column; - }, function(newValue, oldValue) { + }, function (newValue, oldValue) { if (angular.isArray(newValue)) { if (newValue.indexOf(attrs.column) !== -1) { @@ -275,7 +48,7 @@ glancesApp.directive("sortableTh", function() { }); - element.on('click', function() { + element.on('click', function () { scope.sorter.column = attrs.column; @@ -284,8 +57,8 @@ glancesApp.directive("sortableTh", function() { } }; }); -glancesApp.filter('min_size', function() { - return function(input, max) { +glancesApp.filter('min_size', function () { + return function (input, max) { var max = max || 8; if (input.length > max) { return "_" + input.substring(input.length - max) @@ -293,8 +66,8 @@ glancesApp.filter('min_size', function() { return input }; }); -glancesApp.filter('exclamation', function() { - return function(input) { +glancesApp.filter('exclamation', function () { + return function (input) { if (input === undefined || input === '') { return '?'; } @@ -302,840 +75,122 @@ glancesApp.filter('exclamation', function() { }; }); -glancesApp.filter('bytes', function() { +glancesApp.filter('bytes', function () { return function (bytes, low_precision) { low_precision = low_precision || false; - if (isNaN(parseFloat(bytes)) || !isFinite(bytes) || bytes == 0){ + if (isNaN(parseFloat(bytes)) || !isFinite(bytes) || bytes == 0) { return bytes; } var symbols = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; var prefix = { - 'Y': 1208925819614629174706176, - 'Z': 1180591620717411303424, - 'E': 1152921504606846976, - 'P': 1125899906842624, - 'T': 1099511627776, - 'G': 1073741824, - 'M': 1048576, - 'K': 1024 + 'Y': 1208925819614629174706176, + 'Z': 1180591620717411303424, + 'E': 1152921504606846976, + 'P': 1125899906842624, + 'T': 1099511627776, + 'G': 1073741824, + 'M': 1048576, + 'K': 1024 }; var reverseSymbols = _(symbols).reverse().value(); for (var i = 0; i < reverseSymbols.length; i++) { - var symbol = reverseSymbols[i]; - var value = bytes / prefix[symbol]; + var symbol = reverseSymbols[i]; + var value = bytes / prefix[symbol]; - if(value > 1) { - var decimal_precision = 0; + if (value > 1) { + var decimal_precision = 0; - if(value < 10) { - decimal_precision = 2; - } - else if(value < 100) { - decimal_precision = 1; - } + if (value < 10) { + decimal_precision = 2; + } + else if (value < 100) { + decimal_precision = 1; + } - if(low_precision) { - if(symbol == 'MK') { - decimal_precision = 0; - } - else { - decimal_precision = _.min([1, decimal_precision]); - } - } - else if(symbol == 'K') { - decimal_precision = 0; - } + if (low_precision) { + if (symbol == 'MK') { + decimal_precision = 0; + } + else { + decimal_precision = _.min([1, decimal_precision]); + } + } + else if (symbol == 'K') { + decimal_precision = 0; + } - return parseFloat(value).toFixed(decimal_precision) + symbol; - } + return parseFloat(value).toFixed(decimal_precision) + symbol; + } } return bytes.toFixed(0); } }); -glancesApp.filter('bits', ["$filter", function($filter) { +glancesApp.filter('bits', ["$filter", function ($filter) { return function (bits, low_precision) { - bits = Math.round(bits) * 8; - return $filter('bytes')(bits, low_precision) + 'b'; + bits = Math.round(bits) * 8; + return $filter('bytes')(bits, low_precision) + 'b'; } }]); -glancesApp.filter('leftPad', ["$filter", function($filter) { +glancesApp.filter('leftPad', function () { return function (value, length, chars) { - length = length || 0; - chars = chars || ' '; - return _.padStart(value, length, chars); + length = length || 0; + chars = chars || ' '; + return _.padStart(value, length, chars); } -}]); +}); -glancesApp.filter('timemillis', function() { +glancesApp.filter('timemillis', function () { return function (array) { - var sum = 0.0; - for (var i = 0; i < array.length; i++) { - sum += array[i] * 1000.0; - } - return sum; + var sum = 0.0; + for (var i = 0; i < array.length; i++) { + sum += array[i] * 1000.0; + } + return sum; } }); -glancesApp.filter('timedelta', ["$filter", function($filter) { +glancesApp.filter('timedelta', ["$filter", function ($filter) { return function (value) { - var sum = $filter('timemillis')(value); - var d = new Date(sum); + var sum = $filter('timemillis')(value); + var d = new Date(sum); - return { - hours: d.getUTCHours(), // TODO : multiple days ( * (d.getDay() * 24))) - minutes: d.getUTCMinutes(), - seconds: d.getUTCSeconds(), - milliseconds: parseInt("" + d.getUTCMilliseconds() / 10) - }; + return { + hours: d.getUTCHours(), // TODO : multiple days ( * (d.getDay() * 24))) + minutes: d.getUTCMinutes(), + seconds: d.getUTCSeconds(), + milliseconds: parseInt("" + d.getUTCMilliseconds() / 10) + }; } }]); -var keycodes = { - 'a' : '65', - 'c' : '67', - 'm' : '77', - 'p' : '80', - 'i' : '73', - 't' : '84', - 'u' : '85', - 'd' : '68', - 'f' : '70', - 'n' : '78', - 's' : '83', - 'z' : '90', - 'e' : '69', - 'SLASH': '191', - 'D' : '68', - 'b' : '66', - 'l' : '76', - 'w' : '87', - 'x' : '88', - 'ONE': '49', - 'TWO': '50', - 'THREE': '51', - 'FOUR': '52', - 'FIVE': '53', - 'SIX': '54', - 'h' : '72', - 'T' : '84', - 'F' : '70', - 'g' : '71', - 'r' : '82', - 'q' : '81', - 'A' : '65', - 'Q' : '81' -} +glancesApp.service('favicoService', function () { -glancesApp.service('favicoService', function() { - - var favico = new Favico({ - animation : 'none' - }); - - this.badge = function(nb) { - favico.badge(nb); - }; - - this.reset = function() { - favico.reset(); - }; -}); - -glancesApp.service('GlancesStats', ["$http", "$injector", "$q", "GlancesPlugin", function($http, $injector, $q, GlancesPlugin) { - var _stats = [], _views = [], _limits = [], _config = {}; - - var _plugins = { - 'alert': 'GlancesPluginAlert', - 'cloud': 'GlancesPluginCloud', - 'cpu': 'GlancesPluginCpu', - 'diskio': 'GlancesPluginDiskio', - 'irq' : 'GlancesPluginIrq', - 'docker': 'GlancesPluginDocker', - 'ip': 'GlancesPluginIp', - 'fs': 'GlancesPluginFs', - 'folders': 'GlancesPluginFolders', - 'gpu': 'GlancesPluginGpu', - 'load': 'GlancesPluginLoad', - 'mem': 'GlancesPluginMem', - 'memswap': 'GlancesPluginMemSwap', - 'amps': 'GlancesPluginAmps', - 'network': 'GlancesPluginNetwork', - 'percpu': 'GlancesPluginPerCpu', - 'processcount': 'GlancesPluginProcessCount', - 'processlist': 'GlancesPluginProcessList', - 'quicklook': 'GlancesPluginQuicklook', - 'raid': 'GlancesPluginRaid', - 'sensors': 'GlancesPluginSensors', - 'system': 'GlancesPluginSystem', - 'uptime': 'GlancesPluginUptime', - 'ports': 'GlancesPluginPorts', - 'wifi': 'GlancesPluginWifi' - }; - - this.getData = function() { - return $q.all([ - this.getAllStats(), - this.getAllViews() - ]).then(function(results) { - return { - 'stats': results[0], - 'view': results[1] - }; - }); - }; - - this.getAllStats = function() { - return $http.get('/api/2/all').then(function (response) { - _stats = response.data; - - return response.data; - }); - }; - - this.getAllLimits = function() { - return $http.get('/api/2/all/limits').then(function (response) { - _limits = response.data; - - return response.data; - }); - }; - - this.getAllViews = function() { - return $http.get('/api/2/all/views').then(function (response) { - _views = response.data; - - return response.data; - }); - }; - - this.getHelp = function() { - return $http.get('/api/2/help').then(function (response) { - return response.data; - }); - }; - - this.getConfig = function() { - return $http.get('/api/2/config').then(function (response) { - _config = response.data; - - return _config; - }); - }; - - this.getArguments = function() { - return $http.get('/api/2/args').then(function (response) { - return response.data; - }); - }; - - this.getPlugin = function(name) { - var plugin = _plugins[name]; - - if (plugin === undefined) { - throw "Plugin '" + name + "' not found"; - } - - plugin = $injector.get(plugin); - plugin.setData(_stats, _views, _config); - - return plugin; - }; - - // load limits to init GlancePlugin helper - this.getAllLimits().then(function(limits) { - GlancesPlugin.setLimits(limits); + var favico = new Favico({ + animation: 'none' }); -}]); - -glancesApp.service('GlancesPluginAlert', function () { - var _pluginName = "alert"; - var _alerts = []; - - this.setData = function (data, views) { - data = data[_pluginName]; - _alerts = []; - - if(!_.isArray(data)) { - data = []; - } - - for (var i = 0; i < data.length; i++) { - var alertData = data[i]; - var alert = {}; - - alert.name = alertData[3]; - alert.level = alertData[2]; - alert.begin = alertData[0] * 1000; - alert.end = alertData[1] * 1000; - alert.ongoing = alertData[1] == -1; - alert.min = alertData[6]; - alert.mean = alertData[5]; - alert.max = alertData[4]; - - if (!alert.ongoing) { - var duration = alert.end - alert.begin; - var seconds = parseInt((duration / 1000) % 60) - , minutes = parseInt((duration / (1000 * 60)) % 60) - , hours = parseInt((duration / (1000 * 60 * 60)) % 24); - - alert.duration = _.padStart(hours, 2, '0') + ":" + _.padStart(minutes, 2, '0') + ":" + _.padStart(seconds, 2, '0'); - } - - _alerts.push(alert); - } + this.badge = function (nb) { + favico.badge(nb); }; - this.hasAlerts = function () { - return _alerts.length > 0; - }; - - this.getAlerts = function () { - return _alerts; - }; - - this.count = function () { - return _alerts.length; - }; - - this.hasOngoingAlerts = function () { - return _.filter(_alerts, { 'ongoing': true }).length > 0; - }; - - this.countOngoingAlerts = function () { - return _.filter(_alerts, { 'ongoing': true }).length; - } -}); - -glancesApp.service('GlancesPluginAmps', function() { - var _pluginName = "amps"; - this.processes = []; - - this.setData = function(data, views) { - var processes = data[_pluginName]; - - this.processes = []; - angular.forEach(processes, function(process) { - if (process.result !== null) { - this.processes.push(process); - } - }, this); - }; - - this.getDescriptionDecoration = function(process) { - var count = process.count; - var countMin = process.countmin; - var countMax = process.countmax; - var decoration = "ok"; - - if (count > 0) { - if ((countMin == null || count >= countMin) && (countMax == null || count <= countMax)) { - decoration = 'ok'; - } else { - decoration = 'careful'; - } - } else { - decoration = countMin == null ? 'ok' : 'critical'; - } - - return decoration; - } -}); - -glancesApp.service('GlancesPluginCloud', function() { - var _pluginName = "cloud"; - var _provider = null; - var _instance = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - if (data['ami-id'] !== undefined) { - _provider = 'AWS EC2'; - _instance = data['instance-type'] + ' instance ' + data['instance-id'] + ' (' + data['region'] + ')'; - } - } - - this.getProvider = function() { - return _provider; - } - - this.getInstance = function() { - return _instance; - } -}); - -glancesApp.service('GlancesPluginCpu', function() { - var _pluginName = "cpu"; - var _view = {}; - - this.total = null; - this.user = null; - this.system = null; - this.idle = null; - this.nice = null; - this.irq = null; - this.iowait = null; - this.steal = null; - this.ctx_switches = null; - this.interrupts = null; - this.soft_interrupts = null; - this.syscalls = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - this.total = data.total; - this.user = data.user; - this.system = data.system; - this.idle = data.idle; - this.nice = data.nice; - this.irq = data.irq; - this.iowait = data.iowait; - this.steal = data.steal; - - if (data.ctx_switches) { - this.ctx_switches = Math.floor(data.ctx_switches / data.time_since_update); - } - - if (data.interrupts) { - this.interrupts = Math.floor(data.interrupts / data.time_since_update); - } - - if (data.soft_interrupts) { - this.soft_interrupts = Math.floor(data.soft_interrupts / data.time_since_update); - } - - if (data.syscalls) { - this.syscalls = Math.floor(data.syscalls / data.time_since_update); - } - } - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - } -}); - -glancesApp.service('GlancesPluginDiskio', ["$filter", function($filter) { - var _pluginName = "diskio"; - this.disks = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - data = $filter('orderBy')(data,'disk_name'); - this.disks = []; - - for (var i = 0; i < data.length; i++) { - var diskioData = data[i]; - var timeSinceUpdate = diskioData['time_since_update']; - - var diskio = { - 'name': diskioData['disk_name'], - 'bitrate': { - 'txps': $filter('bytes')(diskioData['read_bytes'] / timeSinceUpdate), - 'rxps': $filter('bytes')(diskioData['write_bytes'] / timeSinceUpdate) - }, - 'count': { - 'txps': $filter('bytes')(diskioData['read_count'] / timeSinceUpdate), - 'rxps': $filter('bytes')(diskioData['write_count'] / timeSinceUpdate) - }, - 'alias': diskioData['alias'] !== undefined ? diskioData['alias'] : null - }; - - this.disks.push(diskio); - } - }; -}]); - -glancesApp.service('GlancesPluginDocker', ["GlancesPlugin", function(GlancesPlugin) { - - var _pluginName = "docker"; - this.containers = []; - this.version = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.containers = []; - - if(_.isEmpty(data)) { - return; - } - - for (var i = 0; i < data['containers'].length; i++) { - var containerData = data['containers'][i]; - - var container = { - 'id': containerData.Id, - 'name': containerData.Names[0].split('/').splice(-1)[0], - 'status': containerData.Status, - 'cpu': containerData.cpu.total, - 'memory': containerData.memory.usage != undefined ? containerData.memory.usage : '?', - 'ior': containerData.io.ior != undefined ? containerData.io.ior : '?', - 'iow': containerData.io.iow != undefined ? containerData.io.iow : '?', - 'io_time_since_update': containerData.io.time_since_update, - 'rx': containerData.network.rx != undefined ? containerData.network.rx : '?', - 'tx': containerData.network.tx != undefined ? containerData.network.tx : '?', - 'net_time_since_update': containerData.network.time_since_update, - 'command': containerData.Command, - 'image': containerData.Image - }; - - this.containers.push(container); - } - - this.version = data['version']['Version']; - }; -}]); - -glancesApp.service('GlancesPluginFolders', function() { - var _pluginName = "folders"; - this.folders = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.folders = []; - - for (var i = 0; i < data.length; i++) { - var folderData = data[i]; - - var folder = { - 'path': folderData['path'], - 'size': folderData['size'], - 'careful': folderData['careful'], - 'warning': folderData['warning'], - 'critical': folderData['critical'] - }; - - this.folders.push(folder); - } - }; - - this.getDecoration = function(folder) { - - if (!Number.isInteger(folder.size)) { - return; - } - - if (folder.critical !== null && folder.size > (folder.critical * 1000000)) { - return 'critical'; - } else if (folder.warning !== null && folder.size > (folder.warning * 1000000)) { - return 'warning'; - } else if (folder.careful !== null && folder.size > (folder.careful * 1000000)) { - return 'careful'; - } - - return 'ok'; + this.reset = function () { + favico.reset(); }; }); -glancesApp.service('GlancesPluginFs', function() { - var _pluginName = "fs"; - var _view = {}; - this.fileSystems = []; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - this.fileSystems = []; - - for (var i = 0; i < data.length; i++) { - var fsData = data[i]; - - var shortMountPoint = fsData['mnt_point']; - if (shortMountPoint.length > 9) { - shortMountPoint = '_' + fsData['mnt_point'].slice(-8); - } - - var fs = { - 'name': fsData['device_name'], - 'mountPoint': fsData['mnt_point'], - 'shortMountPoint': shortMountPoint, - 'percent': fsData['percent'], - 'size': fsData['size'], - 'used': fsData['used'], - 'free': fsData['free'] - }; - - this.fileSystems.push(fs); - } - }; - - this.getDecoration = function(mountPoint, field) { - if(_view[mountPoint][field] == undefined) { - return; - } - - return _view[mountPoint][field].decoration.toLowerCase(); - }; -}); - -glancesApp.service('GlancesPluginGpu', function() { - var _pluginName = "gpu"; - var _view = {}; - this.gpus = []; - this.name = "GPU"; - this.mean = {}; - - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; - - if (data.length === 0) { - return; - } - - this.gpus = []; - this.name = "GPU"; - this.mean = { - proc: null, - mem: null - }; - var sameName = true; - - for (var i = 0; i < data.length; i++) { - var gpuData = data[i]; - - var gpu = gpuData; - - this.mean.proc += gpu.proc; - this.mean.mem += gpu.mem; - - this.gpus.push(gpu); - } - - if (data.length === 1 ) { - this.name = data[0].name; - } else if (sameName) { - this.name = data.length + ' GPU ' + data[0].name; - } - - this.mean.proc = this.mean.proc / data.length; - this.mean.mem = this.mean.mem / data.length; - }; - - this.getDecoration = function(gpuId, value) { - if(_view[gpuId][value] == undefined) { - return; - } - - return _view[gpuId][value].decoration.toLowerCase(); - }; - - this.getMeanDecoration = function(value) { - return this.getDecoration(0, value); - }; -}); - -glancesApp.service('GlancesPluginIp', function() { - var _pluginName = "ip"; - - this.address = null; - this.gateway = null; - this.mask = null; - this.maskCidr = null; - this.publicAddress = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - this.address = data.address; - this.gateway = data.gateway; - this.mask = data.mask; - this.maskCidr = data.mask_cidr; - this.publicAddress = data.public_address - }; -}); - -glancesApp.service('GlancesPluginIrq', function() { - var _pluginName = "irq"; - this.irqs = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.irqs = []; - - for (var i = 0; i < data.length; i++) { - var IrqData = data[i]; - var timeSinceUpdate = IrqData['time_since_update']; - - var irq = { - 'irq_line': IrqData['irq_line'], - 'irq_rate': IrqData['irq_rate'] - }; - - this.irqs.push(irq); - } - }; -}); - -glancesApp.service('GlancesPluginLoad', function() { - var _pluginName = "load"; - var _view = {}; - - this.cpucore = null; - this.min1 = null; - this.min5 = null; - this.min15 = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.cpucore = data['cpucore']; - this.min1 = data['min1']; - this.min5 = data['min5']; - this.min15 = data['min15']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); - -glancesApp.service('GlancesPluginMem', function() { - var _pluginName = "mem"; - var _view = {}; - - this.percent = null; - this.total = null; - this.used = null; - this.free = null; - this.version = null; - this.active = null; - this.inactive = null; - this.buffers = null; - this.cached = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.percent = data['percent']; - this.total = data['total']; - this.used = data['used']; - this.free = data['free']; - this.active = data['active']; - this.inactive = data['inactive']; - this.buffers = data['buffers']; - this.cached = data['cached']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); - -glancesApp.service('GlancesPluginMemSwap', function() { - var _pluginName = "memswap"; - var _view = {}; - - this.percent = null; - this.total = null; - this.used = null; - this.free = null; - - this.setData = function(data, views) { - _view = views[_pluginName]; - data = data[_pluginName]; - - this.percent = data['percent']; - this.total = data['total']; - this.used = data['used']; - this.free = data['free']; - }; - - this.getDecoration = function(value) { - if(_view[value] == undefined) { - return; - } - - return _view[value].decoration.toLowerCase(); - }; -}); - -glancesApp.service('GlancesPluginNetwork', function() { - var _pluginName = "network"; - this.networks = []; - - this.setData = function(data, views) { - this.networks = []; - - for (var i = 0; i < data[_pluginName].length; i++) { - var networkData = data[_pluginName][i]; - - var network = { - 'interfaceName': networkData['interface_name'], - 'rx': networkData['rx'], - 'tx': networkData['tx'], - 'cx': networkData['cx'], - 'time_since_update': networkData['time_since_update'], - 'cumulativeRx': networkData['cumulative_rx'], - 'cumulativeTx': networkData['cumulative_tx'], - 'cumulativeCx': networkData['cumulative_cx'] - }; - - this.networks.push(network); - } - }; -}); - -glancesApp.service('GlancesPluginPerCpu', ["$filter", "GlancesPlugin", function($filter, GlancesPlugin) { - var _pluginName = "percpu"; - this.cpus = []; - - this.setData = function(data, views) { - data = data[_pluginName]; - this.cpus = []; - - for (var i = 0; i < data.length; i++) { - var cpuData = data[i]; - - this.cpus.push({ - 'total': cpuData.total, - 'user': cpuData.user, - 'system': cpuData.system, - 'idle': cpuData.idle, - 'iowait': cpuData.iowait, - 'steal': cpuData.steal - }); - } - }; - - this.getUserAlert = function(cpu) { - return GlancesPlugin.getAlert(_pluginName, 'percpu_user_', cpu.user) - }; - - this.getSystemAlert = function(cpu) { - return GlancesPlugin.getAlert(_pluginName, 'percpu_system_', cpu.system); - }; -}]); - -glancesApp.service('GlancesPlugin', function () { +glancesApp.service('GlancesPluginHelper', function () { var plugin = { 'limits': {}, 'limitSuffix': ['critical', 'careful', 'warning'] }; - plugin.setLimits = function(limits){ + plugin.setLimits = function (limits) { this.limits = limits; }; @@ -1171,72 +226,1424 @@ glancesApp.service('GlancesPlugin', function () { return plugin; }); -glancesApp.service('GlancesPluginPorts', function() { - var _pluginName = "ports"; - this.ports = []; +glancesApp.service('GlancesStats', ["$http", "$q", "$rootScope", "$timeout", "GlancesPluginHelper", "REFRESH_TIME", "CONFIG", "ARGUMENTS", function ($http, $q, $rootScope, $timeout, GlancesPluginHelper, REFRESH_TIME, CONFIG, ARGUMENTS) { - this.setData = function(data, views) { - var ports = data[_pluginName]; - this.ports = []; + var _data = false; - angular.forEach(ports, function(port) { - this.ports.push(port); - }, this); - }; - - this.getDecoration = function(port) { - if (port.status === null) { - return 'careful'; + this.getData = function () { + return _data; } - if (port.status === false) { - return 'critical'; + // load config/limit/arguments and execute stats/views auto refresh + this.init = function () { + var refreshData = function () { + return $q.all([ + getAllStats(), + getAllViews() + ]).then(function (results) { + _data = { + 'stats': results[0], + 'views': results[1], + 'isBsd': results[0]['system']['os_name'] === 'FreeBSD', + 'isLinux': results[0]['system']['os_name'] === 'Linux', + 'isMac': results[0]['system']['os_name'] === 'Darwin', + 'isWindows': results[0]['system']['os_name'] === 'Windows' + }; + + $rootScope.$broadcast('data_refreshed', _data); + nextLoad(); + }, function () { + $rootScope.$broadcast('is_disconnected'); + nextLoad(); + }); + }; + + // load limits to init GlancePlugin helper + $http.get('api/2/all/limits').then(function (response) { + GlancesPluginHelper.setLimits(response.data); + }); + $http.get('api/2/config').then(function (response) { + angular.extend(CONFIG, response.data); + }); + $http.get('api/2/args').then(function (response) { + angular.extend(ARGUMENTS, response.data); + }); + + var loadPromise; + var cancelNextLoad = function () { + $timeout.cancel(loadPromise); + }; + + var nextLoad = function () { + cancelNextLoad(); + loadPromise = $timeout(refreshData, REFRESH_TIME * 1000); // in milliseconds + }; + + refreshData(); } - if (port.rtt_warning !== null && port.status > port.rtt_warning) { - return 'warning'; - } - - return 'ok'; - }; -}); - -glancesApp.service('GlancesPluginProcessCount', function() { - var _pluginName = "processcount"; - - this.total = null; - this.running = null; - this.sleeping = null; - this.stopped = null; - this.thread = null; - - this.setData = function(data, views) { - data = data[_pluginName]; - - this.total = data['total'] || 0; - this.running = data['running'] || 0; - this.sleeping = data['sleeping'] || 0; - this.stopped = data['stopped'] || 0; - this.thread = data['thread'] || 0; + var getAllStats = function () { + return $http.get('api/2/all').then(function (response) { + return response.data; + }); }; + + var getAllViews = function () { + return $http.get('api/2/all/views').then(function (response) { + return response.data; + }); + }; +}]); + +'use strict'; + +glancesApp.component('glances', { + controller: GlancesController, + controllerAs: 'vm', + templateUrl: 'components/glances/view.html' }); -glancesApp.service('GlancesPluginProcessList', ["$filter", "GlancesPlugin", function($filter, GlancesPlugin) { - var _pluginName = "processlist"; - var _maxProcessesToDisplay = undefined; - this.processes = []; - this.ioReadWritePresent = false; +'use strict'; + +function GlancesController($scope, GlancesStats, hotkeys, ARGUMENTS) { + var vm = this; + vm.dataLoaded = false; + vm.arguments = ARGUMENTS; + + $scope.$on('data_refreshed', function (event, data) { + vm.hasGpu = data.stats.gpu.length > 0; + vm.dataLoaded = true; + }); + + // A => Enable/disable AMPs + hotkeys.add({ + combo: 'A', + callback: function () { + ARGUMENTS.disable_amps = !ARGUMENTS.disable_amps; + } + }); + + // d => Show/hide disk I/O stats + hotkeys.add({ + combo: 'd', + callback: function () { + ARGUMENTS.disable_diskio = !ARGUMENTS.disable_diskio; + } + }); + + // Q => Show/hide IRQ + hotkeys.add({ + combo: 'Q', + callback: function () { + ARGUMENTS.enable_irq = !ARGUMENTS.enable_irq; + } + }); + + // f => Show/hide filesystem stats + hotkeys.add({ + combo: 'f', + callback: function () { + ARGUMENTS.disable_fs = !ARGUMENTS.disable_fs; + } + }); + + // n => Show/hide network stats + hotkeys.add({ + combo: 'n', + callback: function () { + ARGUMENTS.disable_network = !ARGUMENTS.disable_network; + } + }); + + // s => Show/hide sensors stats + hotkeys.add({ + combo: 's', + callback: function () { + ARGUMENTS.disable_sensors = !ARGUMENTS.disable_sensors; + } + }); + + // 2 => Show/hide left sidebar + hotkeys.add({ + combo: '2', + callback: function () { + ARGUMENTS.disable_left_sidebar = !ARGUMENTS.disable_left_sidebar; + } + }); + + // z => Enable/disable processes stats + hotkeys.add({ + combo: 'z', + callback: function () { + ARGUMENTS.disable_process = !ARGUMENTS.disable_process; + } + }); + + // SLASH => Enable/disable short processes name + hotkeys.add({ + combo: '/', + callback: function () { + ARGUMENTS.process_short_name = !ARGUMENTS.process_short_name; + } + }); + + // D => Enable/disable Docker stats + hotkeys.add({ + combo: 'D', + callback: function () { + ARGUMENTS.disable_docker = !ARGUMENTS.disable_docker; + } + }); + + // b => Bytes or bits for network I/O + hotkeys.add({ + combo: 'b', + callback: function () { + ARGUMENTS.byte = !ARGUMENTS.byte; + } + }); + + // 'B' => Switch between bit/s and IO/s for Disk IO + hotkeys.add({ + combo: 'B', + callback: function () { + ARGUMENTS.diskio_iops = !ARGUMENTS.diskio_iops; + } + }); + + // l => Show/hide alert logs + hotkeys.add({ + combo: 'l', + callback: function () { + ARGUMENTS.disable_alert = !ARGUMENTS.disable_alert; + } + }); + + // 1 => Global CPU or per-CPU stats + hotkeys.add({ + combo: '1', + callback: function () { + ARGUMENTS.percpu = !ARGUMENTS.percpu; + } + }); + + // h => Show/hide this help screen + hotkeys.add({ + combo: 'h', + callback: function () { + ARGUMENTS.help_tag = !ARGUMENTS.help_tag; + } + }); + + // T => View network I/O as combination + hotkeys.add({ + combo: 'T', + callback: function () { + ARGUMENTS.network_sum = !ARGUMENTS.network_sum; + } + }); + + // U => View cumulative network I/O + hotkeys.add({ + combo: 'U', + callback: function () { + ARGUMENTS.network_cumul = !ARGUMENTS.network_cumul; + } + }); + + // F => Show filesystem free space + hotkeys.add({ + combo: 'F', + callback: function () { + ARGUMENTS.fs_free_space = !ARGUMENTS.fs_free_space; + } + }); + + // 3 => Enable/disable quick look plugin + hotkeys.add({ + combo: '3', + callback: function () { + ARGUMENTS.disable_quicklook = !ARGUMENTS.disable_quicklook; + } + }); + + // 6 => Enable/disable mean gpu + hotkeys.add({ + combo: '6', + callback: function () { + ARGUMENTS.meangpu = !ARGUMENTS.meangpu; + } + }); + + // G => Enable/disable gpu + hotkeys.add({ + combo: 'G', + callback: function () { + ARGUMENTS.disable_gpu = !ARGUMENTS.disable_gpu; + } + }); + + hotkeys.add({ + combo: '5', + callback: function () { + ARGUMENTS.disable_quicklook = !ARGUMENTS.disable_quicklook; + ARGUMENTS.disable_cpu = !ARGUMENTS.disable_cpu; + ARGUMENTS.disable_mem = !ARGUMENTS.disable_mem; + ARGUMENTS.disable_memswap = !ARGUMENTS.disable_memswap; + ARGUMENTS.disable_load = !ARGUMENTS.disable_load; + ARGUMENTS.disable_gpu = !ARGUMENTS.disable_gpu; + } + }); + + // I => Show/hide IP module + hotkeys.add({ + combo: 'I', + callback: function () { + ARGUMENTS.disable_ip = !ARGUMENTS.disable_ip; + } + }); + + // P => Enable/disable ports module + hotkeys.add({ + combo: 'P', + callback: function () { + ARGUMENTS.disable_ports = !ARGUMENTS.disable_ports; + } + }); + + // 'W' > Enable/Disable Wifi plugin + hotkeys.add({ + combo: 'W', + callback: function () { + ARGUMENTS.disable_wifi = !ARGUMENTS.disable_wifi; + } + }); +} + +'use strict'; + +glancesApp.component('glancesHelp', { + controller: GlancesHelpController, + controllerAs: 'vm', + templateUrl: 'components/help/view.html' +}); + +'use strict'; + +function GlancesHelpController($http) { + var vm = this; + + $http.get('api/2/help').then(function (response) { + vm.help = response.data; + }); +} + +'use strict'; + +glancesApp.component('glancesPluginAlert', { + controller: GlancesPluginAlertController, + controllerAs: 'vm', + templateUrl: 'components/plugin-alert/view.html' +}); + +'use strict'; + +function GlancesPluginAlertController($scope, favicoService) { + var vm = this; + var _alerts = []; + + $scope.$on('data_refreshed', function (event, data) { + var alertStats = data.stats['alert']; + if (!_.isArray(alertStats)) { + alertStats = []; + } + + _alerts = []; + for (var i = 0; i < alertStats.length; i++) { + var alertalertStats = alertStats[i]; + var alert = {}; + + alert.name = alertalertStats[3]; + alert.level = alertalertStats[2]; + alert.begin = alertalertStats[0] * 1000; + alert.end = alertalertStats[1] * 1000; + alert.ongoing = alertalertStats[1] == -1; + alert.min = alertalertStats[6]; + alert.mean = alertalertStats[5]; + alert.max = alertalertStats[4]; + + if (!alert.ongoing) { + var duration = alert.end - alert.begin; + var seconds = parseInt((duration / 1000) % 60) + , minutes = parseInt((duration / (1000 * 60)) % 60) + , hours = parseInt((duration / (1000 * 60 * 60)) % 24); + + alert.duration = _.padStart(hours, 2, '0') + ":" + _.padStart(minutes, 2, '0') + ":" + _.padStart(seconds, 2, '0'); + } + + _alerts.push(alert); + } + + if (vm.hasOngoingAlerts()) { + favicoService.badge(vm.countOngoingAlerts()); + } else { + favicoService.reset(); + } + }); + + vm.hasAlerts = function () { + return _alerts.length > 0; + }; + + vm.getAlerts = function () { + return _alerts; + }; + + vm.count = function () { + return _alerts.length; + }; + + vm.hasOngoingAlerts = function () { + return _.filter(_alerts, {'ongoing': true}).length > 0; + }; + + vm.countOngoingAlerts = function () { + return _.filter(_alerts, {'ongoing': true}).length; + } +} + +'use strict'; + +glancesApp.component('glancesPluginAmps', { + controller: GlancesPluginAmpsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-amps/view.html' +}); + +'use strict'; + +function GlancesPluginAmpsController($scope, GlancesStats, favicoService) { + var vm = this; + vm.processes = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processes = data.stats['amps']; - this.setData = function(data, views, config) { this.processes = []; - this.ioReadWritePresent = false; - _maxProcessesToDisplay = config.outputs !== undefined ? config.outputs.max_processes_display : undefined;; + angular.forEach(processes, function (process) { + if (process.result !== null) { + this.processes.push(process); + } + }, this); + } - for (var i = 0; i < data[_pluginName].length; i++) { - var process = data[_pluginName][i]; + vm.getDescriptionDecoration = function (process) { + var count = process.count; + var countMin = process.countmin; + var countMax = process.countmax; + var decoration = "ok"; + + if (count > 0) { + if ((countMin == null || count >= countMin) && (countMax == null || count <= countMax)) { + decoration = 'ok'; + } else { + decoration = 'careful'; + } + } else { + decoration = countMin == null ? 'ok' : 'critical'; + } + + return decoration; + } +} + +'use strict'; + +glancesApp.component('glancesPluginCloud', { + controller: GlancesPluginCloudController, + controllerAs: 'vm', + templateUrl: 'components/plugin-cloud/view.html' +}); + +'use strict'; + +function GlancesPluginCloudController($scope, GlancesStats) { + var vm = this; + + vm.provider = null; + vm.instance = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['cloud']; + + if (stats['ami-id'] !== undefined) { + vm.provider = 'AWS EC2'; + vm.instance = stats['instance-type'] + ' instance ' + stats['instance-id'] + ' (' + stats['region'] + ')'; + } + } +} + +'use strict'; + +glancesApp.component('glancesPluginCpu', { + controller: GlancesPluginCpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-cpu/view.html' +}); + +'use strict'; + +function GlancesPluginCpuController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.total = null; + vm.user = null; + vm.system = null; + vm.idle = null; + vm.nice = null; + vm.irq = null; + vm.iowait = null; + vm.steal = null; + vm.ctx_switches = null; + vm.interrupts = null; + vm.soft_interrupts = null; + vm.syscalls = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['cpu']; + _view = data.views['cpu']; + + vm.total = stats.total; + vm.user = stats.user; + vm.system = stats.system; + vm.idle = stats.idle; + vm.nice = stats.nice; + vm.irq = stats.irq; + vm.iowait = stats.iowait; + vm.steal = stats.steal; + + if (stats.ctx_switches) { + vm.ctx_switches = Math.floor(stats.ctx_switches / stats.time_since_update); + } + + if (stats.interrupts) { + vm.interrupts = Math.floor(stats.interrupts / stats.time_since_update); + } + + if (stats.soft_interrupts) { + vm.soft_interrupts = Math.floor(stats.soft_interrupts / stats.time_since_update); + } + + if (stats.syscalls) { + vm.syscalls = Math.floor(stats.syscalls / stats.time_since_update); + } + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginDiskio', { + controller: GlancesPluginDiskioController, + controllerAs: 'vm', + templateUrl: 'components/plugin-diskio/view.html' +}); + +'use strict'; + +function GlancesPluginDiskioController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.disks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['diskio']; + stats = $filter('orderBy')(stats, 'disk_name'); + + vm.disks = []; + for (var i = 0; i < stats.length; i++) { + var diskioData = stats[i]; + var timeSinceUpdate = diskioData['time_since_update']; + + vm.disks.push({ + 'name': diskioData['disk_name'], + 'bitrate': { + 'txps': $filter('bytes')(diskioData['read_bytes'] / timeSinceUpdate), + 'rxps': $filter('bytes')(diskioData['write_bytes'] / timeSinceUpdate) + }, + 'count': { + 'txps': $filter('bytes')(diskioData['read_count'] / timeSinceUpdate), + 'rxps': $filter('bytes')(diskioData['write_count'] / timeSinceUpdate) + }, + 'alias': diskioData['alias'] !== undefined ? diskioData['alias'] : null + }); + } + } +} + +'use strict'; + +glancesApp.component('glancesPluginDocker', { + controller: GlancesPluginDockerController, + controllerAs: 'vm', + templateUrl: 'components/plugin-docker/view.html' +}); + +'use strict'; + +function GlancesPluginDockerController($scope, GlancesStats) { + var vm = this; + vm.containers = []; + vm.version = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['docker']; + this.containers = []; + + if (_.isEmpty(stats)) { + return; + } + + for (var i = 0; i < stats['containers'].length; i++) { + var containerData = stats['containers'][i]; + + var container = { + 'id': containerData.Id, + 'name': containerData.Names[0].split('/').splice(-1)[0], + 'status': containerData.Status, + 'cpu': containerData.cpu.total, + 'memory': containerData.memory.usage != undefined ? containerData.memory.usage : '?', + 'ior': containerData.io.ior != undefined ? containerData.io.ior : '?', + 'iow': containerData.io.iow != undefined ? containerData.io.iow : '?', + 'io_time_since_update': containerData.io.time_since_update, + 'rx': containerData.network.rx != undefined ? containerData.network.rx : '?', + 'tx': containerData.network.tx != undefined ? containerData.network.tx : '?', + 'net_time_since_update': containerData.network.time_since_update, + 'command': containerData.Command, + 'image': containerData.Image + }; + + vm.containers.push(container); + } + + vm.version = stats['version']['Version']; + } +} + +'use strict'; + +glancesApp.component('glancesPluginFolders', { + controller: GlancesPluginFsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-folders/view.html' +}); + +'use strict'; + +function GlancesPluginFoldersController($scope, GlancesStats) { + var vm = this; + vm.folders = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['folders']; + vm.folders = []; + + for (var i = 0; i < stats.length; i++) { + var folderData = stats[i]; + + var folder = { + 'path': folderData['path'], + 'size': folderData['size'], + 'careful': folderData['careful'], + 'warning': folderData['warning'], + 'critical': folderData['critical'] + }; + + vm.folders.push(folder); + } + } + + vm.getDecoration = function (folder) { + + if (!Number.isInteger(folder.size)) { + return; + } + + if (folder.critical !== null && folder.size > (folder.critical * 1000000)) { + return 'critical'; + } else if (folder.warning !== null && folder.size > (folder.warning * 1000000)) { + return 'warning'; + } else if (folder.careful !== null && folder.size > (folder.careful * 1000000)) { + return 'careful'; + } + + return 'ok'; + }; +} + +'use strict'; + +glancesApp.component('glancesPluginFs', { + controller: GlancesPluginFsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-fs/view.html' +}); + +'use strict'; + +function GlancesPluginFsController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + var _view = {}; + vm.arguments = ARGUMENTS; + vm.fileSystems = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['fs']; + _view = data.views['fs']; + + vm.fileSystems = []; + for (var i = 0; i < stats.length; i++) { + var fsData = stats[i]; + + var shortMountPoint = fsData['mnt_point']; + if (shortMountPoint.length > 9) { + shortMountPoint = '_' + fsData['mnt_point'].slice(-8); + } + + vm.fileSystems.push(fs = { + 'name': fsData['device_name'], + 'mountPoint': fsData['mnt_point'], + 'shortMountPoint': shortMountPoint, + 'percent': fsData['percent'], + 'size': fsData['size'], + 'used': fsData['used'], + 'free': fsData['free'] + }); + } + + vm.fileSystems = $filter('orderBy')(vm.fileSystems, 'mnt_point'); + }; + + vm.getDecoration = function (mountPoint, field) { + if (_view[mountPoint][field] == undefined) { + return; + } + + return _view[mountPoint][field].decoration.toLowerCase(); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginGpu', { + controller: GlancesPluginGpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-gpu/view.html' +}); + +'use strict'; + +function GlancesPluginGpuController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + var _view = {}; + vm.gpus = []; + vm.name = "GPU"; + vm.mean = {}; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['gpu']; + _view = data.views['gpu']; + + if (stats.length === 0) { + return; + } + + vm.gpus = []; + vm.name = "GPU"; + vm.mean = { + proc: null, + mem: null + }; + var sameName = true; + + for (var i = 0; i < stats.length; i++) { + var gpuData = stats[i]; + + var gpu = gpuData; + + vm.mean.proc += gpu.proc; + vm.mean.mem += gpu.mem; + + vm.gpus.push(gpu); + } + + if (stats.length === 1) { + vm.name = stats[0].name; + } else if (sameName) { + vm.name = stats.length + ' GPU ' + stats[0].name; + } + + vm.mean.proc = vm.mean.proc / stats.length; + vm.mean.mem = vm.mean.mem / stats.length; + } + + vm.getDecoration = function (gpuId, value) { + if (_view[gpuId][value] == undefined) { + return; + } + + return _view[gpuId][value].decoration.toLowerCase(); + }; + + vm.getMeanDecoration = function (value) { + return vm.getDecoration(0, value); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginIp', { + controller: GlancesPluginIpController, + controllerAs: 'vm', + templateUrl: 'components/plugin-ip/view.html' +}); + +'use strict'; + +function GlancesPluginIpController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + + vm.address = null; + vm.gateway = null; + vm.mask = null; + vm.maskCidr = null; + vm.publicAddress = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var ipStats = data.stats['ip']; + + vm.address = ipStats.address; + vm.gateway = ipStats.gateway; + vm.mask = ipStats.mask; + vm.maskCidr = ipStats.mask_cidr; + vm.publicAddress = ipStats.public_address + } +} + +'use strict'; + +glancesApp.component('glancesPluginIrq', { + controller: GlancesPluginIrqController, + controllerAs: 'vm', + templateUrl: 'components/plugin-irq/view.html' +}); + +'use strict'; + +function GlancesPluginIrqController($scope, GlancesStats) { + var vm = this; + vm.irqs = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['irq']; + vm.irqs = []; + + for (var i = 0; i < stats.length; i++) { + var IrqData = stats[i]; + + var irq = { + 'irq_line': IrqData['irq_line'], + 'irq_rate': IrqData['irq_rate'] + }; + + vm.irqs.push(irq); + } + } +} + +'use strict'; + +glancesApp.component('glancesPluginLoad', { + controller: GlancesPluginLoadController, + controllerAs: 'vm', + templateUrl: 'components/plugin-load/view.html' +}); + +'use strict'; + +function GlancesPluginLoadController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.cpucore = null; + vm.min1 = null; + vm.min5 = null; + vm.min15 = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['load']; + _view = data.views['load']; + + vm.cpucore = stats['cpucore']; + vm.min1 = stats['min1']; + vm.min5 = stats['min5']; + vm.min15 = stats['min15']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginMem', { + controller: GlancesPluginMemController, + controllerAs: 'vm', + templateUrl: 'components/plugin-mem/view.html' +}); + +'use strict'; + +function GlancesPluginMemController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.percent = null; + vm.total = null; + vm.used = null; + vm.free = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['mem']; + _view = data.views['mem']; + + vm.percent = stats['percent']; + vm.total = stats['total']; + vm.used = stats['used']; + vm.free = stats['free']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginMemMore', { + controller: GlancesPluginMemMoreController, + controllerAs: 'vm', + templateUrl: 'components/plugin-mem-more/view.html' +}); + +'use strict'; + +function GlancesPluginMemMoreController($scope, GlancesStats) { + var vm = this; + + vm.active = null; + vm.inactive = null; + vm.buffers = null; + vm.cached = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['mem']; + + vm.active = stats['active']; + vm.inactive = stats['inactive']; + vm.buffers = stats['buffers']; + vm.cached = stats['cached']; + } +} + +'use strict'; + +glancesApp.component('glancesPluginMemswap', { + controller: GlancesPluginMemswapController, + controllerAs: 'vm', + templateUrl: 'components/plugin-memswap/view.html' +}); + +'use strict'; + +function GlancesPluginMemswapController($scope, GlancesStats) { + var vm = this; + var _view = {}; + + vm.percent = null; + vm.total = null; + vm.used = null; + vm.free = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['memswap']; + _view = data.views['memswap']; + + vm.percent = stats['percent']; + vm.total = stats['total']; + vm.used = stats['used']; + vm.free = stats['free']; + } + + this.getDecoration = function (value) { + if (_view[value] === undefined) { + return; + } + + return _view[value].decoration.toLowerCase(); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginNetwork', { + controller: GlancesPluginNetworkController, + controllerAs: 'vm', + templateUrl: 'components/plugin-network/view.html' +}); + +'use strict'; + +function GlancesPluginNetworkController($scope, $filter, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.networks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var networkStats = data.stats['network']; + + vm.networks = []; + for (var i = 0; i < networkStats.length; i++) { + var networkData = networkStats[i]; + + var network = { + 'interfaceName': networkData['interface_name'], + 'rx': networkData['rx'], + 'tx': networkData['tx'], + 'cx': networkData['cx'], + 'time_since_update': networkData['time_since_update'], + 'cumulativeRx': networkData['cumulative_rx'], + 'cumulativeTx': networkData['cumulative_tx'], + 'cumulativeCx': networkData['cumulative_cx'] + }; + + vm.networks.push(network); + } + + vm.networks = $filter('orderBy')(vm.networks, 'interfaceName'); + } +} + +'use strict'; + +glancesApp.component('glancesPluginPercpu', { + controller: GlancesPluginPercpuController, + controllerAs: 'vm', + templateUrl: 'components/plugin-percpu/view.html' +}); + +'use strict'; + +function GlancesPluginPercpuController($scope, GlancesStats, GlancesPluginHelper) { + var vm = this; + vm.cpus = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var percpuStats = data.stats['percpu']; + + vm.cpus = []; + + for (var i = 0; i < percpuStats.length; i++) { + var cpuData = percpuStats[i]; + + vm.cpus.push({ + 'number': cpuData.cpu_number, + 'total': cpuData.total, + 'user': cpuData.user, + 'system': cpuData.system, + 'idle': cpuData.idle, + 'iowait': cpuData.iowait, + 'steal': cpuData.steal + }); + } + } + + vm.getUserAlert = function (cpu) { + return GlancesPluginHelper.getAlert('percpu', 'percpu_user_', cpu.user) + }; + + vm.getSystemAlert = function (cpu) { + return GlancesPluginHelper.getAlert('percpu', 'percpu_system_', cpu.system); + }; +} + +'use strict'; + +glancesApp.component('glancesPluginPorts', { + controller: GlancesPluginPortsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-ports/view.html' +}); + +'use strict'; + +function GlancesPluginPortsController($scope, GlancesStats) { + var vm = this; + vm.ports = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['ports']; + + vm.ports = []; + angular.forEach(stats, function (port) { + vm.ports.push(port); + }, this); + } + + vm.getDecoration = function (port) { + if (port.status === null) { + return 'careful'; + } + + if (port.status === false) { + return 'critical'; + } + + if (port.rtt_warning !== null && port.status > port.rtt_warning) { + return 'warning'; + } + + return 'ok'; + }; +} + +'use strict'; + +glancesApp.component('glancesPluginProcess', { + controller: GlancesPluginProcessController, + controllerAs: 'vm', + templateUrl: 'components/plugin-process/view.html' +}); + +'use strict'; + +function GlancesPluginProcessController(ARGUMENTS, hotkeys) { + var vm = this; + vm.arguments = ARGUMENTS; + + vm.sorter = { + column: "cpu_percent", + auto: true, + isReverseColumn: function (column) { + return !(column === 'username' || column === 'name'); + }, + getColumnLabel: function (column) { + if (_.isEqual(column, ['io_read', 'io_write'])) { + return 'io_counters'; + } else { + return column; + } + } + }; + + // a => Sort processes automatically + hotkeys.add({ + combo: 'a', + callback: function () { + vm.sorter.column = "cpu_percent"; + vm.sorter.auto = true; + } + }); + + // c => Sort processes by CPU% + hotkeys.add({ + combo: 'c', + callback: function () { + vm.sorter.column = "cpu_percent"; + vm.sorter.auto = false; + } + }); + + // m => Sort processes by MEM% + hotkeys.add({ + combo: 'm', + callback: function () { + vm.sorter.column = "memory_percent"; + vm.sorter.auto = false; + } + }); + + // u => Sort processes by user + hotkeys.add({ + combo: 'u', + callback: function () { + vm.sorter.column = "username"; + vm.sorter.auto = false; + } + }); + + // p => Sort processes by name + hotkeys.add({ + combo: 'p', + callback: function () { + vm.sorter.column = "name"; + vm.sorter.auto = false; + } + }); + + // i => Sort processes by I/O rate + hotkeys.add({ + combo: 'i', + callback: function () { + vm.sorter.column = ['io_read', 'io_write']; + vm.sorter.auto = false; + } + }); + + // t => Sort processes by time + hotkeys.add({ + combo: 't', + callback: function () { + vm.sorter.column = "timemillis"; + vm.sorter.auto = false; + } + }); +} + +'use strict'; + +glancesApp.component('glancesPluginProcesscount', { + controller: GlancesPluginProcesscountController, + controllerAs: 'vm', + bindings: { + sorter: '<' + }, + templateUrl: 'components/plugin-processcount/view.html' +}); + +'use strict'; + +function GlancesPluginProcesscountController($scope, GlancesStats) { + var vm = this; + + vm.total = null; + vm.running = null; + vm.sleeping = null; + vm.stopped = null; + vm.thread = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processcountStats = data.stats['processcount']; + + vm.total = processcountStats['total'] || 0; + vm.running = processcountStats['running'] || 0; + vm.sleeping = processcountStats['sleeping'] || 0; + vm.stopped = processcountStats['stopped'] || 0; + vm.thread = processcountStats['thread'] || 0; + } +} + +'use strict'; + +glancesApp.component('glancesPluginProcesslist', { + controller: GlancesPluginProcesslistController, + controllerAs: 'vm', + bindings: { + sorter: '<' + }, + templateUrl: 'components/plugin-processlist/view.html' +}); + +'use strict'; + +function GlancesPluginProcesslistController($scope, GlancesStats, GlancesPluginHelper, $filter, CONFIG, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; + vm.processes = []; + vm.ioReadWritePresent = false; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var processlistStats = data.stats['processlist'] || []; + + vm.processes = []; + vm.ioReadWritePresent = false; + + for (var i = 0; i < processlistStats.length; i++) { + var process = processlistStats[i]; process.memvirt = process.memory_info[1]; - process.memres = process.memory_info[0]; + process.memres = process.memory_info[0]; process.timeplus = $filter('timedelta')(process.cpu_times); process.timemillis = $filter('timemillis')(process.cpu_times); @@ -1244,9 +1651,9 @@ glancesApp.service('GlancesPluginProcessList', ["$filter", "GlancesPlugin", func process.ioWrite = null; if (process.io_counters) { - this.ioReadWritePresent = true; + vm.ioReadWritePresent = true; - process.ioRead = (process.io_counters[0] - process.io_counters[2]) / process.time_since_update; + process.ioRead = (process.io_counters[0] - process.io_counters[2]) / process.time_since_update; if (process.ioRead != 0) { process.ioRead = $filter('bytes')(process.ioRead); @@ -1259,83 +1666,120 @@ glancesApp.service('GlancesPluginProcessList', ["$filter", "GlancesPlugin", func } } - process.isNice = process.nice !== undefined && ((data['system'].os_name === 'Windows' && process.nice != 32) || (data['system'].os_name !== 'Windows' && process.nice != 0)); + process.isNice = process.nice !== undefined && ((data.stats.isWindows && process.nice != 32) || (!data.stats.isWindows && process.nice != 0)); if (Array.isArray(process.cmdline)) { process.cmdline = process.cmdline.join(' '); } - if (data['system'].os_name === 'Windows') { + if (data.isWindows) { process.username = _.last(process.username.split('\\')); } - this.processes.push(process); + vm.processes.push(process); } + } + + vm.getCpuPercentAlert = function (process) { + return GlancesPluginHelper.getAlert('processlist', 'processlist_cpu_', process.cpu_percent); }; - this.getCpuPercentAlert = function(process) { - return GlancesPlugin.getAlert(_pluginName, 'processlist_cpu_', process.cpu_percent); + vm.getMemoryPercentAlert = function (process) { + return GlancesPluginHelper.getAlert('processlist', 'processlist_mem_', process.cpu_percent); }; - this.getMemoryPercentAlert = function(process) { - return GlancesPlugin.getAlert(_pluginName, 'processlist_mem_', process.cpu_percent); + vm.getLimit = function () { + return CONFIG.outputs !== undefined ? CONFIG.outputs.max_processes_display : undefined; }; +} - this.getLimit = function() { - return _maxProcessesToDisplay; - }; -}]); +'use strict'; -glancesApp.service('GlancesPluginQuicklook', function() { - var _pluginName = "quicklook"; +glancesApp.component('glancesPluginQuicklook', { + controller: GlancesPluginQuicklookController, + controllerAs: 'vm', + templateUrl: 'components/plugin-quicklook/view.html' +}); + +'use strict'; + +function GlancesPluginQuicklookController($scope, GlancesStats, ARGUMENTS) { + var vm = this; + vm.arguments = ARGUMENTS; var _view = {}; - this.mem = null; - this.cpu = null; - this.cpu_name = null; - this.cpu_hz_current = null; - this.cpu_hz = null; - this.swap = null; - this.percpus = []; + vm.mem = null; + vm.cpu = null; + vm.cpu_name = null; + vm.cpu_hz_current = null; + vm.cpu_hz = null; + vm.swap = null; + vm.percpus = []; - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; - this.mem = data.mem; - this.cpu = data.cpu; - this.cpu_name = data.cpu_name; - this.cpu_hz_current = data.cpu_hz_current; - this.cpu_hz = data.cpu_hz; - this.swap = data.swap; - this.percpus = []; + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); - angular.forEach(data.percpu, function(cpu) { - this.percpus.push({ + var loadData = function (data) { + var stats = data.stats['quicklook']; + _view = data.views['quicklook']; + + vm.mem = stats.mem; + vm.cpu = stats.cpu; + vm.cpu_name = stats.cpu_name; + vm.cpu_hz_current = stats.cpu_hz_current; + vm.cpu_hz = stats.cpu_hz; + vm.swap = stats.swap; + vm.percpus = []; + + angular.forEach(stats.percpu, function (cpu) { + vm.percpus.push({ 'number': cpu.cpu_number, 'total': cpu.total }); }, this); } - this.getDecoration = function(value) { - if(_view[value] == undefined) { + this.getDecoration = function (value) { + if (_view[value] === undefined) { return; } return _view[value].decoration.toLowerCase(); - } + }; +} + +'use strict'; + +glancesApp.component('glancesPluginRaid', { + controller: GlancesPluginRaidController, + controllerAs: 'vm', + templateUrl: 'components/plugin-raid/view.html' }); -glancesApp.service('GlancesPluginRaid', function () { - var _pluginName = "raid"; - this.disks = []; +'use strict'; - this.setData = function (data, views) { +function GlancesPluginRaidController($scope, GlancesStats) { + var vm = this; + vm.disks = []; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { var disks = []; - data = data[_pluginName]; + var stats = data.stats['raid']; - _.forIn(data, function(diskData, diskKey) { + _.forIn(stats, function (diskData, diskKey) { var disk = { 'name': diskKey, 'type': diskData.type == null ? 'UNKNOWN' : diskData.type, @@ -1348,7 +1792,7 @@ glancesApp.service('GlancesPluginRaid', function () { 'components': [] }; - _.forEach(diskData.components, function(number, name) { + _.forEach(diskData.components, function (number, name) { disk.components.push({ 'number': number, 'name': name @@ -1358,14 +1802,14 @@ glancesApp.service('GlancesPluginRaid', function () { disks.push(disk); }); - this.disks = disks; - }; + vm.disks = disks; + } - this.hasDisks = function() { + vm.hasDisks = function () { return this.disks.length > 0; } - this.getAlert = function(disk) { + vm.getAlert = function (disk) { if (disk.inactive) { return 'critical'; } @@ -1376,109 +1820,171 @@ glancesApp.service('GlancesPluginRaid', function () { return 'ok' } +} + +'use strict'; + +glancesApp.component('glancesPluginSensors', { + controller: GlancesPluginSensorsController, + controllerAs: 'vm', + templateUrl: 'components/plugin-sensors/view.html' }); -glancesApp.service('GlancesPluginSensors', ["GlancesPlugin", function(GlancesPlugin) { +'use strict'; - var _pluginName = "sensors"; - this.sensors = []; +function GlancesPluginSensorsController($scope, GlancesStats, GlancesPluginHelper) { + var vm = this; + vm.sensors = []; - this.setData = function(data, views) { - data = data[_pluginName]; + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; - _.remove(data, function(sensor) { + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['sensors']; + + _.remove(stats, function (sensor) { return (_.isArray(sensor.value) && _.isEmpty(sensor.value)) || sensor.value === 0; }); - this.sensors = data; - }; + vm.sensors = data; + } - this.getAlert = function(sensor) { + vm.getAlert = function (sensor) { var current = sensor.type == 'battery' ? 100 - sensor.value : sensor.value; - return GlancesPlugin.getAlert(_pluginName, 'sensors_' + sensor.type + '_', current); + return GlancesPluginHelper.getAlert('sensors', 'sensors_' + sensor.type + '_', current); }; -}]); +} -glancesApp.service('GlancesPluginSystem', function() { - var _pluginName = "system"; +'use strict'; - this.hostname = null; - this.platform = null; - this.humanReadableName = null; - this.os = { +glancesApp.component('glancesPluginSystem', { + controller: GlancesPluginSystemController, + controllerAs: 'vm', + templateUrl: 'components/plugin-system/view.html' +}); + +'use strict'; + +function GlancesPluginSystemController($scope, GlancesStats) { + var vm = this; + + vm.hostname = null; + vm.platform = null; + vm.humanReadableName = null; + vm.os = { 'name': null, 'version': null }; - this.setData = function(data, views) { - data = data[_pluginName]; - - this.hostname = data['hostname']; - this.platform = data['platform']; - this.os.name = data['os_name']; - this.os.version = data['os_version']; - this.humanReadableName = data['hr_name']; + vm.$onInit = function () { + loadData(GlancesStats.getData()); }; - this.isBsd = function() { - return this.os.name === 'FreeBSD'; - }; + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); - this.isLinux = function() { - return this.os.name === 'Linux'; - }; + $scope.$on('is_disconnected', function () { + vm.isDisconnected = true; + }); - this.isMac = function() { - return this.os.name === 'Darwin'; - }; + var loadData = function (data) { + var stats = data.stats['system']; - this.isWindows = function() { - return this.os.name === 'Windows'; - }; + vm.hostname = stats['hostname']; + vm.platform = stats['platform']; + vm.os.name = stats['os_name']; + vm.os.version = stats['os_version']; + vm.humanReadableName = stats['hr_name']; + vm.isDisconnected = false; + } +} + +'use strict'; + +glancesApp.component('glancesPluginUptime', { + controller: GlancesPluginUptimeController, + controllerAs: 'vm', + templateUrl: 'components/plugin-uptime/view.html' }); -glancesApp.service('GlancesPluginUptime', function() { - this.uptime = null; +'use strict'; - this.setData = function(data, views) { - this.uptime = data['uptime']; +function GlancesPluginUptimeController($scope, GlancesStats) { + var vm = this; + vm.value = null; + + vm.$onInit = function () { + loadData(GlancesStats.getData()); }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + vm.value = data.stats['uptime']; + } +} + +'use strict'; + +glancesApp.component('glancesPluginWifi', { + controller: GlancesPluginWifiController, + controllerAs: 'vm', + templateUrl: 'components/plugin-wifi/view.html' }); -glancesApp.service('GlancesPluginWifi', function() { - var _pluginName = "wifi"; +'use strict'; + +function GlancesPluginWifiController($scope, $filter, GlancesStats) { + var vm = this; var _view = {}; - this.hotspots = []; - this.setData = function(data, views) { - data = data[_pluginName]; - _view = views[_pluginName]; + vm.hotspots = []; - this.hotspots = []; - for (var i = 0; i < data.length; i++) { - var hotspotData = data[i]; + vm.$onInit = function () { + loadData(GlancesStats.getData()); + }; + + $scope.$on('data_refreshed', function (event, data) { + loadData(data); + }); + + var loadData = function (data) { + var stats = data.stats['wifi']; + _view = data.views['wifi']; + + vm.hotspots = []; + for (var i = 0; i < stats.length; i++) { + var hotspotData = stats[i]; if (hotspotData['ssid'] === '') { continue; } - var hotspot = { + vm.hotspots.push({ 'ssid': hotspotData['ssid'], 'encrypted': hotspotData['encrypted'], 'signal': hotspotData['signal'], - 'encryption_type': hotspotData['encryption_type'], - }; - - this.hotspots.push(hotspot); + 'encryption_type': hotspotData['encryption_type'] + }); } - }; - this.getDecoration = function(hotpost, field) { - if(_view[hotpost.ssid][field] == undefined) { + vm.hotspots = $filter('orderBy')(vm.hotspots, 'ssid'); + } + + vm.getDecoration = function (hotpost, field) { + if (_view[hotpost.ssid][field] == undefined) { return; } return _view[hotpost.ssid][field].decoration.toLowerCase(); }; -}); +} diff --git a/glances/outputs/static/public/js/templates.min.js b/glances/outputs/static/public/js/templates.min.js index 3c0c4f24..66d2a728 100644 --- a/glances/outputs/static/public/js/templates.min.js +++ b/glances/outputs/static/public/js/templates.min.js @@ -1,27 +1,29 @@ -angular.module('glancesApp').run(['$templateCache', function($templateCache) {$templateCache.put('plugins/alert.html','
\n
\n
\n{{alert.begin | date : \'yyyy-MM-dd H:mm:ss\'}} ({{ alert.ongoing ? \'ongoing\' : alert.duration }}) - {{alert.level}} on {{alert.name}} ({{alert.max}})\n\t\t
\n
\n
\n'); -$templateCache.put('plugins/alerts.html','No warning or critical alert detected\nWarning or critical alerts (lasts {{statsAlert.count()}} entries)\n'); -$templateCache.put('plugins/amps.html','
\n
\n
{{ process.name }}
\n
{{ process.count }}
\n
{{ process.result }}
\n
\n
\n'); -$templateCache.put('plugins/cloud.html','{{ statsCloud.getProvider() }} {{ statsCloud.getInstance() }}\n'); -$templateCache.put('plugins/cpu.html','
\n
\n
\n
\n
CPU
\n
{{ statsCpu.total }}%
\n
\n
\n
user:
\n
\n {{ statsCpu.user }}%\n
\n
\n
\n
system:
\n
\n {{ statsCpu.system }}%\n
\n
\n
\n
idle:
\n
{{ statsCpu.idle }}%
\n
\n
\n
\n \n \n
\n'); -$templateCache.put('plugins/diskio.html','
\n
DISK I/O
\n
R/s
\n
W/s
\n\n
IOR/s
\n
IOW/s
\n
\n
\n
{{(disk.alias ? disk.alias : disk.name) | min_size}}
\n
{{disk.bitrate.txps }}
\n
{{disk.bitrate.rxps }}
\n\n
{{disk.count.txps }}
\n
{{disk.count.rxps }}
\n
\n'); -$templateCache.put('plugins/docker.html','CONTAINERS {{ statsDocker.containers.length }} (served by Docker {{ statsDocker.version }})\n\n
\n
\n
Name
\n
Status
\n
CPU%
\n
MEM
\n
IOR/s
\n
IOW/s
\n
RX/s
\n
TX/s
\n
Command
\n
\n
\n
{{ container.name }}
\n
{{ container.status }}
\n
{{ container.cpu | number:1 }}
\n
{{ container.memory | bytes }}
\n
{{ container.ior / container.io_time_since_update | bits }}
\n
{{ container.iow / container.io_time_since_update | bits }}
\n
{{ container.rx / container.net_time_since_update | bits }}
\n
{{ container.tx / container.net_time_since_update | bits }}
\n
{{ container.command }}
\n
\n
\n'); -$templateCache.put('plugins/folders.html','
\n
FOLDERS
\n
Size
\n
\n
\n
{{ folder.path }}
\n
{{ folder.size | bytes }}
\n
\n'); -$templateCache.put('plugins/fs.html','
\n
FILE SYS
\n
\n Used\n Free\n
\n
Total
\n
\n
\n
{{ fs.shortMountPoint }} ({{ fs.name }})
\n
\n {{ fs.used | bytes }}\n {{ fs.free | bytes }}\n
\n
{{ fs.size | bytes }}
\n
\n'); -$templateCache.put('plugins/gpu.html','
\n {{ statsGpu.name }}\n
\n
\n
\n
proc:
\n
{{ statsGpu.mean.proc | number : 0 }}%
\n
N/A
\n
\n
\n
mem:
\n
{{ statsGpu.mean.mem | number : 0 }}%
\n
N/A
\n
\n
\n
\n {{ gpu.gpu_id }}:\n {{ gpu.proc | number : 0 }}%\n N/A\n mem:\n {{ gpu.mem | number : 0 }}%\n N/A\n
\n
\n
\n'); -$templateCache.put('plugins/ip.html',' - IP {{ statsIp.address }}/{{ statsIp.maskCidr }} Pub {{ statsIp.publicAddress }}\n'); -$templateCache.put('plugins/irq.html','
\n
IRQ
\n
\n
Rate/s
\n
\n
\n
{{irq.irq_line}}
\n
\n
{{irq.irq_rate}}
\n
\n'); -$templateCache.put('plugins/load.html','
\n
\n
LOAD
\n
{{ statsLoad.cpucore }}-core
\n
\n
\n
1 min:
\n
\n {{ statsLoad.min1 | number : 2}}\n
\n
\n
\n
5 min:
\n
\n {{ statsLoad.min5 | number : 2}}\n
\n
\n
\n
15 min:
\n
\n {{ statsLoad.min15 | number : 2}}\n
\n
\n
\n'); -$templateCache.put('plugins/mem.html','
\n
\n
MEM
\n
{{ statsMem.percent }}%
\n
\n
\n
total:
\n
{{ statsMem.total | bytes }}
\n
\n
\n
used:
\n
\n {{ statsMem.used | bytes:2 }}\n
\n
\n
\n
free:
\n
{{ statsMem.free | bytes }}
\n
\n
\n'); -$templateCache.put('plugins/mem_more.html','
\n
\n
active:
\n
{{ statsMem.active | bytes }}
\n
\n
\n
inactive:
\n
{{ statsMem.inactive | bytes }}
\n
\n
\n
buffers:
\n
{{ statsMem.buffers | bytes }}
\n
\n
\n
cached:
\n
{{ statsMem.cached | bytes }}
\n
\n
\n'); -$templateCache.put('plugins/memswap.html','
\n
\n
SWAP
\n
{{ statsMemSwap.percent }}%
\n
\n
\n
total:
\n
{{ statsMemSwap.total | bytes }}
\n
\n
\n
used:
\n
\n {{ statsMemSwap.used | bytes }}\n
\n
\n
\n
free:
\n
{{ statsMemSwap.free | bytes }}
\n
\n
\n'); -$templateCache.put('plugins/network.html','
\n
NETWORK
\n
Rx/s
\n
Tx/s
\n\n
\n
Rx+Tx/s
\n\n
Rx
\n
Tx
\n\n
\n
Rx+Tx
\n
\n
\n
{{ network.interfaceName | min_size }}
\n
{{ arguments.byte ? (network.rx / network.time_since_update | bytes) : (network.rx / network.time_since_update | bits) }}
\n
{{ arguments.byte ? (network.tx / network.time_since_update | bytes) : (network.tx / network.time_since_update | bits) }}
\n\n
\n
{{ arguments.byte ? (network.cx / network.time_since_update | bytes) : (network.cx / network.time_since_update | bits) }}
\n\n
{{ arguments.byte ? (network.cumulativeRx | bytes) : (network.cumulativeRx | bits) }}
\n
{{ arguments.byte ? (network.cumulativeTx | bytes) : (network.cumulativeTx | bits) }}
\n\n
\n
{{ arguments.byte ? (network.cumulativeCx | bytes) : (network.cumulativeCx | bits) }}
\n
\n'); -$templateCache.put('plugins/per_cpu.html','
\n
\n
PER CPU
\n
{{ percpu.total }}%
\n
\n
\n
user:
\n
\n {{ percpu.user }}%\n
\n
\n
\n
system:
\n
\n {{ percpu.system }}%\n
\n
\n
\n
idle:
\n
{{ percpu.idle }}%
\n
\n
\n
iowait:
\n
\n {{ percpu.iowait }}%\n
\n
\n
\n
steal:
\n
\n {{ percpu.steal }}%\n
\n
\n
\n'); -$templateCache.put('plugins/ports.html','
\n
{{(port.description ? port.description : port.host + \' \' + port.port) | min_size: 20}}
\n
\n
\n Scanning\n Timeout\n Open\n {{port.status * 1000.0 | number:0}}ms\n
\n
\n'); -$templateCache.put('plugins/processcount.html','TASKS\n{{ statsProcessCount.total }} ({{ statsProcessCount.thread }} thr),\n{{ statsProcessCount.running }} run,\n{{ statsProcessCount.sleeping }} slp,\n{{ statsProcessCount.stopped }} oth\n sorted {{ sorter.auto ? \'automatically\' : \'\' }} by {{ sorter.getColumnLabel(sorter.column) }}, flat view\n'); -$templateCache.put('plugins/processlist.html','
\n
\n
CPU%
\n
MEM%
\n \n \n
PID
\n
USER
\n
NI
\n
S
\n \n \n \n
Command
\n
\n
\n
{{process.cpu_percent | number:1}}
\n
{{process.memory_percent | number:1}}
\n \n \n
{{process.pid}}
\n
{{process.username}}
\n
{{process.nice | exclamation}}
\n
{{process.status}}
\n \n \n \n
{{process.name}}
\n
{{process.cmdline}}
\n
\n
\n'); -$templateCache.put('plugins/quicklook.html','
\n {{ statsQuicklook.cpu_name }}\n
\n
\n
\n
CPU
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ statsQuicklook.cpu }}%\n
\n
\n
\n
CPU{{ percpu.number }}
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ percpu.total }}%\n
\n
\n
\n
MEM
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ statsQuicklook.mem }}%\n
\n
\n
\n
SWAP
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ statsQuicklook.swap }}%\n
\n
\n
\n'); -$templateCache.put('plugins/raid.html','
\n
RAID disks
\n
Used
\n
Total
\n
\n
\n
\n {{ disk.type | uppercase }} {{ disk.name }}\n
\u2514\u2500 Degraded mode
\n
   \u2514\u2500 {{ disk.config }}
\n\n
\u2514\u2500 Status {{ disk.status }}
\n
\n    {{ $last ? \'\u2514\u2500\' : \'\u251C\u2500\' }} disk {{ component.number }}: {{ component.name }}\n
\n
\n
{{ disk.used }}
\n
{{ disk.available }}
\n
'); -$templateCache.put('plugins/sensors.html','
\n
SENSORS
\n
\n\n
\n
{{ sensor.label }}
\n
{{ sensor.unit }}
\n
{{ sensor.value }}
\n
\n'); -$templateCache.put('plugins/system.html','Disconnected from\n{{ statsSystem.hostname }}\n\n'); -$templateCache.put('plugins/uptime.html','Uptime: {{ statsUptime.uptime }}\n'); -$templateCache.put('plugins/wifi.html','
\n
WIFI
\n
\n
dBm
\n
\n
\n
{{ hotspot.ssid|limitTo:20 }} {{ hotspot.encryption_type }}
\n
\n
{{ hotspot.signal }}
\n
\n');}]); \ No newline at end of file +angular.module('glancesApp').run(['$templateCache', function($templateCache) {$templateCache.put('components/glances/view.html','
\n
\n \n
Loading...
\n
\n\n \n\n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n
\n\n
\n \n
\n \n
\n
\n \n
\n \n
\n \n
\n \n
\n \n
\n
\n \n
\n
\n
\n \n
\n \n \n \n
\n
\n
\n
\n
\n'); +$templateCache.put('components/help/view.html','
\n
\n
{{vm.help.version}} {{vm.help.psutil_version}}
\n
\n
 
\n
\n
{{vm.help.configuration_file}}
\n
\n
 
\n
\n
{{vm.help.sort_auto}}
\n
{{vm.help.sort_network}}
\n
\n
\n
{{vm.help.sort_cpu}}
\n
{{vm.help.show_hide_alert}}
\n
\n
\n
{{vm.help.sort_mem}}
\n
{{vm.help.percpu}}
\n
\n
\n
{{vm.help.sort_user}}
\n
{{vm.help.show_hide_ip}}
\n
\n
\n
{{vm.help.sort_proc}}
\n
{{vm.help.enable_disable_docker}}
\n
\n
\n
{{vm.help.sort_io}}
\n
{{vm.help.view_network_io_combination}}
\n
\n
\n
{{vm.help.sort_cpu_times}}
\n
{{vm.help.view_cumulative_network}}
\n
\n
\n
{{vm.help.show_hide_diskio}}
\n
{{vm.help.show_hide_filesytem_freespace}}
\n
\n
\n
{{vm.help.show_hide_filesystem}}
\n
{{vm.help.show_hide_vm.help}}
\n
\n
\n
{{vm.help.show_hide_network}}
\n
{{vm.help.diskio_iops}}
\n
\n
\n
{{vm.help.show_hide_sensors}}
\n
{{vm.help.show_hide_top_menu}}
\n
\n
\n
{{vm.help.show_hide_left_sidebar}}
\n
{{vm.help.show_hide_amp}}
\n
\n
\n
{{vm.help.enable_disable_process_stats}}
\n
{{vm.help.show_hide_irq}}
\n
\n
\n
{{vm.help.enable_disable_gpu}}
\n
{{vm.help.enable_disable_mean_gpu}}
\n
\n
\n
{{vm.help.enable_disable_quick_look}}
\n
\n
\n
\n
{{vm.help.enable_disable_short_processname}}
\n
\n
\n
\n
{{vm.help.enable_disable_ports}}
\n
\n
\n\n
\n'); +$templateCache.put('components/plugin-alert/view.html','
\n No warning or critical alert detected\n Warning or critical alerts (lasts {{vm.count()}} entries)\n
\n
\n
\n
\n
\n {{alert.begin | date : \'yyyy-MM-dd H:mm:ss\'}} ({{ alert.ongoing ? \'ongoing\' : alert.duration }}) - {{alert.level}} on {{alert.name}}\n ({{alert.max}})\n
\n
\n
\n
\n'); +$templateCache.put('components/plugin-amps/view.html','
\n
\n
\n
{{ process.name }}
\n
{{ process.count }}
\n
{{ process.result }}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-cloud/view.html','
\n {{ vm.provider }} {{ vm.instance }}\n
\n'); +$templateCache.put('components/plugin-cpu/view.html','
\n
\n
\n
\n
\n
CPU
\n
{{ vm.total }}%
\n
\n
\n
user:
\n
\n {{ vm.user }}%\n
\n
\n
\n
system:
\n
\n {{ vm.system }}%\n
\n
\n
\n
idle:
\n
{{ vm.idle }}%
\n
\n
\n
\n \n \n
\n
\n'); +$templateCache.put('components/plugin-diskio/view.html','
\n
DISK I/O
\n
R/s
\n
W/s
\n\n
IOR/s
\n
IOW/s
\n
\n
\n
{{(disk.alias ? disk.alias : disk.name) | min_size}}
\n
{{disk.bitrate.txps }}
\n
{{disk.bitrate.rxps }}
\n\n
{{disk.count.txps }}
\n
{{disk.count.rxps }}
\n
\n'); +$templateCache.put('components/plugin-docker/view.html','
\n CONTAINERS {{ vm.containers.length }} (served by Docker {{ vm.version }})\n\n
\n
\n
Name
\n
Status
\n
CPU%
\n
MEM
\n
IOR/s
\n
IOW/s
\n
RX/s
\n
TX/s
\n
Command
\n
\n
\n
{{ container.name }}
\n
{{ container.status }}\n
\n
{{ container.cpu | number:1 }}
\n
{{ container.memory | bytes }}
\n
{{ container.ior / container.io_time_since_update | bits }}
\n
{{ container.iow / container.io_time_since_update | bits }}
\n
{{ container.rx / container.net_time_since_update | bits }}
\n
{{ container.tx / container.net_time_since_update | bits }}
\n
{{ container.command }}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-folders/view.html','
\n
FOLDERS
\n
Size
\n
\n
\n
{{ folder.path }}
\n
{{ folder.size | bytes }}
\n
\n'); +$templateCache.put('components/plugin-fs/view.html','
\n
FILE SYS
\n
\n Used\n Free\n
\n
Total
\n
\n
\n
{{ fs.shortMountPoint }} ({{ fs.name }})\n
\n
\n {{ fs.used | bytes }}\n {{ fs.free | bytes }}\n
\n
{{ fs.size | bytes }}
\n
\n'); +$templateCache.put('components/plugin-gpu/view.html','
\n
\n {{ vm.name }}\n
\n
\n
\n
proc:
\n
{{ vm.mean.proc |\n number : 0 }}%\n
\n
N/A
\n
\n
\n
mem:
\n
{{ vm.mean.mem | number :\n 0 }}%\n
\n
N/A
\n
\n
\n
\n {{ gpu.gpu_id }}:\n {{ gpu.proc | number : 0 }}%\n N/A\n mem:\n {{ gpu.mem | number : 0 }}%\n N/A\n
\n
\n
\n
\n'); +$templateCache.put('components/plugin-ip/view.html','
\n  - IP {{ vm.address }}/{{ vm.maskCidr }} Pub {{ vm.publicAddress }}\n
\n'); +$templateCache.put('components/plugin-irq/view.html','
\n
IRQ
\n
\n
Rate/s
\n
\n
\n
{{irq.irq_line}}
\n
\n
{{irq.irq_rate}}
\n
\n'); +$templateCache.put('components/plugin-load/view.html','
\n
\n
\n
LOAD
\n
{{ vm.cpucore }}-core
\n
\n
\n
1 min:
\n
\n {{ vm.min1 | number : 2}}\n
\n
\n
\n
5 min:
\n
\n {{ vm.min5 | number : 2}}\n
\n
\n
\n
15 min:
\n
\n {{ vm.min15 | number : 2}}\n
\n
\n
\n
\n'); +$templateCache.put('components/plugin-mem/view.html','
\n
\n
\n
MEM
\n
{{ vm.percent }}%
\n
\n
\n
total:
\n
{{ vm.total | bytes }}
\n
\n
\n
used:
\n
\n {{ vm.used | bytes:2 }}\n
\n
\n
\n
free:
\n
{{ vm.free | bytes }}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-mem-more/view.html','
\n
\n
\n
active:
\n
{{ vm.active | bytes }}
\n
\n
\n
inactive:
\n
{{ vm.inactive | bytes }}
\n
\n
\n
buffers:
\n
{{ vm.buffers | bytes }}
\n
\n
\n
cached:
\n
{{ vm.cached | bytes }}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-memswap/view.html','
\n
\n
\n
SWAP
\n
{{ vm.percent }}%
\n
\n
\n
total:
\n
{{ vm.total | bytes }}
\n
\n
\n
used:
\n
\n {{ vm.used | bytes }}\n
\n
\n
\n
free:
\n
{{ vm.free | bytes }}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-network/view.html','
\n
NETWORK
\n
Rx/s
\n
Tx/s
\n\n
\n
Rx+Tx/s
\n\n
Rx
\n
Tx
\n\n
\n
Rx+Tx
\n
\n
\n
{{ network.interfaceName | min_size }}
\n
{{ vm.arguments.byte ?\n (network.rx / network.time_since_update | bytes) : (network.rx / network.time_since_update | bits) }}\n
\n
{{ vm.arguments.byte ?\n (network.tx / network.time_since_update | bytes) : (network.tx / network.time_since_update | bits) }}\n
\n\n
\n
{{ vm.arguments.byte ?\n (network.cx / network.time_since_update | bytes) : (network.cx / network.time_since_update | bits) }}\n
\n\n
{{ vm.arguments.byte ?\n (network.cumulativeRx | bytes) : (network.cumulativeRx | bits) }}\n
\n
{{ vm.arguments.byte ?\n (network.cumulativeTx | bytes) : (network.cumulativeTx | bits) }}\n
\n\n
\n
{{ vm.arguments.byte ?\n (network.cumulativeCx | bytes) : (network.cumulativeCx | bits) }}\n
\n
\n'); +$templateCache.put('components/plugin-percpu/view.html','
\n
\n
\n
PER CPU
\n
{{ percpu.total }}%
\n
\n
\n
user:
\n
\n {{ percpu.user }}%\n
\n
\n
\n
system:
\n
\n {{ percpu.system }}%\n
\n
\n
\n
idle:
\n
{{ percpu.idle }}%
\n
\n
\n
iowait:
\n
\n {{ percpu.iowait }}%\n
\n
\n
\n
steal:
\n
\n {{ percpu.steal }}%\n
\n
\n
\n
\n'); +$templateCache.put('components/plugin-ports/view.html','
\n
{{(port.description ? port.description : port.host + \' \' + port.port) | min_size:\n 20}}\n
\n
\n
\n Scanning\n Timeout\n Open\n {{port.status * 1000.0 | number:0}}ms\n
\n
'); +$templateCache.put('components/plugin-process/view.html','
\n \n
\n
\n \n
\n
\n \n
\n
PROCESSES DISABLED (press \'z\' to display)
\n'); +$templateCache.put('components/plugin-processcount/view.html','
\n TASKS\n {{ vm.total }} ({{ vm.thread }} thr),\n {{ vm.running }} run,\n {{ vm.sleeping }} slp,\n {{ vm.stopped }} oth\n sorted {{ vm.sorter.auto ? \'automatically\' : \'\' }} by {{ vm.sorter.getColumnLabel(vm.sorter.column) }}, flat view\n
'); +$templateCache.put('components/plugin-processlist/view.html','
\n
\n
\n
CPU%
\n
MEM%
\n \n \n
PID
\n
USER
\n
NI
\n
S
\n \n \n \n
Command
\n
\n
\n
{{process.cpu_percent | number:1}}
\n
{{process.memory_percent | number:1}}\n
\n \n \n
{{process.pid}}
\n
{{process.username}}
\n
{{process.nice | exclamation}}
\n
{{process.status}}
\n \n \n \n
{{process.name}}
\n
{{process.cmdline}}
\n
\n
\n
\n'); +$templateCache.put('components/plugin-quicklook/view.html','
\n
\n {{ vm.cpu_name }}\n
\n
\n
\n
CPU
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ vm.cpu }}%\n
\n
\n
\n
CPU{{ percpu.number }}
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ percpu.total }}%\n
\n
\n
\n
MEM
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ vm.mem }}%\n
\n
\n
\n
SWAP
\n
\n
\n
\n  \n
\n
\n
\n
\n {{ vm.swap }}%\n
\n
\n
\n
\n'); +$templateCache.put('components/plugin-raid/view.html','
\n
RAID disks
\n
Used
\n
Total
\n
\n
\n
\n {{ disk.type | uppercase }} {{ disk.name }}\n
\u2514\u2500 Degraded mode
\n
   \u2514\u2500 {{ disk.config }}
\n\n
\u2514\u2500 Status {{ disk.status }}
\n
\n    {{ $last ? \'\u2514\u2500\' : \'\u251C\u2500\' }} disk {{ component.number }}: {{ component.name }}\n
\n
\n
{{ disk.used }}
\n
{{ disk.available }}
\n
\n'); +$templateCache.put('components/plugin-sensors/view.html','
\n
SENSORS
\n
\n\n
\n
{{ sensor.label }}
\n
{{ sensor.unit }}
\n
{{ sensor.value }}
\n
\n'); +$templateCache.put('components/plugin-system/view.html','
\n Disconnected from\n {{ vm.hostname }}\n \n \n
\n'); +$templateCache.put('components/plugin-uptime/view.html','
\n Uptime: {{ vm.value }}\n
\n'); +$templateCache.put('components/plugin-wifi/view.html','
\n
\n
WIFI
\n
\n
dBm
\n
\n
\n
{{ hotspot.ssid|limitTo:20 }} {{ hotspot.encryption_type }}\n
\n
\n
{{ hotspot.signal }}
\n
\n
\n');}]); \ No newline at end of file diff --git a/glances/outputs/static/public/js/vendor.min.js b/glances/outputs/static/public/js/vendor.min.js index 7c1b5af7..7ac0fa40 100644 --- a/glances/outputs/static/public/js/vendor.min.js +++ b/glances/outputs/static/public/js/vendor.min.js @@ -1,6 +1,6 @@ /** - * @license AngularJS v1.6.1 - * (c) 2010-2016 Google, Inc. http://angularjs.org + * @license AngularJS v1.6.4 + * (c) 2010-2017 Google, Inc. http://angularjs.org * License: MIT */ (function(window) {'use strict'; @@ -38,31 +38,29 @@ function minErr(module, ErrorConstructor) { ErrorConstructor = ErrorConstructor || Error; return function() { - var SKIP_INDEXES = 2; - - var templateArgs = arguments, - code = templateArgs[0], + var code = arguments[0], + template = arguments[1], message = '[' + (module ? module + ':' : '') + code + '] ', - template = templateArgs[1], + templateArgs = sliceArgs(arguments, 2).map(function(arg) { + return toDebugString(arg, minErrConfig.objectMaxDepth); + }), paramPrefix, i; message += template.replace(/\{\d+\}/g, function(match) { - var index = +match.slice(1, -1), - shiftedIndex = index + SKIP_INDEXES; + var index = +match.slice(1, -1); - if (shiftedIndex < templateArgs.length) { - return toDebugString(templateArgs[shiftedIndex]); + if (index < templateArgs.length) { + return templateArgs[index]; } return match; }); - message += '\nhttp://errors.angularjs.org/1.6.1/' + + message += '\nhttp://errors.angularjs.org/1.6.4/' + (module ? module + '/' : '') + code; - for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { - message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + - encodeURIComponent(toDebugString(templateArgs[i])); + for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { + message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]); } return new ErrorConstructor(message); @@ -79,6 +77,9 @@ function minErr(module, ErrorConstructor) { splice, push, toString, + minErrConfig, + errorHandlingConfig, + isValidObjectMaxDepth, ngMinErr, angularModule, uid, @@ -128,6 +129,7 @@ function minErr(module, ErrorConstructor) { includes, arrayRemove, copy, + simpleCompare, equals, csp, jq, @@ -194,6 +196,50 @@ var VALIDITY_STATE_PROPERTY = 'validity'; var hasOwnProperty = Object.prototype.hasOwnProperty; +var minErrConfig = { + objectMaxDepth: 5 +}; + + /** + * @ngdoc function + * @name angular.errorHandlingConfig + * @module ng + * @kind function + * + * @description + * Configure several aspects of error handling in AngularJS if used as a setter or return the + * current configuration if used as a getter. The following options are supported: + * + * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages. + * + * Omitted or undefined options will leave the corresponding configuration values unchanged. + * + * @param {Object=} config - The configuration object. May only contain the options that need to be + * updated. Supported keys: + * + * * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a + * non-positive or non-numeric value, removes the max depth limit. + * Default: 5 + */ +function errorHandlingConfig(config) { + if (isObject(config)) { + if (isDefined(config.objectMaxDepth)) { + minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN; + } + } else { + return minErrConfig; + } +} + +/** + * @private + * @param {Number} maxDepth + * @return {boolean} + */ +function isValidObjectMaxDepth(maxDepth) { + return isNumber(maxDepth) && maxDepth > 0; +} + /** * @ngdoc function * @name angular.lowercase @@ -916,9 +962,10 @@ function arrayRemove(array, value) { */ -function copy(source, destination) { +function copy(source, destination, maxDepth) { var stackSource = []; var stackDest = []; + maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN; if (destination) { if (isTypedArray(destination) || isArrayBuffer(destination)) { @@ -941,35 +988,39 @@ function copy(source, destination) { stackSource.push(source); stackDest.push(destination); - return copyRecurse(source, destination); + return copyRecurse(source, destination, maxDepth); } - return copyElement(source); + return copyElement(source, maxDepth); - function copyRecurse(source, destination) { + function copyRecurse(source, destination, maxDepth) { + maxDepth--; + if (maxDepth < 0) { + return '...'; + } var h = destination.$$hashKey; var key; if (isArray(source)) { for (var i = 0, ii = source.length; i < ii; i++) { - destination.push(copyElement(source[i])); + destination.push(copyElement(source[i], maxDepth)); } } else if (isBlankObject(source)) { // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty for (key in source) { - destination[key] = copyElement(source[key]); + destination[key] = copyElement(source[key], maxDepth); } } else if (source && typeof source.hasOwnProperty === 'function') { // Slow path, which must rely on hasOwnProperty for (key in source) { if (source.hasOwnProperty(key)) { - destination[key] = copyElement(source[key]); + destination[key] = copyElement(source[key], maxDepth); } } } else { // Slowest path --- hasOwnProperty can't be called as a method for (key in source) { if (hasOwnProperty.call(source, key)) { - destination[key] = copyElement(source[key]); + destination[key] = copyElement(source[key], maxDepth); } } } @@ -977,7 +1028,7 @@ function copy(source, destination) { return destination; } - function copyElement(source) { + function copyElement(source, maxDepth) { // Simple values if (!isObject(source)) { return source; @@ -1006,7 +1057,7 @@ function copy(source, destination) { stackDest.push(destination); return needsRecurse - ? copyRecurse(source, destination) + ? copyRecurse(source, destination, maxDepth) : destination; } @@ -1057,6 +1108,10 @@ function copy(source, destination) { } +// eslint-disable-next-line no-self-compare +function simpleCompare(a, b) { return a === b || (a !== a && b !== b); } + + /** * @ngdoc function * @name angular.equals @@ -1137,7 +1192,7 @@ function equals(o1, o2) { } } else if (isDate(o1)) { if (!isDate(o2)) return false; - return equals(o1.getTime(), o2.getTime()); + return simpleCompare(o1.getTime(), o2.getTime()); } else if (isRegExp(o1)) { if (!isRegExp(o2)) return false; return o1.toString() === o2.toString(); @@ -1548,30 +1603,51 @@ function getNgAttribute(element, ngAttr) { } function allowAutoBootstrap(document) { - if (!document.currentScript) { + var script = document.currentScript; + + if (!script) { + // IE does not have `document.currentScript` return true; } - var src = document.currentScript.getAttribute('src'); - var link = document.createElement('a'); - link.href = src; - if (document.location.origin === link.origin) { - // Same-origin resources are always allowed, even for non-whitelisted schemes. - return true; + + // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack + if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) { + return false; } - // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web. - // This is to prevent angular.js bundled with browser extensions from being used to bypass the - // content security policy in web pages and other browser extensions. - switch (link.protocol) { - case 'http:': - case 'https:': - case 'ftp:': - case 'blob:': - case 'file:': - case 'data:': + + var attributes = script.attributes; + var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')]; + + return srcs.every(function(src) { + if (!src) { return true; - default: + } + if (!src.value) { return false; - } + } + + var link = document.createElement('a'); + link.href = src.value; + + if (document.location.origin === link.origin) { + // Same-origin resources are always allowed, even for non-whitelisted schemes. + return true; + } + // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web. + // This is to prevent angular.js bundled with browser extensions from being used to bypass the + // content security policy in web pages and other browser extensions. + switch (link.protocol) { + case 'http:': + case 'https:': + case 'ftp:': + case 'blob:': + case 'file:': + case 'data:': + return true; + default: + return false; + } + }); } // Cached as it has to run during loading so that document.currentScript is available. @@ -1935,7 +2011,7 @@ function bindJQuery() { extend(jQuery.fn, { scope: JQLitePrototype.scope, isolateScope: JQLitePrototype.isolateScope, - controller: JQLitePrototype.controller, + controller: /** @type {?} */ (JQLitePrototype).controller, injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); @@ -2168,6 +2244,9 @@ function setupModuleLoader(window) { * @returns {angular.Module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { + + var info = {}; + var assertNotHasOwnProperty = function(name, context) { if (name === 'hasOwnProperty') { throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); @@ -2203,6 +2282,45 @@ function setupModuleLoader(window) { _configBlocks: configBlocks, _runBlocks: runBlocks, + /** + * @ngdoc method + * @name angular.Module#info + * @module ng + * + * @param {Object=} info Information about the module + * @returns {Object|Module} The current info object for this module if called as a getter, + * or `this` if called as a setter. + * + * @description + * Read and write custom information about this module. + * For example you could put the version of the module in here. + * + * ```js + * angular.module('myModule', []).info({ version: '1.0.0' }); + * ``` + * + * The version could then be read back out by accessing the module elsewhere: + * + * ``` + * var version = angular.module('myModule').info().version; + * ``` + * + * You can also retrieve this information during runtime via the + * {@link $injector#modules `$injector.modules`} property: + * + * ```js + * var version = $injector.modules['myModule'].info().version; + * ``` + */ + info: function(value) { + if (isDefined(value)) { + if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value'); + info = value; + return this; + } + return info; + }, + /** * @ngdoc property * @name angular.Module#requires @@ -2481,9 +2599,15 @@ function shallowCopy(src, dst) { /* global toDebugString: true */ -function serializeObject(obj) { +function serializeObject(obj, maxDepth) { var seen = []; + // There is no direct way to stringify object until reaching a specific depth + // and a very deep object can cause a performance issue, so we copy the object + // based on this specific depth and then stringify it. + if (isValidObjectMaxDepth(maxDepth)) { + obj = copy(obj, null, maxDepth); + } return JSON.stringify(obj, function(key, val) { val = toJsonReplacer(key, val); if (isObject(val)) { @@ -2496,13 +2620,13 @@ function serializeObject(obj) { }); } -function toDebugString(obj) { +function toDebugString(obj, maxDepth) { if (typeof obj === 'function') { return obj.toString().replace(/ \{[\s\S]*$/, ''); } else if (isUndefined(obj)) { return 'undefined'; } else if (typeof obj !== 'string') { - return serializeObject(obj); + return serializeObject(obj, maxDepth); } return obj; } @@ -2577,7 +2701,6 @@ function toDebugString(obj) { $$ForceReflowProvider, $InterpolateProvider, $IntervalProvider, - $$HashMapProvider, $HttpProvider, $HttpParamSerializerProvider, $HttpParamSerializerJQLikeProvider, @@ -2586,6 +2709,7 @@ function toDebugString(obj) { $jsonpCallbacksProvider, $LocationProvider, $LogProvider, + $$MapProvider, $ParseProvider, $RootScopeProvider, $QProvider, @@ -2623,16 +2747,17 @@ function toDebugString(obj) { var version = { // These placeholder strings will be replaced by grunt's `build` task. // They need to be double- or single-quoted. - full: '1.6.1', + full: '1.6.4', major: 1, minor: 6, - dot: 1, - codeName: 'promise-rectification' + dot: 4, + codeName: 'phenomenal-footnote' }; function publishExternalAPI(angular) { extend(angular, { + 'errorHandlingConfig': errorHandlingConfig, 'bootstrap': bootstrap, 'copy': copy, 'extend': extend, @@ -2767,11 +2892,12 @@ function publishExternalAPI(angular) { $window: $WindowProvider, $$rAF: $$RAFProvider, $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, + $$Map: $$MapProvider, $$cookieReader: $$CookieReaderProvider }); } - ]); + ]) + .info({ angularVersion: '1.6.4' }); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -2975,12 +3101,6 @@ function jqLiteHasData(node) { return false; } -function jqLiteCleanData(nodes) { - for (var i = 0, ii = nodes.length; i < ii; i++) { - jqLiteRemoveData(nodes[i]); - } -} - function jqLiteBuildFragment(html, context) { var tmp, tag, wrap, fragment = context.createDocumentFragment(), @@ -3083,13 +3203,10 @@ function jqLiteClone(element) { } function jqLiteDealoc(element, onlyDescendants) { - if (!onlyDescendants) jqLiteRemoveData(element); + if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]); if (element.querySelectorAll) { - var descendants = element.querySelectorAll('*'); - for (var i = 0, l = descendants.length; i < l; i++) { - jqLiteRemoveData(descendants[i]); - } + jqLite.cleanData(element.querySelectorAll('*')); } } @@ -3387,7 +3504,11 @@ forEach({ data: jqLiteData, removeData: jqLiteRemoveData, hasData: jqLiteHasData, - cleanData: jqLiteCleanData + cleanData: function jqLiteCleanData(nodes) { + for (var i = 0, ii = nodes.length; i < ii; i++) { + jqLiteRemoveData(nodes[i]); + } + } }, function(fn, name) { JQLite[name] = fn; }); @@ -3915,50 +4036,70 @@ function hashKey(obj, nextUidFn) { return key; } -/** - * HashMap which can use objects as keys - */ -function HashMap(array, isolatedUid) { - if (isolatedUid) { - var uid = 0; - this.nextUid = function() { - return ++uid; - }; - } - forEach(array, this.put, this); +// A minimal ES2015 Map implementation. +// Should be bug/feature equivalent to the native implementations of supported browsers +// (for the features required in Angular). +// See https://kangax.github.io/compat-table/es6/#test-Map +var nanKey = Object.create(null); +function NgMapShim() { + this._keys = []; + this._values = []; + this._lastKey = NaN; + this._lastIndex = -1; } -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key, this.nextUid)] = value; +NgMapShim.prototype = { + _idx: function(key) { + if (key === this._lastKey) { + return this._lastIndex; + } + this._lastKey = key; + this._lastIndex = this._keys.indexOf(key); + return this._lastIndex; + }, + _transformKey: function(key) { + return isNumberNaN(key) ? nanKey : key; }, - - /** - * @param key - * @returns {Object} the value for the key - */ get: function(key) { - return this[hashKey(key, this.nextUid)]; + key = this._transformKey(key); + var idx = this._idx(key); + if (idx !== -1) { + return this._values[idx]; + } }, + set: function(key, value) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + idx = this._lastIndex = this._keys.length; + } + this._keys[idx] = key; + this._values[idx] = value; - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key, this.nextUid)]; - delete this[key]; - return value; + // Support: IE11 + // Do not `return this` to simulate the partial IE11 implementation + }, + delete: function(key) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + return false; + } + this._keys.splice(idx, 1); + this._values.splice(idx, 1); + this._lastKey = NaN; + this._lastIndex = -1; + return true; } }; -var $$HashMapProvider = [/** @this */function() { +// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations +// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map` +// implementations get more stable, we can reconsider switching to `window.Map` (when available). +var NgMap = NgMapShim; + +var $$MapProvider = [/** @this */function() { this.$get = [function() { - return HashMap; + return NgMap; }]; }]; @@ -4033,11 +4174,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var $injectorMinErr = minErr('$injector'); function stringifyFn(fn) { - // Support: Chrome 50-51 only - // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51 - // (See https://github.com/angular/angular.js/issues/14487.) - // TODO (gkalpak): Remove workaround when Chrome v52 is released - return Function.prototype.toString.call(fn) + ' '; + return Function.prototype.toString.call(fn); } function extractArgs(fn) { @@ -4146,6 +4283,28 @@ function annotate(fn, strictDi, name) { * As an array of injection names, where the last item in the array is the function to call. */ +/** + * @ngdoc property + * @name $injector#modules + * @type {Object} + * @description + * A hash containing all the modules that have been loaded into the + * $injector. + * + * You can use this property to find out information about a module via the + * {@link angular.Module#info `myModule.info(...)`} method. + * + * For example: + * + * ``` + * var info = $injector.modules['ngAnimate'].info(); + * ``` + * + * **Do not use this property to attempt to modify the modules after the application + * has been bootstrapped.** + */ + + /** * @ngdoc method * @name $injector#get @@ -4611,7 +4770,7 @@ function createInjector(modulesToLoad, strictDi) { var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], - loadedModules = new HashMap([], true), + loadedModules = new NgMap(), providerCache = { $provide: { provider: supportObject(provider), @@ -4639,6 +4798,7 @@ function createInjector(modulesToLoad, strictDi) { instanceInjector = protoInstanceInjector; providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) }; + instanceInjector.modules = providerInjector.modules = createMap(); var runBlocks = loadModules(modulesToLoad); instanceInjector = protoInstanceInjector.get('$injector'); instanceInjector.strictDi = strictDi; @@ -4719,7 +4879,7 @@ function createInjector(modulesToLoad, strictDi) { var runBlocks = [], moduleFn; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; - loadedModules.put(module, true); + loadedModules.set(module, true); function runInvokeQueue(queue) { var i, ii; @@ -4734,6 +4894,7 @@ function createInjector(modulesToLoad, strictDi) { try { if (isString(module)) { moduleFn = angularModule(module); + instanceInjector.modules[module] = moduleFn; runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); runInvokeQueue(moduleFn._invokeQueue); runInvokeQueue(moduleFn._configBlocks); @@ -5205,7 +5366,7 @@ var $$CoreAnimateJsProvider = /** @this */ function() { // this is prefixed with Core since it conflicts with // the animateQueueProvider defined in ngAnimate/animateQueue.js var $$CoreAnimateQueueProvider = /** @this */ function() { - var postDigestQueue = new HashMap(); + var postDigestQueue = new NgMap(); var postDigestElements = []; this.$get = ['$$AnimateRunner', '$rootScope', @@ -5284,7 +5445,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() { jqLiteRemoveClass(elm, toRemove); } }); - postDigestQueue.remove(element); + postDigestQueue.delete(element); } }); postDigestElements.length = 0; @@ -5299,7 +5460,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() { if (classesAdded || classesRemoved) { - postDigestQueue.put(element, data); + postDigestQueue.set(element, data); postDigestElements.push(element); if (postDigestElements.length === 1) { @@ -5324,6 +5485,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() { */ var $AnimateProvider = ['$provide', /** @this */ function($provide) { var provider = this; + var classNameFilter = null; this.$$registeredAnimations = Object.create(null); @@ -5392,15 +5554,16 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) { */ this.classNameFilter = function(expression) { if (arguments.length === 1) { - this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; - if (this.$$classNameFilter) { - var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)'); - if (reservedRegex.test(this.$$classNameFilter.toString())) { - throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); + classNameFilter = (expression instanceof RegExp) ? expression : null; + if (classNameFilter) { + var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]'); + if (reservedRegex.test(classNameFilter.toString())) { + classNameFilter = null; + throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); } } } - return this.$$classNameFilter; + return classNameFilter; }; this.$get = ['$$animateQueue', function($$animateQueue) { @@ -6159,7 +6322,6 @@ function Browser(window, document, $log, $sniffer) { }; cacheState(); - lastHistoryState = cachedState; /** * @name $browser#url @@ -6213,8 +6375,6 @@ function Browser(window, document, $log, $sniffer) { if ($sniffer.history && (!sameBase || !sameState)) { history[replace ? 'replaceState' : 'pushState'](state, '', url); cacheState(); - // Do the assignment again so that those two variables are referentially identical. - lastHistoryState = cachedState; } else { if (!sameBase) { pendingLocation = url; @@ -6263,8 +6423,7 @@ function Browser(window, document, $log, $sniffer) { function cacheStateAndFireUrlChange() { pendingLocation = null; - cacheState(); - fireUrlChange(); + fireStateOrUrlChange(); } // This variable should be used *only* inside the cacheState function. @@ -6278,11 +6437,16 @@ function Browser(window, document, $log, $sniffer) { if (equals(cachedState, lastCachedState)) { cachedState = lastCachedState; } + lastCachedState = cachedState; + lastHistoryState = cachedState; } - function fireUrlChange() { - if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { + function fireStateOrUrlChange() { + var prevLastHistoryState = lastHistoryState; + cacheState(); + + if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) { return; } @@ -6317,8 +6481,8 @@ function Browser(window, document, $log, $sniffer) { self.onUrlChange = function(callback) { // TODO(vojta): refactor to use node's syntax for events if (!urlChangeInit) { - // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) - // don't fire popstate when user change the address bar and don't fire hashchange when url + // We listen on both (hashchange/popstate) when available, as some browsers don't + // fire popstate when user changes the address bar and don't fire hashchange when url // changed by push/replaceState // html5 history api - popstate event @@ -6348,7 +6512,7 @@ function Browser(window, document, $log, $sniffer) { * Needs to be exported to be able to check for changes that have been done in sync, * as hashchange/popstate events fire in async. */ - self.$$checkUrlChange = fireUrlChange; + self.$$checkUrlChange = fireStateOrUrlChange; ////////////////////////////////////////////////////////////// // Misc API @@ -6958,7 +7122,8 @@ function $TemplateCacheProvider() { * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a - * component such as cloning the bound value to prevent accidental mutation of the outer value. + * component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will + * also be called when your bindings are initialized. * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on * changes. Any actions that you wish to take in response to the changes that you detect must be * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook @@ -7106,10 +7271,12 @@ function $TemplateCacheProvider() { * the directive's element. If multiple directives on the same element request a new scope, * only one new scope is created. * - * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The - * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent - * scope. This is useful when creating reusable components, which should not accidentally read or modify - * data in the parent scope. + * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template. + * The 'isolate' scope differs from normal scope in that it does not prototypically + * inherit from its parent scope. This is useful when creating reusable components, which should not + * accidentally read or modify data in the parent scope. Note that an isolate scope + * directive without a `template` or `templateUrl` will not apply the isolate scope + * to its children elements. * * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the * directive's element. These local properties are useful for aliasing values for templates. The keys in @@ -7202,9 +7369,9 @@ function $TemplateCacheProvider() { * initialized. * *
- * **Deprecation warning:** although bindings for non-ES6 class controllers are currently - * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization - * code that relies upon bindings inside a `$onInit` method on the controller, instead. + * **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class + * controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please + * place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead. *
* * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property. @@ -7813,7 +7980,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var bindingCache = createMap(); function parseIsolateBindings(scope, directiveName, isController) { - var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/; + var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/; var bindings = createMap(); @@ -8217,7 +8384,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { * * If disabled (false), the compiler calls the constructor first before assigning bindings. * - * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x. + * The default value is false. + * + * @deprecated + * sinceVersion="1.6.0" + * removeVersion="1.7.0" + * + * This method and the option to assign the bindings before calling the controller's constructor + * will be removed in v1.7.0. */ var preAssignBindingsEnabled = false; this.preAssignBindingsEnabled = function(enabled) { @@ -9985,7 +10159,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (error instanceof Error) { $exceptionHandler(error); } - }).catch(noop); + }); return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { var childBoundTranscludeFn = boundTranscludeFn; @@ -10307,8 +10481,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (parentGet.literal) { compare = equals; } else { - // eslint-disable-next-line no-self-compare - compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }; + compare = simpleCompare; } parentSet = parentGet.assign || function() { // reset the change, or we will throw this exception on every $digest @@ -10383,9 +10556,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { }); function recordChanges(key, currentValue, previousValue) { - if (isFunction(destination.$onChanges) && currentValue !== previousValue && - // eslint-disable-next-line no-self-compare - (currentValue === currentValue || previousValue === previousValue)) { + if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) { // If we have not already scheduled the top level onChangesQueue handler then do so now if (!onChangesQueue) { scope.$$postDigest(flushOnChangesQueue); @@ -11000,7 +11171,12 @@ function defaultHttpResponseTransform(data, headers) { if (tempData) { var contentType = headers('Content-Type'); if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { - data = fromJson(tempData); + try { + data = fromJson(tempData); + } catch (e) { + throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' + + 'Parse error: "{1}"', data, e); + } } } } @@ -12117,7 +12293,8 @@ function $HttpProvider() { if ((config.cache || defaults.cache) && config.cache !== false && (config.method === 'GET' || config.method === 'JSONP')) { cache = isObject(config.cache) ? config.cache - : isObject(defaults.cache) ? defaults.cache + : isObject(/** @type {?} */ (defaults).cache) + ? /** @type {?} */ (defaults).cache : defaultCache; } @@ -12904,7 +13081,7 @@ function $IntervalProvider() { * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. * @param {...*=} Pass additional parameters to the executed function. - * @returns {promise} A promise which will be notified on each iteration. + * @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete. * * @example * @@ -13076,8 +13253,8 @@ function $IntervalProvider() { * how they vary compared to the requested url. */ var $jsonpCallbacksProvider = /** @this */ function() { - this.$get = ['$window', function($window) { - var callbacks = $window.angular.callbacks; + this.$get = function() { + var callbacks = angular.callbacks; var callbackMap = {}; function createCallback(callbackId) { @@ -13144,7 +13321,7 @@ var $jsonpCallbacksProvider = /** @this */ function() { delete callbackMap[callbackPath]; } }; - }]; + }; }; /** @@ -13295,6 +13472,8 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + + this.$$urlUpdatedByLocation = true; }; this.$$parseLinkUrl = function(url, relHref) { @@ -13372,7 +13551,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { withoutHashUrl = ''; if (isUndefined(withoutBaseUrl)) { appBase = url; - this.replace(); + /** @type {?} */ (this).replace(); } } } @@ -13428,6 +13607,8 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + + this.$$urlUpdatedByLocation = true; }; this.$$parseLinkUrl = function(url, relHref) { @@ -13485,6 +13666,8 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' this.$$absUrl = appBase + hashPrefix + this.$$url; + + this.$$urlUpdatedByLocation = true; }; } @@ -13814,6 +13997,7 @@ forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], fun // but we're changing the $$state reference to $browser.state() during the $digest // so the modification window is narrow. this.$$state = isUndefined(state) ? null : state; + this.$$urlUpdatedByLocation = true; return this; }; @@ -14126,36 +14310,40 @@ function $LocationProvider() { // update browser $rootScope.$watch(function $locationWatch() { - var oldUrl = trimEmptyHash($browser.url()); - var newUrl = trimEmptyHash($location.absUrl()); - var oldState = $browser.state(); - var currentReplace = $location.$$replace; - var urlOrStateChanged = oldUrl !== newUrl || - ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); + if (initializing || $location.$$urlUpdatedByLocation) { + $location.$$urlUpdatedByLocation = false; - if (initializing || urlOrStateChanged) { - initializing = false; + var oldUrl = trimEmptyHash($browser.url()); + var newUrl = trimEmptyHash($location.absUrl()); + var oldState = $browser.state(); + var currentReplace = $location.$$replace; + var urlOrStateChanged = oldUrl !== newUrl || + ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); - $rootScope.$evalAsync(function() { - var newUrl = $location.absUrl(); - var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - $location.$$state, oldState).defaultPrevented; + if (initializing || urlOrStateChanged) { + initializing = false; - // if the location was changed by a `$locationChangeStart` handler then stop - // processing this location change - if ($location.absUrl() !== newUrl) return; + $rootScope.$evalAsync(function() { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; - if (defaultPrevented) { - $location.$$parse(oldUrl); - $location.$$state = oldState; - } else { - if (urlOrStateChanged) { - setBrowserUrlWithFallback(newUrl, currentReplace, - oldState === $location.$$state ? null : $location.$$state); + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + } else { + if (urlOrStateChanged) { + setBrowserUrlWithFallback(newUrl, currentReplace, + oldState === $location.$$state ? null : $location.$$state); + } + afterLocationChange(oldUrl, oldState); } - afterLocationChange(oldUrl, oldState); - } - }); + }); + } } $location.$$replace = false; @@ -14233,13 +14421,22 @@ function $LogProvider() { this.debugEnabled = function(flag) { if (isDefined(flag)) { debug = flag; - return this; + return this; } else { return debug; } }; this.$get = ['$window', function($window) { + // Support: IE 9-11, Edge 12-14+ + // IE/Edge display errors in such a way that it requires the user to click in 4 places + // to see the stack trace. There is no way to feature-detect it so there's a chance + // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't + // break apps. Other browsers display errors in a sensible way and some of them map stack + // traces along source maps if available so it makes sense to let browsers display it + // as they want. + var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent); + return { /** * @ngdoc method @@ -14297,7 +14494,7 @@ function $LogProvider() { function formatError(arg) { if (arg instanceof Error) { - if (arg.stack) { + if (arg.stack && formatStackTrace) { arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ? 'Error: ' + arg.message + '\n' + arg.stack : arg.stack; @@ -15055,6 +15252,13 @@ function findConstantAndWatchExpressions(ast, $filter) { if (!property.value.constant) { argsToWatch.push.apply(argsToWatch, property.value.toWatch); } + if (property.computed) { + findConstantAndWatchExpressions(property.key, $filter); + if (!property.key.constant) { + argsToWatch.push.apply(argsToWatch, property.key.toWatch); + } + } + }); ast.constant = allConstants; ast.toWatch = argsToWatch; @@ -15100,15 +15304,13 @@ function isConstant(ast) { return ast.constant; } -function ASTCompiler(astBuilder, $filter) { - this.astBuilder = astBuilder; +function ASTCompiler($filter) { this.$filter = $filter; } ASTCompiler.prototype = { - compile: function(expression) { + compile: function(ast) { var self = this; - var ast = this.astBuilder.ast(expression); this.state = { nextId: 0, filters: {}, @@ -15163,8 +15365,6 @@ ASTCompiler.prototype = { ifDefined, plusFn); this.state = this.stage = undefined; - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); return fn; }, @@ -15567,15 +15767,13 @@ ASTCompiler.prototype = { }; -function ASTInterpreter(astBuilder, $filter) { - this.astBuilder = astBuilder; +function ASTInterpreter($filter) { this.$filter = $filter; } ASTInterpreter.prototype = { - compile: function(expression) { + compile: function(ast) { var self = this; - var ast = this.astBuilder.ast(expression); findConstantAndWatchExpressions(ast, self.$filter); var assignable; var assign; @@ -15614,8 +15812,6 @@ ASTInterpreter.prototype = { if (inputs) { fn.inputs = inputs; } - fn.literal = isLiteral(ast); - fn.constant = isConstant(ast); return fn; }, @@ -15944,20 +16140,21 @@ ASTInterpreter.prototype = { /** * @constructor */ -var Parser = function Parser(lexer, $filter, options) { - this.lexer = lexer; - this.$filter = $filter; - this.options = options; +function Parser(lexer, $filter, options) { this.ast = new AST(lexer, options); - this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : - new ASTCompiler(this.ast, $filter); -}; + this.astCompiler = options.csp ? new ASTInterpreter($filter) : + new ASTCompiler($filter); +} Parser.prototype = { constructor: Parser, parse: function(text) { - return this.astCompiler.compile(text); + var ast = this.ast.ast(text); + var fn = this.astCompiler.compile(ast); + fn.literal = isLiteral(ast); + fn.constant = isConstant(ast); + return fn; } }; @@ -16103,8 +16300,8 @@ function $ParseProvider() { if (parsedExpression.constant) { parsedExpression.$$watchDelegate = constantWatchDelegate; } else if (oneTime) { - parsedExpression.$$watchDelegate = parsedExpression.literal ? - oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; + parsedExpression.oneTime = true; + parsedExpression.$$watchDelegate = oneTimeWatchDelegate; } else if (parsedExpression.inputs) { parsedExpression.$$watchDelegate = inputsWatchDelegate; } @@ -16120,7 +16317,7 @@ function $ParseProvider() { } } - function expressionInputDirtyCheck(newValue, oldValueOfValue) { + function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) { if (newValue == null || oldValueOfValue == null) { // null/undefined return newValue === oldValueOfValue; @@ -16133,7 +16330,7 @@ function $ParseProvider() { // be cheaply dirty-checked newValue = getValueOf(newValue); - if (typeof newValue === 'object') { + if (typeof newValue === 'object' && !compareObjectIdentity) { // objects/arrays are not supported - deep-watching them would be too expensive return false; } @@ -16155,7 +16352,7 @@ function $ParseProvider() { inputExpressions = inputExpressions[0]; return scope.$watch(function expressionInputWatch(scope) { var newInputValue = inputExpressions(scope); - if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) { + if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) { lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); oldInputValueOf = newInputValue && getValueOf(newInputValue); } @@ -16175,7 +16372,7 @@ function $ParseProvider() { for (var i = 0, ii = inputExpressions.length; i < ii; i++) { var newInputValue = inputExpressions[i](scope); - if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { + if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) { oldInputValues[i] = newInputValue; oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); } @@ -16190,6 +16387,7 @@ function $ParseProvider() { } function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { + var isDone = parsedExpression.literal ? isAllDefined : isDefined; var unwatch, lastValue; if (parsedExpression.inputs) { unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression); @@ -16206,9 +16404,9 @@ function $ParseProvider() { if (isFunction(listener)) { listener(value, old, scope); } - if (isDefined(value)) { + if (isDone(value)) { scope.$$postDigest(function() { - if (isDefined(lastValue)) { + if (isDone(lastValue)) { unwatch(); } }); @@ -16216,31 +16414,12 @@ function $ParseProvider() { } } - function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { - var unwatch, lastValue; - unwatch = scope.$watch(function oneTimeWatch(scope) { - return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { - lastValue = value; - if (isFunction(listener)) { - listener(value, old, scope); - } - if (isAllDefined(value)) { - scope.$$postDigest(function() { - if (isAllDefined(lastValue)) unwatch(); - }); - } - }, objectEquality); - - return unwatch; - - function isAllDefined(value) { - var allDefined = true; - forEach(value, function(val) { - if (!isDefined(val)) allDefined = false; - }); - return allDefined; - } + function isAllDefined(value) { + var allDefined = true; + forEach(value, function(val) { + if (!isDefined(val)) allDefined = false; + }); + return allDefined; } function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { @@ -16256,26 +16435,31 @@ function $ParseProvider() { var watchDelegate = parsedExpression.$$watchDelegate; var useInputs = false; - var regularWatch = - watchDelegate !== oneTimeLiteralWatchDelegate && - watchDelegate !== oneTimeWatchDelegate; + var isDone = parsedExpression.literal ? isAllDefined : isDefined; - var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { + function regularInterceptedExpression(scope, locals, assign, inputs) { var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); return interceptorFn(value, scope, locals); - } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { - var value = parsedExpression(scope, locals, assign, inputs); + } + + function oneTimeInterceptedExpression(scope, locals, assign, inputs) { + var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); var result = interceptorFn(value, scope, locals); // we only return the interceptor's result if the // initial value is defined (for bind-once) - return isDefined(value) ? result : value; - }; + return isDone(value) ? result : value; + } - // Propagate $$watchDelegates other then inputsWatchDelegate + var fn = parsedExpression.oneTime ? oneTimeInterceptedExpression : regularInterceptedExpression; + + // Propogate the literal/oneTime attributes + fn.literal = parsedExpression.literal; + fn.oneTime = parsedExpression.oneTime; + + // Propagate or create inputs / $$watchDelegates useInputs = !parsedExpression.inputs; - if (parsedExpression.$$watchDelegate && - parsedExpression.$$watchDelegate !== inputsWatchDelegate) { - fn.$$watchDelegate = parsedExpression.$$watchDelegate; + if (watchDelegate && watchDelegate !== inputsWatchDelegate) { + fn.$$watchDelegate = watchDelegate; fn.inputs = parsedExpression.inputs; } else if (!interceptorFn.$stateful) { // If there is an interceptor, but no watchDelegate then treat the interceptor like @@ -17778,12 +17962,13 @@ function $RootScopeProvider() { current = target; // It's safe for asyncQueuePosition to be a local variable here because this loop can't - // be reentered recursively. Calling $digest from a function passed to $applyAsync would + // be reentered recursively. Calling $digest from a function passed to $evalAsync would // lead to a '$digest already in progress' error. for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) { try { asyncTask = asyncQueue[asyncQueuePosition]; - asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); + fn = asyncTask.fn; + fn(asyncTask.scope, asyncTask.locals); } catch (e) { $exceptionHandler(e); } @@ -17868,6 +18053,10 @@ function $RootScopeProvider() { } } postDigestQueue.length = postDigestQueuePosition = 0; + + // Check for changes to browser url that happened during the $digest + // (for which no event is fired; e.g. via `history.pushState()`) + $browser.$$checkUrlChange(); }, @@ -18013,7 +18202,7 @@ function $RootScopeProvider() { }); } - asyncQueue.push({scope: this, expression: $parse(expr), locals: locals}); + asyncQueue.push({scope: this, fn: $parse(expr), locals: locals}); }, $$postDigest: function(fn) { @@ -18487,12 +18676,21 @@ function $$SanitizeUriProvider() { var $sceMinErr = minErr('$sce'); var SCE_CONTEXTS = { + // HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding). HTML: 'html', + + // Style statements or stylesheets. Currently unused in AngularJS. CSS: 'css', + + // An URL used in a context where it does not refer to a resource that loads code. Currently + // unused in AngularJS. URL: 'url', - // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a - // url. (e.g. ng-include, script src, templateUrl) + + // RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as + // code. (e.g. ng-include, script src binding, templateUrl) RESOURCE_URL: 'resourceUrl', + + // Script. Currently unused in AngularJS. JS: 'js' }; @@ -18554,6 +18752,16 @@ function adjustMatchers(matchers) { * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict * Contextual Escaping (SCE)} services to AngularJS. * + * For an overview of this service and the functionnality it provides in AngularJS, see the main + * page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how + * SCE works in their application, which shouldn't be needed in most cases. + * + *
+ * AngularJS strongly relies on contextual escaping for the security of bindings: disabling or + * modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners, + * changes to this service will also influence users, so be extra careful and document your changes. + *
+ * * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to @@ -18579,10 +18787,14 @@ function adjustMatchers(matchers) { * @description * * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate - * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure - * that the URLs used for sourcing Angular templates are safe. Refer {@link - * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and - * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}. + * + * The `$sceDelegateProvider` allows one to get/set the whitelists and blacklists used to ensure + * that the URLs used for sourcing AngularJS templates and other script-running URLs are safe (all + * places that use the `$sce.RESOURCE_URL` context). See + * {@link ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} + * and + * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}, * * For the general details about this service in Angular, read the main page for {@link ng.$sce * Strict Contextual Escaping (SCE)}. @@ -18611,6 +18823,13 @@ function adjustMatchers(matchers) { * ]); * }); * ``` + * Note that an empty whitelist will block every resource URL from being loaded, and will require + * you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates + * requested by {@link ng.$templateRequest $templateRequest} that are present in + * {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism + * to populate your templates in that cache at config time, then it is a good idea to remove 'self' + * from that whitelist. This helps to mitigate the security impact of certain types of issues, like + * for instance attacker-controlled `ng-includes`. */ function $SceDelegateProvider() { @@ -18626,23 +18845,23 @@ function $SceDelegateProvider() { * @kind function * * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. + * @return {Array} The currently set whitelist array. * - *
- * **Note:** an empty whitelist array will block all URLs! - *
- * - * @return {Array} the currently set whitelist array. + * @description + * Sets/Gets the whitelist of trusted resource URLs. * * The **default value** when no whitelist has been explicitly set is `['self']` allowing only * same origin resource requests. * - * @description - * Sets/Gets the whitelist of trusted resource URLs. + *
+ * **Note:** the default whitelist of 'self' is not recommended if your app shares its origin + * with other apps! It is a good idea to limit it to only your application's directory. + *
*/ this.resourceUrlWhitelist = function(value) { if (arguments.length) { @@ -18657,25 +18876,23 @@ function $SceDelegateProvider() { * @kind function * * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value - * provided. This must be an array or null. A snapshot of this array is used so further - * changes to the array are ignored. + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored.

+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array.

+ * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. + *

+ * Finally, **the blacklist overrides the whitelist** and has the final say. * - * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items - * allowed in this array. - * - * The typical usage for the blacklist is to **block - * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as - * these would otherwise be trusted but actually return content from the redirected domain. - * - * Finally, **the blacklist overrides the whitelist** and has the final say. - * - * @return {Array} the currently set blacklist array. - * - * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there - * is no blacklist.) + * @return {Array} The currently set blacklist array. * * @description * Sets/Gets the blacklist of trusted resource URLs. + * + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) */ this.resourceUrlBlacklist = function(value) { @@ -18759,17 +18976,24 @@ function $SceDelegateProvider() { * @name $sceDelegate#trustAs * * @description - * Returns an object that is trusted by angular for use in specified strict - * contextual escaping contexts (such as ng-bind-html, ng-include, any src - * attribute interpolation, any dom event binding attribute interpolation - * such as for onclick, etc.) that uses the provided value. - * See {@link ng.$sce $sce} for enabling strict contextual escaping. + * Returns a trusted representation of the parameter for the specified context. This trusted + * object will later on be used as-is, without any security check, by bindings or directives + * that require this security context. + * For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass + * the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as + * `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the + * sanitizer loaded, passing the value itself will render all the HTML that does not pose a + * security risk. * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. + * See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those + * trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual + * escaping. + * + * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`, + * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`. + * + * @param {*} value The value that should be considered trusted. + * @return {*} A trusted representation of value, that can be used in the given context. */ function trustAs(type, trustedValue) { var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); @@ -18801,11 +19025,11 @@ function $SceDelegateProvider() { * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. * * If the passed parameter is not a value that had been returned by {@link - * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is. * * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} - * call or anything else. - * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs + * call or anything else. + * @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns * `value` unchanged. */ @@ -18822,33 +19046,38 @@ function $SceDelegateProvider() { * @name $sceDelegate#getTrusted * * @description - * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and - * returns the originally supplied value if the queried context type is a supertype of the - * created type. If this condition isn't satisfied, throws an exception. + * Takes any input, and either returns a value that's safe to use in the specified context, or + * throws an exception. * - *

- * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting - * (XSS) vulnerability in your application. - *
+ * In practice, there are several cases. When given a string, this function runs checks + * and sanitization to make it safe without prior assumptions. When given the result of a {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied + * value if that value's context is valid for this call's context. Finally, this function can + * also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization + * is available or possible.) * - * @param {string} type The kind of context in which this value is to be used. + * @param {string} type The context in which this value is to be used (such as `$sce.HTML`). * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} call. - * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs - * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. + * `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.) + * @return {*} A version of the value that's safe to use in the given context, or throws an + * exception if this is impossible. */ function getTrusted(type, maybeTrusted) { if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') { return maybeTrusted; } var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + // If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return + // as-is. if (constructor && maybeTrusted instanceof constructor) { return maybeTrusted.$$unwrapTrustedValue(); } - // If we get here, then we may only take one of two actions. - // 1. sanitize the value for the requested type, or - // 2. throw an exception. + // Otherwise, if we get here, then we may either make it safe, or throw an exception. This + // depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL), + // some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS + // has no corresponding sinks. if (type === SCE_CONTEXTS.RESOURCE_URL) { + // RESOURCE_URL uses a whitelist. if (isResourceUrlAllowedByPolicy(maybeTrusted)) { return maybeTrusted; } else { @@ -18857,8 +19086,10 @@ function $SceDelegateProvider() { maybeTrusted.toString()); } } else if (type === SCE_CONTEXTS.HTML) { + // htmlSanitizer throws its own error when no sanitizer is available. return htmlSanitizer(maybeTrusted); } + // Default error when the $sce service has no way to make the input safe. throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); } @@ -18894,21 +19125,27 @@ function $SceDelegateProvider() { * * # Strict Contextual Escaping * - * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain - * contexts to result in a value that is marked as safe to use for that context. One example of - * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer - * to these contexts as privileged or SCE contexts. + * Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render + * trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and + * (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier. * - * As of version 1.2, Angular ships with SCE enabled by default. + * ## Overview * - * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow - * one to execute arbitrary javascript by the use of the expression() syntax. Refer - * to learn more about them. - * You can ensure your document is in standards mode and not quirks mode by adding `` - * to the top of your HTML document. + * To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in + * HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically + * run security checks on them (sanitizations, whitelists, depending on context), or throw when it + * cannot guarantee the security of the result. That behavior depends strongly on contexts: HTML + * can be sanitized, but template URLs cannot, for instance. * - * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for - * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. + * To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML: + * we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it + * before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and + * render the input as-is, you will need to mark it as trusted for that context before attempting + * to bind it. + * + * As of version 1.2, AngularJS ships with SCE enabled by default. + * + * ## In practice * * Here's an example of a binding in a privileged context: * @@ -18918,10 +19155,10 @@ function $SceDelegateProvider() { * ``` * * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE - * disabled, this application allows the user to render arbitrary HTML into the DIV. - * In a more realistic example, one may be rendering user comments, blog articles, etc. via - * bindings. (HTML is just one example of a context where rendering user controlled input creates - * security vulnerabilities.) + * disabled, this application allows the user to render arbitrary HTML into the DIV, which would + * be an XSS security bug. In a more realistic example, one may be rendering user comments, blog + * articles, etc. via bindings. (HTML is just one example of a context where rendering user + * controlled input creates security vulnerabilities.) * * For the case of HTML, you might use a library, either on the client side, or on the server side, * to sanitize unsafe HTML before binding to the value and rendering it in the document. @@ -18931,25 +19168,29 @@ function $SceDelegateProvider() { * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some * properties/fields and forgot to update the binding to the sanitized value? * - * To be secure by default, you want to ensure that any such bindings are disallowed unless you can - * determine that something explicitly says it's safe to use a value for binding in that - * context. You can then audit your code (a simple grep would do) to ensure that this is only done - * for those values that you can easily tell are safe - because they were received from your server, - * sanitized by your library, etc. You can organize your codebase to help with this - perhaps - * allowing only the files in a specific directory to do this. Ensuring that the internal API - * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. + * To be secure by default, AngularJS makes sure bindings go through that sanitization, or + * any similar validation process, unless there's a good reason to trust the given value in this + * context. That trust is formalized with a function call. This means that as a developer, you + * can assume all untrusted bindings are safe. Then, to audit your code for binding security issues, + * you just need to ensure the values you mark as trusted indeed are safe - because they were + * received from your server, sanitized by your library, etc. You can organize your codebase to + * help with this - perhaps allowing only the files in a specific directory to do this. + * Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then + * becomes a more manageable task. * * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to - * obtain values that will be accepted by SCE / privileged contexts. - * + * build the trusted versions of your values. * * ## How does it work? * * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted - * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link - * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the - * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. + * $sce.getTrusted(context, value)} rather than to the value directly. Think of this function as + * a way to enforce the required security context in your data sink. Directives use {@link + * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs + * the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also, + * when binding without directives, AngularJS will understand the context of your bindings + * automatically. * * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly @@ -18990,11 +19231,12 @@ function $SceDelegateProvider() { * It's important to remember that SCE only applies to interpolation expressions. * * If your expressions are constant literals, they're automatically trusted and you don't need to - * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. - * `
`) just works. - * - * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them - * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. + * call `$sce.trustAs` on them (e.g. + * `
`) just works. The `$sceDelegate` will + * also use the `$sanitize` service if it is available when binding untrusted values to + * `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you + * wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in + * your application. * * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load * templates in `ng-include` from your application's domain without having to even know about SCE. @@ -19012,11 +19254,17 @@ function $SceDelegateProvider() { * * | Context | Notes | * |---------------------|----------------| - * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | - * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | - * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | - * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | + * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered, and the {@link ngSanitize.$sanitize $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. | + * | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. | + * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`

Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | + * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. | + * + * + * Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them + * through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings + * in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This + * might evolve. * * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
* @@ -19135,14 +19383,15 @@ function $SceDelegateProvider() { * for little coding overhead. It will be much harder to take an SCE disabled application and * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE * for cases where you have a lot of existing code that was written before SCE was introduced and - * you're migrating them a module at a time. + * you're migrating them a module at a time. Also do note that this is an app-wide setting, so if + * you are writing a library, you will cause security bugs applications using it. * * That said, here's how you can completely disable SCE: * * ``` * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { * // Completely disable SCE. For demonstration purposes only! - * // Do not use in new projects. + * // Do not use in new projects or libraries. * $sceProvider.enabled(false); * }); * ``` @@ -19157,8 +19406,8 @@ function $SceProvider() { * @name $sceProvider#enabled * @kind function * - * @param {boolean=} value If provided, then enables/disables SCE. - * @return {boolean} true if SCE is enabled, false otherwise. + * @param {boolean=} value If provided, then enables/disables SCE application-wide. + * @return {boolean} True if SCE is enabled, false otherwise. * * @description * Enables/disables SCE and returns the current value. @@ -19212,9 +19461,9 @@ function $SceProvider() { * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) * will also succeed. * - * Inheritance happens to capture this in a natural way. In some future, we - * may not use inheritance anymore. That is OK because no code outside of - * sce.js and sceSpecs.js would need to be aware of this detail. + * Inheritance happens to capture this in a natural way. In some future, we may not use + * inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to + * be aware of this detail. */ this.$get = ['$parse', '$sceDelegate', function( @@ -19236,8 +19485,8 @@ function $SceProvider() { * @name $sce#isEnabled * @kind function * - * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you - * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. + * @return {Boolean} True if SCE is enabled, false otherwise. If you want to set the value, you + * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. * * @description * Returns a boolean indicating if SCE is enabled. @@ -19264,14 +19513,14 @@ function $SceProvider() { * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, * *result*)} * - * @param {string} type The kind of SCE context in which this result will be used. + * @param {string} type The SCE context in which this result will be used. * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ sce.parseAs = function sceParseAs(type, expr) { var parsed = $parse(expr); @@ -19289,18 +19538,18 @@ function $SceProvider() { * @name $sce#trustAs * * @description - * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, - * returns an object that is trusted by angular for use in specified strict contextual - * escaping contexts (such as ng-bind-html, ng-include, any src attribute - * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) - * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual - * escaping. + * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a + * wrapped object that represents your value, and the trust you have in its safety for the given + * context. AngularJS can then use that value as-is in bindings of the specified secure context. + * This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute + * interpolations. See {@link ng.$sce $sce} for strict contextual escaping. * - * @param {string} type The kind of context in which this value is safe for use. e.g. url, - * resourceUrl, html, js and css. - * @param {*} value The value that that should be considered trusted/safe. - * @returns {*} A value that can be used to stand in for the provided `value` in places - * where Angular expects a $sce.trustAs() return value. + * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`, + * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`. + * + * @param {*} value The value that that should be considered trusted. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in the context you specified. */ /** @@ -19311,11 +19560,23 @@ function $SceProvider() { * Shorthand method. `$sce.trustAsHtml(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml - * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * @param {*} value The value to mark as trusted for `$sce.HTML` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.HTML` context (like `ng-bind-html`). + */ + + /** + * @ngdoc method + * @name $sce#trustAsCss + * + * @description + * Shorthand method. `$sce.trustAsCss(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.CSS` context. + * @return {*} A wrapped version of value that can be used as a trusted variant + * of your `value` in `$sce.CSS` context. This context is currently unused, so there are + * almost no reasons to use this function so far. */ /** @@ -19326,11 +19587,10 @@ function $SceProvider() { * Shorthand method. `$sce.trustAsUrl(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl - * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * @param {*} value The value to mark as trusted for `$sce.URL` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.URL` context. That context is currently unused, so there are almost no reasons + * to use this function so far. */ /** @@ -19341,11 +19601,10 @@ function $SceProvider() { * Shorthand method. `$sce.trustAsResourceUrl(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl - * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the return - * value of {@link ng.$sce#trustAs $sce.trustAs}.) + * @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute + * bindings, ...) */ /** @@ -19356,11 +19615,10 @@ function $SceProvider() { * Shorthand method. `$sce.trustAsJs(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} * - * @param {*} value The value to trustAs. - * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs - * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives - * only accept expressions that are either literal constants or are the - * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + * @param {*} value The value to mark as trusted for `$sce.JS` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.JS` context. That context is currently unused, so there are almost no reasons to + * use this function so far. */ /** @@ -19369,16 +19627,17 @@ function $SceProvider() { * * @description * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, - * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the - * originally supplied value if the queried context type is a supertype of the created type. - * If this condition isn't satisfied, throws an exception. + * takes any input, and either returns a value that's safe to use in the specified context, + * or throws an exception. This function is aware of trusted values created by the `trustAs` + * function and its shorthands, and when contexts are appropriate, returns the unwrapped value + * as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a + * safe value (e.g., no sanitization is available or possible.) * - * @param {string} type The kind of context in which this value is to be used. - * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} - * call. - * @returns {*} The value the was originally provided to - * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. - * Otherwise, throws an exception. + * @param {string} type The context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs + * `$sce.trustAs`} call, or anything else (which will not be considered trusted.) + * @return {*} A version of the value that's safe to use in the given context, or throws an + * exception if this is impossible. */ /** @@ -19390,7 +19649,7 @@ function $SceProvider() { * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` + * @return {*} The return value of `$sce.getTrusted($sce.HTML, value)` */ /** @@ -19402,7 +19661,7 @@ function $SceProvider() { * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` + * @return {*} The return value of `$sce.getTrusted($sce.CSS, value)` */ /** @@ -19414,7 +19673,7 @@ function $SceProvider() { * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` + * @return {*} The return value of `$sce.getTrusted($sce.URL, value)` */ /** @@ -19426,7 +19685,7 @@ function $SceProvider() { * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} * * @param {*} value The value to pass to `$sceDelegate.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` + * @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` */ /** @@ -19438,7 +19697,7 @@ function $SceProvider() { * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. - * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` + * @return {*} The return value of `$sce.getTrusted($sce.JS, value)` */ /** @@ -19450,12 +19709,12 @@ function $SceProvider() { * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} * * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ /** @@ -19467,12 +19726,12 @@ function $SceProvider() { * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} * * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ /** @@ -19484,12 +19743,12 @@ function $SceProvider() { * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} * * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ /** @@ -19501,12 +19760,12 @@ function $SceProvider() { * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} * * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ /** @@ -19518,12 +19777,12 @@ function $SceProvider() { * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} * * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: + * @return {function(context, locals)} A function which represents the compiled expression: * - * * `context` – `{object}` – an object against which any expressions embedded in the strings - * are evaluated against (typically a scope object). - * * `locals` – `{object=}` – local variables context object, useful for overriding values in - * `context`. + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. */ // Shorthand delegations. @@ -19573,7 +19832,10 @@ function $SnifferProvider() { // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by // the presence of an extension runtime ID and the absence of other Chrome runtime APIs // (see https://developer.chrome.com/apps/manifest/sandbox). + // (NW.js apps have access to Chrome APIs, but do support `history`.) + isNw = $window.nw && $window.nw.process, isChromePackagedApp = + !isNw && $window.chrome && ($window.chrome.app && $window.chrome.app.runtime || !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id), @@ -19978,7 +20240,7 @@ var originUrl = urlResolve(window.location.href); * URL will be resolved into an absolute URL in the context of the application document. * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related * properties are all populated to reflect the normalized URL. This approach has wide - * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See + * compatibility - Safari 1+, Mozilla 1+ etc. See * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html * * Implementation Notes for IE @@ -20344,6 +20606,9 @@ function $FilterProvider($provide) { * Selects a subset of items from `array` and returns it as a new array. * * @param {Array} array The source array. + *
+ * **Note**: If the array contains objects that reference themselves, filtering is not possible. + *
* @param {string|Object|function()} expression The predicate to be used for selecting items from * `array`. * @@ -20377,8 +20642,9 @@ function $FilterProvider($provide) { * The final result is an array of those elements that the predicate returned true for. * * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in - * determining if the expected value (from the filter expression) and actual value (from - * the object in the array) should be considered a match. + * determining if values retrieved using `expression` (when it is not a function) should be + * considered a match based on the the expected value (from the filter expression) and actual + * value (from the object in the array). * * Can be one of: * @@ -20561,7 +20827,10 @@ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstA var key; if (matchAgainstAnyProp) { for (key in actual) { - if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) { + // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined + // See: https://github.com/angular/angular.js/issues/15644 + if (key.charAt && (key.charAt(0) !== '$') && + deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) { return true; } } @@ -21070,7 +21339,7 @@ var DATE_FORMATS = { GGGG: longEraGetter }; -var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, +var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/, NUMBER_STRING = /^-?\d+$/; /** @@ -21129,6 +21398,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+ * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence * (e.g. `"h 'o''clock'"`). * + * Any other characters in the `format` string will be output as-is. + * * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is @@ -22334,7 +22605,8 @@ var htmlAnchorDirective = valueFn({ * * @description * - * This directive sets the `disabled` attribute on the element if the + * This directive sets the `disabled` attribute on the element (typically a form control, + * e.g. `input`, `button`, `select` etc.) if the * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. * * A special directive is necessary because we cannot use interpolation inside the `disabled` @@ -24840,15 +25112,27 @@ function isValidForStep(viewValue, stepBase, step) { // and `viewValue` is expected to be a valid stringified number. var value = Number(viewValue); + var isNonIntegerValue = !isNumberInteger(value); + var isNonIntegerStepBase = !isNumberInteger(stepBase); + var isNonIntegerStep = !isNumberInteger(step); + // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers. - if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) { - var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step)); + if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) { + var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0; + var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0; + var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0; + + var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals); var multiplier = Math.pow(10, decimalCount); value = value * multiplier; stepBase = stepBase * multiplier; step = step * multiplier; + + if (isNonIntegerValue) value = Math.round(value); + if (isNonIntegerStepBase) stepBase = Math.round(stepBase); + if (isNonIntegerStep) step = Math.round(step); } return (value - stepBase) % step === 0; @@ -25405,7 +25689,10 @@ var ngValueDirective = function() { * makes it possible to use ngValue as a sort of one-way bind. */ function updateElementValue(element, attr, value) { - element.prop('value', value); + // Support: IE9 only + // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`). + var propValue = isDefined(value) ? value : (msie === 9) ? '' : null; + element.prop('value', propValue); attr.$set('value', value); } @@ -25727,13 +26014,6 @@ function classDirective(name, selector) { return { restrict: 'AC', link: function(scope, element, attr) { - var expression = attr[name].trim(); - var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':'); - - var watchInterceptor = isOneTime ? toFlatValue : toClassString; - var watchExpression = $parse(expression, watchInterceptor); - var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction; - var classCounts = element.data('$classCounts'); var oldModulo = true; var oldClassString; @@ -25756,7 +26036,7 @@ function classDirective(name, selector) { scope.$watch(indexWatchExpression, ngClassIndexWatchAction); } - scope.$watch(watchExpression, watchAction, isOneTime); + scope.$watch($parse(attr[name], toClassString), ngClassWatchAction); function addClasses(classString) { classString = digestClassCounts(split(classString), 1); @@ -25798,9 +26078,9 @@ function classDirective(name, selector) { } function ngClassIndexWatchAction(newModulo) { - // This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it + // This watch-action should run before the `ngClassWatchAction()`, thus it // adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the - // `ngClass[OneTime]WatchAction()` will update the classes. + // `ngClassWatchAction()` will update the classes. if (newModulo === selector) { addClasses(oldClassString); } else { @@ -25810,15 +26090,13 @@ function classDirective(name, selector) { oldModulo = newModulo; } - function ngClassOneTimeWatchAction(newClassValue) { - var newClassString = toClassString(newClassValue); - - if (newClassString !== oldClassString) { - ngClassWatchAction(newClassString); - } - } - function ngClassWatchAction(newClassString) { + // When using a one-time binding the newClassString will return + // the pre-interceptor value until the one-time is complete + if (!isString(newClassString)) { + newClassString = toClassString(newClassString); + } + if (oldModulo === selector) { updateClasses(oldClassString, newClassString); } @@ -25865,34 +26143,6 @@ function classDirective(name, selector) { return classString; } - - function toFlatValue(classValue) { - var flatValue = classValue; - - if (isArray(classValue)) { - flatValue = classValue.map(toFlatValue); - } else if (isObject(classValue)) { - var hasUndefined = false; - - flatValue = Object.keys(classValue).filter(function(key) { - var value = classValue[key]; - - if (!hasUndefined && isUndefined(value)) { - hasUndefined = true; - } - - return value; - }); - - if (hasUndefined) { - // Prevent the `oneTimeLiteralWatchInterceptor` from unregistering - // the watcher, by including at least one `undefined` value. - flatValue.push(undefined); - } - } - - return flatValue; - } } /** @@ -26733,15 +26983,15 @@ forEach( return { restrict: 'A', compile: function($element, attr) { - // We expose the powerful $event object on the scope that provides access to the Window, - // etc. that isn't protected by the fast paths in $parse. We explicitly request better - // checks at the cost of speed since event handler expressions are not executed as - // frequently as regular change detection. - var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true); + // NOTE: + // We expose the powerful `$event` object on the scope that provides access to the Window, + // etc. This is OK, because expressions are not sandboxed any more (and the expression + // sandbox was never meant to be a security feature anyway). + var fn = $parse(attr[directiveName]); return function ngEventHandler(scope, element) { element.on(eventName, function(event) { var callback = function() { - fn(scope, {$event:event}); + fn(scope, {$event: event}); }; if (forceAsyncEvents[eventName] && $rootScope.$$phase) { scope.$evalAsync(callback); @@ -27822,32 +28072,57 @@ var ngModelMinErr = minErr('ngModel'); * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue * is set. + * * @property {*} $modelValue The value in the model that the control is bound to. + * * @property {Array.} $parsers Array of functions to execute, as a pipeline, whenever - the control reads value from the DOM. The functions are called in array order, each passing - its return value through to the next. The last return value is forwarded to the - {@link ngModel.NgModelController#$validators `$validators`} collection. + * the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue + `$viewValue`} from the DOM, usually via user input. + See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation. + Note that the `$parsers` are not called when the bound ngModel expression changes programmatically. -Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue -`$viewValue`}. + The functions are called in array order, each passing + its return value through to the next. The last return value is forwarded to the + {@link ngModel.NgModelController#$validators `$validators`} collection. -Returning `undefined` from a parser means a parse error occurred. In that case, -no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` -will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} -is set to `true`. The parse error is stored in `ngModel.$error.parse`. + Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue + `$viewValue`}. + + Returning `undefined` from a parser means a parse error occurred. In that case, + no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` + will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} + is set to `true`. The parse error is stored in `ngModel.$error.parse`. + + This simple example shows a parser that would convert text input value to lowercase: + * ```js + * function parse(value) { + * if (value) { + * return value.toLowerCase(); + * } + * } + * ngModelController.$parsers.push(parse); + * ``` * * @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever - the model value changes. The functions are called in reverse array order, each passing the value through to the - next. The last return value is used as the actual DOM value. - Used to format / convert values for display in the control. + the bound ngModel expression changes programmatically. The `$formatters` are not called when the + value of the control is changed by user interaction. + + Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue + `$modelValue`} for display in the control. + + The functions are called in reverse array order, each passing the value through to the + next. The last return value is used as the actual DOM value. + + This simple example shows a formatter that would convert the model value to uppercase: + * ```js - * function formatter(value) { + * function format(value) { * if (value) { * return value.toUpperCase(); * } * } - * ngModel.$formatters.push(formatter); + * ngModel.$formatters.push(format); * ``` * * @property {Object.} $validators A collection of validators that are applied @@ -28047,7 +28322,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $ this.$$currentValidationRunId = 0; - this.$$scope = $scope; + // https://github.com/angular/angular.js/issues/15833 + // Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched + Object.defineProperty(this, '$$scope', {value: $scope}); this.$$attr = $attr; this.$$element = $element; this.$$animate = $animate; @@ -28555,9 +28832,10 @@ NgModelController.prototype = { * * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers` * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged - * value sent directly for processing, finally to be applied to `$modelValue` and then the - * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners, - * in the `$viewChangeListeners` list, are called. + * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and + * `$asyncValidators` are called and the value is applied to `$modelValue`. + * Finally, the value is set to the **expression** specified in the `ng-model` attribute and + * all the registered change listeners, in the `$viewChangeListeners` list are called. * * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn` * and the `default` trigger is not listed, all those actions will remain pending until one of the @@ -28620,6 +28898,29 @@ NgModelController.prototype = { that.$commitViewValue(); }); } + }, + + /** + * @ngdoc method + * + * @name ngModel.NgModelController#$overrideModelOptions + * + * @description + * + * Override the current model options settings programmatically. + * + * The previous `ModelOptions` value will not be modified. Instead, a + * new `ModelOptions` object will inherit from the previous one overriding + * or inheriting settings that are defined in the given parameter. + * + * See {@link ngModelOptions} for information about what options can be specified + * and how model option inheritance works. + * + * @param {Object} options a hash of settings to override the previous options + * + */ + $overrideModelOptions: function(options) { + this.$options = this.$options.createChild(options); } }; @@ -28632,8 +28933,8 @@ function setupModelWatcher(ctrl) { // -> scope value did not change since the last digest as // ng-change executes in apply phase // 4. view should be changed back to 'a' - ctrl.$$scope.$watch(function ngModelWatch() { - var modelValue = ctrl.$$ngModelGet(ctrl.$$scope); + ctrl.$$scope.$watch(function ngModelWatch(scope) { + var modelValue = ctrl.$$ngModelGet(scope); // if scope model value and ngModel value are out of sync // TODO(perf): why not move this to the action fn? @@ -29461,13 +29762,8 @@ var ngOptionsMinErr = minErr('ngOptions'); * is not matched against any `