mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-14 00:24:40 +00:00
Reporting / Traffic: Upgrade chart.js to v3.9.1 and improve UX (#6000)
* Reporting / Traffic: Bump chart.js version and improve UX
This commit is contained in:
parent
8cb79d511b
commit
cc6efa4a16
4
plist
4
plist
@ -1633,7 +1633,11 @@
|
||||
/usr/local/opnsense/www/js/bootstrap3-typeahead.min.js
|
||||
/usr/local/opnsense/www/js/chart.js
|
||||
/usr/local/opnsense/www/js/chart.min.js
|
||||
/usr/local/opnsense/www/js/chartjs-adapter-moment.js
|
||||
/usr/local/opnsense/www/js/chartjs-adapter-moment.min.js
|
||||
/usr/local/opnsense/www/js/chartjs-adapter-moment.min.js.map
|
||||
/usr/local/opnsense/www/js/chartjs-plugin-colorschemes.js
|
||||
/usr/local/opnsense/www/js/chartjs-plugin-colorschemes.min.js
|
||||
/usr/local/opnsense/www/js/chartjs-plugin-streaming.js
|
||||
/usr/local/opnsense/www/js/chartjs-plugin-streaming.min.js
|
||||
/usr/local/opnsense/www/js/d3.min.js
|
||||
|
||||
@ -26,10 +26,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#}
|
||||
{% set theme_name = ui_theme|default('opnsense') %}
|
||||
<script src="{{ cache_safe('/ui/js/moment-with-locales.min.js') }}"></script>
|
||||
<script src="{{ cache_safe('/ui/js/chart.min.js') }}"></script>
|
||||
<script src="{{ cache_safe('/ui/js/chartjs-plugin-streaming.min.js') }}"></script>
|
||||
<script src="{{ cache_safe('/ui/js/chartjs-plugin-colorschemes.js') }}"></script>
|
||||
<script src="{{ cache_safe('/ui/js/moment-with-locales.min.js') }}"></script>
|
||||
<script src="{{ cache_safe('/ui/js/chartjs-adapter-moment.js') }}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{ cache_safe(theme_file_or_default('/css/chart.css', theme_name)) }}" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
@ -44,6 +45,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
'use strict';
|
||||
|
||||
$( document ).ready(function() {
|
||||
function set_alpha(color, opacity) {
|
||||
const op = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255);
|
||||
return color + op.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
function limit(number, lower_bound, upper_bound) {
|
||||
return Math.min(Math.max(parseInt(number), lower_bound), upper_bound);
|
||||
}
|
||||
|
||||
function format_field(value) {
|
||||
if (!isNaN(value) && value > 0) {
|
||||
let fileSizeTypes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"];
|
||||
@ -57,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create new traffic chart
|
||||
*/
|
||||
@ -68,7 +79,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
label: init_data.interfaces[intf].name,
|
||||
hidden: true,
|
||||
borderColor: init_data.interfaces[intf].color,
|
||||
backgroundColor: init_data.interfaces[intf].color,
|
||||
backgroundColor: set_alpha(init_data.interfaces[intf].color, 0.5),
|
||||
pointHoverBackgroundColor: init_data.interfaces[intf].color,
|
||||
pointHoverBorderColor: init_data.interfaces[intf].color,
|
||||
pointBackgroundColor: init_data.interfaces[intf].color,
|
||||
@ -88,19 +99,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
datasets: all_datasets
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: graph_title
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
elements: {
|
||||
line: {
|
||||
fill: true,
|
||||
cubicInterpolationMode: 'monotone',
|
||||
clip: 0
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
x: {
|
||||
time: {
|
||||
tooltipFormat:'HH:mm:ss',
|
||||
unit: 'second',
|
||||
stepSize: init_data.interval < 10000 ? 5 : init_data.interval / 1000,
|
||||
minUnit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss',
|
||||
@ -110,26 +122,16 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
type: 'realtime',
|
||||
realtime: {
|
||||
duration: 20000,
|
||||
refresh: 2000,
|
||||
delay: 2000
|
||||
refresh: init_data.interval,
|
||||
delay: init_data.interval
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
callback: function (value, index, values) {
|
||||
return format_field(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem, data) {
|
||||
let ds = data.datasets[tooltipItem.datasetIndex];
|
||||
return ds.label + " : " + format_field(ds.data[tooltipItem.index].y).toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
hover: {
|
||||
@ -137,11 +139,27 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return context.dataset.label + ": " + format_field(context.dataset.data[context.dataIndex].y).toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: graph_title
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
streaming: {
|
||||
frameRate: 30
|
||||
frameRate: 30
|
||||
},
|
||||
colorschemes: {
|
||||
scheme: 'brewer.Paired12'
|
||||
scheme: 'brewer.Paired12'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,7 +178,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
label: init_data.interfaces[intf].name,
|
||||
hidden: true,
|
||||
borderColor: init_data.interfaces[intf].color,
|
||||
backgroundColor: init_data.interfaces[intf].color,
|
||||
backgroundColor: set_alpha(init_data.interfaces[intf].color, 0.5),
|
||||
pointHoverBackgroundColor: init_data.interfaces[intf].color,
|
||||
pointHoverBorderColor: init_data.interfaces[intf].color,
|
||||
pointBackgroundColor: init_data.interfaces[intf].color,
|
||||
@ -180,19 +198,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
datasets: all_datasets
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: graph_title
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
x: {
|
||||
time: {
|
||||
tooltipFormat:'HH:mm:ss',
|
||||
unit: 'second',
|
||||
stepSize: init_data.interval < 10000 ? 5 : init_data.interval / 1000,
|
||||
minUnit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss',
|
||||
@ -202,30 +214,16 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
type: 'realtime',
|
||||
realtime: {
|
||||
duration: 40000,
|
||||
refresh: 3000,
|
||||
delay: 500
|
||||
refresh: init_data.interval,
|
||||
delay: init_data.interval,
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
callback: function (value, index, values) {
|
||||
return format_field(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem, data) {
|
||||
let ds = data.datasets[tooltipItem.datasetIndex];
|
||||
return [
|
||||
tooltipItem.xLabel,
|
||||
ds.label + " : " + ds.data[tooltipItem.index].address,
|
||||
"@ " + format_field(ds.data[tooltipItem.index].y).toString()
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
hover: {
|
||||
@ -233,6 +231,28 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let split = context.formattedValue.split(",")[0]
|
||||
let time = split.replace('(', '')
|
||||
return [
|
||||
time,
|
||||
context.dataset.label + ": " + context.dataset.data[context.dataIndex].address,
|
||||
"@ " + format_field(context.dataset.data[context.dataIndex].y).toString()
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: graph_title
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
streaming: {
|
||||
frameRate: 30
|
||||
},
|
||||
@ -328,10 +348,34 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
});
|
||||
}
|
||||
|
||||
// Store references to the charts globally
|
||||
let g_charts = {traffic: [], traffic_top: []};
|
||||
|
||||
$("#intervals").change(function() {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem("api.diagnostics.traffic.interval", $(this).val());
|
||||
}
|
||||
|
||||
g_charts['interval'] = limit(Number($("#intervals").val()), 500, 10000);
|
||||
|
||||
Object.keys(g_charts).forEach(function(key) {
|
||||
if (Array.isArray(g_charts[key])) {
|
||||
g_charts[key].forEach(function(chart) {
|
||||
chart.config.options.scales.x.realtime = {
|
||||
duration: 20000,
|
||||
refresh: g_charts['interval'],
|
||||
delay: g_charts['interval']
|
||||
};
|
||||
chart.config.options.scales.x.time.stepSize = g_charts['interval'] < 10000 ? 5 : g_charts['interval'] / 1000;
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* startup, fetch initial interface stats and create graphs
|
||||
*/
|
||||
ajaxGet('/api/diagnostics/traffic/interface',{}, function(data, status){
|
||||
ajaxGet('/api/diagnostics/traffic/interface',{}, function(data, status) {
|
||||
// XXX: startup selected interfaces load/save in localStorage in a future version
|
||||
let tmp = window.localStorage ? window.localStorage.getItem("api.diagnostics.traffic.interface") : null;
|
||||
let selected_interfaces = ['lan', 'wan'];
|
||||
@ -357,99 +401,108 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
});
|
||||
$('#interfaces').selectpicker('refresh');
|
||||
|
||||
// register traffic update event
|
||||
$( document ).on( "updateTrafficCharts", {
|
||||
charts: [
|
||||
traffic_graph($("#rxChart"), '{{ lang._('In (bps)') }}', data),
|
||||
traffic_graph($("#txChart"), '{{ lang._('Out (bps)') }}', data)
|
||||
]
|
||||
}, function( event, data) {
|
||||
let charts = event.data.charts;
|
||||
for (var i =0 ; i < charts.length; ++i) {
|
||||
let this_chart = charts[i];
|
||||
Object.keys(data.interfaces).forEach(function(intf) {
|
||||
this_chart.config.data.datasets.forEach(function(dataset) {
|
||||
if (dataset.intf == intf) {
|
||||
let calc_data = data.interfaces[intf][dataset.src_field];
|
||||
let elapsed_time = data.time - dataset.last_time;
|
||||
dataset.hidden = !$("#interfaces").val().includes(intf);
|
||||
dataset.data.push({
|
||||
x: Date.now(),
|
||||
y: Math.round(((calc_data - dataset.last_data) / elapsed_time) * 8, 0)
|
||||
});
|
||||
dataset.last_time = data.time;
|
||||
dataset.last_data = calc_data;
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
this_chart.update();
|
||||
// XXX: limit the amount of minimum interval that can be set
|
||||
$("#intervals").val(2000);
|
||||
if (window.localStorage && window.localStorage.getItem("api.diagnostics.traffic.interval") !== null) {
|
||||
$("#intervals").val(window.localStorage.getItem("api.diagnostics.traffic.interval"));
|
||||
}
|
||||
$('#intervals').selectpicker('refresh');
|
||||
|
||||
data.interval = limit(Number($("#intervals").val()), 500, 10000);
|
||||
g_charts['interval'] = data.interval;
|
||||
|
||||
const chart_types = ["rxChart", "txChart", "rxTopChart", "txTopChart"];
|
||||
chart_types.forEach(function(chart) {
|
||||
/* Create the charts */
|
||||
if (chart.includes('Top')) {
|
||||
let rxtx = chart.includes('rx') ? '{{ lang._('Top hosts in (bps)') }}' : '{{ lang._('Top hosts out (bps)') }}';
|
||||
let graph = traffic_top_graph($("#" + chart), rxtx, data);
|
||||
g_charts['traffic_top'].push(graph);
|
||||
} else {
|
||||
let rxtx = chart.includes('rx') ? '{{ lang._('In (bps)') }}' : '{{ lang._('Out (bps)') }}';
|
||||
let graph = traffic_graph($("#" + chart), rxtx, data);
|
||||
g_charts['traffic'].push(graph);
|
||||
}
|
||||
});
|
||||
|
||||
// register traffic update event
|
||||
$( document ).on( "updateTrafficTopCharts", {
|
||||
charts: [
|
||||
traffic_top_graph($("#rxTopChart"), '{{ lang._('Top hosts in (bps)') }}', data),
|
||||
traffic_top_graph($("#txTopChart"), '{{ lang._('Top hosts out (bps)') }}', data)
|
||||
]
|
||||
}, function( event, data) {
|
||||
let charts = event.data.charts;
|
||||
for (var i =0 ; i < charts.length; ++i) {
|
||||
let this_chart = charts[i];
|
||||
Object.keys(data).forEach(function(intf) {
|
||||
this_chart.config.data.datasets.forEach(function(dataset) {
|
||||
if (dataset.intf == intf) {
|
||||
let calc_data = data[intf]['records'];
|
||||
dataset.hidden = !$("#interfaces").val().includes(intf);
|
||||
for (var i=0; i < data[intf]['records'].length ; ++i) {
|
||||
dataset.data.push({
|
||||
x: Date.now(),
|
||||
y: data[intf]['records'][i]['rate_bits_' + dataset.src_field],
|
||||
r: 4,
|
||||
address: data[intf]['records'][i]['address']
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
this_chart.update();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* poll for new stats and update selected charts
|
||||
*/
|
||||
(function traffic_poller(){
|
||||
(function traffic_poller() {
|
||||
ajaxGet("/api/diagnostics/traffic/interface", {}, function(data, status) {
|
||||
if (data.interfaces !== undefined) {
|
||||
$( document ).trigger( "updateTrafficCharts", [ data ] );
|
||||
update_traffic_charts(g_charts['traffic'], data);
|
||||
}
|
||||
});
|
||||
setTimeout(traffic_poller, 2000);
|
||||
setTimeout(traffic_poller, g_charts['interval']);
|
||||
})();
|
||||
(function top_traffic_poller(){
|
||||
|
||||
(function top_traffic_poller() {
|
||||
if ($("#interfaces").val().length > 0) {
|
||||
ajaxGet('/api/diagnostics/traffic/top/' + $("#interfaces").val().join(","), {}, function(data, status){
|
||||
if (status == 'success') {
|
||||
$( document ).trigger( "updateTrafficTopCharts", [ data ] );
|
||||
update_top_charts(g_charts['traffic_top'], data);
|
||||
updateTopTable(data);
|
||||
top_traffic_poller();
|
||||
} else {
|
||||
setTimeout(top_traffic_poller, 2000);
|
||||
setTimeout(top_traffic_poller, g_charts['interval']);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
function update_traffic_charts(charts, data) {
|
||||
charts.forEach(function(chart) {
|
||||
Object.keys(data.interfaces).forEach(function(intf) {
|
||||
chart.config.data.datasets.forEach(function(dataset) {
|
||||
if (dataset.intf == intf) {
|
||||
let calc_data = data.interfaces[intf][dataset.src_field];
|
||||
let elapsed_time = data.time - dataset.last_time;
|
||||
dataset.hidden = !$("#interfaces").val().includes(intf);
|
||||
dataset.data.push({
|
||||
x: Date.now(),
|
||||
y: Math.round(((calc_data - dataset.last_data) / elapsed_time) * 8, 0)
|
||||
});
|
||||
dataset.last_time = data.time;
|
||||
dataset.last_data = calc_data;
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
chart.update('quiet');
|
||||
});
|
||||
}
|
||||
|
||||
function update_top_charts(charts, data) {
|
||||
charts.forEach(function(chart) {
|
||||
Object.keys(data).forEach(function(intf) {
|
||||
chart.config.data.datasets.forEach(function(dataset) {
|
||||
if (dataset.intf == intf) {
|
||||
let calc_data = data[intf]['records'];
|
||||
dataset.hidden = !$("#interfaces").val().includes(intf);
|
||||
for (var i=0; i < data[intf]['records'].length ; ++i) {
|
||||
dataset.data.push({
|
||||
x: Date.now(),
|
||||
y: data[intf]['records'][i]['rate_bits_' + dataset.src_field],
|
||||
r: 4,
|
||||
address: data[intf]['records'][i]['address']
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
chart.update('quiet');
|
||||
});
|
||||
}
|
||||
|
||||
$("#interfaces").change(function(){
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem("api.diagnostics.traffic.interface", $(this).val());
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -458,15 +511,36 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
.badge-color-1 {
|
||||
background: navy !important;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" id="graph_tab" href="#graph">{{ lang._('Graph') }}</a></li>
|
||||
<li><a data-toggle="tab" id="gtid_tab" href="#toptalkers">{{ lang._('Top talkers') }}</a></li>
|
||||
<div class="pull-right">
|
||||
<select class="selectpicker" id="interfaces" multiple=multiple>
|
||||
</select>
|
||||
|
||||
<div class="right">
|
||||
<select class="selectpicker" id="interfaces" multiple=multiple>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
<div class="left">
|
||||
<select class="selectpicker" id="intervals" data-width="auto">
|
||||
<option value="500">500 Milliseconds</option>
|
||||
<option value="1000">1 Second</option>
|
||||
<option value="2000">2 Seconds</option>
|
||||
<option value="5000">5 Seconds</option>
|
||||
<option value="10000">10 Seconds</option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
<div class="tab-content content-box">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
12
src/opnsense/www/js/chart.min.js
vendored
12
src/opnsense/www/js/chart.min.js
vendored
File diff suppressed because one or more lines are too long
72
src/opnsense/www/js/chartjs-adapter-moment.js
Normal file
72
src/opnsense/www/js/chartjs-adapter-moment.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* chartjs-adapter-moment v1.0.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2022 chartjs-adapter-moment Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('moment'), require('chart.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['moment', 'chart.js'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.moment, global.Chart));
|
||||
}(this, (function (moment, chart_js) { 'use strict';
|
||||
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||||
|
||||
var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
|
||||
|
||||
const FORMATS = {
|
||||
datetime: 'MMM D, YYYY, h:mm:ss a',
|
||||
millisecond: 'h:mm:ss.SSS a',
|
||||
second: 'h:mm:ss a',
|
||||
minute: 'h:mm a',
|
||||
hour: 'hA',
|
||||
day: 'MMM D',
|
||||
week: 'll',
|
||||
month: 'MMM YYYY',
|
||||
quarter: '[Q]Q - YYYY',
|
||||
year: 'YYYY'
|
||||
};
|
||||
|
||||
chart_js._adapters._date.override(typeof moment__default['default'] === 'function' ? {
|
||||
_id: 'moment', // DEBUG ONLY
|
||||
|
||||
formats: function() {
|
||||
return FORMATS;
|
||||
},
|
||||
|
||||
parse: function(value, format) {
|
||||
if (typeof value === 'string' && typeof format === 'string') {
|
||||
value = moment__default['default'](value, format);
|
||||
} else if (!(value instanceof moment__default['default'])) {
|
||||
value = moment__default['default'](value);
|
||||
}
|
||||
return value.isValid() ? value.valueOf() : null;
|
||||
},
|
||||
|
||||
format: function(time, format) {
|
||||
return moment__default['default'](time).format(format);
|
||||
},
|
||||
|
||||
add: function(time, amount, unit) {
|
||||
return moment__default['default'](time).add(amount, unit).valueOf();
|
||||
},
|
||||
|
||||
diff: function(max, min, unit) {
|
||||
return moment__default['default'](max).diff(moment__default['default'](min), unit);
|
||||
},
|
||||
|
||||
startOf: function(time, unit, weekday) {
|
||||
time = moment__default['default'](time);
|
||||
if (unit === 'isoWeek') {
|
||||
weekday = Math.trunc(Math.min(Math.max(0, weekday), 6));
|
||||
return time.isoWeekday(weekday).startOf('day').valueOf();
|
||||
}
|
||||
return time.startOf(unit).valueOf();
|
||||
},
|
||||
|
||||
endOf: function(time, unit) {
|
||||
return moment__default['default'](time).endOf(unit).valueOf();
|
||||
}
|
||||
} : {});
|
||||
|
||||
})));
|
||||
8
src/opnsense/www/js/chartjs-adapter-moment.min.js
vendored
Normal file
8
src/opnsense/www/js/chartjs-adapter-moment.min.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* chartjs-adapter-moment v1.0.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2022 chartjs-adapter-moment Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
|
||||
//# sourceMappingURL=chartjs-adapter-moment.min.js.map
|
||||
1
src/opnsense/www/js/chartjs-adapter-moment.min.js.map
Normal file
1
src/opnsense/www/js/chartjs-adapter-moment.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"chartjs-adapter-moment.min.js","sources":["../src/index.js"],"sourcesContent":["'use strict';\n\nimport moment from 'moment';\nimport {_adapters} from 'chart.js';\n\nconst FORMATS = {\n datetime: 'MMM D, YYYY, h:mm:ss a',\n millisecond: 'h:mm:ss.SSS a',\n second: 'h:mm:ss a',\n minute: 'h:mm a',\n hour: 'hA',\n day: 'MMM D',\n week: 'll',\n month: 'MMM YYYY',\n quarter: '[Q]Q - YYYY',\n year: 'YYYY'\n};\n\n_adapters._date.override(typeof moment === 'function' ? {\n _id: 'moment', // DEBUG ONLY\n\n formats: function() {\n return FORMATS;\n },\n\n parse: function(value, format) {\n if (typeof value === 'string' && typeof format === 'string') {\n value = moment(value, format);\n } else if (!(value instanceof moment)) {\n value = moment(value);\n }\n return value.isValid() ? value.valueOf() : null;\n },\n\n format: function(time, format) {\n return moment(time).format(format);\n },\n\n add: function(time, amount, unit) {\n return moment(time).add(amount, unit).valueOf();\n },\n\n diff: function(max, min, unit) {\n return moment(max).diff(moment(min), unit);\n },\n\n startOf: function(time, unit, weekday) {\n time = moment(time);\n if (unit === 'isoWeek') {\n weekday = Math.trunc(Math.min(Math.max(0, weekday), 6));\n return time.isoWeekday(weekday).startOf('day').valueOf();\n }\n return time.startOf(unit).valueOf();\n },\n\n endOf: function(time, unit) {\n return moment(time).endOf(unit).valueOf();\n }\n} : {});\n"],"names":["FORMATS","datetime","millisecond","second","minute","hour","day","week","month","quarter","year","_adapters","_date","override","moment","_id","formats","parse","value","format","isValid","valueOf","time","add","amount","unit","diff","max","min","startOf","weekday","Math","trunc","isoWeekday","endOf"],"mappings":";;;;;;gXAKA,MAAMA,EAAU,CACdC,SAAU,yBACVC,YAAa,gBACbC,OAAQ,YACRC,OAAQ,SACRC,KAAM,KACNC,IAAK,QACLC,KAAM,KACNC,MAAO,WACPC,QAAS,cACTC,KAAM,QAGRC,YAAUC,MAAMC,SAA2B,mBAAXC,UAAwB,CACtDC,IAAK,SAELC,QAAS,WACP,OAAOhB,GAGTiB,MAAO,SAASC,EAAOC,GAMrB,MALqB,iBAAVD,GAAwC,iBAAXC,EACtCD,EAAQJ,UAAOI,EAAOC,GACXD,aAAiBJ,YAC5BI,EAAQJ,UAAOI,IAEVA,EAAME,UAAYF,EAAMG,UAAY,MAG7CF,OAAQ,SAASG,EAAMH,GACrB,OAAOL,UAAOQ,GAAMH,OAAOA,IAG7BI,IAAK,SAASD,EAAME,EAAQC,GAC1B,OAAOX,UAAOQ,GAAMC,IAAIC,EAAQC,GAAMJ,WAGxCK,KAAM,SAASC,EAAKC,EAAKH,GACvB,OAAOX,UAAOa,GAAKD,KAAKZ,UAAOc,GAAMH,IAGvCI,QAAS,SAASP,EAAMG,EAAMK,GAE5B,OADAR,EAAOR,UAAOQ,GACD,YAATG,GACFK,EAAUC,KAAKC,MAAMD,KAAKH,IAAIG,KAAKJ,IAAI,EAAGG,GAAU,IAC7CR,EAAKW,WAAWH,GAASD,QAAQ,OAAOR,WAE1CC,EAAKO,QAAQJ,GAAMJ,WAG5Ba,MAAO,SAASZ,EAAMG,GACpB,OAAOX,UAAOQ,GAAMY,MAAMT,GAAMJ,YAEhC"}
|
||||
@ -1,16 +1,16 @@
|
||||
/*!
|
||||
* chartjs-plugin-colorschemes v0.4.0
|
||||
* https://nagix.github.io/chartjs-plugin-colorschemes
|
||||
* (c) 2019 Akihiko Kusanagi
|
||||
* (c) 2022 Akihiko Kusanagi
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
|
||||
(global = global || self, global.ChartColorSchemes = factory(global.Chart));
|
||||
}(this, function (Chart) { 'use strict';
|
||||
}(this, (function (Chart) { 'use strict';
|
||||
|
||||
Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart;
|
||||
Chart = Chart && Object.prototype.hasOwnProperty.call(Chart, 'default') ? Chart['default'] : Chart;
|
||||
|
||||
// eslint-disable-next-line one-var
|
||||
var
|
||||
@ -318,6 +318,7 @@ var
|
||||
SetThree12 = ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'];
|
||||
|
||||
var brewer = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
YlGn3: YlGn3,
|
||||
YlGn4: YlGn4,
|
||||
YlGn5: YlGn5,
|
||||
@ -721,6 +722,7 @@ var
|
||||
YellowOrange6 = ['#f0a22e', '#a5644e', '#b58b80', '#c3986d', '#a19574', '#c17529'];
|
||||
|
||||
var office = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
Adjacency6: Adjacency6,
|
||||
Advantage6: Advantage6,
|
||||
Angles6: Angles6,
|
||||
@ -942,6 +944,7 @@ var
|
||||
ClassicRedGreenLight11 = ['#ffb2b6', '#fcbdc0', '#f8c7c9', '#f2d1d2', '#ecdbdc', '#e5e5e5', '#dde6d9', '#d4e6cc', '#cae6c0', '#c1e6b4', '#b7e6a7'];
|
||||
|
||||
var tableau = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
Tableau10: Tableau10,
|
||||
Tableau20: Tableau20,
|
||||
ColorBlind10: ColorBlind10,
|
||||
@ -1037,11 +1040,17 @@ var hoverReset = Chart.DatasetController.prototype.removeHoverStyle.length === 2
|
||||
|
||||
var EXPANDO_KEY = '$colorschemes';
|
||||
|
||||
Chart.defaults.global.plugins.colorschemes = {
|
||||
// pluginBase snippet fixes the chartjs 3 incompatibility, and is backwards-compatible
|
||||
// by Github user gebrits (https://github.com/gebrits/chartjs-plugin-colorschemes)
|
||||
//
|
||||
// Chartjs 2 => Chart.defaults.global
|
||||
// Chartjs 3 => Chart.defaults
|
||||
const pluginBase = Chart.defaults.global || Chart.defaults;
|
||||
pluginBase.plugins.colorschemes = {
|
||||
scheme: 'brewer.Paired12',
|
||||
fillAlpha: 0.5,
|
||||
reverse: false,
|
||||
override: false
|
||||
overrideExisting: false
|
||||
};
|
||||
|
||||
function getScheme(scheme) {
|
||||
@ -1071,11 +1080,17 @@ function getScheme(scheme) {
|
||||
var ColorSchemesPlugin = {
|
||||
id: 'colorschemes',
|
||||
|
||||
beforeUpdate: function(chart, options) {
|
||||
beforeUpdate: function(chart, args, options) {
|
||||
// Please note that in v3, the args argument was added. It was not used before it was added,
|
||||
// so we just check if it is not actually our options object
|
||||
if (options === undefined) {
|
||||
options = args;
|
||||
}
|
||||
|
||||
var scheme = getScheme(options.scheme);
|
||||
var fillAlpha = options.fillAlpha;
|
||||
var reverse = options.reverse;
|
||||
var override = options.override;
|
||||
var override = options.overrideExisting;
|
||||
var custom = options.custom;
|
||||
var schemeClone, customResult, length, colorIndex, color;
|
||||
|
||||
@ -1140,6 +1155,17 @@ var ColorSchemesPlugin = {
|
||||
});
|
||||
}
|
||||
break;
|
||||
// For bar chart backgroundColor (including fillAlpha) and borderColor are set
|
||||
case 'bar':
|
||||
if (typeof dataset.backgroundColor === 'undefined' || override) {
|
||||
dataset[EXPANDO_KEY].backgroundColor = dataset.backgroundColor;
|
||||
dataset.backgroundColor = helpers.color(color).alpha(fillAlpha).rgbString();
|
||||
}
|
||||
if (typeof dataset.borderColor === 'undefined' || override) {
|
||||
dataset[EXPANDO_KEY].borderColor = dataset.borderColor;
|
||||
dataset.borderColor = color;
|
||||
}
|
||||
break;
|
||||
// For the other chart, only backgroundColor is set
|
||||
default:
|
||||
if (typeof dataset.backgroundColor === 'undefined' || override) {
|
||||
@ -1186,10 +1212,11 @@ var ColorSchemesPlugin = {
|
||||
}
|
||||
};
|
||||
|
||||
Chart.plugins.register(ColorSchemesPlugin);
|
||||
const registerPlugin = Chart.register || Chart.plugins.register;
|
||||
registerPlugin(ColorSchemesPlugin);
|
||||
|
||||
Chart.colorschemes = colorschemes;
|
||||
|
||||
return ColorSchemesPlugin;
|
||||
|
||||
}));
|
||||
})));
|
||||
|
||||
7
src/opnsense/www/js/chartjs-plugin-colorschemes.min.js
vendored
Normal file
7
src/opnsense/www/js/chartjs-plugin-colorschemes.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -53,10 +53,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
}
|
||||
|
||||
?>
|
||||
<script src="<?=cache_safe('/ui/js/moment-with-locales.min.js');?>"></script>
|
||||
<script src="<?=cache_safe('/ui/js/chart.min.js');?>"></script>
|
||||
<script src="<?=cache_safe('/ui/js/chartjs-plugin-streaming.min.js');?>"></script>
|
||||
<script src="<?=cache_safe('/ui/js/chartjs-plugin-colorschemes.js');?>"></script>
|
||||
<script src="<?=cache_safe('/ui/js/moment-with-locales.min.js');?>"></script>
|
||||
<script src="<?=cache_safe('/ui/js/chartjs-adapter-moment.js');?>"></script>
|
||||
<link rel="stylesheet" type="text/css" href="<?=cache_safe(get_themed_filename('/css/chart.css'));?>" rel="stylesheet" />
|
||||
|
||||
<script>
|
||||
@ -64,6 +65,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
* page setup
|
||||
*/
|
||||
$("#dashboard_container").on("WidgetsReady", function() {
|
||||
function set_alpha(color, opacity) {
|
||||
const op = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255);
|
||||
return color + op.toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
function format_field(value) {
|
||||
if (!isNaN(value) && value > 0) {
|
||||
let fileSizeTypes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"];
|
||||
@ -77,6 +83,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create new traffic chart
|
||||
*/
|
||||
@ -108,15 +115,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
datasets: all_datasets
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
elements: {
|
||||
line: {
|
||||
fill: true,
|
||||
cubicInterpolationMode: 'monotone',
|
||||
clip: 0
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
x: {
|
||||
time: {
|
||||
tooltipFormat:'HH:mm:ss',
|
||||
unit: 'second',
|
||||
@ -132,23 +140,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
refresh: 5000,
|
||||
delay: 5000
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
callback: function (value, index, values) {
|
||||
return format_field(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem, data) {
|
||||
let ds = data.datasets[tooltipItem.datasetIndex];
|
||||
return ds.label + " : " + format_field(ds.data[tooltipItem.index].y).toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
hover: {
|
||||
@ -156,6 +154,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem, data) {
|
||||
return context.dataset.label + ": " + format_field(context.dataset.data[context.dataIndex].y).toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
streaming: {
|
||||
frameRate: 30
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user