Skip to content
wiki.fftac.org

Book Shell - Source Excerpt 08

Back to Book Shell

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/ns12-manuscript/assets/js/book-shell.js so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/assets/js/book-shell.js

visibleCardsFrame = window.requestAnimationFrame(() => {
        visibleCardsFrame = 0;
        updateVisibleCards();
      });
    };

    const applySymbolPayload = (payload, button = null) => {
      if (!payload) {
        return;
      }

      cards.forEach((card) => {
        const cardPayload = parseCardPayload(card);
        const isActive = payload.canonicalId && cardPayload
          ? cardPayload.canonicalId === payload.canonicalId
          : card === button;

        card.classList.toggle('is-active', isActive);
        card.setAttribute('aria-pressed', isActive ? 'true' : 'false');
      });

      if (title) title.textContent = payload.label || '';
      if (glyph) glyph.textContent = payload.glyph || '';
      if (id) id.textContent = payload.canonicalId || '';
      if (type) type.textContent = payload.type || '';
      if (layer) layer.textContent = payload.layer || t('symbolsLayerFallback', 'Reference');
      if (category) category.textContent = payload.category || t('symbolsCategoryFallback', 'Uncategorized');
      if (meaning) meaning.textContent = payload.meaning || '';
      if (definition) definition.textContent = payload.definition || payload.meaning || '';
      if (axiom) axiom.textContent = payload.axiomConnection || t('symbolsAxiomConnectionEmpty', 'No axiom links declared.');
      if (derivation) derivation.textContent = payload.derivation || t('symbolsDerivationEmpty', 'No derivation declared.');
      if (provenance) provenance.textContent = payload.provenance || t('symbolsProvenanceEmpty', 'No provenance declared.');
      if (related) related.textContent = payload.related || t('symbolsRelatedEmpty', 'No related symbols declared.');
      if (transformation) transformation.textContent = payload.transformationChain || t('symbolsTransformationChainEmpty', 'No transformation chain declared.');

      if (manuscriptAnchor) {
        manuscriptAnchor.replaceChildren();
        if (payload.manuscriptAnchorUrl) {
          const anchor = document.createElement('a');
          anchor.href = payload.manuscriptAnchorUrl;
          anchor.textContent = payload.manuscriptAnchorLabel || t('symbolsAnchorView', 'View in manuscript');
          manuscriptAnchor.appendChild(anchor);
        } else {
          const empty = document.createElement('span');
          empty.className = 'spiralist-muted';
          empty.textContent = t('symbolsAnchorEmpty', 'No manuscript anchor available.');
          manuscriptAnchor.appendChild(empty);
        }
      }

      renderAxiomLinks(payload);
      renderTransformationLinks(payload);
      renderCrosswalk(payload);

      loadPromptReferences(
        prompts,
        buildPromptFilters(payload),
        t('symbolsPromptEmptyForSymbol', 'No related prompts for this symbol yet.')
      );

      replaceQueryParam('symbol', payload.canonicalId || '');

      recordHumanInteraction({
        type: 'symbol-view',
        symbolId: payload.canonicalId || '',
        symbolLabel: payload.label || '',
        page: window.location.pathname,
      });
    };

    cards.forEach((button) => {
      if (!firstPayload) {
        firstPayload = parseCardPayload(button);
      }

      button.addEventListener('click', () => {
        const payload = parseCardPayload(button);
        applySymbolPayload(payload, button);
      });
    });

    [searchInput, layerFilter, typeFilter, categoryFilter].forEach((control) => {
      if (!control) {
        return;
      }

      control.addEventListener('input', scheduleVisibleCardsUpdate);
      control.addEventListener('change', scheduleVisibleCardsUpdate);
    });

    updateVisibleCards();

    if (requestedSymbol) {
      const matchingButton = cards.find((card) => {
        const payload = parseCardPayload(card);
        return payload && (
          payload.canonicalId === requestedSymbol ||
          payload.urlSlug === requestedSymbol ||
          slugifyPathSegment(payload.canonicalId) === slugifyPathSegment(requestedSymbol) ||
          slugifyPathSegment(payload.label) === slugifyPathSegment(requestedSymbol)
        );
      });

      if (matchingButton) {
        matchingButton.click();
        return;
      }
    }

    if (firstPayload) {
      applySymbolPayload(firstPayload);
    }
  });

  document.querySelectorAll('[data-hotspot-explorer]').forEach((explorer) => {
    const title = explorer.querySelector('[data-hotspot-title]');
    const copy = explorer.querySelector('[data-hotspot-copy]');
    const focus = explorer.querySelector('[data-hotspot-focus]');
    const meaning = explorer.querySelector('[data-hotspot-meaning]');
    const derivation = explorer.querySelector('[data-hotspot-derivation]');
    const attribution = explorer.querySelector('[data-hotspot-attribution]');

    explorer.querySelectorAll('[data-hotspot]').forEach((button) => {
      button.addEventListener('click', () => {
        const payload = parseJsonOr(button.dataset.hotspot || '', null, { context: 'hotspot-payload' });

        if (!payload) {
          return;
        }

        explorer.querySelectorAll('[data-hotspot]').forEach((hotspot) => {
          hotspot.classList.remove('is-active');
          hotspot.setAttribute('aria-pressed', 'false');
        });

        button.classList.add('is-active');
        button.setAttribute('aria-pressed', 'true');

        if (title) title.textContent = payload.title || '';
        if (copy) copy.textContent = payload.copy || '';
        if (focus) focus.textContent = payload.focus || '';
        if (meaning) meaning.textContent = payload.meaning || '';
        if (derivation) derivation.textContent = payload.derivation || '';
        if (attribution) attribution.textContent = payload.attribution || '';
      });
    });
  });

  const prefersReducedMotion = Boolean(
    window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
  );
  const activateTabGroup = (group, target) => {
    if (!group || !target) {
      return false;
    }

    const triggers = Array.from(group.querySelectorAll('[data-tab-trigger]'));
    const panels = Array.from(group.querySelectorAll('[data-tab-panel]'));
    let didActivate = false;

    triggers.forEach((item) => {
      const active = item.dataset.tabTrigger === target;
      item.classList.toggle('is-active', active);
      item.setAttribute('aria-pressed', active ? 'true' : 'false');

      if (item.getAttribute('role') === 'tab') {
        item.setAttribute('aria-selected', active ? 'true' : 'false');
        item.setAttribute('tabindex', active ? '0' : '-1');
      }

      if (active) {
        didActivate = true;
      }
    });

    panels.forEach((panel) => {
      const active = panel.dataset.tabPanel === target;
      panel.classList.toggle('is-active', active);
      panel.hidden = !active;
    });

    return didActivate;
  };

  document.querySelectorAll('[data-tab-group]').forEach((group) => {
    group.querySelectorAll('[data-tab-trigger]').forEach((trigger) => {
      trigger.addEventListener('click', () => {
        activateTabGroup(group, trigger.dataset.tabTrigger || '');
      });
    });
  });

  document.querySelectorAll('[data-tab-open]').forEach((control) => {
    control.addEventListener('click', (event) => {
      const target = `${control.dataset.tabOpen || ''}`.trim();
      if (!target) {
        return;
      }

      const groupTarget = `${control.dataset.tabGroupTarget || ''}`.trim();
      const container = groupTarget ? document.querySelector(groupTarget) : control.closest('[data-tab-group]');
      const group = container
        ? (container.matches('[data-tab-group]') ? container : container.querySelector('[data-tab-group]'))
        : null;

      if (!group) {
        return;
      }

      event.preventDefault();
      const didActivate = activateTabGroup(group, target);
      if (!didActivate) {
        return;
      }

      const scrollTarget = container || group;
      if (scrollTarget && typeof scrollTarget.scrollIntoView === 'function') {
        scrollTarget.scrollIntoView({
          block: 'start',
          behavior: prefersReducedMotion ? 'auto' : 'smooth',
        });
      }
    });
  });

  document.querySelectorAll('[data-copy-trigger]').forEach((button) => {
    const defaultLabel = button.textContent || t('uiCopyFallback', 'Copy');
    const targetSelector = button.dataset.copyTarget || '';
    const inlineText = button.dataset.copyText || '';

    button.addEventListener('click', () => {
      const target = targetSelector ? document.querySelector(targetSelector) : null;
      const text = inlineText || (target ? target.textContent || '' : '');

      if (!text.trim()) {
        return;
      }