let findAPITries = 0; // Find SCORM 1.2 API algorithm taken from the SCORM documentation at: // https://scorm.com/scorm-explained/technical-scorm/run-time/api-discovery-algorithms/ function findAPI(win) { // Check to see if the window (win) contains the API // if the window (win) does not contain the API and // the window (win) has a parent window and the parent window // is not the same as the window (win) while ( (win.API == null) && (win.parent != null) && (win.parent != win) ) { // increment the number of findAPITries findAPITries++; // Note: 7 is an arbitrary number, but should be more than sufficient if (findAPITries > 7) { alert("Error finding API -- too deeply nested."); return null; } // set the variable that represents the window being // being searched to be the parent of the current window // then search for the API again win = win.parent; } return win.API; } function _getAPI() { // start by looking for the API in the current window var theAPI = findAPI(window); // if the API is null (could not be found in the current window) // and the current window has an opener window if ( (theAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined") ) { // try to find the API in the current window’s opener theAPI = findAPI(window.opener); } // if the API has not been found if (theAPI == null) { // Alert the user that the API Adapter could not be found alert("Unable to find an API adapter"); } return theAPI; } // SCORM 1.2 defines a bool as a string with the values "true" or "false" function isScormTrue(b) { return b === "true"; } class Scorm { api = null; // ensure LMSInitialize is only called once. initialized = false; // ensure LMSFinish is only called once. finished = false; getAPI() { if (this.api === null) { this.api = _getAPI(); } return this.api; } initialize() { if (this.initialized) return true; const api = this.getAPI(); if (!api) { console.warn("SCORM API not found"); return false; } const r = api.LMSInitialize(""); this.initialized = isScormTrue(r); return this.initialized; } finish() { if (this.finished) return true; const api = this.getAPI(); if (!api) return false; const r = api.LMSFinish(""); this.finished = isScormTrue(r); return this.finished; } getValue(el) { const api = this.getAPI(); if (!api) return null; return api.LMSGetValue(el); } setValue(el, val) { const api = this.getAPI(); if (!api) return false; const r = api.LMSSetValue(el, String(val)); return isScormTrue(r); }; commit() { const api = this.getAPI(); if (!api) return false; const r = api.LMSCommit(""); return isScormTrue(r); } setScore(raw, min, max) { this.setValue("cmi.core.score.raw", raw); if (min !== null) this.setValue("cmi.core.score.min", min); if (max !== null) this.setValue("cmi.core.score.max", max); this.commit(); } } window.SCORM = new Scorm(); window.addEventListener("load", function() { window.SCORM.initialize(); }); // Finish on the before unloading the SCORM content as advised by the documentation: // https://scorm.com/scorm-explained/technical-scorm/run-time/ window.addEventListener("beforeunload", function() { window.SCORM.commit(); window.SCORM.finish(); });