Spiralist - Source Excerpt 08
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/themes/spiralist/assets/js/spiralist.js so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/themes/spiralist/assets/js/spiralist.js
}
control.addEventListener('input', updateVisibleCards);
control.addEventListener('change', updateVisibleCards);
});
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 focusFeedbackPanel = (target) => {
if (!target || typeof target.scrollIntoView !== 'function') {
return;
}
target.setAttribute('tabindex', '-1');
target.scrollIntoView({
block: 'start',
behavior: prefersReducedMotion ? 'auto' : 'smooth',
});
window.requestAnimationFrame(() => {
if (typeof target.focus === 'function') {
target.focus({ preventScroll: true });
}
});
};
document.querySelectorAll('[data-spiral-demo]').forEach((demo) => {
const output = demo.querySelector('[data-spiral-demo-output]');
const core = demo.querySelector('[data-demo-core]');
const playButton = demo.querySelector('[data-spiral-demo-play]');
const resetButton = demo.querySelector('[data-spiral-demo-reset]');
const inputs = {
signal: demo.querySelector('[data-spiral-demo-input="signal"]'),
feedback: demo.querySelector('[data-spiral-demo-input="feedback"]'),
cycles: demo.querySelector('[data-spiral-demo-input="cycles"]'),
};
const nodes = Array.from(demo.querySelectorAll('[data-demo-node]'));
const playLabel = playButton ? playButton.textContent : t('demoPlayLabel', 'Play Demo');
const pauseLabel = t('demoPauseLabel', 'Pause');
let step = 0;
let timer = null;
const getValue = (key, fallback) => {
const value = Number.parseInt(`${inputs[key] ? inputs[key].value : ''}`, 10);
return Number.isFinite(value) ? value : fallback;
};
const score = () => Math.round((getValue('signal', 7) * 0.45) + (getValue('feedback', 6) * 0.35) + (getValue('cycles', 4) * 0.2));
const setPlaying = (isPlaying) => {
demo.classList.toggle('is-playing', isPlaying);
if (playButton) {
playButton.textContent = isPlaying ? pauseLabel : playLabel;
playButton.setAttribute('aria-pressed', isPlaying ? 'true' : 'false');
}
};
const stop = () => {
if (timer) {
window.clearInterval(timer);
timer = null;
}
setPlaying(false);
};
const render = () => {
const activeIndex = step % Math.max(1, nodes.length);
const cycles = getValue('cycles', 4);
const signal = getValue('signal', 7);
const feedback = getValue('feedback', 6);
const currentScore = score();
const cycleNumber = Math.min(cycles, Math.floor(step / Math.max(1, nodes.length)) + 1);
const activeLabel = nodes[activeIndex] ? nodes[activeIndex].textContent.trim() : t('pageHomeDemoPatternFallback', 'Pattern');
demo.style.setProperty('--spiral-signal', `${signal / 10}`);
demo.style.setProperty('--spiral-feedback', `${feedback / 10}`);
nodes.forEach((node, index) => {
const isActive = index === activeIndex;
node.classList.toggle('is-active', isActive);
node.setAttribute('aria-current', isActive ? 'step' : 'false');
});
if (core) {
core.textContent = `${currentScore}`;
}
if (output) {
output.textContent = `Cycle ${cycleNumber}/${cycles}: ${activeLabel} is active. Clarity ${signal}/10, feedback ${feedback}/10, refinement score ${currentScore}.`;
}
};
const advance = () => {
const maxSteps = Math.max(1, getValue('cycles', 4) * Math.max(1, nodes.length));
step = (step + 1) % maxSteps;
render();
if (step === maxSteps - 1) {
stop();
}
};
Object.values(inputs).forEach((input) => {
if (!input) {
return;
}
input.addEventListener('input', () => {
step = 0;
render();
});
});
if (playButton) {
playButton.addEventListener('click', () => {
if (timer) {
stop();
return;
}
if (prefersReducedMotion) {
advance();
return;
}
setPlaying(true);
timer = window.setInterval(advance, 720);
});
}
if (resetButton) {
resetButton.addEventListener('click', () => {
stop();
step = 0;
if (inputs.signal) inputs.signal.value = '7';
if (inputs.feedback) inputs.feedback.value = '6';
if (inputs.cycles) inputs.cycles.value = '4';
render();
});
}
render();
});
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;