67 lines
3.3 KiB
JavaScript
67 lines
3.3 KiB
JavaScript
(() => {
|
|
const form = document.querySelector('[data-story-point-form]');
|
|
if (!form) return;
|
|
const book = form.querySelector('[data-point-book]');
|
|
const chapter = form.querySelector('[data-point-chapter]');
|
|
const scene = form.querySelector('[data-point-scene]');
|
|
const chapters = [...chapter.options].map(x => x.cloneNode(true));
|
|
const scenes = [...scene.options].map(x => x.cloneNode(true));
|
|
const snapshot = document.querySelector('[data-point-snapshot]');
|
|
const feedback = document.querySelector('[data-point-feedback]');
|
|
let pending;
|
|
let request = 0;
|
|
function filter(select, options, key, value) {
|
|
const previous = select.value;
|
|
select.replaceChildren(...options.filter(x => x.dataset[key] === value).map(x => x.cloneNode(true)));
|
|
if ([...select.options].some(x => x.value === previous)) select.value = previous;
|
|
select.disabled = !select.options.length;
|
|
}
|
|
function filterScenes() { filter(scene, scenes, 'chapter', chapter.value); }
|
|
function filterChapters() { filter(chapter, chapters, 'book', book.value); filterScenes(); }
|
|
async function load(push = true) {
|
|
const current = ++request;
|
|
pending?.abort();
|
|
if (!scene.value) {
|
|
snapshot.replaceChildren();
|
|
feedback.textContent = 'No scenes in this selection.';
|
|
return;
|
|
}
|
|
pending = new AbortController();
|
|
const url = new URL(form.action, location.href);
|
|
url.search = new URLSearchParams(new FormData(form)).toString();
|
|
feedback.textContent = 'Loading story state…';
|
|
snapshot.replaceChildren();
|
|
snapshot.setAttribute('aria-busy', 'true');
|
|
try {
|
|
const response = await fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }, signal: pending.signal });
|
|
if (response.redirected) { location.assign(response.url); return; }
|
|
if (!response.ok) throw new Error('load');
|
|
const html = await response.text();
|
|
if (current !== request) return;
|
|
snapshot.innerHTML = html;
|
|
if (push) history.pushState({}, '', url);
|
|
feedback.textContent = 'Story state updated.';
|
|
} catch (error) {
|
|
if (error.name !== 'AbortError' && current === request) {
|
|
snapshot.replaceChildren();
|
|
feedback.textContent = 'Story state could not be loaded. Use Show state to retry.';
|
|
}
|
|
} finally { if (current === request) snapshot.removeAttribute('aria-busy'); }
|
|
}
|
|
book.addEventListener('change', () => { filterChapters(); load(); });
|
|
chapter.addEventListener('change', () => { filterScenes(); load(); });
|
|
scene.addEventListener('change', () => load());
|
|
form.addEventListener('submit', event => { event.preventDefault(); load(); });
|
|
window.addEventListener('popstate', () => {
|
|
const id = new URL(location.href).searchParams.get('sceneId');
|
|
const chosenScene = scenes.find(x => x.value === id);
|
|
const chosenChapter = chapters.find(x => x.value === chosenScene?.dataset.chapter);
|
|
if (!chosenScene || !chosenChapter) { location.reload(); return; }
|
|
book.value = chosenChapter.dataset.book;
|
|
filterChapters(); chapter.value = chosenChapter.value;
|
|
filterScenes(); scene.value = id;
|
|
load(false);
|
|
});
|
|
filterChapters();
|
|
})();
|