2 //= require cal-heatmap/dist/cal-heatmap
4 //= require cal-heatmap/dist/plugins/Tooltip
6 /* global CalHeatmap, Tooltip */
7 document.addEventListener("DOMContentLoaded", () => {
8 const heatmapElement = document.querySelector("#cal-heatmap");
10 if (!heatmapElement) {
14 /** @type {{date: string; max_id: number; total_changes: number}[]} */
15 const heatmapData = heatmapElement.dataset.heatmap ? JSON.parse(heatmapElement.dataset.heatmap) : [];
16 const displayName = heatmapElement.dataset.displayName;
17 const colorScheme = document.documentElement.getAttribute("data-bs-theme") ?? "auto";
18 const rangeColorsDark = ["#14432a", "#4dd05a"];
19 const rangeColorsLight = ["#4dd05a", "#14432a"];
20 const startDate = new Date(Date.now() - (365 * 24 * 60 * 60 * 1000));
22 const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
24 let cal = new CalHeatmap();
25 let currentTheme = getTheme();
27 function renderHeatmap() {
29 cal = new CalHeatmap();
32 itemSelector: "#cal-heatmap",
38 text: (timestamp) => new Date(timestamp).toLocaleString(OSM.i18n.locale, { timeZone: "UTC", month: "short" }),
42 dynamicDimension: true
64 range: currentTheme === "dark" ? rangeColorsDark : rangeColorsLight,
65 domain: [0, Math.max(0, ...heatmapData.map(d => d.total_changes))]
70 text: (date, value) => getTooltipText(date, value)
74 cal.on("mouseover", (event, timestamp, value) => {
75 if (!displayName || !value) return;
76 if (event.target.parentElement.nodeName === "a") return;
78 for (const { date, max_id } of heatmapData) {
79 if (!max_id) continue;
80 if (timestamp !== Date.parse(date)) continue;
82 const params = new URLSearchParams({ before: max_id + 1 });
83 const a = document.createElementNS("http://www.w3.org/2000/svg", "a");
84 a.setAttribute("href", `/user/${encodeURIComponent(displayName)}/history?${params}`);
85 $(event.target).wrap(a);
91 function getTooltipText(date, value) {
92 const localizedDate = OSM.i18n.l("date.formats.long", date);
95 return OSM.i18n.t("javascripts.heatmap.tooltip.contributions", { count: value, date: localizedDate });
98 return OSM.i18n.t("javascripts.heatmap.tooltip.no_contributions", { date: localizedDate });
101 function getTheme() {
102 if (colorScheme === "auto") {
103 return mediaQuery.matches ? "dark" : "light";
109 if (colorScheme === "auto") {
110 mediaQuery.addEventListener("change", (e) => {
111 currentTheme = e.matches ? "dark" : "light";