116 lines
4.0 KiB
JavaScript
116 lines
4.0 KiB
JavaScript
function el(tag, className, html) {
|
|
const node = document.createElement(tag);
|
|
if (className) node.className = className;
|
|
if (html !== undefined) node.innerHTML = html;
|
|
return node;
|
|
}
|
|
|
|
function calcAge(birthDateStr) {
|
|
const birth = new Date(birthDateStr);
|
|
const today = new Date();
|
|
let age = today.getFullYear() - birth.getFullYear();
|
|
const monthDiff = today.getMonth() - birth.getMonth();
|
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
|
age--;
|
|
}
|
|
return age;
|
|
}
|
|
|
|
function pluralizeYears(age) {
|
|
const mod10 = age % 10;
|
|
const mod100 = age % 100;
|
|
if (mod10 === 1 && mod100 !== 11) return `${age} год`;
|
|
if ([2, 3, 4].includes(mod10) && ![12, 13, 14].includes(mod100)) return `${age} года`;
|
|
return `${age} лет`;
|
|
}
|
|
|
|
function renderProfile(data) {
|
|
document.title = `${data.profile.role} ${data.profile.name}`;
|
|
document.getElementById("avatar").src = data.profile.avatar;
|
|
document.getElementById("avatar").alt = data.profile.name;
|
|
document.getElementById("name").textContent = data.profile.name;
|
|
document.getElementById("role").textContent = data.profile.role;
|
|
|
|
const metaParts = [];
|
|
if (data.profile.gender) metaParts.push(data.profile.gender);
|
|
if (data.profile.birthDate) metaParts.push(pluralizeYears(calcAge(data.profile.birthDate)));
|
|
if (data.profile.location) metaParts.push(data.profile.location);
|
|
document.getElementById("meta").textContent = metaParts.join(" · ");
|
|
|
|
const aboutEl = document.getElementById("about");
|
|
if (data.profile.about) {
|
|
aboutEl.textContent = data.profile.about;
|
|
} else {
|
|
aboutEl.remove();
|
|
}
|
|
}
|
|
|
|
function renderContacts(data) {
|
|
const wrap = document.getElementById("contacts");
|
|
data.contacts.forEach((c) => {
|
|
const a = el("a", "contact-icon", `<i class="${c.icon}"></i>`);
|
|
a.href = c.href;
|
|
a.title = c.label;
|
|
a.target = c.href.startsWith("http") ? "_blank" : "_self";
|
|
a.rel = "noopener";
|
|
wrap.appendChild(a);
|
|
});
|
|
}
|
|
|
|
function renderSkills(data) {
|
|
const section = document.getElementById("skills-section");
|
|
const hasSkills = data.skills && data.skills.some((g) => g.items && g.items.length);
|
|
if (!hasSkills) {
|
|
section.remove();
|
|
return;
|
|
}
|
|
const wrap = document.getElementById("skills");
|
|
data.skills.forEach((group) => {
|
|
if (!group.items || group.items.length === 0) return;
|
|
const groupEl = el("div", "skill-group");
|
|
groupEl.appendChild(el("div", "skill-category", group.category));
|
|
const tags = el("div", "skill-tags");
|
|
group.items.forEach((item) => tags.appendChild(el("span", "skill-tag", item)));
|
|
groupEl.appendChild(tags);
|
|
wrap.appendChild(groupEl);
|
|
});
|
|
}
|
|
|
|
function renderTimeline(containerId, items) {
|
|
const wrap = document.getElementById(containerId);
|
|
items.forEach((item) => {
|
|
const entry = el("div", "timeline-item" + (item.current ? " is-current" : ""));
|
|
entry.appendChild(el("div", "timeline-period", item.period));
|
|
const body = el("div", "timeline-body");
|
|
body.appendChild(el("div", "timeline-title", item.title));
|
|
if (item.place) body.appendChild(el("div", "timeline-place", item.place));
|
|
entry.appendChild(body);
|
|
wrap.appendChild(entry);
|
|
});
|
|
}
|
|
|
|
function renderProjects(data) {
|
|
const wrap = document.getElementById("projects");
|
|
data.projects.forEach((p) => {
|
|
const card = el("a", "project-card");
|
|
card.href = p.href;
|
|
card.target = "_blank";
|
|
card.rel = "noopener";
|
|
card.appendChild(el("div", "project-title", p.title));
|
|
if (p.description) card.appendChild(el("div", "project-desc", p.description));
|
|
card.appendChild(el("div", "project-link", "Открыть проект →"));
|
|
wrap.appendChild(card);
|
|
});
|
|
}
|
|
|
|
(function init() {
|
|
renderProfile(SITE_DATA);
|
|
renderContacts(SITE_DATA);
|
|
renderSkills(SITE_DATA);
|
|
renderTimeline("education", SITE_DATA.education);
|
|
renderTimeline("experience", SITE_DATA.experience);
|
|
renderProjects(SITE_DATA);
|
|
document.getElementById("footer").textContent =
|
|
`© ${new Date().getFullYear()} ${SITE_DATA.profile.name}`;
|
|
})();
|