Skip to content
wiki.fftac.org

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 = [];