1 $(document).on("turbo:frame-load", function () {
2 const heatmap = $(".heatmap").removeClass("d-none").addClass("d-grid");
3 const weekInfo = getWeekInfo();
4 const maxPerDay = heatmap.data("max-per-day");
5 const weekdayLabels = heatmap.find("[data-weekday]");
6 const monthLabelStartIndex = Math.min(...heatmap.find("[data-month]").get().map(l => l.dataset.month));
8 let previousMonth = null;
10 for (const day of weekdayLabels) {
12 const weekday = $day.data("weekday");
13 if (weekday < weekInfo.firstDay % 7) {
14 $day.insertAfter(weekdayLabels.last());
16 const weekdayRow = getWeekdayRow(weekday);
17 if (weekdayRow % 2 === 0) $day.remove();
18 $day.css("grid-area", weekdayRow + " / 1");
21 for (const day of heatmap.find("[data-date]")) {
23 const date = new Date($day.data("date"));
24 if (date.getUTCDay() === weekInfo.firstDay % 7) {
26 const currentMonth = getMonthOfThisWeek(date);
27 if (previousMonth === null) {
28 previousMonth = currentMonth + (Math.round((monthLabelStartIndex - currentMonth) / 12) * 12);
29 heatmap.find(`[data-month]:has( ~ [data-month="${previousMonth}"])`).remove();
30 heatmap.find("[data-month]").first().css("grid-column-start", 2);
32 if (previousMonth % 12 !== currentMonth % 12) {
33 heatmap.find(`[data-month="${previousMonth}"]`).css("grid-column-end", weekColumn);
35 heatmap.find(`[data-month="${previousMonth}"]`).css("grid-column-start", weekColumn);
38 if (weekColumn === 1) {
42 const count = $day.data("count") ?? 0;
43 const tooltipText = getTooltipText($day.data("date"), count);
45 .css("grid-area", getWeekdayRow(date.getUTCDay()) + " / " + weekColumn)
46 .attr("aria-label", tooltipText)
50 delay: { show: 0, hide: 0 }
53 .css("opacity", Math.sqrt(count / maxPerDay));
55 heatmap.find(`[data-month="${previousMonth}"] ~ [data-month]`).remove();
56 heatmap.find("[data-month]").last().css("grid-column-end", weekColumn + 1);
58 function getMonthOfThisWeek(date) {
59 const nextDate = new Date(date);
60 nextDate.setUTCDate(date.getUTCDate() + weekInfo.minimalDays - 1);
61 return nextDate.getUTCMonth() + 1;
64 function getWeekdayRow(weekday) {
65 return ((weekday - weekInfo.firstDay + 7) % 7) + 2;
68 function getTooltipText(date, value) {
69 const localizedDate = OSM.i18n.l("date.formats.heatmap", date);
72 return OSM.i18n.t("javascripts.heatmap.tooltip.contributions", { count: value, date: localizedDate });
75 return OSM.i18n.t("javascripts.heatmap.tooltip.no_contributions", { date: localizedDate });
78 function getWeekInfo() {
79 const weekInfo = { firstDay: 1, minimalDays: 4 }; // ISO 8601
80 const locale = new Intl.Locale(OSM.i18n.locale);
81 return { ...weekInfo, ...locale.weekInfo, ...locale.getWeekInfo?.() };