/home/edulekha/public_html/wp-content/plugins/wp-slimstat/admin/assets/js/slimstat-chart.js
var slimstatClosestPoint = false;
document.addEventListener("DOMContentLoaded", function () {
var chartElements = document.querySelectorAll('[id^="slimstat_chart_data_"]');
var charts = {};
function reinitializeCharts(id) {
var updatedChartElements = document.querySelectorAll('[id^="slimstat_chart_data_' + id + '"]');
for (var i = 0; i < updatedChartElements.length; i++) {
var element = updatedChartElements[i];
var chartId = id;
var existingChart = charts[chartId];
if (existingChart && typeof existingChart.destroy === "function") {
existingChart.destroy();
}
initializeChart(element, chartId);
setupGranularitySelect(chartId);
}
}
window.reinitializeSlimStatCharts = reinitializeCharts;
Array.prototype.forEach.call(chartElements, function (element) {
var chartId = element.id.replace("slimstat_chart_data_", "");
initializeChart(element, chartId);
setupGranularitySelect(chartId);
});
function initializeChart(element, chartId) {
var args = JSON.parse(element.getAttribute("data-args"));
var data = JSON.parse(element.getAttribute("data-data"));
var prevData = JSON.parse(element.getAttribute("data-prev-data"));
var daysBetween = parseInt(element.getAttribute("data-days-between"));
var chartLabels = JSON.parse(element.getAttribute("data-chart-labels"));
var translations = JSON.parse(element.getAttribute("data-translations"));
var totals = JSON.parse(element.getAttribute("data-totals") || "{}");
var labels = data.labels;
var prevLabels = data.prev_labels;
// Fix: Check for null/undefined datasets before using them
var datasets = prepareDatasets(data.datasets, chartLabels, labels, data.today);
var prevDatasets = prepareDatasets(prevData.datasets, chartLabels, prevData.labels, null, true);
prevDatasets = prevDatasets.filter(function (ds) {
return (
Array.isArray(ds.data) &&
ds.data.some(function (v) {
return v > 0;
})
);
});
// Fix for infinite height: set a default height if not present (for widgets or non-dashboard usage)
var chartCanvas = document.getElementById("slimstat_chart_" + chartId);
if (chartCanvas && (!chartCanvas.style.height || chartCanvas.offsetHeight > 2000)) {
chartCanvas.style.height = "260px";
chartCanvas.style.maxHeight = "320px";
chartCanvas.style.minHeight = "180px";
}
var ctx = chartCanvas.getContext("2d");
var chart = createChart(ctx, labels, prevLabels, datasets, prevDatasets, totals, args.granularity, data.today, translations, daysBetween, chartId);
charts[chartId] = chart;
renderCustomLegend(chart, chartId, datasets, prevDatasets, totals, translations);
}
function setupGranularitySelect(chartId) {
var select = document.getElementById("slimstat_granularity_" + chartId);
if (!select) return;
// Debounce the event listener to reduce server requests
var debounceTimeout;
select.addEventListener("change", function () {
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(function () {
var granularity = select.value;
fetchChartData(chartId, granularity);
}, 300);
});
}
function fetchChartData(chartId, granularity) {
var element = document.getElementById("slimstat_chart_data_" + chartId);
var args = JSON.parse(element.getAttribute("data-args"));
var inside = document.querySelector(".inside:has(#slimstat_chart_" + chartId + ")");
var loadingIndicator = document.createElement("p");
loadingIndicator.classList.add("loading");
var spinner = document.createElement("i");
spinner.classList.add("slimstat-font-spin4", "animate-spin");
loadingIndicator.appendChild(spinner);
inside.appendChild(loadingIndicator);
document.querySelector(".slimstat-chart-wrap:has(#slimstat_chart_" + chartId + ")").style.display = "none";
var xhr = new XMLHttpRequest();
xhr.open("POST", slimstat_chart_vars.ajax_url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var result = JSON.parse(xhr.responseText);
if (!result.success) {
console.error("AJAX error:", result.data.message);
return;
}
element.dataset.granularity = granularity;
var oldToggleButtons = document.querySelectorAll(".slimstat-postbox-chart--item-" + chartId);
for (var i = 0; i < oldToggleButtons.length; i++) {
oldToggleButtons[i].remove();
}
var args2 = result.data.args;
var data2 = result.data.data;
var totals2 = result.data.totals;
var prev_data2 = result.data.prev_data;
var days_between2 = result.data.days_between;
var chart_labels2 = result.data.chart_labels;
var translations2 = result.data.translations;
var labels = data2.labels;
var datasets = prepareDatasets(data2.datasets, chart_labels2, labels, data2.today);
var prevDatasets = prepareDatasets(prev_data2.datasets, chart_labels2, prev_data2.labels, null, true);
// Destroy previous chart and create a new one to ensure correct tick callback
var chartCanvas = document.getElementById("slimstat_chart_" + chartId);
var prevChart = charts[chartId];
if (prevChart) prevChart.destroy();
var ctx = chartCanvas.getContext("2d");
var chart = createChart(ctx, labels, data2.prev_labels, datasets, prevDatasets, totals2, granularity, data2.today, translations2, days_between2, chartId);
charts[chartId] = chart;
renderCustomLegend(chart, chartId, datasets, prevDatasets, totals2, translations2);
element.dataset.args = JSON.stringify(args2);
element.dataset.data = JSON.stringify(data2);
element.dataset.prevData = JSON.stringify(prev_data2);
element.dataset.daysBetween = days_between2;
element.dataset.chartLabels = JSON.stringify(chart_labels2);
element.dataset.translations = JSON.stringify(translations2);
inside.removeChild(loadingIndicator);
document.querySelector(".slimstat-chart-wrap:has(#slimstat_chart_" + chartId + ")").style.display = "block";
} else {
console.error("XHR error:", xhr.statusText);
}
}
};
var params = "action=" + encodeURIComponent("slimstat_fetch_chart_data") + "&nonce=" + encodeURIComponent(slimstat_chart_vars.nonce) + "&args=" + encodeURIComponent(JSON.stringify(args)) + "&granularity=" + encodeURIComponent(granularity);
xhr.send(params);
}
function prepareDatasets(rawDatasets, chartLabels, labels, today, isPrevious) {
if (typeof isPrevious === "undefined") {
isPrevious = false;
}
if (rawDatasets === undefined || rawDatasets === null) {
return [];
}
var colors = ["#e8294c", "#2b76f6", "#ffacb6", "#24cb7d", "#942bf6"];
var result = [];
var i = 0;
for (var key in rawDatasets) {
if (!Object.prototype.hasOwnProperty.call(rawDatasets, key)) {
continue;
}
var values = rawDatasets[key];
if (!Array.isArray(values)) {
var entries = [];
for (var k in values) {
if (Object.prototype.hasOwnProperty.call(values, k) && !isNaN(k) && Number(k) >= 0) {
entries.push([k, values[k]]);
}
}
entries.sort(function (a, b) {
return Number(a[0]) - Number(b[0]);
});
var mapped = [];
for (var eIdx = 0; eIdx < entries.length; eIdx++) {
mapped.push(entries[eIdx][1]);
}
values = mapped;
}
if (Array.isArray(values)) {
values = values.slice(0, labels.length);
}
var labelText = key;
if (Array.isArray(chartLabels) && typeof chartLabels[i] !== "undefined" && chartLabels[i] !== null) {
labelText = chartLabels[i];
}
(function (iCopy, labelTextCopy, valuesCopy, keyCopy) {
var color = colors[iCopy % colors.length];
result.push({
label: isPrevious ? "Previous " + labelTextCopy : labelTextCopy,
key: keyCopy,
data: valuesCopy,
borderColor: color,
borderWidth: isPrevious ? 1 : 2,
fill: false,
tension: 0.3,
pointBorderColor: "transparent",
pointBackgroundColor: color,
pointBorderWidth: 2,
pointRadius: 0,
pointHoverRadius: 4,
pointHoverBorderWidth: 2,
hitRadius: 10,
pointHitRadius: 10,
segment: {
borderDash: (function (isPrev) {
return function (ctx) {
if (isPrev) {
return [3, 3];
}
return labels[ctx.p1DataIndex] === "'" + today + "'" ? [5, 3] : [];
};
})(isPrevious),
},
});
})(i, labelText, values, key);
i++;
}
return result;
}
function createChart(ctx, labels, prevLabels, datasets, prevDatasets, total, unitTime, today, translations, daysBetween, chartId) {
var isRTL = document.documentElement.dir === "rtl" || document.body.classList.contains("rtl");
var customCrosshair = {
id: "customCrosshair",
afterEvent: function (chart, args) {
chart._lastEvent = args.event;
},
afterDraw: function (chart) {
var event = chart._lastEvent;
if (!event) return;
var ctx2 = chart.ctx;
var top = chart.chartArea.top;
var bottom = chart.chartArea.bottom;
var activePoints = chart.getElementsAtEventForMode(event.native || event, "nearest", { intersect: false }, false);
if (!activePoints || !activePoints.length) return;
var pt = activePoints[0].element;
if (!pt || typeof pt.x !== "number") return;
ctx2.save();
ctx2.lineWidth = 1;
ctx2.setLineDash([2, 2]);
ctx2.strokeStyle = "rgba(0, 0, 0, 0.3)";
ctx2.beginPath();
ctx2.moveTo(pt.x, top);
ctx2.lineTo(pt.x, bottom);
ctx2.stroke();
ctx2.restore();
},
};
var maxTicks = 8;
var uniqueTickIndexes = [];
var xTickRotation = 0;
var xAutoSkip = false;
if (["daily", "monthly", "hourly", "weekly"].indexOf(unitTime) !== -1) {
if (unitTime === "weekly") {
maxTicks = 8;
}
if (unitTime === "monthly") {
maxTicks = 6;
}
if (labels.length % 2 !== 0) {
maxTicks += 1;
}
if (jQuery("body").hasClass("index-php")) {
maxTicks = 5;
}
if (labels.length > maxTicks) {
var tickIndexes = [0];
var step = (labels.length - 1) / (maxTicks - 1);
for (var i = 1; i < maxTicks - 1; i++) {
var idx = Math.round(i * step);
if (tickIndexes.indexOf(idx) === -1) {
tickIndexes.push(idx);
} else {
var nextIdx = idx + 1;
while (nextIdx < labels.length - 1 && tickIndexes.indexOf(nextIdx) !== -1) {
nextIdx++;
}
if (nextIdx < labels.length - 1) {
tickIndexes.push(nextIdx);
}
}
}
tickIndexes.push(labels.length - 1);
var seen = {};
uniqueTickIndexes = [];
for (var ui = 0; ui < tickIndexes.length; ui++) {
var val = tickIndexes[ui];
if (!seen[val]) {
seen[val] = true;
uniqueTickIndexes.push(val);
}
}
uniqueTickIndexes.sort(function (a, b) {
return a - b;
});
if (uniqueTickIndexes.length > maxTicks) {
var toRemove = uniqueTickIndexes.length - maxTicks;
var middle = Math.floor(uniqueTickIndexes.length / 2);
uniqueTickIndexes.splice(middle, toRemove);
}
}
} else {
uniqueTickIndexes = [];
for (var k2 = 0; k2 < labels.length; k2++) {
uniqueTickIndexes.push(k2);
}
}
var minLabelSpacingPx = 60;
var chartWidth = ctx.canvas.offsetWidth || ctx.canvas.width;
var tickCount = labels.length <= maxTicks ? labels.length : uniqueTickIndexes.length;
var approxSpacing = chartWidth / (tickCount - 1);
if (approxSpacing > minLabelSpacingPx) {
xTickRotation = 0;
} else if (window.innerWidth < 600) {
xTickRotation = 35;
} else if (unitTime === "weekly") {
xTickRotation = 0;
}
function customTickCallback(value, index, values) {
if (labels.length <= maxTicks || uniqueTickIndexes.indexOf(index) !== -1) {
var label = this.getLabelForValue(value).replace(/'/g, "");
try {
return slimstatGetLabel(label, false, unitTime, translations);
} catch (e) {
console.warn("SlimStat: Error processing label:", label, e);
return label; // Return original label if processing fails
}
}
return "";
}
return new Chart(ctx, {
type: "line",
data: {
labels: labels,
datasets: datasets.concat(prevDatasets),
},
options: {
layout: { padding: 20 },
locale: "en-US",
direction: isRTL ? "rtl" : "ltr",
plugins: {
legend: {
display: false,
rtl: isRTL,
textDirection: isRTL ? "rtl" : "ltr",
labels: {
textAlign: isRTL ? "right" : "left",
font: {
family: "Open Sans, sans-serif",
},
color: "#222",
},
},
tooltip: {
enabled: false,
external: createTooltip(labels, prevLabels, translations, daysBetween, chartId),
rtl: isRTL,
textDirection: isRTL ? "rtl" : "ltr",
bodyAlign: isRTL ? "right" : "left",
titleAlign: isRTL ? "right" : "left",
footerAlign: isRTL ? "right" : "left",
backgroundColor: "#fff",
borderColor: "#e0e0e0",
borderWidth: 1,
titleFont: {
family: "Open Sans, sans-serif",
},
bodyFont: {
family: "Open Sans, sans-serif",
},
titleColor: "#222",
bodyColor: "#222",
},
},
scales: {
x: {
reverse: isRTL,
ticks: {
callback: customTickCallback,
minRotation: 0,
maxRotation: xTickRotation,
autoSkip: xAutoSkip,
maxTicksLimit: labels.length,
align: "center",
font: {
family: "Open Sans, sans-serif",
},
color: "#222",
},
grid: {
display: true,
},
},
y: {
ticks: {
font: {
family: "Open Sans, sans-serif",
},
color: "#222",
},
grid: {
display: false,
},
},
},
maintainAspectRatio: false,
responsive: true,
animations: {
x: {
duration: 250,
easing: "easeOutCubic",
},
},
interaction: {
intersect: false,
mode: "index",
},
},
plugins: [customCrosshair],
});
}
function renderCustomLegend(chart, chartId, currentDatasets, previousDatasets, totals, translations) {
var legendContainer = document.getElementById("slimstat-postbox-custom-legend_" + chartId);
legendContainer.innerHTML = "";
for (var di = 0; di < chart.data.datasets.length; di++) {
(function (index) {
var dataset = chart.data.datasets[index];
var isPrevious = dataset.label.indexOf("Previous") !== -1;
if (isPrevious) {
return;
}
var key = dataset.key;
var currentValue = totals.current && totals.current[key] != null ? totals.current[key] : 0;
var previousValue = totals.previous && totals.previous[key] != null ? totals.previous[key] : 0;
var legendItem = document.createElement("div");
legendItem.className = "slimstat-postbox-chart--item";
var html = "";
html += '<span class="slimstat-postbox-chart--item-label">' + dataset.label + "</span>";
html += '<span class="slimstat-postbox-chart--item--color" style="background-color: ' + dataset.borderColor + '"></span>';
html += '<span class="slimstat-postbox-chart--item-value">' + currentValue.toLocaleString() + "</span>";
if (previousValue && previousValue !== currentValue) {
html += '<span class="slimstat-postbox-chart--item--color" style="background-image: repeating-linear-gradient(to right, ' + dataset.borderColor + ", " + dataset.borderColor + ' 4px, transparent 0px, transparent 6px); background-size: auto 6px; height: 2px; margin-bottom: 0px; margin-left: 10px;"></span>';
html += '<span class="slimstat-postbox-chart--item-value">' + previousValue.toLocaleString() + "</span>";
}
legendItem.innerHTML = html;
legendItem.addEventListener("click", function () {
var currentMeta = chart.getDatasetMeta(index);
var previousMeta = chart.getDatasetMeta(index + currentDatasets.length);
var togglePrevBtn = document.querySelector(".slimstat-toggle-prev-datasets.slimstat-postbox-chart--item-" + chartId);
var showPrevious = !!(togglePrevBtn && togglePrevBtn.classList.contains("active"));
currentMeta.hidden = !currentMeta.hidden;
legendItem.classList.toggle("slimstat-postbox-chart--item-hidden");
if (showPrevious) {
previousMeta.hidden = currentMeta.hidden;
} else {
previousMeta.hidden = true;
}
chart.update();
});
legendContainer.appendChild(legendItem);
})(di);
}
var hasPreviousData = false;
for (var pi = 0; pi < previousDatasets.length; pi++) {
var ds = previousDatasets[pi];
if (ds && Array.isArray(ds.data)) {
for (var pv = 0; pv < ds.data.length; pv++) {
if (ds.data[pv] > 0) {
hasPreviousData = true;
break;
}
}
}
if (hasPreviousData) break;
}
if (!hasPreviousData) return;
var existingToggles = document.querySelectorAll("#slimstat-postbox-custom-legend_" + chartId + " .slimstat-toggle-prev-datasets");
for (var et = 0; et < existingToggles.length; et++) {
existingToggles[et].remove();
}
var togglePrevBtn = document.createElement("span");
togglePrevBtn.innerText = translations.previous_period;
togglePrevBtn.className = "active slimstat-toggle-prev-datasets slimstat-postbox-chart--item-" + chartId + " more-info-icon slimstat-tooltip-trigger corner";
togglePrevBtn.title = translations.previous_period_tooltip || "Tap here to show/hide comparison.";
if (!togglePrevBtn.querySelector(".slimstat-tooltip-content")) {
var tooltip = document.createElement("span");
tooltip.className = "slimstat-tooltip-content";
tooltip.innerHTML = "<strong>" + translations.previous_period + "</strong><br>" + (translations.previous_period_tooltip || "Tap here to show/hide comparison.");
togglePrevBtn.appendChild(tooltip);
}
var previousVisible = true;
togglePrevBtn.addEventListener("click", function () {
previousVisible = !previousVisible;
for (var di2 = 0; di2 < chart.data.datasets.length; di2++) {
var dataset2 = chart.data.datasets[di2];
if (dataset2.label.indexOf("Previous") !== -1) {
var correspondingCurrentMeta = chart.getDatasetMeta(di2 - currentDatasets.length);
chart.getDatasetMeta(di2).hidden = previousVisible ? correspondingCurrentMeta.hidden : true;
}
}
togglePrevBtn.classList.toggle("active");
chart.update();
});
legendContainer.parentNode.insertBefore(togglePrevBtn, legendContainer);
}
function getEndOfWeek(dateInput, startOfWeek, respectEndOfPeriod) {
if (typeof startOfWeek === "undefined") {
startOfWeek = 1;
}
if (typeof respectEndOfPeriod === "undefined") {
respectEndOfPeriod = false;
}
var date = new Date(dateInput);
var day = date.getDay();
var diff = (7 - startOfWeek + day) % 7;
var nextWeek = new Date(date.getTime() + (7 - diff) * 24 * 60 * 60 * 1000);
if (respectEndOfPeriod) {
var today2 = new Date(respectEndOfPeriod);
if (nextWeek.getTime() > today2.getTime()) {
return new Date(respectEndOfPeriod);
}
}
return nextWeek;
}
function slimstatGetLabel(label, long, unitTime, translations, justTranslation) {
if (typeof long === "undefined") {
long = true;
}
if (typeof justTranslation === "undefined") {
justTranslation = false;
}
label = label.replace(/'/g, "");
var now = new Date();
function getMonthYear(date) {
return {
month: date.toLocaleString("default", { month: "long" }),
shortMonth: date.toLocaleString("default", { month: "short" }),
year: date.getFullYear(),
};
}
function extendObj(base, opts) {
var res = {};
for (var k in base) {
if (Object.prototype.hasOwnProperty.call(base, k)) {
res[k] = base[k];
}
}
if (opts) {
for (var k2 in opts) {
if (Object.prototype.hasOwnProperty.call(opts, k2)) {
res[k2] = opts[k2];
}
}
}
return res;
}
function formatDate(date, opts) {
var options = extendObj({ timeZone: "UTC" }, opts);
return date.toLocaleDateString("default", options);
}
function formatDateTime(date, opts) {
var options = extendObj({ timeZone: "UTC" }, opts);
return date.toLocaleString("default", options);
}
if (unitTime === "monthly") {
var labelToParse = justTranslation || label;
var monthYearRegex = /^([A-Za-z]+)\s+(\d{4})$/;
var match = (labelToParse || "").match(monthYearRegex);
if (match) {
try {
var monthName = match[1];
var year = parseInt(match[2], 10);
var monthIndex = new Date(monthName + " 1, 2000").getMonth();
var d = new Date(year, monthIndex, 1);
var my = getMonthYear(d);
var isThisMonth = now.getMonth() === d.getMonth() && now.getFullYear() === my.year;
var baseLabel = my.month + ", " + my.year;
var shortBaseLabel = my.shortMonth + ", " + my.year;
var extra = isThisMonth ? " (" + translations.now + ")" : "";
var formattedLabel = label + ' <span class="slimstat-postbox-chart--item--prev">' + baseLabel + "</span>";
if (long) {
return justTranslation ? formattedLabel : baseLabel + extra;
} else {
return justTranslation ? formattedLabel : shortBaseLabel;
}
} catch (e) {
console.warn("SlimStat: Error processing monthly label:", label, e);
return label; // Return original label if processing fails
}
}
// Debug: Log labels that don't match the expected format
if (console && console.debug) {
console.debug("SlimStat: Monthly label does not match expected format:", label);
}
// If the label doesn't match the expected format, return it as-is to prevent "Invalid Date, NaN"
return label;
} else if (unitTime === "weekly") {
var rawDate = (justTranslation || label).replace(/\//g, "-");
var d2 = new Date(rawDate + "T00:00:00Z");
var weekEnd = getEndOfWeek(rawDate + "T00:00:00Z", slimstat_chart_vars.start_of_week, slimstat_chart_vars.end_date_string.replace(/\//g, "-").replace(" ", "T") + "Z");
var weekStart = formatDate(d2, { month: long ? "long" : "short", day: "numeric" });
var weekEndFormatted = formatDate(weekEnd, { month: long ? "long" : "short", day: "numeric" });
return weekEndFormatted === weekStart ? weekStart : weekStart + " - " + weekEndFormatted;
} else if (unitTime === "daily") {
var rawDate2 = (justTranslation || label).replace(/\//g, "-");
var d3 = new Date(rawDate2 + "T00:00:00Z");
var isToday = new Date().toISOString().slice(0, 10) === rawDate2;
var longFormat = formatDateTime(d3, {
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
});
var shortFormat = formatDate(d3, {
month: "short",
day: "2-digit",
}).replace(/-/g, "/");
var finalFormatted = long ? longFormat : shortFormat;
var formattedLabel2 = label + ' <span class="slimstat-postbox-chart--item--prev">' + finalFormatted + "</span>";
return justTranslation ? (long && isToday ? formattedLabel2 + " (" + translations.today + ")" : formattedLabel2) : long && isToday ? finalFormatted + " (" + translations.today + ")" : finalFormatted;
} else if (unitTime === "hourly") {
var rawDate3 = (justTranslation || label).replace(/\//g, "-").replace(" ", "T") + "Z";
if (!rawDate3) return label;
var d4 = new Date(rawDate3);
var hour = d4.getUTCHours();
var minutes = d4.getUTCMinutes();
var minutesStr = minutes < 10 ? "0" + minutes : String(minutes);
var datePart2 = long ? formatDateTime(d4, { weekday: "long", month: "long", day: "numeric", year: "numeric" }) : formatDate(d4, { month: "short", day: "2-digit" }).replace(/-/g, "/");
var isSameHour = new Date().toISOString().slice(0, 10) === rawDate3 && new Date().getUTCHours() === d4.getUTCHours();
var nowStr = new Date().toLocaleTimeString("default", { hour: "numeric", minute: "2-digit", hourCycle: "h23" });
var labelStr = long ? (isSameHour ? datePart2 + " " + hour + ":" + minutesStr + "-" + nowStr + " (" + translations.now + ")" : datePart2 + " " + hour + ":" + minutesStr + "-" + (hour + 1) + ":" + minutesStr) : datePart2 + " " + hour + ":" + minutesStr;
var formattedLabel3 = label + ' <span class="slimstat-postbox-chart--item--prev">' + datePart2 + " " + hour + ":" + minutesStr + "</span>";
return justTranslation ? formattedLabel3 : labelStr;
} else if (unitTime === "yearly") {
var labelToParse2 = justTranslation || label;
var yearMatch = (labelToParse2 || "").match(/^(\d{4})$/);
if (yearMatch) {
var year2 = parseInt(yearMatch[1], 10);
var d5 = new Date(year2, 0, 1);
var isThisYear = now.getFullYear() === d5.getFullYear();
var formattedLabel4 = label + ' <span class="slimstat-postbox-chart--item--prev">' + d5.getFullYear() + "</span>";
return justTranslation ? formattedLabel4 : isThisYear && long ? d5.getFullYear() + " (" + (translations.this_year || "This Year") + ")" : "" + d5.getFullYear();
}
return label;
}
return label;
}
function createTooltip(labels, prevLabels, translations, daysBetween, chartId) {
return function (context) {
var unitTime = document.getElementById("slimstat_chart_data_" + chartId).dataset.granularity;
var data = JSON.parse(document.getElementById("slimstat_chart_data_" + chartId).getAttribute("data-data"));
prevLabels = data.prev_labels;
var tooltipEl = document.getElementById("chartjs-tooltip");
if (!tooltipEl) {
tooltipEl = document.createElement("div");
tooltipEl.id = "chartjs-tooltip";
tooltipEl.innerHTML = "<table></table>";
document.body.appendChild(tooltipEl);
}
var chart = context.chart;
var tooltip = context.tooltip;
if (tooltip.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
tooltipEl.classList.remove("above", "below", "no-transform");
if (tooltip.yAlign) tooltipEl.classList.add(tooltip.yAlign);
else tooltipEl.classList.add("no-transform");
var titleLines = tooltip.title || [];
var grouped = [];
for (var i = 0; i < tooltip.dataPoints.length; i++) {
var label = tooltip.body[i].lines[0].split(": ")[0];
var value = tooltip.body[i].lines[0].split(": ")[1];
if (label.indexOf("Previous ") === 0) continue;
var prevValue = null,
prevDate = null;
for (var j = 0; j < tooltip.dataPoints.length; j++) {
var prevLabel = tooltip.body[j].lines[0].split(": ")[0];
if (prevLabel === "Previous " + label) {
prevValue = tooltip.body[j].lines[0].split(": ")[1];
prevDate = prevLabels[tooltip.dataPoints[j].dataIndex];
break;
}
}
grouped.push({ label: label, value: value, prevValue: prevValue, prevDate: prevDate });
}
var innerHtml = "<thead>";
for (var t = 0; t < titleLines.length; t++) {
var title = titleLines[t];
innerHtml += '<tr><th style="font-weight: bold; font-size: 14px; padding-bottom: 6px; text-align: left;">' + slimstatGetLabel(title.replace(/'/g, ""), true, unitTime, translations) + "</th></tr>";
}
innerHtml += "</thead><tbody>";
for (var g = 0; g < grouped.length; g++) {
var item = grouped[g];
var color = tooltip.labelColors[g];
innerHtml += '<tr data-index="' + g + '" class="slimstat-postbox-chart--item"><td><div class="slimstat-postbox-chart--item--color" style="background-color: ' + color.backgroundColor + '; margin-bottom: 3px; margin-right: 10px;"></div><span class="tooltip-item-title">' + item.label + '</span>: <span class="tooltip-item-content">' + item.value + "</span>";
if (item.prevValue !== null && item.prevDate) {
var hex = color.backgroundColor.replace("#", "");
var rgb = hex
.match(/.{1,2}/g)
.map(function (x) {
return parseInt(x, 16);
})
.join(",");
innerHtml += '<br><span class="slimstat-postbox-chart--item--color" style="display:inline-block;width:18px;height:2px;background-image:repeating-linear-gradient(to right, rgba(' + rgb + ",0.7), rgba(" + rgb + ',0.7) 4px, transparent 0px, transparent 6px);background-size:auto 6px;opacity:1;margin-bottom:0px;margin-left:0px;vertical-align:middle;"></span> <span class="tooltip-item-title" style="font-size:12px;opacity:.7;">' + slimstatGetLabel(item.prevDate, false, unitTime, translations) + ': </span><span class="tooltip-item-content" style="font-size:12px;opacity:.7;">' + item.prevValue + "</span>";
}
innerHtml += "</td></tr>";
}
innerHtml += "</tbody>";
innerHtml +=
'<div class="align-indicator" style="\
width: 15px;\
height: 15px;\
background-color: #fff;\
border-bottom-left-radius: 5px;\
display: inline-block;\
position: absolute;\
bottom: -8px;\
border-bottom: solid 1px #e0e0e0;\
border-left: solid 1px #e0e0e0;\
transform: rotate(-45deg);\
transition: left 0.1s ease;\
"></div>';
tooltipEl.querySelector("table").innerHTML = innerHtml;
var chartRect = chart.canvas.getBoundingClientRect();
var tooltipWidth = tooltipEl.offsetWidth;
var tooltipHeight = tooltipEl.offsetHeight;
var left = chartRect.left + window.pageXOffset + tooltip.caretX - tooltipWidth / 2;
var dataPointYs = [];
for (var dp = 0; dp < tooltip.dataPoints.length; dp++) {
dataPointYs.push(tooltip.dataPoints[dp].element.y);
}
var highestY = dataPointYs[0];
for (var h = 1; h < dataPointYs.length; h++) {
if (dataPointYs[h] < highestY) highestY = dataPointYs[h];
}
var top = chartRect.top + window.pageYOffset + highestY - tooltipHeight - 24;
var chartLeft = chartRect.left + window.pageXOffset;
var chartRight = chartRect.right + window.pageXOffset;
if (left < chartLeft + 4) {
left = chartLeft + 4;
}
if (left + tooltipWidth > chartRight - 4) {
left = chartRight - tooltipWidth - 4;
}
tooltipEl.style.opacity = 1;
tooltipEl.style.position = "absolute";
tooltipEl.style.left = left + "px";
tooltipEl.style.top = top + "px";
var alignIndicator = tooltipEl.querySelector(".align-indicator");
if (alignIndicator) {
var indicatorWidth = alignIndicator.offsetWidth;
var tooltipLeft = left;
var mouseX = chartRect.left + window.pageXOffset + tooltip.caretX;
var indicatorLeft = mouseX - tooltipLeft - indicatorWidth / 2;
var minLeft = 4;
var maxLeft = tooltipWidth - indicatorWidth - 4;
indicatorLeft = Math.max(minLeft, Math.min(indicatorLeft, maxLeft));
alignIndicator.style.left = indicatorLeft + "px";
}
};
}
});