var SCORM_API = null, unloaded = false, isInitialized = false, isTerminated = false, courseStatus, value_store = [], lastCommand, setValueWasSuccessful = true, CaptivateSWF; //Cache the reference to the SWF to avoid future lookups /* cmiCache(property, value) Caches CMI value to help prevent sending duplicate data to LMS Parameters: property (CMI property name), value (CMI value, normally a string) Returns: property value if cached version found, false if not cached. */ var cmiCache = function(property, value){ //Ensure we have a valid property to work with if(typeof property === "undefined"){ return false; } //Replace all periods in CMI property names so we don't run into JS errors property = property.replace(/\./g,'_'); //If cached value exists, return it if(typeof value_store[property] !== "undefined"){ return value_store[property]; } //Otherwise add to cache if(typeof value !== "undefined"){ value_store[property] = value; } return false; }; /* findAPI(window) Adapted from pipwerks SCORM wrapper https://github.com/pipwerks/scorm-api-wrapper Looks for an object named API in parent and opener windows Parameters: window (the browser window object). Returns: Object if API is found, null if no API found */ var findAPI = function(win){ var API, findAttempts = 0, findAttemptLimit = 500; while (!win.API_1484_11 && win.parent && win.parent != win && findAttempts <= findAttemptLimit){ findAttempts++; win = win.parent; } API = win.API_1484_11 || null; /* if(!API){ alert("Error finding API. \nFind attempts: " +findAttempts +". \nFind attempt limit: " +findAttemptLimit); } */ return API; }; /* getAPI() Adapted from pipwerks SCORM wrapper https://github.com/pipwerks/scorm-api-wrapper Looks for an object named API_1484_11, first in the current window's frame hierarchy and then, if necessary, in the current window's opener window hierarchy (if there is an opener window). Parameters: None. Returns: Object if API found, null if no API found */ var getAPI = function(){ var API = null, win = window; //Look in parent windows first if(win.parent && win.parent != win){ API = findAPI(win.parent); } //Look in opener windows next if(!API && win.top.opener){ API = findAPI(win.top.opener); } //Plateau LMS needs special hand-holding if(!API && win.top.opener && win.top.opener.document) { API = findAPI(win.top.opener.document); } //if(!API){ alert("getAPI failed: Can't find the API!"); } return API; }; var Captivate_DoExternalInterface = function (command, parameter, value, variable) { console.log("Captivate_DoExternalInterface: " +command +", " +parameter +", " +value); var strErr = "true", intercept = false; //Ensure SCORM API was initialized if(!isInitialized){ return; } if(command === "Initialize"){ //We already initialized, just nod politely //and tell the SWF everything is okay! } else if(command === "SetValue"){ if(parameter === "completion_status"){ courseStatus = value; } //Check to see if value is already cached var cached_value = cmiCache(parameter, value); //Only send value to LMS if it hasn't already been sent; //If value is cached and matches what is about to be sent //to the LMS, prevent value from being sent a second time. if(!cached_value || cached_value !== value){ console.log(parameter +"(" +value +") is not cached. Sending to LMS."); strErr = SCORM_API.SetValue(parameter, value); setValueWasSuccessful = (strErr === "true"); } else { console.log(parameter +"(" +value +") has already been sent. Preventing redundant LMS communication."); //Fakin' it for Captivate's sake. setValueWasSuccessful = true; } } else if(command === "Terminate"){ strErr = SCORM_API.Terminate(""); isTerminated = (strErr === "true"); } else if(command === "Commit"){ strErr = SCORM_API.Commit(""); } else if(command === "GetLastError"){ if(lastCommand === "SetValue" && setValueWasSuccessful){ strErr = ""; console.log("Last Get/Set was successful. Preventing pointless GetLastError invocation."); } else { strErr = SCORM_API.GetLastError(); } } else if(value && value.length > 0){ strErr = SCORM_API[command](parameter); } CaptivateSWF.SetScormVariable(variable, strErr); lastCommand = command; return strErr; }; var initializeSCORM = function (){ isInitialized = SCORM_API.Initialize(""); if(isInitialized){ console.log("SCORM initialized. Ready to go!"); courseStatus = SCORM_API.GetValue("cmi.completion_status"); if(courseStatus === "not attempted"){ SCORM_API.SetValue("cmi.completion_status", "incomplete"); } } }; //Used by SWFObject var callbackFn = function (e){ //e.ref is the aka SWF file. No need for getElementById if(e.success && e.ref){ CaptivateSWF = e.ref; CaptivateSWF.tabIndex = -1; //Set tabIndex to enable focus on non-form elements CaptivateSWF.focus(); //Initialze the SCORM API, don't wait for the SWF to do it. initializeSCORM(); } }; var unloadHandler = function (){ if(!unloaded && isInitialized && !isTerminated){ var exit_status = (courseStatus === "incomplete") ? "suspend" : "normal"; SCORM_API.SetValue("cmi.exit", exit_status); //Set exit to whatever is needed SCORM_API.Commit(""); //Ensure that LMS saves all data isTerminated = (SCORM_API.Terminate("") === "true"); //close the SCORM API connection properly unloaded = true; //Ensure we don't invoke unloadHandler more than once. } }; window.onbeforeunload = unloadHandler; window.onunload = unloadHandler; //Initialize SCORM API SCORM_API = getAPI(); //Only embed SWF if SCORM API is found if(SCORM_API){ swfobject.embedSWF(CONFIG.FILEPATH + "?SCORM_API=1.0&SCORM_TYPE=0", CONFIG.TARGET, CONFIG.WIDTH, CONFIG.HEIGHT, CONFIG.FPVERSION, false, flashvars, params, attributes, callbackFn); } else { //Provide a useful error message for the learner. Will only show up if SCORM API is not found! swfobject.addDomLoadEvent(function(){ document.getElementById(CONFIG.TARGET).innerHTML = CONFIG.NOSCORM; }); }