Manuscript Workstation - Source Excerpt 17
Back to Manuscript Workstation
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/ns12-manuscript/assets/js/manuscript-workstation.js so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/assets/js/manuscript-workstation.js
this.setCopyFeedback(true);
this.updateStatus('Deep link copied. It preserves the current folio, view mode, zoom, overlay state, and manuscript search.');
}, {
context: 'workstation-copy-deep-link',
onError: () => {
this.setCopyFeedback(false);
this.updateStatus('Deep link could not be copied automatically. Select the address bar to copy the viewer URL manually.');
},
});
}
resetViewerState() {
const defaultViewMode = this.getDefaultViewMode();
this.isApplyingViewerState = true;
this.userSelectedViewMode = false;
this.baseFitMode = 'width';
this.state.viewMode = defaultViewMode;
this.state.fitMode = 'width';
this.state.rawMode = false;
this.state.guidesVisible = true;
this.state.panX = 0;
this.state.panY = 0;
this.applyOverlayVisibility(false, { announce: false });
if (this.searchAbortController) {
this.searchAbortController.abort();
}
this.lastSearchQuery = '';
if (this.searchInput) {
this.searchInput.value = '';
}
if (this.searchClear) {
this.searchClear.hidden = true;
}
this.applyThumbFilter('');
this.clearSearchResults();
this.setSearchStatus(this.getSearchIdleCopy());
this.swapImageSources().finally(() => {
this.ensureActiveImagesReady(() => {
this.fitTo('width', false);
this.isApplyingViewerState = false;
this.syncButtons();
this.requestViewerStateSync({ immediate: true });
this.updateStatus('Viewer state reset. The manuscript returned to its clean default reading surface.');
});
});
}
openDrawer(drawerName = '') {
if (drawerName === 'dossier' && this.dossierDrawer) {
this.dossierDrawer.open = true;
}
if (drawerName === 'context' && this.contextDrawer) {
this.contextDrawer.open = true;
}
}
syncModalBodyState() {
document.body.classList.toggle('is-manuscript-modal-open', this.isAnyModalOpen());
}
isAnyModalOpen() {
return this.isSearchModalOpen() || this.isTocModalOpen();
}
isSearchModalOpen() {
return Boolean(this.searchModal && !this.searchModal.hidden);
}
getSearchFocusable() {
if (!this.searchDialog) {
return [];
}
return Array.from(
this.searchDialog.querySelectorAll(
'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex="-1"])'
)
).filter(
(element) =>
element instanceof HTMLElement &&
!element.hasAttribute('hidden') &&
!element.closest('[hidden]')
);
}
openSearchModal() {
if (!this.searchModal) {
return;
}
this.previousModalFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
this.searchModal.hidden = false;
this.searchModal.setAttribute('aria-hidden', 'false');
this.syncModalBodyState();
if (this.searchScroll) {
this.searchScroll.scrollTop = 0;
}
window.requestAnimationFrame(() => {
if (this.searchInput instanceof HTMLElement) {
this.searchInput.focus({ preventScroll: true });
return;
}
const fallbackFocus = this.getSearchFocusable()[0];
if (fallbackFocus instanceof HTMLElement) {
fallbackFocus.focus({ preventScroll: true });
}
});
}
closeSearchModal() {
if (!this.searchModal || this.searchModal.hidden) {
return;
}
this.searchModal.hidden = true;
this.searchModal.setAttribute('aria-hidden', 'true');
this.syncModalBodyState();
if (this.previousModalFocus && typeof this.previousModalFocus.focus === 'function') {
this.previousModalFocus.focus({ preventScroll: true });
}
}
bindSearchModal() {
if (!this.searchModal) {
return;
}
this.searchOpeners.forEach((opener) => {
opener.addEventListener('click', (event) => {
event.preventDefault();
this.openSearchModal();
});
});
this.searchClosers.forEach((closer) => {
closer.addEventListener('click', (event) => {
event.preventDefault();
this.closeSearchModal();
});
});
document.addEventListener('keydown', (event) => {
if (!this.isSearchModalOpen()) {
return;
}
if (event.key === 'Escape') {
event.preventDefault();
this.closeSearchModal();
return;
}
if (event.key !== 'Tab') {
return;
}
const focusable = this.getSearchFocusable();
if (!focusable.length) {
event.preventDefault();
return;
}
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus({ preventScroll: true });
return;
}
if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus({ preventScroll: true });
}
});
}
isTocModalOpen() {
return Boolean(this.tocModal && !this.tocModal.hidden);
}
resetTocTree() {
this.tocSections.forEach((section) => {
if (!(section instanceof HTMLDetailsElement)) {
return;
}
section.open = false;
});
}
getTocFocusable() {
if (!this.tocDialog) {
return [];
}
return Array.from(
this.tocDialog.querySelectorAll(
'button:not([disabled]), [href], summary, [tabindex]:not([tabindex="-1"])'
)
).filter(
(element) =>
element instanceof HTMLElement &&
!element.hasAttribute('hidden') &&
!element.closest('[hidden]')
);
}
openTocModal() {
if (!this.tocModal) {
return;
}
this.previousModalFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
this.resetTocTree();
this.tocModal.hidden = false;
this.tocModal.setAttribute('aria-hidden', 'false');
this.syncModalBodyState();
if (this.tocScroll) {
this.tocScroll.scrollTop = 0;
}
const focusTarget =
(this.tocDialog && this.tocDialog.querySelector('summary')) ||
(this.tocDialog && this.tocDialog.querySelector('button, [href]'));
window.requestAnimationFrame(() => {
if (focusTarget instanceof HTMLElement) {
focusTarget.focus({ preventScroll: true });
}
});
}
closeTocModal() {
if (!this.tocModal || this.tocModal.hidden) {
return;
}
this.tocModal.hidden = true;
this.tocModal.setAttribute('aria-hidden', 'true');
this.syncModalBodyState();
if (this.previousModalFocus && typeof this.previousModalFocus.focus === 'function') {
this.previousModalFocus.focus({ preventScroll: true });
}
}
bindTocModal() {
if (!this.tocModal) {
return;
}
this.tocOpeners.forEach((opener) => {
opener.addEventListener('click', (event) => {
event.preventDefault();
this.openTocModal();
});
});
this.tocClosers.forEach((closer) => {
closer.addEventListener('click', (event) => {
event.preventDefault();
this.closeTocModal();
});
});
document.addEventListener('keydown', (event) => {
if (!this.isTocModalOpen()) {
return;
}
if (event.key === 'Escape') {
event.preventDefault();
this.closeTocModal();
return;
}
if (event.key !== 'Tab') {
return;
}
const focusable = this.getTocFocusable();
if (!focusable.length) {
event.preventDefault();
return;
}
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus({ preventScroll: true });
return;
}
if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus({ preventScroll: true });
}
});
}
getNavigationHref(link) {
if (!(link instanceof HTMLElement)) {
return '';
}