Skip to content

Instantly share code, notes, and snippets.

@pipwerks
Created January 18, 2012 05:47
Show Gist options
  • Save pipwerks/1631259 to your computer and use it in GitHub Desktop.
Save pipwerks/1631259 to your computer and use it in GitHub Desktop.

Revisions

  1. pipwerks revised this gist Jan 25, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion sample.htm
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    <!DOCTYPE HTML">
    <!DOCTYPE HTML>
    <!-- saved from url=(0014)about:internet -->
    <html lang="en">
    <head>
  2. pipwerks revised this gist Jan 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion sample.htm
    Original file line number Diff line number Diff line change
    @@ -21,7 +21,7 @@
    NOSCORM: "Sorry, but the course is not available at this time (SCORM API not found). Please try again. If you continue to encounter problems, please contact the course administrator."
    },
    flashvars = {},
    params = { bgcolor: CONFIG.BGCOLOR, menu: "false" },
    params = { bgcolor: CONFIG.BGCOLOR, menu: "false", wmode: CONFIG.WMODE },
    attributes = { id: "Captivate", name: "Captivate" };

    </script>
  3. pipwerks revised this gist Jan 18, 2012. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion scorm_support.js
    Original file line number Diff line number Diff line change
    @@ -237,6 +237,8 @@ if(SCORM_API){
    } else {

    //Provide a useful error message for the learner. Will only show up if SCORM API is not found!
    document.getElementById(CONFIG.TARGET).innerHTML = CONFIG.NOSCORM;
    swfobject.addDomLoadEvent(function(){
    document.getElementById(CONFIG.TARGET).innerHTML = CONFIG.NOSCORM;
    });

    }
  4. pipwerks revised this gist Jan 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion scorm_support.js
    Original file line number Diff line number Diff line change
    @@ -104,7 +104,7 @@ var getAPI = function(){
    }

    //Plateau LMS needs special hand-holding
    if(!API && win.top.opener.document) {
    if(!API && win.top.opener && win.top.opener.document) {
    API = findAPI(win.top.opener.document);
    }

  5. pipwerks revised this gist Jan 18, 2012. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions sample.htm
    Original file line number Diff line number Diff line change
    @@ -10,15 +10,15 @@
    // document.domain="";

    var CONFIG = {
    TITLE = "@MOVIETITLE",
    FILEPATH = "@MOVIENAME",
    BGCOLOR = "@SKINCOLOR",
    FPVERSION = "@FlashPlayerVersion",
    WIDTH = "@MOVIEWIDTH",
    HEIGHT = "@MOVIEHEIGHT",
    WMODE = "@WMODEVALUE",
    TARGET = "CaptivateContent",
    NOSCORM = "Sorry, but the course is not available at this time (SCORM API not found). Please try again. If you continue to encounter problems, please contact the course administrator."
    TITLE: "@MOVIETITLE",
    FILEPATH: "@MOVIENAME",
    BGCOLOR: "@SKINCOLOR",
    FPVERSION: "@FlashPlayerVersion",
    WIDTH: "@MOVIEWIDTH",
    HEIGHT: "@MOVIEHEIGHT",
    WMODE: "@WMODEVALUE",
    TARGET: "CaptivateContent",
    NOSCORM: "Sorry, but the course is not available at this time (SCORM API not found). Please try again. If you continue to encounter problems, please contact the course administrator."
    },
    flashvars = {},
    params = { bgcolor: CONFIG.BGCOLOR, menu: "false" },
  6. pipwerks created this gist Jan 18, 2012.
    45 changes: 45 additions & 0 deletions sample.htm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    <!DOCTYPE HTML">
    <!-- saved from url=(0014)about:internet -->
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <title>@MOVIETITLE</title>
    <script>

    // set document.domain property here, if it works for your environment/SCORM implementation
    // document.domain="";

    var CONFIG = {
    TITLE = "@MOVIETITLE",
    FILEPATH = "@MOVIENAME",
    BGCOLOR = "@SKINCOLOR",
    FPVERSION = "@FlashPlayerVersion",
    WIDTH = "@MOVIEWIDTH",
    HEIGHT = "@MOVIEHEIGHT",
    WMODE = "@WMODEVALUE",
    TARGET = "CaptivateContent",
    NOSCORM = "Sorry, but the course is not available at this time (SCORM API not found). Please try again. If you continue to encounter problems, please contact the course administrator."
    },
    flashvars = {},
    params = { bgcolor: CONFIG.BGCOLOR, menu: "false" },
    attributes = { id: "Captivate", name: "Captivate" };

    </script>
    <script src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
    <script src="standard.js"></script>
    <script src="SCORM_support/scorm_support.js"></script>
    <style>
    * { margin: 0; padding: 0; } /* remove all padding and margins on page */
    body { text-align: center; background: @SKINCOLOR }
    </style>
    </head>

    <body>
    <div id="CaptivateContent">
    <noscript>
    This course requires JavaScript to be enabled in your browser. Please enable JavaScript, then relaunch the course.
    </noscript>
    </div>
    </body>

    </html>
    242 changes: 242 additions & 0 deletions scorm_support.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,242 @@
    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.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 <object> 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!
    document.getElementById(CONFIG.TARGET).innerHTML = CONFIG.NOSCORM;

    }