450 lines
13 KiB
JavaScript
450 lines
13 KiB
JavaScript
// create views chart label
|
|
function createViewsChartLabel(ctx) {
|
|
const period = ctx.dataset.period;
|
|
let labels = [];
|
|
|
|
if (period === "day") {
|
|
const nowHour = new Date().getHours();
|
|
for (let i = 23; i >= 0; --i) {
|
|
let h = nowHour - i;
|
|
if (h < 0) h = 24 + h;
|
|
labels.push(`${Math.floor(h)}:00`);
|
|
}
|
|
}
|
|
|
|
if (period === "week") {
|
|
const nowDay = new Date().getDate();
|
|
for (let i = 6; i >= 0; --i) {
|
|
const date = new Date(new Date().setDate(nowDay - i));
|
|
labels.push(`${date.getDate()} ${date.toLocaleString("default",{month:"short"})}`);
|
|
}
|
|
}
|
|
|
|
if (period === "month") {
|
|
const nowDay = new Date().getDate();
|
|
for (let i = 29; i >= 0; --i) {
|
|
const date = new Date(new Date().setDate(nowDay - i));
|
|
labels.push(`${date.getDate()} ${date.toLocaleString("default",{month:"short"})}`);
|
|
}
|
|
}
|
|
|
|
if (period === "year") {
|
|
const nowMonth = new Date().getMonth();
|
|
for (let i = 11; i >= 0; --i) {
|
|
const date = new Date(new Date().setMonth(nowMonth - i));
|
|
labels.push(`${date.toLocaleString("default",{month:"short"})} ${date.toLocaleString("default",{year:"numeric"})}`);
|
|
}
|
|
}
|
|
|
|
return labels;
|
|
}
|
|
|
|
// change stats period for showing charts and data
|
|
function changeStatsPeriod(event) {
|
|
const period = event.target.dataset.period;
|
|
if (!period) return;
|
|
const canvases = document.querySelector("#stats").querySelectorAll("[data-period]");
|
|
const buttons = document.querySelector("#stats").querySelectorAll(".nav");
|
|
if (!buttons || !canvases) return;
|
|
buttons.forEach(b => b.disabled = false);
|
|
event.target.disabled = true;
|
|
canvases.forEach(canvas => {
|
|
if (canvas.dataset.period === period) {
|
|
canvas.classList.remove("hidden");
|
|
} else {
|
|
canvas.classList.add("hidden");
|
|
}
|
|
});
|
|
feedMapData(period);
|
|
}
|
|
|
|
// beautify browser lables
|
|
function beautifyBrowserName(name) {
|
|
if (name === "firefox") return "Firefox";
|
|
if (name === "chrome") return "Chrome";
|
|
if (name === "edge") return "Edge";
|
|
if (name === "opera") return "Opera";
|
|
if (name === "safari") return "Safari";
|
|
if (name === "other") return "Other";
|
|
if (name === "ie") return "IE";
|
|
return name;
|
|
}
|
|
|
|
|
|
// create views chart
|
|
function createViewsChart() {
|
|
const canvases = document.querySelectorAll("canvas.visits");
|
|
if (!canvases || !canvases.length) return;
|
|
|
|
canvases.forEach(ctx => {
|
|
const data = JSON.parse(ctx.dataset.data);
|
|
const period = ctx.dataset.period;
|
|
|
|
const labels = createViewsChartLabel(ctx);
|
|
const maxTicksLimitX = period === "year" ? 6 : period === "month" ? 15 : 12;
|
|
|
|
const gradient = ctx.getContext("2d").createLinearGradient(0, 0, 0, 300);
|
|
gradient.addColorStop(0, "rgba(179, 157, 219, 0.95)");
|
|
gradient.addColorStop(1, "rgba(179, 157, 219, 0.05)");
|
|
|
|
new Chart(ctx, {
|
|
type: "line",
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: "Views",
|
|
data,
|
|
tension: 0.3,
|
|
|
|
elements: {
|
|
point: {
|
|
pointRadius: 0,
|
|
pointHoverRadius: 4
|
|
}
|
|
},
|
|
fill: {
|
|
target: "start",
|
|
},
|
|
backgroundColor: gradient,
|
|
borderColor: "rgb(179, 157, 219)",
|
|
borderWidth: 1,
|
|
}]
|
|
},
|
|
options: {
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
titleColor: "#333",
|
|
titleFont: { weight: "normal", size: 15 },
|
|
bodyFont: { weight: "normal", size: 16 },
|
|
bodyColor: "rgb(179, 157, 219)",
|
|
padding: 12,
|
|
cornerRadius: 2,
|
|
borderColor: "rgba(0, 0, 0, 0.1)",
|
|
borderWidth: 1,
|
|
displayColors: false,
|
|
}
|
|
},
|
|
responsive: true,
|
|
interaction: {
|
|
intersect: false,
|
|
usePointStyle: true,
|
|
mode: "index",
|
|
},
|
|
scales: {
|
|
y: {
|
|
grace: "10%",
|
|
beginAtZero: true,
|
|
ticks: {
|
|
maxTicksLimit: 5
|
|
}
|
|
},
|
|
x: {
|
|
ticks: {
|
|
maxTicksLimit: maxTicksLimitX,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// reset the display: block style that chart.js applies automatically
|
|
ctx.style.display = "";
|
|
});
|
|
}
|
|
|
|
// create browsers chart
|
|
function createBrowsersChart() {
|
|
const canvases = document.querySelectorAll("canvas.browsers");
|
|
if (!canvases || !canvases.length) return;
|
|
|
|
canvases.forEach(ctx => {
|
|
const data = JSON.parse(ctx.dataset.data);
|
|
const period = ctx.dataset.period;
|
|
|
|
const gradient = ctx.getContext("2d").createLinearGradient(500, 0, 0, 0);
|
|
const gradientHover = ctx.getContext("2d").createLinearGradient(500, 0, 0, 0);
|
|
gradient.addColorStop(0, "rgba(179, 157, 219, 0.95)");
|
|
gradient.addColorStop(1, "rgba(179, 157, 219, 0.05)");
|
|
gradientHover.addColorStop(0, "rgba(179, 157, 219, 0.9)");
|
|
gradientHover.addColorStop(1, "rgba(179, 157, 219, 0.4)");
|
|
|
|
new Chart(ctx, {
|
|
type: "bar",
|
|
data: {
|
|
labels: data.map(d => beautifyBrowserName(d.name)),
|
|
datasets: [{
|
|
label: "Views",
|
|
data: data.map(d => d.value),
|
|
backgroundColor: gradient,
|
|
borderColor: "rgba(179, 157, 219, 1)",
|
|
borderWidth: 1,
|
|
hoverBackgroundColor: gradientHover,
|
|
hoverBorderWidth: 2
|
|
}]
|
|
},
|
|
options: {
|
|
indexAxis: "y",
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
titleColor: "#333",
|
|
titleFont: { weight: "normal", size: 15 },
|
|
bodyFont: { weight: "normal", size: 16 },
|
|
bodyColor: "rgb(179, 157, 219)",
|
|
padding: 12,
|
|
cornerRadius: 2,
|
|
borderColor: "rgba(0, 0, 0, 0.1)",
|
|
borderWidth: 1,
|
|
displayColors: false,
|
|
}
|
|
},
|
|
responsive: true,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: "index",
|
|
axis: "y"
|
|
},
|
|
scales: {
|
|
x: {
|
|
grace: "5%",
|
|
beginAtZero: true,
|
|
ticks: {
|
|
maxTicksLimit: 6,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// reset the display: block style that chart.js applies automatically
|
|
ctx.style.display = "";
|
|
});
|
|
}
|
|
|
|
// create referrers chart
|
|
function createReferrersChart() {
|
|
const canvases = document.querySelectorAll("canvas.referrers");
|
|
if (!canvases || !canvases.length) return;
|
|
|
|
canvases.forEach(ctx => {
|
|
const data = JSON.parse(ctx.dataset.data);
|
|
const period = ctx.dataset.period;
|
|
let max = Array.from(data).sort((a, b) => a.value > b.value ? -1 : 1)[0];
|
|
|
|
let tooltipEnabled = true;
|
|
let hoverBackgroundColor = "rgba(179, 157, 219, 1)";
|
|
let hoverBorderWidth = 2;
|
|
let borderColor = "rgba(179, 157, 219, 1)";
|
|
if (data.length === 0) {
|
|
data.push({ name: "No views.", value: 1 });
|
|
max = { value: 1000 };
|
|
tooltipEnabled = false;
|
|
hoverBackgroundColor = "rgba(179, 157, 219, 0.1)";
|
|
hoverBorderWidth = 1;
|
|
borderColor = "rgba(179, 157, 219, 0.2)";
|
|
}
|
|
|
|
new Chart(ctx, {
|
|
type: "doughnut",
|
|
data: {
|
|
labels: data.map(d => d.name.replace(/\[dot\]/g, ".")),
|
|
datasets: [{
|
|
label: "Views",
|
|
data: data.map(d => d.value),
|
|
backgroundColor: data.map(d => `rgba(179, 157, 219, ${Math.max((d.value / max.value) - 0.2, 0.1).toFixed(2)})`),
|
|
borderWidth: 1,
|
|
borderColor,
|
|
hoverBackgroundColor,
|
|
hoverBorderWidth,
|
|
}]
|
|
},
|
|
options: {
|
|
plugins: {
|
|
legend: {
|
|
position: "left",
|
|
labels: {
|
|
boxWidth: 25,
|
|
font: { size: 11 }
|
|
}
|
|
},
|
|
tooltip: {
|
|
enabled: tooltipEnabled,
|
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
titleColor: "#333",
|
|
titleFont: { weight: "normal", size: 15 },
|
|
bodyFont: { weight: "normal", size: 16 },
|
|
bodyColor: "rgb(179, 157, 219)",
|
|
padding: 12,
|
|
cornerRadius: 2,
|
|
borderColor: "rgba(0, 0, 0, 0.1)",
|
|
borderWidth: 1,
|
|
displayColors: false,
|
|
}
|
|
},
|
|
responsive: false,
|
|
}
|
|
});
|
|
|
|
// reset the display: block style that chart.js applies automatically
|
|
ctx.style.display = "";
|
|
});
|
|
}
|
|
|
|
|
|
// beautify browser lables
|
|
function beautifyOsName(name) {
|
|
if (name === "android") return "Android";
|
|
if (name === "ios") return "iOS";
|
|
if (name === "linux") return "Linux";
|
|
if (name === "macos") return "macOS";
|
|
if (name === "windows") return "Windows";
|
|
if (name === "other") return "Other";
|
|
return name;
|
|
}
|
|
|
|
|
|
// create operating systems chart
|
|
function createOsChart() {
|
|
const canvases = document.querySelectorAll("canvas.os");
|
|
if (!canvases || !canvases.length) return;
|
|
|
|
canvases.forEach(ctx => {
|
|
const data = JSON.parse(ctx.dataset.data);
|
|
const period = ctx.dataset.period;
|
|
|
|
const gradient = ctx.getContext("2d").createLinearGradient(500, 0, 0, 0);
|
|
const gradientHover = ctx.getContext("2d").createLinearGradient(500, 0, 0, 0);
|
|
gradient.addColorStop(0, "rgba(179, 157, 219, 0.95)");
|
|
gradient.addColorStop(1, "rgba(179, 157, 219, 0.05)");
|
|
gradientHover.addColorStop(0, "rgba(179, 157, 219, 0.9)");
|
|
gradientHover.addColorStop(1, "rgba(179, 157, 219, 0.4)");
|
|
|
|
new Chart(ctx, {
|
|
type: "bar",
|
|
data: {
|
|
labels: data.map(d => beautifyOsName(d.name)),
|
|
datasets: [{
|
|
label: "Views",
|
|
data: data.map(d => d.value),
|
|
backgroundColor: gradient,
|
|
borderColor: "rgba(179, 157, 219, 1)",
|
|
borderWidth: 1,
|
|
hoverBackgroundColor: gradientHover,
|
|
hoverBorderWidth: 2
|
|
}]
|
|
},
|
|
options: {
|
|
indexAxis: "y",
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
titleColor: "#333",
|
|
titleFont: { weight: "normal", size: 15 },
|
|
bodyFont: { weight: "normal", size: 16 },
|
|
bodyColor: "rgb(179, 157, 219)",
|
|
padding: 12,
|
|
cornerRadius: 2,
|
|
borderColor: "rgba(0, 0, 0, 0.1)",
|
|
borderWidth: 1,
|
|
displayColors: false,
|
|
}
|
|
},
|
|
responsive: true,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: "index",
|
|
axis: "y"
|
|
},
|
|
scales: {
|
|
x: {
|
|
grace:"5%",
|
|
beginAtZero: true,
|
|
ticks: {
|
|
maxTicksLimit: 6,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// reset the display: block style that chart.js applies automatically
|
|
ctx.style.display = "";
|
|
});
|
|
}
|
|
|
|
// add data to the map
|
|
function feedMapData(period) {
|
|
const map = document.querySelector("svg.map");
|
|
const paths = map.querySelectorAll("path");
|
|
if (!map || !paths || !paths.length) return;
|
|
|
|
let data = JSON.parse(map.dataset[period || "day"]);
|
|
if (!data) return;
|
|
|
|
let max = data.sort((a, b) => a.value > b.value ? -1 : 1)[0];
|
|
|
|
if (!max) max = { value: 1 }
|
|
|
|
data = data.reduce((a, c) => ({ ...a, [c.name]: c.value }), {});
|
|
|
|
for (let i = 0; i < paths.length; ++i) {
|
|
const id = paths[i].dataset.id;
|
|
const views = data[id] || 0;
|
|
paths[i].dataset.views = views;
|
|
const colorLevel = Math.ceil((views / max.value) * 6);
|
|
const classList = paths[i].classList;
|
|
for (let j = 1; j < 7; j++) {
|
|
paths[i].classList.remove(`color-${j}`);
|
|
}
|
|
paths[i].classList.add(`color-${colorLevel}`)
|
|
paths[i].dataset.views = views;
|
|
}
|
|
}
|
|
|
|
// handle map tooltip hover
|
|
function mapTooltipHoverOver() {
|
|
const tooltip = document.querySelector("#map-tooltip");
|
|
if (!tooltip) return;
|
|
if (!event.target.dataset.id) return mapTooltipHoverOut();
|
|
if (!tooltip.classList.contains("active")) {
|
|
tooltip.classList.add("visible");
|
|
}
|
|
tooltip.dataset.tooltip = `${event.target.ariaLabel}: ${event.target.dataset.views || 0}`;
|
|
const rect = event.target.getBoundingClientRect();
|
|
tooltip.style.top = rect.top + (rect.height / 2) + "px";
|
|
tooltip.style.left = rect.left + (rect.width / 2) + "px";
|
|
event.target.classList.add("active");
|
|
}
|
|
function mapTooltipHoverOut() {
|
|
const tooltip = document.querySelector("#map-tooltip");
|
|
const map = document.querySelector("svg.map");
|
|
const paths = map.querySelectorAll("path");
|
|
if (!tooltip || !map) return;
|
|
tooltip.classList.remove("visible");
|
|
for (let i = 0; i < paths.length; ++i) {
|
|
paths[i].classList.remove("active");
|
|
}
|
|
}
|
|
|
|
// create stats charts
|
|
function createCharts() {
|
|
if (Chart === undefined) {
|
|
setTimeout(function() { createCharts() }, 100);
|
|
return;
|
|
}
|
|
createViewsChart();
|
|
createBrowsersChart();
|
|
createReferrersChart();
|
|
createOsChart();
|
|
feedMapData();
|
|
} |