Files
SkinbaseNova/resources/js/lib/nav-context.js

125 lines
3.6 KiB
JavaScript

/**
* Nova Gallery Navigation Context
*
* Stores artwork list context in sessionStorage when a card is clicked,
* so the artwork page can provide prev/next navigation without page reload.
*
* Context shape:
* { source, key, ids: number[], index: number, page: string, ts: number }
*/
(function () {
'use strict';
var STORAGE_KEY = 'nav_ctx';
function getPageContext() {
var path = window.location.pathname;
var search = window.location.search;
// /tag/{slug}
var tagMatch = path.match(/^\/tag\/([^/]+)\/?$/);
if (tagMatch) return { source: 'tag', key: 'tag:' + tagMatch[1] };
// /browse/{contentType}/{category...}
var browseMatch = path.match(/^\/browse\/([^/]+)(?:\/(.+))?\/?$/);
if (browseMatch) {
var browsePart = browseMatch[1] + (browseMatch[2] ? '/' + browseMatch[2] : '');
return { source: 'browse', key: 'browse:' + browsePart };
}
// /search?q=...
if (path === '/search' || path.startsWith('/search?')) {
var q = new URLSearchParams(search).get('q') || '';
return { source: 'search', key: 'search:' + q };
}
// /@{username}
var profileMatch = path.match(/^\/@([^/]+)\/?$/);
if (profileMatch) return { source: 'profile', key: 'profile:' + profileMatch[1] };
// /members/...
if (path.startsWith('/members')) return { source: 'members', key: 'members' };
// home
if (path === '/' || path === '/home') return { source: 'home', key: 'home' };
return { source: 'page', key: 'page:' + path };
}
function collectIds() {
var cards = document.querySelectorAll('article[data-art-id]');
var ids = [];
for (var i = 0; i < cards.length; i++) {
var raw = cards[i].getAttribute('data-art-id');
var id = parseInt(raw, 10);
if (id > 0 && !isNaN(id)) ids.push(id);
}
return ids;
}
function saveContext(artId, ids, context) {
var index = ids.indexOf(artId);
if (index === -1) index = 0;
var ctx = {
source: context.source,
key: context.key,
ids: ids,
index: index,
page: window.location.href,
ts: Date.now(),
};
try {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(ctx));
} catch (_) {
// quota exceeded or private mode — silently skip
}
}
function findArticle(el) {
var node = el;
while (node && node !== document.body) {
if (node.tagName === 'ARTICLE' && node.hasAttribute('data-art-id')) {
return node;
}
node = node.parentElement;
}
return null;
}
function init() {
// Only act on pages that have artwork cards (not the artwork detail page itself)
var cards = document.querySelectorAll('article[data-art-id]');
if (cards.length === 0) return;
// Don't inject on the artwork detail page (has #artwork-page mount)
if (document.getElementById('artwork-page')) return;
var context = getPageContext();
document.addEventListener(
'click',
function (event) {
var article = findArticle(event.target);
if (!article) return;
// Make sure click was on or inside the card's <a> link
var link = article.querySelector('a[href]');
if (!link) return;
var artId = parseInt(article.getAttribute('data-art-id'), 10);
if (!artId || isNaN(artId)) return;
var currentIds = collectIds();
saveContext(artId, currentIds, context);
},
true // capture phase: store before navigation fires
);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();