const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const monthsFull = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const now = new Date();
let contributions;
(() => {
setRelativeTime();
const dom = document.querySelector('#contributions');
if (!dom) {
return;
}
contributions = JSON.parse(dom.getAttribute('data'));
let year = 0;
for (const item of contributions) {
item.publishDate = decodeURI(item.publishDate).replace(' ', 'T');
item.date = new Date(item.publishDate);
if (item.date.getFullYear() > year) {
year = item.date.getFullYear();
}
item.title = decodeURI(item.title);
}
yearList();
switchYear(year.toString());
})();
function switchYear(year) {
let startDate;
let endDate;
if (year !== now.getFullYear().toString()) {
const date = new Date(Number(year), 0, 1, 0, 0, 0, 0);
startDate = new Date(date.getFullYear(), 0, 1);
endDate = new Date(date.getFullYear(), 11, 31);
} else {
endDate = now;
startDate = new Date(endDate.getTime() - 364 * 24 * 60 * 60 * 1000 - endDate.getDay() * 24 * 60 * 60 * 1000);
}
startDate.setHours(0, 0, 0, 0);
endDate.setHours(23, 59, 59, 999);
const posts = [];
const ms = [];
for (const item of contributions) {
if (item.date >= startDate && item.date <= endDate) {
posts.push(item);
const time = item.date.getFullYear().toString() + "-" + item.date.getMonth().toString();
if (!ms.includes(time)) {
ms.push(time);
}
}
}
posts.sort((a, b) => { return b - a });
document.querySelector('#posts-activity').innerHTML = '';
for (const time of ms) {
const node = document.createElement('div');
const array = time.split("-");
node.innerHTML = monthly(array[0], Number(array[1]), posts);
document.querySelector('#posts-activity').appendChild(node);
}
graph(year, posts, startDate, endDate);
const yearList = document.querySelectorAll('.js-year-link');
for (const elem of yearList) {
if (elem.innerText === year) {
elem.classList.add('selected');
} else {
elem.classList.remove('selected');
}
}
}
function monthly(year, month, posts) {
const monthPosts = posts.filter(post =>
post.date.getFullYear().toString() === year && post.date.getMonth() === month
);
let liHtml = '';
for (const post of monthPosts) {
liHtml += `
`;
}
return `
${monthsFull[month]} ${monthPosts.length > 0 ? monthPosts[0].date.getFullYear() : year}
Created ${monthPosts.length} post${monthPosts.length > 1 ? 's' : ''}
`;
}
function yearList() {
const years = [];
for (const item of contributions) {
const year = item.date.getFullYear();
if (!years.includes(year)) {
years.push(year);
}
}
years.sort((a, b) => { return b - a });
for (let i = 0; i < years.length; i++) {
const year = years[i];
const node = document.createElement('li');
node.innerHTML = `${year}`;
document.querySelector('#year-list').appendChild(node);
}
}
function graph(year, posts, startDate, endDate) {
const postsStr = posts.length === 1 ? "post" : "posts";
if (year === now.getFullYear().toString()) {
document.querySelector('#posts-count').innerText = `${posts.length} ${postsStr} in the last year`;
} else {
document.querySelector('#posts-count').innerText = `${posts.length} ${postsStr} in ${year}`;
}
let html = ``;
const count = {};
for (const post of posts) {
const date = `${post.date.getFullYear()}-${(post.date.getMonth() + 1).toString().padStart(2, '0')}-${post.date.getDate().toString().padStart(2, '0')}`;
if (count[date] === undefined) {
count[date] = 1;
} else {
count[date]++;
}
}
const monthPos = [];
let startMonth = -1;
const weekday = startDate.getDay();
for (let i = 0; i < 53; i++) {
html += ``;
for (let j = 0; j < 7; j++) {
const date = new Date(startDate.getTime() + (i * 7 + j - weekday) * 24 * 60 * 60 * 1000);
const dataDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
if (date < startDate || date > endDate) {
continue;
}
if (j === 0) {
if (i <= 51) {
if (startMonth !== date.getMonth()) {
monthPos.push(i);
startMonth = date.getMonth();
}
}
}
let c;
if (count[dataDate] === undefined) {
c = 0;
} else {
c = count[dataDate];
}
let color;
switch (c) {
case 0:
color = "var(--color-calendar-graph-day-bg)";
break;
case 1:
color = "var(--color-calendar-graph-day-L1-bg)";
break;
case 2:
color = "var(--color-calendar-graph-day-L2-bg)";
break;
case 3:
color = "var(--color-calendar-graph-day-L3-bg)";
break;
default:
color = "var(--color-calendar-graph-day-L4-bg)";
}
html += ``;
}
html += '';
}
if (monthPos[1] - monthPos[0] < 2) {
monthPos[0] = -1;
}
for (let i = 0; i < monthPos.length; i++) {
const month = monthPos[i];
if (month === -1) {
continue;
}
html += `${months[(i + startDate.getMonth()) % 12]}`;
}
html += `
Sun
Mon
Tue
Wed
Thu
Fri
Sat
`;
document.querySelector('#graph-svg').innerHTML = html;
}
let svgElem = document.createElement('div');
svgElem.style.cssText = 'pointer-events: none; display: none;';
svgElem.classList.add(...["svg-tip", "svg-tip-one-line"]);
document.body.appendChild(svgElem);
function svgTip(elem, count, dateStr) {
if (window.screen.width < 768) {
return;
}
const rect = getCoords(elem);
const date = new Date(dateStr);
const dateFmt = `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
if (count) {
svgElem.innerHTML = `${count} posts on ${dateFmt}`;
} else {
svgElem.innerHTML = `No posts on ${dateFmt}`;
}
svgElem.style.display = 'block';
const tipRect = svgElem.getBoundingClientRect();
svgElem.style.top = `${rect.top - 50}px`;
svgElem.style.left = `${rect.left - tipRect.width / 2 + rect.width / 2}px`;
}
function hideTip() {
svgElem.style.display = 'none';
}
function getCoords(elem) {
const box = elem.getBoundingClientRect();
const body = document.body;
const docEl = document.documentElement;
const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
const clientTop = docEl.clientTop || body.clientTop || 0;
const clientLeft = docEl.clientLeft || body.clientLeft || 0;
const top = box.top + scrollTop - clientTop;
const left = box.left + scrollLeft - clientLeft;
return { top, left, width: box.width, height: box.height };
}
function relativeTime(dateStr) {
const now = new Date();
const date = new Date(dateStr);
const diff = Math.floor((now.getTime() - date.getTime()) / 1000);
const seconds = Math.floor(diff);
const minutes = Math.floor(diff / 60);
const hours = Math.floor(diff / 60 / 60);
const days = Math.floor(diff / 60 / 60 / 24);
if (seconds < 60) {
return `${seconds} seconds ago`;
}
if (minutes < 60) {
return `${minutes} minutes ago`;
}
if (hours < 24) {
return `${hours} hours ago`;
}
if (days < 30) {
return `${days} days ago`;
}
if (date.getFullYear() === now.getFullYear()) {
return `${date.getDate()} ${months[date.getMonth()]}`;
}
return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`;
}
function setRelativeTime() {
document.querySelectorAll('relative-time').forEach(elem => {
const dateStr = elem.getAttribute('datetime');
elem.innerHTML = relativeTime(dateStr);
elem.setAttribute('title', new Date(dateStr).toLocaleString());
});
}