Tracking guide
You author tracking once, declaratively, and the runtime projects it onto whichever target you package for. You don’t write per-target code.
oelt.track.score(0.85);That one call becomes cmi.core.score.raw on SCORM 1.2, score.scaled on SCORM 2004, result.score.scaled in a cmi5 statement, or a stored number with no LMS at all.
The runtime API
Section titled “The runtime API”The global oelt is available after the page loads. Most courses never call it directly — components and the declared rules drive it — but it’s there for the manual completion rule and bespoke interactions.
| Call | What it does |
|---|---|
oelt.track.complete() | Mark the course complete (used by the manual rule). |
oelt.track.score(0.85) | Report a 0–1 score. |
oelt.track.progress(0.4) | Report 0–1 progress. |
oelt.track.interaction({ id, type, result, score?, response? }) | Report a question-level interaction. result ∈ passed | failed | completed; score is 0–1. |
oelt.state.get(key) / oelt.state.set(key, value) | Suspend/resume key-value store (see the budget below). |
oelt.nav.pages, oelt.nav.current(), oelt.nav.go(i), oelt.nav.next(), oelt.nav.prev() | Navigation derived from the manifest. |
Declaring tracking
Section titled “Declaring tracking”Tracking is configured in the tracking block of course.json. Omit it for the zero-config default — complete when all pages are viewed, no score — which is impossible to misconfigure.
{ "tracking": { "completion": { "rule": "required-interactions-passed" }, "score": { "rule": "weighted-interactions", "mastery": 0.8 }, "progress": { "rule": "pages-viewed" } }}Completion rules:
all-pages-viewed— complete when every page has been viewed (default).pages-viewed— requiresthreshold(0–1), e.g. view 80% of pages.required-interactions-completed— everyrequired: trueinteraction answered.required-interactions-passed— everyrequired: trueinteraction passed.manual— your code callsoelt.track.complete().
Score rules:
none— no score (default).single-interaction— requiressource(an interaction id).weighted-interactions— weighted mean using each interaction’sweight.mastery(0–1) may be set on a scoring rule to decide pass/fail.
Progress rules: pages-viewed (default) or none.
The SCORM 1.2 collapse rule
Section titled “The SCORM 1.2 collapse rule”SCORM 1.2 has one status field (cmi.core.lesson_status) for both completion and success. OELTKit collapses your richer model into it with a fixed, normative rule — you don’t implement it, but you should know what survives. This is the exact rule from the toolkit’s tracking-semantics spec (§4.2), quoted verbatim:
On SCORM 2004 and cmi5 no collapse happens: completion and success are reported on their own channels (completion_status + success_status; completed then passed/failed statements).
What each target speaks
Section titled “What each target speaks”| Concept | SCORM 1.2 | SCORM 2004 | cmi5 / xAPI | Standalone web |
|---|---|---|---|---|
| Completion | cmi.core.lesson_status (collapsed — see above) | completion_status + success_status (separate) | completed / passed / failed statements | localStorage record |
| Score | score.raw = round(scaled × 100) | score.scaled (also raw/min/max) | result.score.scaled | number 0–1 |
| Progress | none (omitted) | progress_measure 0–1 | progress extension | stored number |
| Interactions | cmi.interactions.n.* (tight limits) | cmi.interactions.n.* (richer) | one statement per interaction | local event log |
Interaction reporting is independent of your completion and score rules: a declared interaction that fires is recorded wherever the target supports it — free analytics that costs you nothing.
For cmi5/xAPI, OELTKit uses the cmi5-defined verbs plus ADL verbs (answered, interacted, …); it does not mint its own. A dedicated OELT xAPI Profile is deferred to a later phase.
State and the suspend-data budget
Section titled “State and the suspend-data budget”All persisted state goes through oelt.state — never call the LMS API directly, or you bypass size-guarding and break resume.
Reporting an interaction by hand
Section titled “Reporting an interaction by hand”For a one-off interaction, emit the same payload the components use; the runtime forwards it:
<button onclick="oelt.track.interaction({ id: 'final-quiz', type: 'quiz', result: 'passed', score: 1 })"> Submit</button>