-
Notifications
You must be signed in to change notification settings - Fork 0
Module Reference
Per-file API walkthrough. Signatures, line references, dependencies. For the math behind the algorithm functions listed here, see Decompression-Model and the individual algorithm chapters.
All paths are relative to the repository root. Line numbers are kept current per the wiki-update rule in CLAUDE.md.
Implements Haldane and Schreiner kinetics, M-values, gradient-factor interpolation, NDL search, and the deco-stop scheduling loop. The largest module in the code base (~1300 lines).
Imports: COMPARTMENTS, getRateConstant from tissueCompartments.js.
Imported by: diveSetup.js, mvalues.js, main.js, tissueEducation.js, visualization.js, and every chart in js/charts/.
Constants
| Name | Value | Purpose |
|---|---|---|
CALC_INTERVAL |
10 |
Simulation time step, seconds |
SURFACE_PRESSURE |
1.01325 |
1 atm in bar |
WATER_VAPOR_PRESSURE |
0.0627 |
At 37 °C, in bar |
N2_FRACTION |
0.7902 |
Lumps argon with N₂ (standard deco-model convention) |
PRESSURE_PER_METER |
0.1 |
bar per metre of sea water |
DEFAULT_GF_LOW, DEFAULT_GF_HIGH
|
1.0, 1.0
|
As fractions (not percentages) |
DECO_STOP_MAX_MINUTES |
300 |
Safety cap per stop; exceeding throws DecoCapExceededError
|
DecoCapExceededError |
class | Thrown when a single stop would need > 5 h |
Exports — Pressure & alveolar
| Signature | Line | Description |
|---|---|---|
getAmbientPressure(depth) |
72 | SURFACE_PRESSURE + depth × 0.1 |
getAlveolarN2Pressure(ambient, n2=0.7902) |
84 | (ambient − 0.0627) × n2Frac |
getInitialTissueN2(n2=0.7902) |
93 | Alveolar N₂ at surface (saturation initialiser) |
Exports — Kinetics
| Signature | Line | Description |
|---|---|---|
haldaneEquation(P0, Palv, t, halfTime) |
108 | Constant-depth exponential loading. See Model-02-Haldane-Equation. |
schreinerEquation(P0, Palv0, R, t, halfTime) |
125 | Linear-rate loading. See Model-03-Schreiner-Equation. |
simulateDepthTime(tissues, depth, t, n2) |
838 | Vector-apply Haldane across all 16 compartments |
simulateDepthChange(tissues, startDepth, endDepth, t, n2) |
860 | Vector-apply Schreiner across all 16 compartments |
Exports — M-values & ceilings
| Signature | Line | Description |
|---|---|---|
getMValue(ambient, a, b) |
145 |
a + ambient/b — raw Bühlmann limit |
getAdjustedMValue(ambient, a, b, gf) |
160 | ambient + gf × (M − ambient) |
getCompartmentCeiling(Pt, a, b, gf) |
361 | Minimum ambient pressure this compartment permits |
getDiveCeiling(tissues, gf) |
377 | Deepest (most-restrictive) ceiling across 16 compartments; returns {ceiling, ceilingDepth, controllingCompartment}
|
getFirstStopDepth(tissues, gfLow, stopIncrement=3) |
456 | First mandatory stop rounded up to 3 m grid |
Exports — Gradient factors
| Signature | Description |
|---|---|
calculateInstantGF(Pt, ambient, compartment) |
(Pt − ambient) / (M − ambient), expressed as 0–1 |
calculateMaxGF(tissues, ambient) |
Returns {gfMax, leadingCompartment, allGFs}
|
findFirstStopAtGFLow(tissues, depth, n2, gfLow, stopIncrement, ascentRate, gasSwitchPoints) |
The convention's first-stop search: shallowest stop-grid depth where the dive ceiling at GF_low is satisfied after simulated ascent. Returns {anchorDepth, pAnchor, tissuesAtAnchor}. This is the canonical pAnchor source used by generateDecoSchedule, calculateCeilingTimeSeriesDetailed, MValueChart, and GFChart. See Algo-03-First-Stop-Ramped-GF. |
interpolateGF(ambient, pAnchor, gfLow, gfHigh) |
Linear ramp from GF-low at pAnchor to GF-high at surface (1.01325 bar) |
findGFLowAnchor(tissues, depth, n2, gfLow, ascentRate, gasSwitchPoints) |
Legacy: simulates ascent in 0.1-bar steps; returns the leading compartment's GF_low ceiling. No longer drives the scheduler — kept as a building block. |
findFirstStopWithRampedGF(tissues, depth, pAnchor, n2, gfLow, gfHigh, stopIncrement, gasSwitchPoints) |
Legacy: scans the stop grid using the ramped GF for the ceiling check. No longer drives the scheduler. |
getFirstStopDepth(tissues, gfLow, stopIncrement=3) |
Static (no ascent simulation): rounds the current dive ceiling at GF_low up to the stop grid. Used by quick-lookups; the deco scheduler uses findFirstStopAtGFLow instead. |
Exports — NDL & deco scheduling
| Signature | Line | Description |
|---|---|---|
calculateNDL(depth, n2=0.7902, gfLow=1.0) |
741 | Binary search; returns {ndl, controllingCompartment, descentTime, ndlExact}
|
generateDecoSchedule(tissues, depth, n2, gfLow, gfHigh, gases=null, options={}) |
899 | Returns {stops, gasSwitches, totalTime, totalAscentTime, pAnchor, anchorDepth}. Throws DecoCapExceededError if any stop would exceed DECO_STOP_MAX_MINUTES. See Algo-04-Deco-Stop-Loop and Algo-05-Multi-Gas-Switching. |
generateDecoSchedule options:
-
stopIncrement(default 3 m) — vertical grid; pass 0.1 for continuous mode -
timeIncrement(default 1 min) — stop-time quantum -
ascentRate(default 10 m/min) — transit between stops -
gasSwitchTime(default 0) — minutes held at switch depth -
maxPpO2(default 1.6) — cap for deco gas MOD -
safetyStop— passed through for safety-stop handling
Exports — Full-dive simulation
| Signature | Line | Description |
|---|---|---|
calculateTissueLoading(profile, surfaceInterval=60, options={}) |
1178 | Main entry: walks the waypoint array at CALC_INTERVAL resolution, returns {timePoints, depthPoints, ambientPressures, compartments: {1:{pressures:[]},…}, n2Fractions}
|
calculateCeilingTimeSeries(results, gfLow, gfHigh=gfLow) |
590 | Flat array of ceiling depths at each time point |
calculateCeilingTimeSeriesDetailed(results, gfLow, gfHigh, providedPAnchor=null) |
617 | Returns per-compartment ceiling series plus gfValues and pAnchor; used by M-value and profile charts |
Implementation notes
- Haldane is coded as
Palv + (P0 − Palv) × e^(−k·t)atdecoModel.js:110. - Schreiner is coded as a three-term form at
decoModel.js:127–129: constant inert-gas source + exponential offset. -
pAnchoris an ambient-pressure value, not a depth.findGFLowAnchorsimulates the ascent untilcalculateMaxGF()first hitsgfLow, then refines withgetCompartmentCeiling()on the leading tissue (decoModel.js:316). This differs from decotengu, which anchors the GF ramp at the rounded first-stop depth; seetests/decotengu-comparison.test.mjs:15–22. - Ascent permission in the deco loop (
decoModel.js:1099–1102) checks the GF-adjusted ceiling at current depth against the next-shallower stop, without Schreiner-crediting the short ascent segment. This matches decotengu's convention. -
gasKey()helper atdecoModel.js:918normalises gases with or without anidfield, so the deco loop tolerates both library gases and custom mixes.
16 Bühlmann ZH-L16 compartment definitions, runtime-switchable between variants A / B / C.
Imports: none.
Imported by: decoModel.js, diveSetup.js, mvalues.js, tissueEducation.js, every chart, every test file.
Exports
| Name | Signature | Description |
|---|---|---|
ZHL16_VARIANTS |
{A: 'ZH-L16A', B: 'ZH-L16B', C: 'ZH-L16C'} |
Enum |
COMPARTMENTS |
array of 16 objects | Current active compartments. Rebuilt in-place on variant switch. |
getZHL16Variant() |
() => string |
Current active variant name |
setZHL16Variant(variant) |
(string) => void |
Switch variant and rebuild COMPARTMENTS in place |
getCompartmentsForVariant(variant) |
(string) => Array |
Inspect a variant without mutating global state |
getRateConstant(halfTime) |
(number) => number |
ln(2) / halfTime |
getCompartmentCategory(halfTime) |
(number) => string |
Returns "Fast" / "Medium" / "Medium-Slow" / "Slow" |
Implementation notes
- Each compartment object has
{id, halfTime, bN2, aN2, label, color}after build; seebuildCompartments()aroundtissueCompartments.js:140. - TC1's half-time and
bcoefficient swap as a pair between variants. ZH-L16A uses 4.0 min /b=0.5050; B and C use 5.0 min /b=0.5578. SeeCOMPARTMENT_1_HALFTIMEat line 46 andCOMPARTMENT_1_B_N2around line 55. - All variants share TC2–16 half-times; only the
acoefficient varies between A, B, C for compartments 5–15 (A_COEFFICIENTS_16A/B/C at lines 102, 115, 128). -
setZHL16Variant()clears and repushes the existingCOMPARTMENTSarray (tissueCompartments.js:190–191) rather than reassigning, so downstream code holding a reference (e.g.decoModel.js) picks up the change without reimporting. - Default active variant is ZH-L16C (
currentVariant = ZHL16_VARIANTS.Cat line 40). Matches the decotengu default. - All times in minutes, pressures in bar. See Model-01-Compartments for the full coefficient table.
Standalone page controller for the interactive M-value diagram on m-values.html. Not an algorithm module — it orchestrates dataset building for the Chart.js P-P diagram.
Imports: COMPARTMENTS from tissueCompartments.js; calculateTissueLoading, getAmbientPressure, getAdjustedMValue, getFirstStopDepth, etc. from decoModel.js; loadDiveSetup, getDiveSetupWaypoints, getGases, getGradientFactors from diveSetup.js.
Imported by: m-values.html only (direct script tag).
Exports
| Name | Description |
|---|---|
CHART_CONFIG |
Render settings (colours, point sizes, playback speed) |
loadSelectedProfile() |
Fetches profile, runs calculateTissueLoading, draws the P-P chart |
populateCoefficientsTable() |
Builds the HTML reference table of ZH-L16 coefficients for the active variant |
Implementation notes
- Dataset construction (
buildDatasets, around line 811) draws the ambient liney=x, the alveolar liney=0.7902·x, one M-value line per visible compartment, the GF-adjusted corridor (GF-low at pAnchor, GF-high at surface), the tissue trail, and the current-time points. - pAnchor for the GF corridor comes from
getFirstStopDepth()+interpolateGF(); seemvalues.js:869–892. The same pAnchor is passed intocalculateCeilingTimeSeriesDetailed()so the chart and the profile ceiling agree. - Chart animation is disabled on update (
mvalues.js:1118) so timeline-slider scrubbing is smooth. - Pure UI — no algorithm equations live here. All math delegates to
decoModel.js.
Dive configuration, gas library, profile generation, and gas-switch waypoint insertion. The second-largest module (~1470 lines).
Imports: calculateNDL, generateDecoSchedule, simulateDepthTime, simulateDepthChange, getInitialTissueN2, and other helpers from decoModel.js; COMPARTMENTS from tissueCompartments.js; translate from i18n.js.
Imported by: main.js, mvalues.js, every chart, DiveSetupEditor.js.
| Export | Line | Description |
|---|---|---|
BOTTOM_GASES |
40 | Air (O₂ 0.2098 / N₂ 0.7902), EAN32, EAN36 |
DECO_GASES |
50 | EAN50, EAN80, O₂ 100% |
PREDEFINED_GASES |
59 | Union of the two above |
BOTTOM_CYLINDERS, STAGE_CYLINDERS
|
65, 81 | Cylinder size presets (litres) |
getPredefinedGas(id), getBottomGas(id), getDecoGas(id)
|
118, 127, 136 | ID lookups |
Note: BOTTOM_GASES[0].n2 is 0.7902, matching N2_FRACTION in decoModel.js.
| Signature | Line | Description |
|---|---|---|
loadDiveSetup(path='data/dive-setup.json') |
145 | async; tries localStorage, falls back to JSON fetch, caches in module scope |
getDefaultSetup() |
176 | Hardcoded 40 m / 20 min air dive |
clearCache() |
1187 | Clears the module-level cache |
saveDiveSetup(setup, key='diveSetup') |
1196 | Persist to localStorage |
loadSavedSetup(key='diveSetup') |
1209 | Restore from localStorage |
extendDiveSetup(base, overrides) |
694 | Deep merge with validation |
| Signature | Line | Description |
|---|---|---|
generateSimpleProfile(maxDepth, bottomTime, safetyStop, options) |
239 | No-deco profile. Descent 20 m/min, ascent 10 m/min, optional 3 min @ 5 m. |
generateDecoProfile(maxDepth, bottomTime, gases, gfLow, gfHigh, safetyStop, options) |
326 | Async. Runs NDL check; if exceeded, calls generateDecoSchedule() and splices stops into the waypoint array. Returns {waypoints, ndl, requiresDeco, decoStops, totalDecoTime, controllingCompartment, pAnchor, anchorDepth}. |
generateDecoProfileSync(...) |
552 | Synchronous variant accepting a pre-loaded compartments array. |
getNDLForDepth(depth, gas, gfLow) |
682 | Convenience wrapper around calculateNDL. |
"Bottom time" means the absolute time at which ascent begins, not time spent at max depth. Descent duration is exact (not rounded); ascent time snaps to 0.1 min unless continuousDeco=true (diveSetup.js:260).
| Signature | Line | Description |
|---|---|---|
calculateMOD(o2Fraction, maxPpO2=1.4) |
807 |
floor((ppO₂/o2 − 1) × 10), metres |
calculateEND(depth, heFraction=0) |
820 | (depth + 10) × (1 − fHe) − 10 |
calculatePartialPressure(depth, gasFraction) |
832 | fraction × (1.01325 + depth/10) |
getGasCylinderVolume(gas), getCylinderVolume(setup)
|
842, 851 | Litres |
getGasStartPressure(gas) |
861 | bar |
computeGasConsumption(results, gases, sacRate, decoSacRate, reservePressure=50) |
1339 | Per-gas consumption over the profile |
| Signature | Line | Description |
|---|---|---|
NOAA_CNS_LIMITS |
1259 | Discrete ppO₂ / max-exposure lookup table |
getCNSPerMinute(ppO2) |
1282 | Percent per minute; 0 if ppO₂ < 0.5 |
calculateOTU(ppO2, timeMinutes) |
1304 |
t × ((ppO₂ − 0.5)/0.5)^0.83 (NOAA form) |
OTU_LIMITS |
1312 | Daily / series exposure limits |
Toxicity is informational; not fed back into the deco loop.
| Signature | Line | Description |
|---|---|---|
getDiveSetupWaypoints(setup) |
754 | Extracts dives[0].waypoints
|
getSurfaceInterval(setup), getGFLow(setup), getGFHigh(setup), getGradientFactors(setup)
|
767, 776, 785, 794 | Getters |
getGases(setup), getBottomGasFromSetup(setup), getDecoGasesFromSetup(setup)
|
884, 905, 915 | Gas collections |
getGasAtWaypoint(waypoint, gases), getGasAtTime(waypoints, gases, time)
|
927, 949 | Active gas lookup |
getGasSwitchEvents(waypoints, gases) |
974 | Returns [{time, depth, fromGas, toGas}]
|
insertGasSwitchWaypoints(waypoints, gases, ascentRate=10, maxPpO2=1.6, gasSwitchTime=0) |
1009 | Inserts switch waypoints at MOD depths on ascent; rounds to 3 m grid |
| Signature | Line | Description |
|---|---|---|
generateProfileName(setup) |
1225 | Short label for UI |
formatDiveSetupSummary(setup) |
1241 | Multi-line human summary |
renderDivePlanTableHTML(waypoints, gases, opts) |
1435 | Returns HTML for the dive-plan table. Preserves explicit gas-switch rows; does not fold them into neighbouring rows. |
DEFAULT_GAS_SWITCH_TIME=0 (line 34), DEFAULT_START_PRESSURE=200, DEFAULT_RESERVE_PRESSURE=50 (lines 91, 96), DEFAULT_GF_LOW=100 / DEFAULT_GF_HIGH=100 as percentages (lines 101–102), DEFAULT_SAFETY_STOP={enabled:true, depth:5, time:3} (line 107).
Waypoint-array validation and statistics. No algorithm content.
Imports: none.
Imported by: main.js.
| Signature | Line | Description |
|---|---|---|
createDefaultProfile() |
12 | Hardcoded 40 m × 20 min with 9/6/3 m stops |
validateProfile(profile) |
32 | Returns {valid, errors, warnings}
|
parseProfileInput(inputData) |
105 | Parses time\tdepth-style text into waypoint array |
calculateRates(profile) |
117 | Returns `[{from, to, rate, type: 'descent' |
getDiveStats(profile) |
145 | {maxDepth, totalTime, maxDescentRate, maxAscentRate, waypointCount} |
First waypoint must be (time=0, depth=0); this is enforced so decompression dives can be detected correctly (diveProfile.js:45–50). Depths greater than 60 m and non-surface endings produce warnings, not errors.
Chart.js-driven educational animations for tissue-loading.html. Not imported as a library (no exports); executes on module load.
Constants used (duplicated from decoModel.js for page self-containment):
-
WATER_VAPOR_PRESSURE = 0.0627(line 7) -
SURFACE_PRESSURE = 1.01325(line 8) -
N2_FRACTION = 0.7902(line 9) -
SURFACE_ALVEOLAR_N2≈ 0.755 bar (line 10)
Internal functions: animateTissueBars() (line 129), calculateOngassing() (line 320), calculateOffgassing() (line 462). All use P(T) = target + (initial − target) × e^(−ln2·T) on a 0–6 half-time axis with 0.5 T steps.
Class component (~1600 lines) rendering the depth-vs-time chart with ceiling overlay, gas-switch markers, deco-stop annotations, and optional tissue-compartment overlays.
Exports: class DiveProfileChart (line 63), factory createDiveProfileChart(container, config) (line 1609).
Imports: COMPARTMENTS from tissueCompartments.js; calculateTissueLoading, calculateCeilingTimeSeries, getAmbientPressure, and others from decoModel.js; theme, applyChartTheme, formatAxis from chartTheme.js; validateDiveSetup, normalizeDiveSetup, mergeOptions, DEFAULT_DIVE_PROFILE_OPTIONS from chartTypes.js; translate from i18n.js; getGases, getGasSwitchEvents from diveSetup.js.
Key methods: constructor(container, config), _render(), update(diveSetup), destroy(), _buildTissueControls(). Accepts options.mode values for profile-only, tissue-overlay, or ppO₂/ppN₂ overlays. Listens for the global languagechange event.
Class component (~1400 lines) rendering the pressure-pressure (P-P) M-value diagram with time-slider playback.
Exports: class MValueChart (line 87), factory createMValueChart(container, config) (line 1434).
Imports same as DiveProfileChart.js plus chart-specific helpers. Uses createInteractionLockBtn from interactionLock.js.
Key methods: _setupKeyboardShortcuts() (arrow keys step time, shift+arrow jumps waypoint, space toggles play/pause, home/end jump to start/end). Playback loop redraws with chart.update('none') to keep the slider smooth.
Class component (~1300 lines) plotting instantaneous GF (%) per compartment against ambient pressure, with the GF corridor as a shaded band.
Exports: class GFChart (line 81), factory createGFChart(container, config) (line 1311).
Plots 100 × (P_tissue − P_amb) / (a + P_amb/b − P_amb) per compartment, the 100 % Bühlmann reference line, a shaded corridor from GF-low at pAnchor to GF-high at surface, and a vertical line at pAnchor.
Chart.js theme glue (~200 lines). No state; idempotent.
| Export | Line | Description |
|---|---|---|
theme() |
30 | Reads CSS custom properties from :root, returns a palette object |
applyChartTheme() |
62 | Applies defaults to Chart.defaults globally |
depthGradient(ctx, area, strong, weak) |
149 | Canvas gradient helper |
formatAxis(v, decimals=0) |
166 | Axis-tick formatter |
watchThemeChanges(onChange) |
180 | Observes prefers-color-scheme / data-theme attribute |
Validation and normalisation of diveSetup objects for chart consumption (~300 lines).
| Export | Line | Description |
|---|---|---|
DEFAULT_ENVIRONMENT |
146 | Salinity, altitude |
DEFAULT_DIVE_PROFILE_OPTIONS |
156 | Chart display toggles |
DEFAULT_TISSUE_PRESSURE_OPTIONS |
184 | Tissue overlay defaults |
mergeOptions(defaults, user) |
208 | Shallow-per-key deep merge |
validateDiveSetup(setup) |
230 | Returns {valid, errors}
|
normalizeDiveSetup(setup) |
298 | Applies defaults, coerces types, returns a fresh object |
Small helper (~100 lines) that adds a toggle button to lock/unlock Chart.js zoom/pan on mobile.
| Export | Line | Description |
|---|---|---|
createInteractionLockBtn(getChart, container, opts) |
22 | Injects a button and wires the zoom plugin's enabled flag |
class BubbleModel (line 28). A visualisation-only toy model, not wired into the decompression algorithm.
class DiveSetupEditor extends EventTarget (line 115; ~1700 lines). Embeddable form editor that produces the diveSetup JSON consumed by the three chart components.
Default export at line 1674. Emits change events with detail.diveSetup when the form mutates (configurable via options.emitOnInput). Sections: gas management (library + custom), waypoint editor with drag-reorder, gradient-factor sliders with presets (Bühlmann, Conservative, Deco Planner), safety stop, SAC rates, import/export JSON textarea. Re-renders on languagechange.
Multi-dive toggle (showMultiDive) exists but only dives[0] is rendered by the chart components; see the note in CLAUDE.md.
class TissueSaturationSim (line 51). Pure UI controller for the tissue-saturation canvas on tissue-loading.html. No algorithm exports.
Small UI helpers:
| Export | Line | Description |
|---|---|---|
mountHeroMotion(root) |
82 in HeroMotion.js
|
Landing-page animated background |
initStickyTOC(options) |
26 in StickyTOC.js
|
Scroll-spy for theory-page TOCs |
initTooltipShortcut() |
13 in tooltipShortcut.js
|
Keyboard access for help tooltips |
Custom JSON-based i18n (no library). Loads locales/{lang}.json, applies translations to data-i18n DOM attributes, persists selection in localStorage['deco-theory-lang'].
Exports (all from line 333):
| Name | Description |
|---|---|
initI18n() |
Detects browser language, loads translations, wires languagechange
|
setLanguage(lang) |
Switches active language and fires languagechange
|
getCurrentLanguage() |
Returns the active code ('en', 'cs', 'es') |
createLanguageSwitcher(container, options) |
Renders the dropdown switcher |
translate(key, vars) |
Lookup by dot-notation key, supports {0}, {1} interpolation |
SUPPORTED_LANGS |
['en', 'cs', 'es'] |
Locale files are in locales/ (en.json, cs.json, es.json).
Centralised navigation menu for all HTML pages.
Exports (line 247): initNavigation, NAV_ITEMS. NAV_ITEMS is the authoritative menu structure (sandbox, theory, tests, about), and initNavigation() renders it into .nav-links, detects the active page, and handles subdirectory path prefixing for /sandbox/.
URL-based dive-setup share links.
| Export | Line | Description |
|---|---|---|
encodeDiveSetup(setup) |
28 | Compact base64-ish encoding for ?p=…
|
decodeDiveSetup(encoded) |
70 | Inverse |
getSandboxUrl(setup, options) |
109 | Produces an "Open in Sandbox" link |
getChartModeFromUrl() |
133 | Reads ?mode=…
|
getProfileFromUrl() |
143 | Reads ?p=… and decodes |
updateUrlWithProfile(setup) |
155 |
history.replaceState without reloading |
SVG-sprite helper.
| Export | Line | Description |
|---|---|---|
iconHTML(name, cls, title) |
20 | Returns an <svg><use …/></svg> string |
iconElement(name, cls, title) |
37 | Returns a live DOM element |
Entry point for the legacy single-page sandbox view. No exports — executes on module load. Wires DiveSetupEditor-less controls directly: loads a setup, validates it with validateProfile, runs calculateTissueLoading, renders via visualization.js.
Legacy Chart.js visualisation used by main.js. Modern pages use the class components in js/charts/ instead.
| Export | Line | Description |
|---|---|---|
renderChart(canvas, results, visibleCompartments, gasSwitchEvents, ceilingDepths) |
19 | One-shot chart render |
toggleCompartment(id, visible) |
272 | Show/hide a compartment line |
showOnlyCompartments(ids) |
289 | Isolate a selection |
showAllCompartments() |
305 | Reset |
hideAllCompartments() |
321 | Clear |
getChart() |
338 | Access the underlying Chart instance |
Generic quiz engine for the seven CMAS / SPČR quiz pages. No exports — executes on DOM load; reads data/quiz-{name}.json based on a URL parameter, renders questions with category filtering, shuffling, and scoring. See Extending-DecoJS for the JSON format.