Manuscript Workstation - Source Excerpt 03
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
if (!this.viewport || !this.surface || !this.content || !this.folios.length) {
return;
}
this.syncViewportLayoutMetrics();
const initialSourceSwap = this.seedStateFromUrl();
this.openAnchoredStudyTools();
this.renderAllCrops();
this.bindControls();
this.bindRuntimeControls();
this.bindFullscreenControl();
this.bindViewport();
this.bindCropHandles();
this.bindKeyboard();
this.bindDrawers();
this.bindSearchModal();
this.bindTocModal();
this.bindNavigationEnhancements();
this.bindSearch();
this.bindBeforeUnload();
this.syncButtons();
this.updateDirtyState();
this.loadRuntimePath({ silent: true });
this.activateEntryMotion();
Promise.resolve(initialSourceSwap).finally(() => {
this.ensureActiveImagesReady(() => {
this.applyInitialViewerState().finally(() => {
this.prefetchVisibleRoutes();
});
});
});
}
readRenderedFolios() {
return Array.from(this.root.querySelectorAll('[data-workstation-folio]')).map((element) => {
const image = element.querySelector('[data-workstation-image]');
const previewUrl = `${element.dataset.previewSrc || ''}`.trim();
const standardUrl = `${element.dataset.standardSrc || previewUrl}`.trim();
const fullResolutionUrl = `${element.dataset.fullSrc || standardUrl || previewUrl}`.trim();
const currentLevel = image
? `${image.dataset.currentLevel || (image.classList.contains('is-preview-source') ? 'preview' : 'standard')}`.trim() || 'preview'
: 'preview';
const loadedSrc = image
? `${image.dataset.loadedSrc || image.currentSrc || image.getAttribute('src') || ''}`.trim()
: '';
if (image) {
image.dataset.currentLevel = currentLevel;
image.dataset.loadedSrc =
loadedSrc || (currentLevel === 'full' ? fullResolutionUrl : currentLevel === 'standard' ? standardUrl : previewUrl);
image.classList.toggle('is-preview-source', currentLevel === 'preview');
}
return {
element,
slug: `${element.dataset.folioSlug || ''}`.trim(),
title: `${element.dataset.folioTitle || ''}`.trim(),
sequence: Number.parseInt(`${element.dataset.folioSequence || ''}`.trim(), 10) || 0,
isCurrent: element.dataset.folioCurrent === 'true',
image,
frame: element.querySelector('[data-workstation-folio-frame]'),
cropLayer: element.querySelector('[data-workstation-crop-layer]'),
studyLayer: element.querySelector('[data-workstation-study-layer]'),
crop: parseCrop(element.dataset.folioCrop || ''),
overlayData: parseOverlayData(element.dataset.workstationOverlay || ''),
previewUrl,
standardUrl,
fullResolutionUrl,
};
});
}
syncViewportLayoutMetrics() {
const viewportHeight =
window.visualViewport && Number.isFinite(window.visualViewport.height)
? window.visualViewport.height
: window.innerHeight;
const topbar = document.querySelector('.spiralist-topbar');
const topOffset = topbar instanceof HTMLElement
? Math.max(0, Math.ceil(topbar.getBoundingClientRect().bottom))
: 0;
if (this.page instanceof HTMLElement) {
const availableHeight = Math.max(480, Math.round(viewportHeight - topOffset));
this.page.style.setProperty('--spiralist-manuscript-top-offset', `${topOffset}px`);
this.page.style.setProperty('--spiralist-manuscript-stage-min-height', `${availableHeight}px`);
}
if (!(this.viewer instanceof HTMLElement) || !(this.viewport instanceof HTMLElement)) {
return;
}
const viewerStyle = window.getComputedStyle(this.viewer);
const parsePx = (value) => {
const numericValue = Number.parseFloat(value || '0');
return Number.isFinite(numericValue) ? numericValue : 0;
};
const visibleChildren = Array.from(this.viewer.children).filter(
(child) => child instanceof HTMLElement && !child.hidden && child.getClientRects().length > 0
);
const nonViewportChildren = visibleChildren.filter((child) => child !== this.viewport);
const gapValue = parsePx(viewerStyle.rowGap || viewerStyle.gap);
let chromeHeight =
parsePx(viewerStyle.paddingTop) +
parsePx(viewerStyle.paddingBottom) +
gapValue * Math.max(0, visibleChildren.length - 1);
nonViewportChildren.forEach((child) => {
chromeHeight += Math.ceil(child.getBoundingClientRect().height);
});
this.root.style.setProperty(
'--spiralist-manuscript-viewer-chrome-height',
`${Math.max(176, Math.ceil(chromeHeight + 16))}px`
);
}
normalizeFolioRecord(record = {}) {
const sequence = Number.parseInt(`${record.sequence || ''}`.trim(), 10) || 0;
const slug = `${record.slug || ''}`.trim();
if (!sequence || !slug) {
return null;
}
const previewUrl = `${record.preview_url || record.previewUrl || record.thumbnail_url || record.thumbnailUrl || record.image_url || record.imageUrl || ''}`.trim();
const standardUrl = `${record.standard_url || record.standardUrl || record.prefetch_url || record.prefetchUrl || record.image_url || record.imageUrl || previewUrl}`.trim();
const fullResolutionUrl = `${record.full_resolution_url || record.fullResolutionUrl || standardUrl || previewUrl}`.trim();
const prefetchUrl = `${record.prefetch_url || record.prefetchUrl || standardUrl || fullResolutionUrl || previewUrl}`.trim();
return {
slug,
title: `${record.title || 'Manuscript folio'}`.trim(),
label: `${record.label || 'Page'}`.trim(),
sequence,
imageUrl: previewUrl,
previewUrl,
thumbnailUrl: `${record.thumbnail_url || record.thumbnailUrl || previewUrl || standardUrl}`.trim(),
standardUrl,
fullResolutionUrl,
prefetchUrl,
routeUrl: `${record.route_url || record.routeUrl || ''}`.trim(),
crop: parseCrop(JSON.stringify(record.crop || {})),
caption: `${record.caption || ''}`.trim(),
summary: `${record.summary || ''}`.trim(),
alt: `${record.alt || ''}`.trim(),
sectionSlug: `${record.section_slug || record.sectionSlug || ''}`.trim(),
sectionLabel: `${record.section_label || record.sectionLabel || 'Manuscript'}`.trim(),
eyebrow: `${record.eyebrow || ''}`.trim(),
isStudySurface: Boolean(record.is_study_surface || record.isStudySurface),
overlayData: record.overlay_data && typeof record.overlay_data === 'object'
? record.overlay_data
: (record.overlayData && typeof record.overlayData === 'object' ? record.overlayData : {}),
};
}
readFolioRecords() {
const source = this.root.querySelector('[data-workstation-folio-records]');
let records = [];