mirror of
https://github.com/pi-hole/web.git
synced 2026-02-15 07:25:39 +00:00
Update chart.js from 2.9.4 to 3.9.1. (#2373)
This commit is contained in:
24
index.php
24
index.php
@@ -79,8 +79,8 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Total queries over last <span class="maxlogage-interval">24</span> hours</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<canvas id="queryOverTimeChart" width="800" height="140"></canvas>
|
||||
<div class="chart" style="width: 100%; height: 180px">
|
||||
<canvas id="queryOverTimeChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
@@ -97,8 +97,8 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Client activity over last <span class="maxlogage-interval">24</span> hours</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<canvas id="clientsChart" width="800" height="140" class="extratooltipcanvas no-user-select"></canvas>
|
||||
<div class="chart" style="width: 100%; height: 180px">
|
||||
<canvas id="clientsChart" class="extratooltipcanvas no-user-select"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
@@ -116,12 +116,10 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Query Types</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="pull-left" style="width:50%">
|
||||
<canvas id="queryTypePieChart" width="120" height="120"></canvas>
|
||||
</div>
|
||||
<div class="pull-left" style="width:50%">
|
||||
<div id="query-types-legend" class="chart-legend"></div>
|
||||
<div style="width:50%">
|
||||
<canvas id="queryTypePieChart" width="280" height="280"></canvas>
|
||||
</div>
|
||||
<div class="chart-legend" style="width:50%" id="query-types-legend" ></div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
@@ -135,12 +133,10 @@ require_once 'scripts/pi-hole/php/gravity.php';
|
||||
<h3 class="box-title">Upstream servers</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="pull-left" style="width:50%">
|
||||
<canvas id="forwardDestinationPieChart" width="120" height="120" class="extratooltipcanvas no-user-select"></canvas>
|
||||
</div>
|
||||
<div class="pull-left" style="width:50%">
|
||||
<div id="forward-destinations-legend" class="chart-legend extratooltipcanvas no-user-select"></div>
|
||||
<div style="width:50%">
|
||||
<canvas id="forwardDestinationPieChart" width="280" height="280" class="extratooltipcanvas no-user-select"></canvas>
|
||||
</div>
|
||||
<div class="chart-legend" style="width:50%" id="forward-destinations-legend"></div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-sync fa-spin"></i>
|
||||
|
||||
@@ -120,25 +120,25 @@ function updateQueriesOverTime() {
|
||||
|
||||
interval = computeInterval(from, until);
|
||||
// Default displaying axis scaling
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "hour";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "hour";
|
||||
|
||||
var duration = until - from;
|
||||
// Xaxis scaling based on selected daterange
|
||||
if (duration > 4 * 365 * 24 * 60 * 60) {
|
||||
// If the requested data is more than 4 years, set ticks interval to year
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "year";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "year";
|
||||
} else if (duration >= 366 * 24 * 60 * 60) {
|
||||
// If the requested data is more than 1 year, set ticks interval to quarter
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "quarter";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "quarter";
|
||||
} else if (duration >= 92 * 24 * 60 * 60) {
|
||||
// If the requested data is more than 3 months, set ticks interval to months
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "month";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "month";
|
||||
} else if (duration >= 31 * 24 * 60 * 60) {
|
||||
// If the requested data is 1 month or more, set ticks interval to weeks
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "week";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "week";
|
||||
} else if (duration > 3 * 24 * 60 * 60) {
|
||||
// If the requested data is more than 3 days (72 hours), set ticks interval to days
|
||||
timeLineChart.options.scales.xAxes[0].time.unit = "day";
|
||||
timeLineChart.options.scales.xAxes.time.unit = "day";
|
||||
}
|
||||
|
||||
$.getJSON(
|
||||
@@ -196,9 +196,9 @@ function updateQueriesOverTime() {
|
||||
}
|
||||
}
|
||||
|
||||
timeLineChart.options.scales.xAxes[0].ticks.min = from * 1000;
|
||||
timeLineChart.options.scales.xAxes[0].ticks.max = until * 1000;
|
||||
timeLineChart.options.scales.xAxes[0].display = true;
|
||||
timeLineChart.options.scales.xAxes.ticks.min = from * 1000;
|
||||
timeLineChart.options.scales.xAxes.ticks.max = until * 1000;
|
||||
timeLineChart.options.scales.xAxes.display = true;
|
||||
$("#queries-over-time .overlay").hide();
|
||||
timeoutWarning.hide();
|
||||
timeLineChart.update();
|
||||
@@ -220,163 +220,163 @@ $(function () {
|
||||
datasets: [
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: blockedColor,
|
||||
borderColor: blockedColor,
|
||||
pointBorderColor: blockedColor,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
},
|
||||
{
|
||||
label: "Permitted DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: permittedColor,
|
||||
borderColor: permittedColor,
|
||||
pointBorderColor: permittedColor,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function (tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = new Date(label);
|
||||
var fromDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
utils.padNumber(time.getDate());
|
||||
var fromTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
utils.padNumber(time.getSeconds());
|
||||
time = new Date(time.valueOf() + 1000 * interval);
|
||||
var untilDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
utils.padNumber(time.getDate());
|
||||
var untilTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
utils.padNumber(time.getSeconds());
|
||||
responsive: true,
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
yAlign: "bottom",
|
||||
intersect: false,
|
||||
mode: "x",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
callbacks: {
|
||||
label: function (tooltipLabel) {
|
||||
var label = tooltipLabel.dataset.label;
|
||||
// Add percentage only for blocked queries
|
||||
if (tooltipLabel.datasetIndex === 0) {
|
||||
var percentage = 0;
|
||||
var permitted = parseInt(tooltipLabel.parsed._stacks.y[1], 10);
|
||||
var blocked = parseInt(tooltipLabel.parsed._stacks.y[0], 10);
|
||||
if (permitted + blocked > 0) {
|
||||
percentage = (100 * blocked) / (permitted + blocked);
|
||||
}
|
||||
|
||||
if (fromDate === untilDate) {
|
||||
// Abbreviated form for intervals on the same day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
untilTime += "\n";
|
||||
label += ": " + tooltipLabel.parsed.y + " (" + percentage.toFixed(1) + "%)";
|
||||
} else {
|
||||
label += ": " + tooltipLabel.parsed.y;
|
||||
}
|
||||
|
||||
return ("Queries from " + fromTime + " to " + untilTime + " on " + fromDate).split(
|
||||
"\n "
|
||||
);
|
||||
}
|
||||
return label;
|
||||
},
|
||||
title: function (tooltipTitle) {
|
||||
var title = tooltipTitle[0].label;
|
||||
var time = new Date(title);
|
||||
var fromDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
utils.padNumber(time.getDate());
|
||||
var fromTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
utils.padNumber(time.getSeconds());
|
||||
time = new Date(time.valueOf() + 1000 * interval);
|
||||
var untilDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
utils.padNumber(time.getDate());
|
||||
var untilTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
utils.padNumber(time.getSeconds());
|
||||
|
||||
// Full tooltip for intervals spanning more than one day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
fromDate += "\n";
|
||||
}
|
||||
if (fromDate === untilDate) {
|
||||
// Abbreviated form for intervals on the same day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
untilTime += "\n";
|
||||
}
|
||||
|
||||
return (
|
||||
"Queries from " +
|
||||
fromDate +
|
||||
" " +
|
||||
fromTime +
|
||||
" to " +
|
||||
untilDate +
|
||||
" " +
|
||||
untilTime
|
||||
).split("\n ");
|
||||
},
|
||||
label: function (tooltipItems, data) {
|
||||
if (tooltipItems.datasetIndex === 0) {
|
||||
var percentage = 0;
|
||||
var permitted = parseInt(data.datasets[1].data[tooltipItems.index], 10);
|
||||
var blocked = parseInt(data.datasets[0].data[tooltipItems.index], 10);
|
||||
if (permitted + blocked > 0) {
|
||||
percentage = (100 * blocked) / (permitted + blocked);
|
||||
return ("Queries from " + fromTime + " to " + untilTime + " on " + fromDate).split(
|
||||
"\n "
|
||||
);
|
||||
}
|
||||
|
||||
// Full tooltip for intervals spanning more than one day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
fromDate += "\n";
|
||||
}
|
||||
|
||||
return (
|
||||
data.datasets[tooltipItems.datasetIndex].label +
|
||||
": " +
|
||||
tooltipItems.yLabel +
|
||||
" (" +
|
||||
percentage.toFixed(1) +
|
||||
"%)"
|
||||
);
|
||||
}
|
||||
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
"Queries from " +
|
||||
fromDate +
|
||||
" " +
|
||||
fromTime +
|
||||
" to " +
|
||||
untilDate +
|
||||
" " +
|
||||
untilTime
|
||||
).split("\n ");
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
type: "time",
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
minute: "HH:mm",
|
||||
hour: "HH:mm",
|
||||
day: "MMM DD",
|
||||
week: "MMM DD",
|
||||
month: "MMM",
|
||||
quarter: "YYYY MMM",
|
||||
year: "YYYY",
|
||||
},
|
||||
},
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
zeroLineColor: gridColor,
|
||||
},
|
||||
ticks: {
|
||||
fontColor: ticksColor,
|
||||
xAxes: {
|
||||
type: "time",
|
||||
stacked: true,
|
||||
offset: false,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
minute: "HH:mm",
|
||||
hour: "HH:mm",
|
||||
day: "MMM DD",
|
||||
week: "MMM DD",
|
||||
month: "MMM",
|
||||
quarter: "YYYY MMM",
|
||||
year: "YYYY",
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
fontColor: ticksColor,
|
||||
},
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
drawBorder: false,
|
||||
offset: false,
|
||||
},
|
||||
],
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
},
|
||||
},
|
||||
yAxes: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
precision: 0,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
borderWidth: 0,
|
||||
spanGaps: false,
|
||||
fill: true,
|
||||
},
|
||||
point: {
|
||||
radius: 0,
|
||||
hoverRadius: 5,
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
@@ -391,10 +391,15 @@ $("#querytime").on("apply.daterangepicker", function (ev, picker) {
|
||||
});
|
||||
|
||||
$("#queryOverTimeChart").click(function (evt) {
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
var activePoints = timeLineChart.getElementsAtEventForMode(
|
||||
evt,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
false
|
||||
);
|
||||
if (activePoints.length > 0) {
|
||||
//get the internal index in the chart
|
||||
var clickedElementindex = activePoints[0]._index;
|
||||
var clickedElementindex = activePoints[0].index;
|
||||
|
||||
//get specific label by index
|
||||
var label = timeLineChart.data.labels[clickedElementindex];
|
||||
|
||||
@@ -30,7 +30,8 @@ var THEME_COLORS = [
|
||||
"#d2d6de",
|
||||
];
|
||||
|
||||
var customTooltips = function (tooltip) {
|
||||
var customTooltips = function (context) {
|
||||
var tooltip = context.tooltip;
|
||||
var tooltipEl = document.getElementById(this._chart.canvas.id + "-customTooltip");
|
||||
if (!tooltipEl) {
|
||||
// Create Tooltip Element once per chart
|
||||
@@ -42,11 +43,12 @@ var customTooltips = function (tooltip) {
|
||||
// font-size was set to 14px by bootstrap's css
|
||||
var fontZoom = parseFloat($("body").css("font-size")) / 14;
|
||||
// set styles and font
|
||||
tooltipEl.style.padding = tooltip.yPadding + "px " + tooltip.xPadding + "px";
|
||||
tooltipEl.style.borderRadius = tooltip.cornerRadius + "px";
|
||||
tooltipEl.style.fontFamily = tooltip._bodyFontFamily;
|
||||
tooltipEl.style.fontSize = tooltip.bodyFontSize / fontZoom + "px";
|
||||
tooltipEl.style.fontStyle = tooltip._bodyFontStyle;
|
||||
tooltipEl.style.padding = tooltip.options.padding + "px " + tooltip.options.padding + "px";
|
||||
tooltipEl.style.borderRadius = tooltip.options.cornerRadius + "px";
|
||||
tooltipEl.style.font = tooltip.options.bodyFont.string;
|
||||
tooltipEl.style.fontFamily = tooltip.options.bodyFont.family;
|
||||
tooltipEl.style.fontSize = tooltip.options.bodyFont.size / fontZoom + "px";
|
||||
tooltipEl.style.fontStyle = tooltip.options.bodyFont.style;
|
||||
// append Tooltip next to canvas-containing box
|
||||
tooltipEl.ancestor = this._chart.canvas.closest(".box[id]").parentNode;
|
||||
tooltipEl.ancestor.append(tooltipEl);
|
||||
@@ -110,9 +112,9 @@ var customTooltips = function (tooltip) {
|
||||
var tooltipHeight = tooltipEl.offsetHeight;
|
||||
var caretX = tooltip.caretX;
|
||||
var caretY = tooltip.caretY;
|
||||
var caretPadding = tooltip.caretPadding;
|
||||
var caretPadding = tooltip.options.caretPadding;
|
||||
var tooltipX, tooltipY, arrowX;
|
||||
var arrowMinIndent = 2 * tooltip.cornerRadius;
|
||||
var arrowMinIndent = 2 * tooltip.options.cornerRadius;
|
||||
var arrowSize = 5;
|
||||
|
||||
// Compute X position
|
||||
@@ -315,117 +317,16 @@ function updateQueryTypesPie() {
|
||||
queryTypePieChart.data.datasets[0] = dd;
|
||||
queryTypePieChart.data.labels = k;
|
||||
$("#query-types-pie .overlay").hide();
|
||||
queryTypePieChart.chart.config.options.cutoutPercentage = 50;
|
||||
queryTypePieChart.update();
|
||||
|
||||
// Don't use rotation animation for further updates
|
||||
queryTypePieChart.options.animation.duration = 0;
|
||||
queryTypePieChart.options.legendCallback = customLegend;
|
||||
|
||||
// Generate legend in separate div
|
||||
$("#query-types-legend").html(queryTypePieChart.generateLegend());
|
||||
$("#query-types-legend > ul > li").click(function (e) {
|
||||
if (iscolorBox(e.target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window.location.href = "queries.php?querytype=" + querytypeids[$(this).index()];
|
||||
});
|
||||
$("#query-types-legend .colorBoxWrapper").click(function (e) {
|
||||
hidePieSlice(e);
|
||||
});
|
||||
}).done(function () {
|
||||
// Reload graph after minute
|
||||
setTimeout(updateQueryTypesPie, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
function customLegend(chart) {
|
||||
var text = [];
|
||||
var data = chart.data;
|
||||
var datasets = data.datasets;
|
||||
var labels = data.labels;
|
||||
|
||||
text.push('<ul class="' + chart.id + '-legend">');
|
||||
|
||||
if (datasets.length > 0) {
|
||||
for (var i = 0; i < datasets[0].data.length; ++i) {
|
||||
var color = datasets[0].backgroundColor[i];
|
||||
|
||||
var txt = "";
|
||||
|
||||
// legend box icon
|
||||
txt =
|
||||
'<span class="colorBoxWrapper" style="color: ' +
|
||||
color +
|
||||
'" title="Toggle visibility">' +
|
||||
'<i class="fa fa-check-square"></i>' +
|
||||
"</span>";
|
||||
|
||||
// color block
|
||||
txt += '<span class="legend-color-box" style="background-color:' + color + '"></span>';
|
||||
|
||||
// label
|
||||
if (labels[i]) {
|
||||
txt +=
|
||||
'<span class="legend-label-text" title="List ' +
|
||||
labels[i] +
|
||||
' queries">' +
|
||||
labels[i] +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
text.push("<li>" + txt + "</li>");
|
||||
}
|
||||
}
|
||||
|
||||
text.push("</ul>");
|
||||
return text.join("");
|
||||
}
|
||||
|
||||
function hidePieSlice(event) {
|
||||
togglecolorBox(event.target);
|
||||
|
||||
var legendID = $(event.target).closest(".chart-legend").attr("id");
|
||||
var ci =
|
||||
legendID === "query-types-legend"
|
||||
? event.view.queryTypePieChart
|
||||
: event.view.forwardDestinationPieChart;
|
||||
|
||||
var listItemParent = $(event.target).closest("li");
|
||||
$(listItemParent).toggleClass("strike");
|
||||
|
||||
var index = $(listItemParent).index();
|
||||
var mobj = ci.data.datasets[0]._meta;
|
||||
var metas = Object.keys(mobj).map(function (e) {
|
||||
return mobj[e];
|
||||
});
|
||||
metas.forEach(function (meta) {
|
||||
var curr = meta.data[index];
|
||||
curr.hidden = !curr.hidden;
|
||||
});
|
||||
|
||||
ci.update();
|
||||
}
|
||||
|
||||
function togglecolorBox(target) {
|
||||
var parentListItem = $(target).closest("li");
|
||||
var colorBox = $(parentListItem).find(".fa-check-square, .fa-square");
|
||||
|
||||
if (colorBox) {
|
||||
$(colorBox).toggleClass("fa-check-square");
|
||||
$(colorBox).toggleClass("fa-square");
|
||||
}
|
||||
}
|
||||
|
||||
function iscolorBox(target) {
|
||||
// See if click happened on colorBoxWrapper or child SVG
|
||||
if ($(target).closest(".colorBoxWrapper")[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateClientsOverTime() {
|
||||
$.getJSON("api.php?overTimeDataClients&getClientNames", function (data) {
|
||||
if ("FTLnotrunning" in data) {
|
||||
@@ -561,27 +462,10 @@ function updateForwardDestinationsPie() {
|
||||
forwardDestinationPieChart.data.datasets[0] = dd;
|
||||
// and push it at once
|
||||
$("#forward-destinations-pie .overlay").hide();
|
||||
forwardDestinationPieChart.chart.config.options.cutoutPercentage = 50;
|
||||
forwardDestinationPieChart.update();
|
||||
|
||||
// Don't use rotation animation for further updates
|
||||
forwardDestinationPieChart.options.animation.duration = 0;
|
||||
forwardDestinationPieChart.options.legendCallback = customLegend;
|
||||
|
||||
// Generate legend in separate div
|
||||
$("#forward-destinations-legend").html(forwardDestinationPieChart.generateLegend());
|
||||
$("#forward-destinations-legend > ul > li").click(function (e) {
|
||||
if (iscolorBox(e.target)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var obj = encodeURIComponent(e.target.textContent);
|
||||
if (obj.length > 0) {
|
||||
window.location.href = "queries.php?forwarddest=" + obj;
|
||||
}
|
||||
});
|
||||
$("#forward-destinations-legend .colorBoxWrapper").click(function (e) {
|
||||
hidePieSlice(e);
|
||||
});
|
||||
}).done(function () {
|
||||
// Reload graph after one minute
|
||||
setTimeout(updateForwardDestinationsPie, 60000);
|
||||
@@ -839,32 +723,23 @@ function updateSummaryData(runOnce) {
|
||||
});
|
||||
}
|
||||
|
||||
function doughnutTooltip(tooltipItems, data) {
|
||||
var dataset = data.datasets[tooltipItems.datasetIndex];
|
||||
var label = " " + data.labels[tooltipItems.index];
|
||||
// Compute share of total and of displayed
|
||||
var scale = 0,
|
||||
total = 0;
|
||||
var metas = Object.keys(dataset._meta).map(function (e) {
|
||||
return dataset._meta[e];
|
||||
});
|
||||
metas.forEach(function (meta) {
|
||||
meta.data.forEach(function (val, i) {
|
||||
if (val.hidden) scale += dataset.data[i];
|
||||
total += dataset.data[i];
|
||||
});
|
||||
});
|
||||
if (scale === 0)
|
||||
function doughnutTooltip(tooltipLabel) {
|
||||
var percentageTotalShown = tooltipLabel.chart._metasets[0].total.toFixed(2);
|
||||
var label = " " + tooltipLabel.label;
|
||||
|
||||
if (percentageTotalShown >= 100) {
|
||||
// All items shown
|
||||
return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%";
|
||||
return (
|
||||
label +
|
||||
":<br>• " +
|
||||
dataset.data[tooltipItems.index].toFixed(1) +
|
||||
"% of all queries<br>• " +
|
||||
((dataset.data[tooltipItems.index] * 100) / (total - scale)).toFixed(1) +
|
||||
"% of shown items"
|
||||
);
|
||||
return label + ": " + tooltipLabel.parsed.toFixed(1) + "%";
|
||||
} else {
|
||||
return (
|
||||
label +
|
||||
":<br>• " +
|
||||
tooltipLabel.parsed.toFixed(1) +
|
||||
"% of all queries<br>• " +
|
||||
((tooltipLabel.parsed * 100) / percentageTotalShown).toFixed(1) +
|
||||
"% of shown items"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var maxlogage = "24";
|
||||
@@ -878,6 +753,91 @@ function getMaxlogage() {
|
||||
});
|
||||
}
|
||||
|
||||
// chartjs plugin used by the custom doughnut legend
|
||||
const getOrCreateLegendList = (chart, id) => {
|
||||
const legendContainer = document.getElementById(id);
|
||||
let listContainer = legendContainer.querySelector("ul");
|
||||
|
||||
if (!listContainer) {
|
||||
listContainer = document.createElement("ul");
|
||||
listContainer.style.display = "flex";
|
||||
listContainer.style.flexDirection = "column";
|
||||
listContainer.style.margin = 0;
|
||||
listContainer.style.padding = 0;
|
||||
|
||||
legendContainer.append(listContainer);
|
||||
}
|
||||
|
||||
return listContainer;
|
||||
};
|
||||
|
||||
const htmlLegendPlugin = {
|
||||
id: "htmlLegend",
|
||||
afterUpdate(chart, args, options) {
|
||||
const ul = getOrCreateLegendList(chart, options.containerID);
|
||||
|
||||
// Remove old legend items
|
||||
while (ul.firstChild) {
|
||||
ul.firstChild.remove();
|
||||
}
|
||||
|
||||
// Reuse the built-in legendItems generator
|
||||
const items = chart.options.plugins.legend.labels.generateLabels(chart);
|
||||
|
||||
items.forEach(item => {
|
||||
const li = document.createElement("li");
|
||||
li.style.alignItems = "center";
|
||||
li.style.cursor = "pointer";
|
||||
li.style.display = "flex";
|
||||
li.style.flexDirection = "row";
|
||||
|
||||
// Color checkbox (toggle visibility)
|
||||
const boxSpan = document.createElement("span");
|
||||
boxSpan.title = "Toggle visibility";
|
||||
boxSpan.style.color = item.fillStyle;
|
||||
boxSpan.style.display = "inline-block";
|
||||
boxSpan.style.margin = "0 10px";
|
||||
boxSpan.innerHTML = item.hidden
|
||||
? '<i class="colorBoxWrapper fa fa-square"></i>'
|
||||
: '<i class="colorBoxWrapper fa fa-check-square"></i>';
|
||||
|
||||
boxSpan.addEventListener("click", () => {
|
||||
const { type } = chart.config;
|
||||
|
||||
if (type === "pie" || type === "doughnut") {
|
||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||
chart.toggleDataVisibility(item.index);
|
||||
} else {
|
||||
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
|
||||
}
|
||||
|
||||
chart.update();
|
||||
});
|
||||
|
||||
// Text (link to query log page)
|
||||
const textLink = document.createElement("p");
|
||||
textLink.title = "List " + item.text + " queries";
|
||||
textLink.style.color = item.fontColor;
|
||||
textLink.style.margin = 0;
|
||||
textLink.style.padding = 0;
|
||||
textLink.style.textDecoration = item.hidden ? "line-through" : "";
|
||||
textLink.className = "legend-label-text";
|
||||
textLink.append(item.text);
|
||||
|
||||
textLink.addEventListener("click", () => {
|
||||
if (chart.canvas.id === "queryTypePieChart") {
|
||||
window.location.href = "queries.php?querytype=" + querytypeids[item.index];
|
||||
} else if (chart.canvas.id === "forwardDestinationPieChart") {
|
||||
window.location.href = "queries.php?forwarddest=" + encodeURIComponent(item.text);
|
||||
}
|
||||
});
|
||||
|
||||
li.append(boxSpan, textLink);
|
||||
ul.append(li);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
$(function () {
|
||||
// Pull in data via AJAX
|
||||
getMaxlogage();
|
||||
@@ -890,100 +850,104 @@ $(function () {
|
||||
type: utils.getGraphType(),
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
callbacks: {
|
||||
title: function (tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = utils.padNumber(h) + ":" + utils.padNumber(m - 5) + ":00";
|
||||
var to = utils.padNumber(h) + ":" + utils.padNumber(m + 4) + ":59";
|
||||
return "Queries from " + from + " to " + to;
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
intersect: false,
|
||||
mode: "x",
|
||||
yAlign: "bottom",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
label: function (tooltipItems, data) {
|
||||
if (tooltipItems.datasetIndex === 0) {
|
||||
var percentage = 0;
|
||||
var permitted = parseInt(data.datasets[1].data[tooltipItems.index], 10);
|
||||
var blocked = parseInt(data.datasets[0].data[tooltipItems.index], 10);
|
||||
var total = permitted + blocked;
|
||||
if (total > 0) {
|
||||
percentage = (100 * blocked) / total;
|
||||
callbacks: {
|
||||
title: function (tooltipTitle) {
|
||||
var label = tooltipTitle[0].label;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = utils.padNumber(h) + ":" + utils.padNumber(m - 5) + ":00";
|
||||
var to = utils.padNumber(h) + ":" + utils.padNumber(m + 4) + ":59";
|
||||
return "Queries from " + from + " to " + to;
|
||||
},
|
||||
label: function (tooltipLabel) {
|
||||
var label = tooltipLabel.dataset.label;
|
||||
// Add percentage only for blocked queries
|
||||
if (tooltipLabel.datasetIndex === 0) {
|
||||
var percentage = 0;
|
||||
var permitted = parseInt(tooltipLabel.parsed._stacks.y[1], 10);
|
||||
var blocked = parseInt(tooltipLabel.parsed._stacks.y[0], 10);
|
||||
if (permitted + blocked > 0) {
|
||||
percentage = (100 * blocked) / (permitted + blocked);
|
||||
}
|
||||
|
||||
label += ": " + tooltipLabel.parsed.y + " (" + percentage.toFixed(1) + "%)";
|
||||
} else {
|
||||
label += ": " + tooltipLabel.parsed.y;
|
||||
}
|
||||
|
||||
return (
|
||||
data.datasets[tooltipItems.datasetIndex].label +
|
||||
": " +
|
||||
tooltipItems.yLabel +
|
||||
" (" +
|
||||
percentage.toFixed(1) +
|
||||
"%)"
|
||||
);
|
||||
}
|
||||
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
return label;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
type: "time",
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm",
|
||||
},
|
||||
tooltipFormat: "HH:mm",
|
||||
},
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
zeroLineColor: gridColor,
|
||||
},
|
||||
ticks: {
|
||||
fontColor: ticksColor,
|
||||
xAxes: {
|
||||
type: "time",
|
||||
stacked: true,
|
||||
offset: false,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm",
|
||||
},
|
||||
tooltipFormat: "HH:mm",
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
fontColor: ticksColor,
|
||||
precision: 0,
|
||||
},
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
zeroLineColor: gridColor,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
offset: false,
|
||||
drawBorder: false,
|
||||
},
|
||||
],
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
},
|
||||
},
|
||||
yAxes: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
precision: 0,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
borderWidth: 0,
|
||||
spanGaps: false,
|
||||
fill: true,
|
||||
},
|
||||
point: {
|
||||
radius: 0,
|
||||
hoverRadius: 5,
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
|
||||
updateQueriesOverTime();
|
||||
|
||||
// Create / load "Top Clients over Time" only if authorized
|
||||
@@ -994,78 +958,84 @@ $(function () {
|
||||
type: utils.getGraphType(),
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
mode: "x-axis",
|
||||
custom: customTooltips,
|
||||
yAlign: "top",
|
||||
itemSort: function (a, b) {
|
||||
return b.yLabel - a.yLabel;
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
callbacks: {
|
||||
title: function (tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = utils.padNumber(h) + ":" + utils.padNumber(m - 5) + ":00";
|
||||
var to = utils.padNumber(h) + ":" + utils.padNumber(m + 4) + ":59";
|
||||
return "Client activity from " + from + " to " + to;
|
||||
tooltip: {
|
||||
// Disable the on-canvas tooltip
|
||||
enabled: false,
|
||||
intersect: false,
|
||||
mode: "x",
|
||||
external: customTooltips,
|
||||
yAlign: "top",
|
||||
itemSort: function (a, b) {
|
||||
return b.raw - a.raw;
|
||||
},
|
||||
label: function (tooltipItems, data) {
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
callbacks: {
|
||||
title: function (tooltipTitle) {
|
||||
var label = tooltipTitle[0].label;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = utils.padNumber(h) + ":" + utils.padNumber(m - 5) + ":00";
|
||||
var to = utils.padNumber(h) + ":" + utils.padNumber(m + 4) + ":59";
|
||||
return "Client activity from " + from + " to " + to;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
type: "time",
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm",
|
||||
},
|
||||
tooltipFormat: "HH:mm",
|
||||
},
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
zeroLineColor: gridColor,
|
||||
},
|
||||
ticks: {
|
||||
fontColor: ticksColor,
|
||||
xAxes: {
|
||||
type: "time",
|
||||
stacked: true,
|
||||
offset: false,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm",
|
||||
},
|
||||
tooltipFormat: "HH:mm",
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
fontColor: ticksColor,
|
||||
precision: 0,
|
||||
},
|
||||
stacked: true,
|
||||
gridLines: {
|
||||
color: gridColor,
|
||||
zeroLineColor: gridColor,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
offset: false,
|
||||
drawBorder: false,
|
||||
},
|
||||
],
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
},
|
||||
},
|
||||
yAxes: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
color: ticksColor,
|
||||
precision: 0,
|
||||
},
|
||||
stacked: true,
|
||||
grid: {
|
||||
color: gridColor,
|
||||
drawBorder: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
borderWidth: 0,
|
||||
spanGaps: false,
|
||||
fill: true,
|
||||
point: {
|
||||
radius: 0,
|
||||
hoverRadius: 5,
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
hover: {
|
||||
animationDuration: 0,
|
||||
},
|
||||
@@ -1087,11 +1057,15 @@ $(function () {
|
||||
}
|
||||
|
||||
$("#queryOverTimeChart").click(function (evt) {
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
var activePoints = timeLineChart.getElementsAtEventForMode(
|
||||
evt,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
false
|
||||
);
|
||||
if (activePoints.length > 0) {
|
||||
//get the internal index of slice in pie chart
|
||||
var clickedElementindex = activePoints[0]._index;
|
||||
|
||||
//get the internal index
|
||||
var clickedElementindex = activePoints[0].index;
|
||||
//get specific label by index
|
||||
var label = timeLineChart.data.labels[clickedElementindex];
|
||||
|
||||
@@ -1105,10 +1079,15 @@ $(function () {
|
||||
});
|
||||
|
||||
$("#clientsChart").click(function (evt) {
|
||||
var activePoints = clientsChart.getElementAtEvent(evt);
|
||||
var activePoints = clientsChart.getElementsAtEventForMode(
|
||||
evt,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
false
|
||||
);
|
||||
if (activePoints.length > 0) {
|
||||
//get the internal index of slice in pie chart
|
||||
var clickedElementindex = activePoints[0]._index;
|
||||
//get the internal index
|
||||
var clickedElementindex = activePoints[0].index;
|
||||
|
||||
//get specific label by index
|
||||
var label = clientsChart.data.labels[clickedElementindex];
|
||||
@@ -1128,33 +1107,39 @@ $(function () {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
plugins: [htmlLegendPlugin],
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
elements: {
|
||||
arc: {
|
||||
borderColor: $(".box").css("background-color"),
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
custom: customTooltips,
|
||||
callbacks: {
|
||||
title: function () {
|
||||
return "Query types";
|
||||
},
|
||||
label: function (tooltipItems, data) {
|
||||
return doughnutTooltip(tooltipItems, data);
|
||||
plugins: {
|
||||
htmlLegend: {
|
||||
containerID: "query-types-legend",
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
// Disable the on-canvas tooltip
|
||||
enabled: false,
|
||||
external: customTooltips,
|
||||
callbacks: {
|
||||
title: function () {
|
||||
return "Query type";
|
||||
},
|
||||
label: doughnutTooltip,
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 750,
|
||||
},
|
||||
cutoutPercentage: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1168,33 +1153,39 @@ $(function () {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }],
|
||||
datasets: [{ data: [], parsing: false }],
|
||||
},
|
||||
plugins: [htmlLegendPlugin],
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
elements: {
|
||||
arc: {
|
||||
borderColor: $(".box").css("background-color"),
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
custom: customTooltips,
|
||||
callbacks: {
|
||||
title: function () {
|
||||
return "Forward destinations";
|
||||
},
|
||||
label: function (tooltipItems, data) {
|
||||
return doughnutTooltip(tooltipItems, data);
|
||||
plugins: {
|
||||
htmlLegend: {
|
||||
containerID: "forward-destinations-legend",
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
// Disable the on-canvas tooltip
|
||||
enabled: false,
|
||||
external: customTooltips,
|
||||
callbacks: {
|
||||
title: function () {
|
||||
return "Upstream server";
|
||||
},
|
||||
label: doughnutTooltip,
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 750,
|
||||
},
|
||||
cutoutPercentage: 0,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@ $cacheVer = filemtime(__FILE__);
|
||||
<script src="scripts/vendor/datatables.select.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/vendor/datatables.buttons.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/vendor/moment.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/vendor/Chart.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/vendor/chart.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/vendor/chartjs-adapter-moment.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<?php } ?>
|
||||
<script src="style/vendor/font-awesome/js/all.min.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?php echo $cacheVer; ?>"></script>
|
||||
|
||||
7
scripts/vendor/Chart.min.js
vendored
7
scripts/vendor/Chart.min.js
vendored
File diff suppressed because one or more lines are too long
13
scripts/vendor/chart.min.js
vendored
Normal file
13
scripts/vendor/chart.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
scripts/vendor/chartjs-adapter-moment.js
vendored
Normal file
8
scripts/vendor/chartjs-adapter-moment.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* chartjs-adapter-moment v1.0.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2021 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
scripts/vendor/chartjs-adapter-moment.min.js.map
vendored
Normal file
1
scripts/vendor/chartjs-adapter-moment.min.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"chartjs-adapter-moment.min.js","sources":["chartjs-adapter-moment.min"],"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"}
|
||||
@@ -234,49 +234,37 @@ td.lookatme {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.chart-legend {
|
||||
overflow: auto;
|
||||
#query-types-pie .box-body,
|
||||
#forward-destinations-pie .box-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.chart-legend ul {
|
||||
list-style-type: none;
|
||||
padding-left: 45px;
|
||||
.chart-legend {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.chart-legend li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
margin: 0 0 6px;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.chart-legend li span {
|
||||
display: inline-block;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.colorBoxWrapper {
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.colorBoxWrapper:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.chart-legend li .colorBoxWrapper {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
left: -20px;
|
||||
top: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chart-legend li .legend-color-box {
|
||||
display: none;
|
||||
width: 12px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-legend li .legend-label-text {
|
||||
line-height: 1;
|
||||
word-break: break-word;
|
||||
|
||||
Reference in New Issue
Block a user