in pontoon/contributors/static/js/profile.js [160:291]
renderContributionGraph: function () {
const graph = $('#contribution-graph');
const contributions = graph.data('contributions');
// Set start date to 365 days before now
const startDate = new Date();
startDate.setMonth(startDate.getMonth() - 12);
startDate.setUTCDate(startDate.getUTCDate() + 1);
startDate.setUTCHours(0, 0, 0, 0);
const endDate = new Date();
let graphHTML = '';
const step = 13;
const currentDate = startDate;
let currentMonth = currentDate.getMonth();
const monthPosition = [{ monthIndex: currentMonth, x: 0 }];
let week = 0;
let gx = week * step;
let weekHTML = `<g transform="translate(${gx.toString()}, 0)">`;
while (currentDate.getTime() <= endDate.getTime()) {
if (currentDate.getDay() == 0) {
weekHTML = `<g transform="translate(${gx.toString()}, 0)">`;
}
const monthOfTheDay = currentDate.getMonth();
if (currentDate.getDay() == 0 && monthOfTheDay != currentMonth) {
currentMonth = monthOfTheDay;
monthPosition.push({ monthIndex: currentMonth, x: gx });
}
const count = contributions[currentDate.getTime()] || 0;
// Pick color based on count range
let color;
switch (true) {
case count === 0:
color = style.getPropertyValue('--dark-grey-1');
break;
case count < 10:
color = style.getPropertyValue('--forest-green-1');
break;
case count < 25:
color = style.getPropertyValue('--green');
break;
case count < 50:
color = style.getPropertyValue('--green-2');
break;
default:
color = style.getPropertyValue('--status-translated');
}
const y = currentDate.getDay() * step;
const date = currentDate.getTime();
weekHTML += `<rect class="day" width="10" height="10" y="${y}" fill="${color}" data-count="${count}" data-date="${date}" rx="2" ry="2"/>`;
if (currentDate.getDay() == 6) {
weekHTML += '</g>';
graphHTML += weekHTML;
weekHTML = null;
week++;
gx = week * step;
}
currentDate.setUTCDate(currentDate.getUTCDate() + 1);
}
// Add week items
if (weekHTML != null) {
weekHTML += '</g>';
graphHTML += weekHTML;
}
// Remove the first incomplete month label
if (monthPosition[1].x - monthPosition[0].x < 40) {
monthPosition.shift();
}
// Remove the last incomplete month label
if (monthPosition.at(-1).x > 660) {
monthPosition.pop();
}
// Add month labels
for (let x = 0; x < monthPosition.length; x++) {
const item = monthPosition[x];
const monthName = monthNameFormat.format(
new Date(2022, item.monthIndex),
);
graphHTML += `<text x="${item.x}" y="-7" class="month">${monthName}</text>`;
}
// Add day labels
graphHTML += `
<text text-anchor="middle" class="wday" dx="-10" dy="23">M<title>Monday</title></text>
<text text-anchor="middle" class="wday" dx="-10" dy="49">W<title>Wednesday</title></text>
<text text-anchor="middle" class="wday" dx="-10" dy="75">F<title>Friday</title></text>`;
graph.html(`
<svg width="690" height="110" viewBox="0 0 702 110" class="js-calendar-graph-svg">
<g transform="translate(16, 20)">${graphHTML}</g>
</svg>`);
// Handle tooltip
$(graph)
.find('.day')
.hover(
function (e) {
const count = $(e.target).attr('data-count');
const date = shortDateFormat.format(
$(e.target).attr('data-date'),
);
const action = count == 1 ? 'contribution' : 'contributions';
const text = `${count} ${action} on ${date}`;
const tooltip = $('.svg-tip').show();
tooltip.html(text);
const offset = $(e.target).offset();
const width = Math.round(tooltip.width() / 2 + 5);
const height = tooltip.height() * 2 + 10;
tooltip.css({ top: offset.top - height - 5 });
tooltip.css({ left: offset.left - width });
},
function () {
$('.svg-tip').hide();
},
);
},