276 lines
8.1 KiB
JavaScript
276 lines
8.1 KiB
JavaScript
(function () {
|
|
var currentArtId = null;
|
|
var currentImageIdx = null;
|
|
|
|
var mainContent = document.getElementById('main-content');
|
|
var infoOverlay = document.getElementById('info-overlay');
|
|
var infoContent = document.getElementById('info-overlay-content');
|
|
var closeInfo = document.getElementById('close-info');
|
|
var prevArt = document.getElementById('prev-art');
|
|
var nextArt = document.getElementById('next-art');
|
|
var imageOverlay = document.getElementById('image-overlay');
|
|
var fullscreenImage = document.getElementById('fullscreen-image');
|
|
var navHome = document.getElementById('nav-home');
|
|
var navDocs = document.getElementById('nav-docs');
|
|
var homeLink = document.getElementById('home-link');
|
|
|
|
function getArtIndex(id) {
|
|
for (var i = 0; i < DATA.arts.length; i++) {
|
|
if (DATA.arts[i].id === id) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function getArtById(id) {
|
|
for (var i = 0; i < DATA.arts.length; i++) {
|
|
if (DATA.arts[i].id === id) return DATA.arts[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function formatParagraphs(text) {
|
|
var parts = text.split('\n\n');
|
|
var result = '';
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var p = parts[i].trim();
|
|
if (p) {
|
|
result += '<p>' + p.replace(/\n/g, '<br>') + '</p>';
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function updateUrl() {
|
|
var params = new URLSearchParams();
|
|
if (currentArtId) {
|
|
params.set('view', currentArtId);
|
|
if (currentImageIdx !== null) {
|
|
params.set('img', currentImageIdx);
|
|
}
|
|
}
|
|
var qs = params.toString();
|
|
var url = qs ? '?' + qs : window.location.pathname;
|
|
window.history.replaceState(null, '', url);
|
|
}
|
|
|
|
function parseUrl() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
return {
|
|
view: params.get('view'),
|
|
img: params.get('img') !== null ? parseInt(params.get('img'), 10) : null
|
|
};
|
|
}
|
|
|
|
function renderMainPage() {
|
|
var html = '<section id="overview">' + formatParagraphs(DATA.overview) + '</section>';
|
|
html += '<section id="gallery"><div class="section-header" style="border-bottom:1px solid #222;padding-bottom:0.5rem;margin-bottom:1.5rem;color:#666;font-size:0.85rem;">ARTWORK</div><div class="art-grid">';
|
|
|
|
for (var i = 0; i < DATA.arts.length; i++) {
|
|
var art = DATA.arts[i];
|
|
html += '<div class="art-card" data-art-id="' + art.id + '">' +
|
|
'<img src="' + art.images[0] + '" alt="' + art.title.replace(/"/g, '"') + '" loading="lazy">' +
|
|
'<div class="art-title">' + art.title + '</div></div>';
|
|
}
|
|
|
|
html += '</div></section>';
|
|
mainContent.innerHTML = html;
|
|
|
|
var cards = document.querySelectorAll('.art-card');
|
|
for (var i = 0; i < cards.length; i++) {
|
|
cards[i].addEventListener('click', function () {
|
|
openInfoOverlay(this.dataset.artId);
|
|
});
|
|
}
|
|
}
|
|
|
|
function renderInfoOverlay(artId) {
|
|
var art = getArtById(artId);
|
|
if (!art) return;
|
|
|
|
var isMulti = art.images.length > 1;
|
|
var imagesHtml = '';
|
|
for (var i = 0; i < art.images.length; i++) {
|
|
imagesHtml += '<img src="' + art.images[i] + '" alt="' + art.title.replace(/"/g, '"') + ' ' + (i + 1) + '" data-img-index="' + i + '">';
|
|
}
|
|
|
|
var html = '<h2>' + art.title + '</h2>' +
|
|
'<div class="overlay-image-container' + (isMulti ? ' multi' : '') + '">' +
|
|
imagesHtml + '</div>' +
|
|
'<div class="description">' + formatParagraphs(art.description) + '</div>';
|
|
|
|
infoContent.innerHTML = html;
|
|
|
|
var imgs = infoContent.querySelectorAll('.overlay-image-container img');
|
|
for (var i = 0; i < imgs.length; i++) {
|
|
imgs[i].addEventListener('click', function (e) {
|
|
e.stopPropagation();
|
|
openImageOverlay(artId, parseInt(this.dataset.imgIndex, 10));
|
|
});
|
|
}
|
|
|
|
var idx = getArtIndex(artId);
|
|
prevArt.style.display = idx > 0 ? 'block' : 'none';
|
|
nextArt.style.display = idx < DATA.arts.length - 1 ? 'block' : 'none';
|
|
}
|
|
|
|
function renderDocsView() {
|
|
var html = '<div id="docs-view">' +
|
|
'<div class="docs-tree">' +
|
|
'<div class="folder-label">docs/</div><ul>';
|
|
|
|
for (var i = 0; i < DATA.docFiles.length; i++) {
|
|
var doc = DATA.docFiles[i];
|
|
html += '<li data-file="' + doc.filename.replace(/"/g, '"') + '">' +
|
|
doc.filename + '<span class="type-badge">' + (doc.type || '?') + '</span></li>';
|
|
}
|
|
|
|
html += '</ul></div>' +
|
|
'<div class="docs-content">' +
|
|
'<pre id="docs-content-pre">Select a file to view</pre>' +
|
|
'</div></div>';
|
|
|
|
mainContent.innerHTML = html;
|
|
|
|
var items = document.querySelectorAll('.docs-tree li');
|
|
for (var i = 0; i < items.length; i++) {
|
|
items[i].addEventListener('click', function () {
|
|
var active = document.querySelectorAll('.docs-tree li.active');
|
|
for (var j = 0; j < active.length; j++) {
|
|
active[j].classList.remove('active');
|
|
}
|
|
this.classList.add('active');
|
|
var filename = this.dataset.file;
|
|
for (var k = 0; k < DATA.docFiles.length; k++) {
|
|
if (DATA.docFiles[k].filename === filename) {
|
|
document.getElementById('docs-content-pre').textContent = DATA.docFiles[k].raw;
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function openInfoOverlay(artId, imgIdx) {
|
|
currentArtId = artId;
|
|
currentImageIdx = (imgIdx !== undefined && imgIdx !== null) ? imgIdx : null;
|
|
renderInfoOverlay(artId);
|
|
infoOverlay.classList.remove('hidden');
|
|
|
|
if (currentImageIdx !== null) {
|
|
openImageOverlay(artId, currentImageIdx);
|
|
} else {
|
|
imageOverlay.classList.add('hidden');
|
|
}
|
|
|
|
updateUrl();
|
|
}
|
|
|
|
function closeInfoOverlay() {
|
|
currentArtId = null;
|
|
currentImageIdx = null;
|
|
infoOverlay.classList.add('hidden');
|
|
imageOverlay.classList.add('hidden');
|
|
updateUrl();
|
|
}
|
|
|
|
function openImageOverlay(artId, idx) {
|
|
var art = getArtById(artId);
|
|
if (!art || !art.images[idx]) return;
|
|
|
|
fullscreenImage.src = art.images[idx];
|
|
imageOverlay.classList.remove('hidden');
|
|
currentImageIdx = idx;
|
|
updateUrl();
|
|
}
|
|
|
|
function closeImageOverlay() {
|
|
imageOverlay.classList.add('hidden');
|
|
currentImageIdx = null;
|
|
updateUrl();
|
|
}
|
|
|
|
function navigateArt(direction) {
|
|
var idx = getArtIndex(currentArtId);
|
|
if (idx === -1) return;
|
|
var newIdx = idx + direction;
|
|
if (newIdx < 0 || newIdx >= DATA.arts.length) return;
|
|
openInfoOverlay(DATA.arts[newIdx].id);
|
|
}
|
|
|
|
function goHome() {
|
|
closeInfoOverlay();
|
|
currentArtId = null;
|
|
currentImageIdx = null;
|
|
renderMainPage();
|
|
updateUrl();
|
|
}
|
|
|
|
function goDocs() {
|
|
closeInfoOverlay();
|
|
renderDocsView();
|
|
currentArtId = null;
|
|
currentImageIdx = null;
|
|
updateUrl();
|
|
}
|
|
|
|
closeInfo.addEventListener('click', closeInfoOverlay);
|
|
|
|
prevArt.addEventListener('click', function () { navigateArt(-1); });
|
|
nextArt.addEventListener('click', function () { navigateArt(1); });
|
|
|
|
imageOverlay.addEventListener('click', function (e) {
|
|
if (e.target === imageOverlay || e.target === fullscreenImage) {
|
|
closeImageOverlay();
|
|
}
|
|
});
|
|
|
|
navHome.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
goHome();
|
|
});
|
|
|
|
navDocs.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
goDocs();
|
|
});
|
|
|
|
homeLink.addEventListener('click', function (e) {
|
|
if (!infoOverlay.classList.contains('hidden') ||
|
|
mainContent.querySelector('#docs-view')) {
|
|
e.preventDefault();
|
|
goHome();
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keydown', function (e) {
|
|
if (!infoOverlay.classList.contains('hidden')) {
|
|
if (e.key === 'Escape') {
|
|
if (!imageOverlay.classList.contains('hidden')) {
|
|
closeImageOverlay();
|
|
} else {
|
|
closeInfoOverlay();
|
|
}
|
|
} else if (e.key === 'ArrowLeft') {
|
|
navigateArt(-1);
|
|
} else if (e.key === 'ArrowRight') {
|
|
navigateArt(1);
|
|
}
|
|
}
|
|
});
|
|
|
|
function init() {
|
|
var params = parseUrl();
|
|
|
|
if (params.view === 'docs') {
|
|
renderDocsView();
|
|
} else if (params.view && getArtById(params.view)) {
|
|
renderMainPage();
|
|
openInfoOverlay(params.view, params.img);
|
|
} else {
|
|
renderMainPage();
|
|
}
|
|
}
|
|
|
|
init();
|
|
})();
|