-/*******************************************************************************\r
- **\r
- ** This software is provided "AS IS," without a warranty of any kind. ALL\r
- ** EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY\r
- ** IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-\r
- ** INFRINGEMENT, ARE HEREBY EXCLUDED. PAUL BECKWITH AND HIS LICENSORS SHALL NOT BE LIABLE\r
- ** FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR\r
- ** DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL PAUL BECKWITH OR ITS\r
- ** LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,\r
- ** INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER\r
- ** CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF\r
- ** OR INABILITY TO USE SOFTWARE, EVEN IF CTC HAS BEEN ADVISED OF THE POSSIBILITY\r
- ** OF SUCH DAMAGES.\r
- ** javascript:\r
- ** var result = doLMSInitialize();\r
- ** if (result != true)\r
- ** {\r
-** // handle error\r
-** }\r
- **\r
- *******************************************************************************/\r
-\r
-\r
-var _Debug = false; // set this to false to turn debugging off\r
- // and get rid of those annoying alert boxes.\r
-\r
-// Define exception/error codes\r
-var _NoError = 0;\r
-var _GeneralException = 101;\r
-var _ServerBusy = 102;\r
-var _InvalidArgumentError = 201;\r
-var _ElementCannotHaveChildren = 202;\r
-var _ElementIsNotAnArray = 203;\r
-var _NotInitialized = 301;\r
-var _NotImplementedError = 401;\r
-var _InvalidSetValue = 402;\r
-var _ElementIsReadOnly = 403;\r
-var _ElementIsWriteOnly = 404;\r
-var _IncorrectDataType = 405;\r
-\r
-\r
-// local variable definitions\r
-var apiHandle = null;\r
-var API = null;\r
-var findAPITries = 0;\r
-\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function: doLMSInitialize()\r
- ** Inputs: None\r
- ** Return: CMIBoolean true if the initialization was successful, or\r
- ** CMIBoolean false if the initialization failed.\r
- **\r
- ** Description:\r
- ** Initialize communication with LMS by calling the LMSInitialize\r
- ** function which will be implemented by the LMS.\r
- **\r
- *******************************************************************************/\r
-function doLMSInitialize() {\r
- startTimer();\r
-\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSInitialize was not successful.");\r
- return "false";\r
- }\r
+/*global console*/\r
\r
- var result = api.LMSInitialize("");\r
+/* ===========================================================\r
\r
- if (result.toString() != "true") {\r
- var err = ErrorHandler();\r
- }\r
+pipwerks SCORM Wrapper for JavaScript\r
+v1.1.20160322\r
\r
- return result.toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSFinish()\r
- ** Inputs: None\r
- ** Return: CMIBoolean true if successful\r
- ** CMIBoolean false if failed.\r
- **\r
- ** Description:\r
- ** Close communication with LMS by calling the LMSFinish\r
- ** function which will be implemented by the LMS\r
- **\r
- *******************************************************************************/\r
-function doLMSFinish() {\r
-\r
- setSessionTime();\r
-\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSFinish was not successful.");\r
- return "false";\r
- }\r
- else {\r
- // call the LMSFinish function that should be implemented by the API\r
+Created by Philip Hutchison, January 2008-2016\r
+https://github.com/pipwerks/scorm-api-wrapper\r
\r
- var result = api.LMSFinish("");\r
- if (result.toString() != "true") {\r
- var err = ErrorHandler();\r
- }\r
+Copyright (c) Philip Hutchison\r
+MIT-style license: http://pipwerks.mit-license.org/\r
+\r
+This wrapper works with both SCORM 1.2 and SCORM 2004.\r
+\r
+Inspired by APIWrapper.js, created by the ADL and\r
+Concurrent Technologies Corporation, distributed by\r
+the ADL (http://www.adlnet.gov/scorm).\r
+\r
+SCORM.API.find() and SCORM.API.get() functions based\r
+on ADL code, modified by Mike Rustici\r
+(http://www.scorm.com/resources/apifinder/SCORMAPIFinder.htm),\r
+further modified by Philip Hutchison\r
+\r
+=============================================================== */\r
+\r
+\r
+var pipwerks = {}; //pipwerks 'namespace' helps ensure no conflicts with possible other "SCORM" variables\r
+pipwerks.UTILS = {}; //For holding UTILS functions\r
+pipwerks.debug = {isActive: true}; //Enable (true) or disable (false) for debug mode\r
+\r
+pipwerks.SCORM = { //Define the SCORM object\r
+ version: null, //Store SCORM version.\r
+ handleCompletionStatus: true, //Whether or not the wrapper should automatically handle the initial completion status\r
+ handleExitMode: true, //Whether or not the wrapper should automatically handle the exit mode\r
+ API: {\r
+ handle: null,\r
+ isFound: false\r
+ }, //Create API child object\r
+ connection: {isActive: false}, //Create connection child object\r
+ data: {\r
+ completionStatus: null,\r
+ exitStatus: null\r
+ }, //Create data child object\r
+ debug: {} //Create debug child object\r
+};\r
+\r
+\r
+/* --------------------------------------------------------------------------------\r
+ pipwerks.SCORM.isAvailable\r
+ A simple function to allow Flash ExternalInterface to confirm\r
+ presence of JS wrapper before attempting any LMS communication.\r
+\r
+ Parameters: none\r
+ Returns: Boolean (true)\r
+----------------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.isAvailable = function () {\r
+ return true;\r
+};\r
+\r
+\r
+// ------------------------------------------------------------------------- //\r
+// --- SCORM.API functions ------------------------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
\r
- }\r
\r
- return result.toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSGetValue(name)\r
- ** Inputs: name - string representing the cmi data model defined category or\r
- ** element (e.g. cmi.core.student_id)\r
- ** Return: The value presently assigned by the LMS to the cmi data model\r
- ** element defined by the element or category identified by the name\r
- ** input value.\r
- **\r
- ** Description:\r
- ** Wraps the call to the LMS LMSGetValue method\r
- **\r
- *******************************************************************************/\r
-function doLMSGetValue(name) {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSGetValue was not successful.");\r
- return "";\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.API.find(window)\r
+ Looks for an object named API in parent and opener windows\r
+\r
+ Parameters: window (the browser window object).\r
+ Returns: Object if API is found, null if no API found\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.API.find = function (win) {\r
+\r
+ var API = null,\r
+ findAttempts = 0,\r
+ findAttemptLimit = 500,\r
+ traceMsgPrefix = "SCORM.API.find",\r
+ trace = pipwerks.UTILS.trace,\r
+ scorm = pipwerks.SCORM;\r
+\r
+ while ((!win.API && !win.API_1484_11) &&\r
+ (win.parent) &&\r
+ (win.parent != win) &&\r
+ (findAttempts <= findAttemptLimit)) {\r
+\r
+ findAttempts++;\r
+ win = win.parent;\r
+\r
}\r
- else {\r
- var value = api.LMSGetValue(name);\r
- var errCode = api.LMSGetLastError().toString();\r
- if (errCode != _NoError) {\r
- // an error was encountered so display the error description\r
- var errDescription = api.LMSGetErrorString(errCode);\r
- console.log("LMSGetValue(" + name + ") failed. \n" + errDescription);\r
- return "";\r
- }\r
- else {\r
\r
- return value.toString();\r
+ //If SCORM version is specified by user, look for specific API\r
+ if (scorm.version) {\r
+\r
+ switch (scorm.version) {\r
+\r
+ case "2004" :\r
+\r
+ if (win.API_1484_11) {\r
+\r
+ API = win.API_1484_11;\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": SCORM version 2004 was specified by user, but API_1484_11 cannot be found.");\r
+\r
+ }\r
+\r
+ break;\r
+\r
+ case "1.2" :\r
+\r
+ if (win.API) {\r
+\r
+ API = win.API;\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": SCORM version 1.2 was specified by user, but API cannot be found.");\r
+\r
+ }\r
+\r
+ break;\r
+\r
}\r
- }\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSSetValue(name, value)\r
- ** Inputs: name -string representing the data model defined category or element\r
- ** value -the value that the named element or category will be assigned\r
- ** Return: CMIBoolean true if successful\r
- ** CMIBoolean false if failed.\r
- **\r
- ** Description:\r
- ** Wraps the call to the LMS LMSSetValue function\r
- **\r
- *******************************************************************************/\r
-function doLMSSetValue(name, value) {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSSetValue was not successful.");\r
- return;\r
- }\r
- else {\r
- var result = api.LMSSetValue(name, value);\r
- if (result.toString() != "true") {\r
- var err = ErrorHandler();\r
+\r
+ } else { //If SCORM version not specified by user, look for APIs\r
+\r
+ if (win.API_1484_11) { //SCORM 2004-specific API.\r
+\r
+ scorm.version = "2004"; //Set version\r
+ API = win.API_1484_11;\r
+\r
+ } else if (win.API) { //SCORM 1.2-specific API\r
+\r
+ scorm.version = "1.2"; //Set version\r
+ API = win.API;\r
+\r
}\r
+\r
}\r
\r
- return;\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSCommit()\r
- ** Inputs: None\r
- ** Return: None\r
- **\r
- ** Description:\r
- ** Call the LMSCommit function\r
- **\r
- *******************************************************************************/\r
-function doLMSCommit() {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSCommit was not successful.");\r
- return "false";\r
+ if (API) {\r
+\r
+ trace(traceMsgPrefix + ": API found. Version: " + scorm.version);\r
+ trace("API: " + API);\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": Error finding API. \nFind attempts: " + findAttempts + ". \nFind attempt limit: " + findAttemptLimit);\r
+\r
}\r
- else {\r
- var result = api.LMSCommit("");\r
- if (result != "true") {\r
- var err = ErrorHandler();\r
- }\r
+\r
+ return API;\r
+\r
+};\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.API.get()\r
+ Looks for an object named API, first in the current window's frame\r
+ hierarchy and then, if necessary, in the current window's opener window\r
+ hierarchy (if there is an opener window).\r
+\r
+ Parameters: None.\r
+ Returns: Object if API found, null if no API found\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.API.get = function () {\r
+\r
+ var API = null,\r
+ win = window,\r
+ scorm = pipwerks.SCORM,\r
+ find = scorm.API.find,\r
+ trace = pipwerks.UTILS.trace;\r
+\r
+ API = find(win);\r
+\r
+ if (!API && win.parent && win.parent != win) {\r
+ API = find(win.parent);\r
}\r
\r
- return result.toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSGetLastError()\r
- ** Inputs: None\r
- ** Return: The error code that was set by the last LMS function call\r
- **\r
- ** Description:\r
- ** Call the LMSGetLastError function\r
- **\r
- *******************************************************************************/\r
-function doLMSGetLastError() {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSGetLastError was not successful.");\r
- //since we can't get the error code from the LMS, return a general error\r
- return _GeneralError;\r
+ if (!API && win.top && win.top.opener) {\r
+ API = find(win.top.opener);\r
}\r
\r
- return api.LMSGetLastError().toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSGetErrorString(errorCode)\r
- ** Inputs: errorCode - Error Code\r
- ** Return: The textual description that corresponds to the input error code\r
- **\r
- ** Description:\r
- ** Call the LMSGetErrorString function\r
- **\r
- ********************************************************************************/\r
-function doLMSGetErrorString(errorCode) {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSGetErrorString was not successful.");\r
+ //Special handling for Plateau\r
+ //Thanks to Joseph Venditti for the patch\r
+ if (!API && win.top && win.top.opener && win.top.opener.document) {\r
+ API = find(win.top.opener.document);\r
}\r
\r
- return api.LMSGetErrorString(errorCode).toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function doLMSGetDiagnostic(errorCode)\r
- ** Inputs: errorCode - Error Code(integer format), or null\r
- ** Return: The vendor specific textual description that corresponds to the\r
- ** input error code\r
- **\r
- ** Description:\r
- ** Call the LMSGetDiagnostic function\r
- **\r
- *******************************************************************************/\r
-function doLMSGetDiagnostic(errorCode) {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSGetDiagnostic was not successful.");\r
+ if (API) {\r
+ scorm.API.isFound = true;\r
+ } else {\r
+ trace("API.get failed: Can't find the API!");\r
}\r
\r
- return api.LMSGetDiagnostic(errorCode).toString();\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function LMSIsInitialized()\r
- ** Inputs: none\r
- ** Return: true if the LMS API is currently initialized, otherwise false\r
- **\r
- ** Description:\r
- ** Determines if the LMS API is currently initialized or not.\r
- **\r
- *******************************************************************************/\r
-function LMSIsInitialized() {\r
- // there is no direct method for determining if the LMS API is initialized\r
- // for example an LMSIsInitialized function defined on the API so we'll try\r
- // a simple LMSGetValue and trap for the LMS Not Initialized Error\r
-\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nLMSIsInitialized() failed.");\r
- return false;\r
+ return API;\r
+\r
+};\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.API.getHandle()\r
+ Returns the handle to API object if it was previously set\r
+\r
+ Parameters: None.\r
+ Returns: Object (the pipwerks.SCORM.API.handle variable).\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.API.getHandle = function () {\r
+\r
+ var API = pipwerks.SCORM.API;\r
+\r
+ if (!API.handle && !API.isFound) {\r
+\r
+ API.handle = API.get();\r
+\r
}\r
- else {\r
- var value = api.LMSGetValue("cmi.core.student_name");\r
- var errCode = api.LMSGetLastError().toString();\r
- if (errCode == _NotInitialized) {\r
- return false;\r
- }\r
- else {\r
- return true;\r
+\r
+ return API.handle;\r
+\r
+};\r
+\r
+\r
+// ------------------------------------------------------------------------- //\r
+// --- pipwerks.SCORM.connection functions --------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.connection.initialize()\r
+ Tells the LMS to initiate the communication session.\r
+\r
+ Parameters: None\r
+ Returns: Boolean\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.connection.initialize = function () {\r
+\r
+ var success = false,\r
+ scorm = pipwerks.SCORM,\r
+ completionStatus = scorm.data.completionStatus,\r
+ trace = pipwerks.UTILS.trace,\r
+ makeBoolean = pipwerks.UTILS.StringToBoolean,\r
+ debug = scorm.debug,\r
+ traceMsgPrefix = "SCORM.connection.initialize ";\r
+\r
+ trace("connection.initialize called.");\r
+\r
+ if (!scorm.connection.isActive) {\r
+\r
+ var API = scorm.API.getHandle(),\r
+ errorCode = 0;\r
+\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = makeBoolean(API.LMSInitialize(""));\r
+ break;\r
+ case "2004":\r
+ success = makeBoolean(API.Initialize(""));\r
+ break;\r
+ }\r
+\r
+ if (success) {\r
+\r
+ //Double-check that connection is active and working before returning 'true' boolean\r
+ errorCode = debug.getCode();\r
+\r
+ if (errorCode !== null && errorCode === 0) {\r
+\r
+ scorm.connection.isActive = true;\r
+\r
+ if (scorm.handleCompletionStatus) {\r
+\r
+ //Automatically set new launches to incomplete\r
+ completionStatus = scorm.status("get");\r
+\r
+ if (completionStatus) {\r
+\r
+ switch (completionStatus) {\r
+\r
+ //Both SCORM 1.2 and 2004\r
+ case "not attempted":\r
+ scorm.status("set", "incomplete");\r
+ scorm.set('cmi.success_status', 'unknown');\r
+ break;\r
+\r
+ //SCORM 2004 only\r
+ case "unknown" :\r
+ scorm.status("set", "incomplete");\r
+ scorm.set('cmi.success_status', 'unknown');\r
+ break;\r
+\r
+ //Additional options, presented here in case you'd like to use them\r
+ //case "completed" : break;\r
+ //case "incomplete" : break;\r
+ //case "passed" : break; //SCORM 1.2 only\r
+ //case "failed" : break; //SCORM 1.2 only\r
+ //case "browsed" : break; //SCORM 1.2 only\r
+\r
+ }\r
+\r
+ //Commit changes\r
+ scorm.save();\r
+\r
+ }\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ success = false;\r
+ trace(traceMsgPrefix + "failed. \nError code: " + errorCode + " \nError info: " + debug.getInfo(errorCode));\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ errorCode = debug.getCode();\r
+\r
+ if (errorCode !== null && errorCode !== 0) {\r
+\r
+ trace(traceMsgPrefix + "failed. \nError code: " + errorCode + " \nError info: " + debug.getInfo(errorCode));\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: No response from server.");\r
+\r
+ }\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API is null.");\r
+\r
}\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "aborted: Connection already active.");\r
+\r
}\r
-}\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function ErrorHandler()\r
- ** Inputs: None\r
- ** Return: The current value of the LMS Error Code\r
- **\r
- ** Description:\r
- ** Determines if an error was encountered by the previous API call\r
- ** and if so, displays a message to the user. If the error code\r
- ** has associated text it is also displayed.\r
- **\r
- *******************************************************************************/\r
-function ErrorHandler() {\r
- var api = getAPIHandle();\r
- if (api == null) {\r
- console.log("Unable to locate the LMS's API Implementation.\nCannot determine LMS error code.");\r
- return;\r
- }\r
\r
- // check for errors caused by or from the LMS\r
- var errCode = api.LMSGetLastError().toString();\r
- if (errCode != _NoError) {\r
- // an error was encountered so display the error description\r
- var errDescription = api.LMSGetErrorString(errCode);\r
-\r
- if (_Debug == true) {\r
- errDescription += "\n";\r
- errDescription += api.LMSGetDiagnostic(null);\r
- // by passing null to LMSGetDiagnostic, we get any available diagnostics\r
- // on the previous error.\r
+ return success;\r
+\r
+};\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.connection.terminate()\r
+ Tells the LMS to terminate the communication session\r
+\r
+ Parameters: None\r
+ Returns: Boolean\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.connection.terminate = function () {\r
+\r
+ var success = false,\r
+ scorm = pipwerks.SCORM,\r
+ exitStatus = scorm.data.exitStatus,\r
+ completionStatus = scorm.data.completionStatus,\r
+ trace = pipwerks.UTILS.trace,\r
+ makeBoolean = pipwerks.UTILS.StringToBoolean,\r
+ debug = scorm.debug,\r
+ traceMsgPrefix = "SCORM.connection.terminate ";\r
+\r
+\r
+ if (scorm.connection.isActive) {\r
+\r
+ var API = scorm.API.getHandle(),\r
+ errorCode = 0;\r
+\r
+ if (API) {\r
+\r
+ if (scorm.handleExitMode && !exitStatus) {\r
+\r
+ if (completionStatus !== "completed" && completionStatus !== "passed") {\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = scorm.set("cmi.core.exit", "suspend");\r
+ break;\r
+ case "2004":\r
+ success = scorm.set("cmi.exit", "suspend");\r
+ break;\r
+ }\r
+\r
+ } else {\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = scorm.set("cmi.core.exit", "logout");\r
+ break;\r
+ case "2004":\r
+ success = scorm.set("cmi.exit", "normal");\r
+ break;\r
+ }\r
+\r
+ }\r
+\r
+ }\r
+\r
+ //Ensure we persist the data\r
+ success = scorm.save();\r
+\r
+ if (success) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = makeBoolean(API.LMSFinish(""));\r
+ break;\r
+ case "2004":\r
+ success = makeBoolean(API.Terminate(""));\r
+ break;\r
+ }\r
+\r
+ if (success) {\r
+\r
+ scorm.connection.isActive = false;\r
+\r
+ } else {\r
+\r
+ errorCode = debug.getCode();\r
+ trace(traceMsgPrefix + "failed. \nError code: " + errorCode + " \nError info: " + debug.getInfo(errorCode));\r
+\r
+ }\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API is null.");\r
+\r
}\r
\r
- console.log(errDescription);\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "aborted: Connection already terminated.");\r
+\r
}\r
\r
- return errCode;\r
-}\r
-\r
-/******************************************************************************\r
- **\r
- ** Function getAPIHandle()\r
- ** Inputs: None\r
- ** Return: value contained by APIHandle\r
- **\r
- ** Description:\r
- ** Returns the handle to API object if it was previously set,\r
- ** otherwise it returns null\r
- **\r
- *******************************************************************************/\r
-function getAPIHandle() {\r
- if (apiHandle == null) {\r
- apiHandle = getAPI();\r
+ return success;\r
+\r
+};\r
+\r
+\r
+// ------------------------------------------------------------------------- //\r
+// --- pipwerks.SCORM.data functions --------------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.data.get(parameter)\r
+ Requests information from the LMS.\r
+\r
+ Parameter: parameter (string, name of the SCORM data model element)\r
+ Returns: string (the value of the specified data model element)\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.data.get = function (parameter) {\r
+\r
+ var value = null,\r
+ scorm = pipwerks.SCORM,\r
+ trace = pipwerks.UTILS.trace,\r
+ debug = scorm.debug,\r
+ traceMsgPrefix = "SCORM.data.get('" + parameter + "') ";\r
+\r
+ if (scorm.connection.isActive) {\r
+\r
+ var API = scorm.API.getHandle(),\r
+ errorCode = 0;\r
+\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ value = API.LMSGetValue(parameter);\r
+ break;\r
+ case "2004":\r
+ value = API.GetValue(parameter);\r
+ break;\r
+ }\r
+\r
+ errorCode = debug.getCode();\r
+\r
+ //GetValue returns an empty string on errors\r
+ //If value is an empty string, check errorCode to make sure there are no errors\r
+ if (value !== "" || errorCode === 0) {\r
+\r
+ //GetValue is successful.\r
+ //If parameter is lesson_status/completion_status or exit status, let's\r
+ //grab the value and cache it so we can check it during connection.terminate()\r
+ switch (parameter) {\r
+\r
+ case "cmi.core.lesson_status":\r
+ case "cmi.completion_status" :\r
+ scorm.data.completionStatus = value;\r
+ break;\r
+\r
+ case "cmi.core.exit":\r
+ case "cmi.exit" :\r
+ scorm.data.exitStatus = value;\r
+ break;\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed. \nError code: " + errorCode + "\nError info: " + debug.getInfo(errorCode));\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API is null.");\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API connection is inactive.");\r
+\r
}\r
\r
- return apiHandle;\r
-}\r
-\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function findAPI(win)\r
- ** Inputs: win - a Window Object\r
- ** Return: If an API object is found, it's returned, otherwise null is returned\r
- **\r
- ** Description:\r
- ** This function looks for an object named API in parent and opener windows\r
- **\r
- *******************************************************************************/\r
-function findAPI(win) {\r
- while ((win.API == null) && (win.parent != null) && (win.parent != win)) {\r
- findAPITries++;\r
- // Note: 7 is an arbitrary number, but should be more than sufficient\r
- if (findAPITries > 7) {\r
- console.log("Error finding API -- too deeply nested.");\r
- return null;\r
+ trace(traceMsgPrefix + " value: " + value);\r
+\r
+ return String(value);\r
+\r
+};\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.data.set()\r
+ Tells the LMS to assign the value to the named data model element.\r
+ Also stores the SCO's completion status in a variable named\r
+ pipwerks.SCORM.data.completionStatus. This variable is checked whenever\r
+ pipwerks.SCORM.connection.terminate() is invoked.\r
+\r
+ Parameters: parameter (string). The data model element\r
+ value (string). The value for the data model element\r
+ Returns: Boolean\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.data.set = function (parameter, value) {\r
+\r
+ var success = false,\r
+ scorm = pipwerks.SCORM,\r
+ trace = pipwerks.UTILS.trace,\r
+ makeBoolean = pipwerks.UTILS.StringToBoolean,\r
+ debug = scorm.debug,\r
+ traceMsgPrefix = "SCORM.data.set('" + parameter + "') ";\r
+\r
+\r
+ if (scorm.connection.isActive) {\r
+\r
+ var API = scorm.API.getHandle(),\r
+ errorCode = 0;\r
+\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = makeBoolean(API.LMSSetValue(parameter, value));\r
+ break;\r
+ case "2004":\r
+ success = makeBoolean(API.SetValue(parameter, value));\r
+ break;\r
+ }\r
+\r
+ if (success) {\r
+\r
+ if (parameter === "cmi.core.lesson_status" || parameter === "cmi.completion_status") {\r
+\r
+ scorm.data.completionStatus = value;\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ errorCode = debug.getCode();\r
+\r
+ trace(traceMsgPrefix + "failed. \nError code: " + errorCode + ". \nError info: " + debug.getInfo(errorCode));\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API is null.");\r
+\r
}\r
\r
- win = win.parent;\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + "failed: API connection is inactive.");\r
\r
}\r
- return win.API;\r
-}\r
-\r
-\r
-/*******************************************************************************\r
- **\r
- ** Function getAPI()\r
- ** Inputs: none\r
- ** Return: If an API object is found, it's returned, otherwise null is returned\r
- **\r
- ** Description:\r
- ** This function looks for an object named API, first in the current window's\r
- ** frame hierarchy and then, if necessary, in the current window's opener window\r
- ** hierarchy (if there is an opener window).\r
- **\r
- *******************************************************************************/\r
-function getAPI() {\r
- var theAPI = findAPI(window);\r
- if ((theAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined")) {\r
- theAPI = findAPI(window.opener);\r
+\r
+ trace(traceMsgPrefix + " value: " + value);\r
+\r
+ return success;\r
+\r
+};\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.data.save()\r
+ Instructs the LMS to persist all data to this point in the session\r
+\r
+ Parameters: None\r
+ Returns: Boolean\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.data.save = function () {\r
+\r
+ var success = false,\r
+ scorm = pipwerks.SCORM,\r
+ trace = pipwerks.UTILS.trace,\r
+ makeBoolean = pipwerks.UTILS.StringToBoolean,\r
+ traceMsgPrefix = "SCORM.data.save failed";\r
+\r
+\r
+ if (scorm.connection.isActive) {\r
+\r
+ var API = scorm.API.getHandle();\r
+\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ success = makeBoolean(API.LMSCommit(""));\r
+ break;\r
+ case "2004":\r
+ success = makeBoolean(API.Commit(""));\r
+ break;\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": API is null.");\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": API connection is inactive.");\r
+\r
}\r
- if (theAPI == null) {\r
- console.log("Unable to find an API adapter");\r
+\r
+ return success;\r
+\r
+};\r
+\r
+\r
+pipwerks.SCORM.status = function (action, status) {\r
+\r
+ var success = false,\r
+ scorm = pipwerks.SCORM,\r
+ trace = pipwerks.UTILS.trace,\r
+ traceMsgPrefix = "SCORM.getStatus failed",\r
+ cmi = "";\r
+\r
+ if (action !== null) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ cmi = "cmi.core.lesson_status";\r
+ break;\r
+ case "2004":\r
+ cmi = "cmi.completion_status";\r
+ break;\r
+ }\r
+\r
+ switch (action) {\r
+\r
+ case "get":\r
+ success = scorm.data.get(cmi);\r
+ break;\r
+\r
+ case "set":\r
+ if (status !== null) {\r
+\r
+ success = scorm.data.set(cmi, status);\r
+\r
+ } else {\r
+\r
+ success = false;\r
+ trace(traceMsgPrefix + ": status was not specified.");\r
+\r
+ }\r
+\r
+ break;\r
+\r
+ default :\r
+ success = false;\r
+ trace(traceMsgPrefix + ": no valid action was specified.");\r
+\r
+ }\r
+\r
+ } else {\r
+\r
+ trace(traceMsgPrefix + ": action was not specified.");\r
+\r
}\r
- return theAPI\r
-}\r
\r
+ return success;\r
+\r
+};\r
+\r
+\r
+// ------------------------------------------------------------------------- //\r
+// --- pipwerks.SCORM.debug functions -------------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
+\r
+\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.debug.getCode\r
+ Requests the error code for the current error state from the LMS\r
+\r
+ Parameters: None\r
+ Returns: Integer (the last error code).\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.debug.getCode = function () {\r
+\r
+ var scorm = pipwerks.SCORM,\r
+ API = scorm.API.getHandle(),\r
+ trace = pipwerks.UTILS.trace,\r
+ code = 0;\r
\r
-var startTime;\r
+ if (API) {\r
\r
-function startTimer() {\r
- startTime = new Date().getTime();\r
-}\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ code = parseInt(API.LMSGetLastError(), 10);\r
+ break;\r
+ case "2004":\r
+ code = parseInt(API.GetLastError(), 10);\r
+ break;\r
+ }\r
+\r
+ } else {\r
\r
+ trace("SCORM.debug.getCode failed: API is null.");\r
\r
-// call function right before LMSFinish\r
-function setSessionTime() {\r
+ }\r
\r
- var currentTime = new Date();\r
+ return code;\r
\r
- var endTime = currentTime.getTime()\r
+};\r
\r
- var calculatedTime = endTime - startTime;\r
\r
- var totalHours = Math.floor(calculatedTime / 1000 / 60 / 60);\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.debug.getInfo()\r
+ "Used by a SCO to request the textual description for the error code\r
+ specified by the value of [errorCode]."\r
\r
- calculatedTime = calculatedTime - totalHours * 1000 * 60 * 60\r
+ Parameters: errorCode (integer).\r
+ Returns: String.\r
+----------------------------------------------------------------------------- */\r
\r
- if (totalHours < 1000 && totalHours > 99) {\r
+pipwerks.SCORM.debug.getInfo = function (errorCode) {\r
\r
- totalHours = "0" + totalHours;\r
+ var scorm = pipwerks.SCORM,\r
+ API = scorm.API.getHandle(),\r
+ trace = pipwerks.UTILS.trace,\r
+ result = "";\r
\r
- } else if (totalHours < 100 && totalHours > 9) {\r
\r
- totalHours = "00" + totalHours;\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ result = API.LMSGetErrorString(errorCode.toString());\r
+ break;\r
+ case "2004":\r
+ result = API.GetErrorString(errorCode.toString());\r
+ break;\r
+ }\r
\r
- } else if (totalHours < 10) {\r
+ } else {\r
\r
- totalHours = "000" + totalHours;\r
+ trace("SCORM.debug.getInfo failed: API is null.");\r
\r
}\r
\r
+ return String(result);\r
\r
- var totalMinutes = Math.floor(calculatedTime / 1000 / 60);\r
+};\r
\r
- calculatedTime = calculatedTime - totalMinutes * 1000 * 60;\r
\r
- if (totalMinutes < 10) {\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.SCORM.debug.getDiagnosticInfo\r
+ "Exists for LMS specific use. It allows the LMS to define additional\r
+ diagnostic information through the API Instance."\r
\r
- totalMinutes = "0" + totalMinutes;\r
+ Parameters: errorCode (integer).\r
+ Returns: String (Additional diagnostic information about the given error code).\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.SCORM.debug.getDiagnosticInfo = function (errorCode) {\r
+\r
+ var scorm = pipwerks.SCORM,\r
+ API = scorm.API.getHandle(),\r
+ trace = pipwerks.UTILS.trace,\r
+ result = "";\r
+\r
+ if (API) {\r
+\r
+ switch (scorm.version) {\r
+ case "1.2" :\r
+ result = API.LMSGetDiagnostic(errorCode);\r
+ break;\r
+ case "2004":\r
+ result = API.GetDiagnostic(errorCode);\r
+ break;\r
+ }\r
+\r
+ } else {\r
+\r
+ trace("SCORM.debug.getDiagnosticInfo failed: API is null.");\r
\r
}\r
\r
+ return String(result);\r
+\r
+};\r
+\r
+\r
+// ------------------------------------------------------------------------- //\r
+// --- Shortcuts! ---------------------------------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
+\r
+// Because nobody likes typing verbose code.\r
+\r
+pipwerks.SCORM.init = pipwerks.SCORM.connection.initialize;\r
+pipwerks.SCORM.get = pipwerks.SCORM.data.get;\r
+pipwerks.SCORM.set = pipwerks.SCORM.data.set;\r
+pipwerks.SCORM.save = pipwerks.SCORM.data.save;\r
+pipwerks.SCORM.quit = pipwerks.SCORM.connection.terminate;\r
+\r
\r
- var totalSeconds = Math.floor(calculatedTime / 1000);\r
+// ------------------------------------------------------------------------- //\r
+// --- pipwerks.UTILS functions -------------------------------------------- //\r
+// ------------------------------------------------------------------------- //\r
\r
- if (totalSeconds < 10) {\r
\r
- totalSeconds = "0" + totalSeconds;\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.UTILS.StringToBoolean()\r
+ Converts 'boolean strings' into actual valid booleans.\r
\r
+ (Most values returned from the API are the strings "true" and "false".)\r
+\r
+ Parameters: String\r
+ Returns: Boolean\r
+---------------------------------------------------------------------------- */\r
+\r
+pipwerks.UTILS.StringToBoolean = function (value) {\r
+ var t = typeof value;\r
+ switch (t) {\r
+ //typeof new String("true") === "object", so handle objects as string via fall-through.\r
+ //See https://github.com/pipwerks/scorm-api-wrapper/issues/3\r
+ case "object":\r
+ case "string":\r
+ return (/(true|1)/i).test(value);\r
+ case "number":\r
+ return !!value;\r
+ case "boolean":\r
+ return value;\r
+ case "undefined":\r
+ return null;\r
+ default:\r
+ return false;\r
}\r
+};\r
\r
- var sessionTime = totalHours + ":" + totalMinutes + ":" + totalSeconds;\r
\r
- doLMSSetValue("cmi.core.session_time", sessionTime);\r
+/* -------------------------------------------------------------------------\r
+ pipwerks.UTILS.trace()\r
+ Displays error messages when in debug mode.\r
\r
-}\r
+ Parameters: msg (string)\r
+ Return: None\r
+---------------------------------------------------------------------------- */\r
\r
+pipwerks.UTILS.trace = function (msg) {\r
\r
+ if (pipwerks.debug.isActive) {\r
+\r
+ if (window.console && window.console.log) {\r
+ window.console.log(msg);\r
+ } else {\r
+ //alert(msg);\r
+ }\r
+\r
+ }\r
+};\r
SCORM = true;\r
+SCORM_INITED = false;\r
+SCORM_START_TIME = null;\r
+SCORM_INTERACTION_TIMESTAMPS = [];\r
+SCORM_CORRECT_ANSWERS = [];\r
+SCORM_ID_TO_N = {};\r
+SCORM_WEIGHTING = 0;\r
+SCORM_QUESTIONS = [];\r
+SCORM_SUCCESS_STATUS = 'unknown';\r
+SCORM_SUCCESS_SCORE = 0;\r
+\r
+var _CMI12 = {\r
+ 'location': 'cmi.core.lesson_location',\r
+ 'status': "cmi.core.lesson_status",\r
+ 'session_time': 'cmi.core.session_time',\r
+ 'success_status': '',\r
+};\r
+\r
+var _CMI2004 = {\r
+ 'location': 'cmi.location',\r
+ 'status': 'cmi.completion_status',\r
+ 'session_time': 'cmi.session_time',\r
+ 'success_status': 'cmi.success_status',\r
+}\r
+\r
$(function () {\r
if (fluidbook) {\r
- initScormEvents();\r
+ initScorm();\r
} else {\r
$(document).on('fluidbookready', function () {\r
- initScormEvents();\r
+ initScorm();\r
});\r
}\r
});\r
\r
+function initScorm() {\r
+ if (SCORM_INITED) {\r
+ return;\r
+ }\r
+ SCORM_INITED = true;\r
+ pipwerks.SCORM.init();\r
+ startScormTimer();\r
+ initScormEvents();\r
+ initScormInteractions();\r
+}\r
+\r
+function _cmi(key) {\r
+ var res = null;\r
+ switch (pipwerks.SCORM.version) {\r
+ case "1.2" :\r
+ res = _CMI12[key];\r
+ break;\r
+ case '2004':\r
+ res = _CMI2004[key];\r
+ break;\r
+ }\r
+ if (res == undefined || res == null) {\r
+ res = key;\r
+ }\r
+ return res;\r
+}\r
+\r
function initScormEvents() {\r
- doLMSInitialize();\r
+ initScorm();\r
+\r
$(window).on('unload', function () {\r
- doLMSFinish();\r
+ finishScorm();\r
});\r
\r
- var currentStatus = getScormValue('cmi.core.lesson_status');\r
- if (currentStatus != 'passed' && currentStatus != 'completed') {\r
- setScormValue('cmi.core.lesson_status', 'incomplete');\r
- }\r
-\r
- var currentPage = getScormValue('cmi.core.lesson_location');\r
+ var currentPage = getScormValue('location');\r
try {\r
if (currentPage != '') {\r
var e = currentPage.split('_');\r
}\r
}\r
} catch (err) {\r
- fb(err);\r
+ console.log(err);\r
}\r
\r
$(fluidbook).on('changePage', function (e, page) {\r
- setScormValue('cmi.core.lesson_location', 'page_' + page);\r
- doLMSCommit();\r
+ setScormValue('location', 'page_' + page);\r
});\r
}\r
\r
+function finishScorm() {\r
+ setSessionTime();\r
+ pipwerks.SCORM.quit();\r
+}\r
+\r
+function startScormTimer() {\r
+ SCORM_START_TIME = new Date();\r
+}\r
+\r
function scormComplete() {\r
- setScormValue("cmi.core.lesson_status", "completed");\r
+ setScormValue('status', "completed");\r
window.close();\r
- doLMSFinish();\r
+ finishScorm();\r
}\r
\r
function getScormValue(elementName) {\r
- var result = String(doLMSGetValue(elementName));\r
+ var cmi = _cmi(elementName);\r
+ if (cmi == '') {\r
+ return null;\r
+ }\r
+ var result = pipwerks.SCORM.get(cmi);\r
return result;\r
}\r
\r
function setScormValue(elementName, value) {\r
- var result = doLMSSetValue(elementName, value);\r
- doLMSCommit();\r
+ var cmi = _cmi(elementName);\r
+ if (cmi == '') {\r
+ return false;\r
+ }\r
+ var result = pipwerks.SCORM.set(cmi, value);\r
+ pipwerks.SCORM.save();\r
return result;\r
+}\r
+\r
+function initScormInteractions() {\r
+ if (fluidbook.datas.scorm_quizdata === '') {\r
+ return;\r
+ }\r
+ var n = 0;\r
+ for (var sheet in fluidbook.datas.scorm_quizdata) {\r
+ var s = fluidbook.datas.scorm_quizdata[sheet];\r
+ for (var l in s) {\r
+ if (l == 0) {\r
+ continue;\r
+ }\r
+ var line = s[l];\r
+ if (line[1] === null || line[1] === '') {\r
+ continue;\r
+ }\r
+ defineScormInteraction(n, line[0], line[2], line[1], line[3], parseFloat(line[4]));\r
+ n++;\r
+ }\r
+ }\r
+ if (SCORM_WEIGHTING > 0) {\r
+ pipwerks.SCORM.set('cmi.score.min', '0');\r
+ pipwerks.SCORM.set('cmi.score.max', SCORM_WEIGHTING);\r
+ SCORM_SUCCESS_SCORE = parseFloat(pipwerks.SCORM.get('cmi.scaled_passing_score'));\r
+\r
+ updateInteractionsScore();\r
+ }\r
+}\r
+\r
+function defineScormInteraction(number, id, type, question, correct_response, weighting) {\r
+\r
+ if (weighting === undefined) {\r
+ weighting = 1;\r
+ }\r
+ if (question === undefined) {\r
+ question = '';\r
+ }\r
+ if (type === undefined) {\r
+ type = 'other';\r
+ }\r
+ if (id === undefined) {\r
+ id = 'Q' + number;\r
+ }\r
+\r
+ SCORM_QUESTIONS[number] = {\r
+ id: id,\r
+ question: question,\r
+ type: type,\r
+ correct_response: correct_response,\r
+ weighting: weighting,\r
+ timestamp: null,\r
+ result: false\r
+ };\r
+ SCORM_ID_TO_N[id] = number;\r
+\r
+ setScormValue('cmi.interactions.' + number + '.id', id);\r
+ setScormValue('cmi.interactions.' + number + '.description', question);\r
+ setScormValue('cmi.interactions.' + number + '.type', type);\r
+ setScormValue('cmi.interactions.' + number + '.correct_responses.0.pattern', correct_response);\r
+ setScormValue('cmi.interactions.' + number + '.weighting', weighting);\r
+\r
+ SCORM_WEIGHTING += weighting;\r
+}\r
+\r
+function getLearnerResponseById(id) {\r
+ return getScormValue('cmi.interactions.' + scormIdToN(id) + '.learner_response');\r
+}\r
+\r
+function scormIdToN(id) {\r
+ return SCORM_ID_TO_N[id];\r
+}\r
+\r
+function displayScormInteraction(id) {\r
+ var n = scormIdToN(id);\r
+ var now = new Date();\r
+ if (SCORM_QUESTIONS[n].timestamp !== null) {\r
+ return;\r
+ }\r
+ SCORM_QUESTIONS[n].timestamp = now;\r
+ setScormValue('cmi.interactions.' + n + '.timestamp', dateToScormTime(now));\r
+}\r
+\r
+function answerScormInteraction(id, learner_response, result, set_latency) {\r
+ var n = scormIdToN(id);\r
+ var currentTime = Math.round(new Date().getTime() / 1000);\r
+ var latency = getScormTimeInterval(SCORM_QUESTIONS[n].timestamp, new Date());\r
+ if (result === undefined) {\r
+ result = (learner_response === SCORM_QUESTIONS[n].correct_response);\r
+ }\r
+\r
+ setScormValue('cmi.interactions.' + n + '.result', result ? 'correct' : 'incorrect');\r
+ setScormValue('cmi.interactions.' + n + '.learner_response', learner_response);\r
+ setScormValue('cmi.interactions.' + n + '.latency', latency);\r
+\r
+ updateInteractionsScore();\r
+}\r
+\r
+function updateInteractionsScore() {\r
+ var score = 0;\r
+ var answered = 0;\r
+ $.each(SCORM_QUESTIONS, function (n, q) {\r
+ var r = getScormValue('cmi.interactions.' + n + '.result');\r
+ if (r == '') {\r
+ return;\r
+ }\r
+ answered++;\r
+ if (r == 'correct') {\r
+ score += q.weighting;\r
+ }\r
+ });\r
+\r
+ var scaled = score / SCORM_WEIGHTING;\r
+\r
+ setScormValue('cmi.score.raw', score);\r
+ setScormValue('cmi.score.scaled', scaled);\r
+ var status = 'unknown'\r
+ if (answered > 0) {\r
+ if (scaled >= SCORM_SUCCESS_SCORE) {\r
+ status = 'passed';\r
+ } else {\r
+ status = 'failed';\r
+ }\r
+ }\r
+ setScormValue('cmi.success_status', status);\r
+}\r
+\r
+function setSessionTime() {\r
+ var currentTime = new Date();\r
+ var sessionTime;\r
+\r
+ var endTime = currentTime.getTime()\r
+ var calculatedTime = endTime - SCORM_START_TIME;\r
+\r
+ if (pipwerks.SCORM.version == '1.2') {\r
+ var totalHours = Math.floor(calculatedTime / 1000 / 60 / 60);\r
+ calculatedTime = calculatedTime - totalHours * 1000 * 60 * 60\r
+ if (totalHours < 1000 && totalHours > 99) {\r
+ totalHours = "0" + totalHours;\r
+ } else if (totalHours < 100 && totalHours > 9) {\r
+ totalHours = "00" + totalHours;\r
+ } else if (totalHours < 10) {\r
+ totalHours = "000" + totalHours;\r
+ }\r
+\r
+ var totalMinutes = Math.floor(calculatedTime / 1000 / 60);\r
+ calculatedTime = calculatedTime - totalMinutes * 1000 * 60;\r
+ if (totalMinutes < 10) {\r
+ totalMinutes = "0" + totalMinutes;\r
+ }\r
+\r
+ var totalSeconds = Math.floor(calculatedTime / 1000);\r
+ if (totalSeconds < 10) {\r
+ totalSeconds = "0" + totalSeconds;\r
+ }\r
+ sessionTime = totalHours + ":" + totalMinutes + ":" + totalSeconds;\r
+ } else {\r
+ sessionTime = Math.round(calculatedTime / 1000);\r
+ }\r
+ setScormValue('session_time', sessionTime);\r
+}\r
+\r
+function dateToScormTime(date) {\r
+ var res = date.toISOString();\r
+ var e = res.split('.');\r
+ e.pop();\r
+ return res.join('.');\r
+}\r
+\r
+function getScormTimeInterval(start, end) {\r
+ var diff = Math.round((end.getTime() - start.getTime()) / 1000);\r
+ var h = Math.floor(diff / 3600);\r
+ diff = diff % 3600;\r
+ var m = Math.floor(diff / 60);\r
+ var s = diff % 60;\r
+ return 'PT' + h + 'H' + m + 'M' + s + 'S';\r
}
\ No newline at end of file