diff --git a/README.md b/README.md index 8e61516a..9bcb6026 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,10 @@ The attributes listed below are used in *config.json* to configure **Spoor**, an >>**_commitOnVisibilityChangeHidden** (boolean): Determines whether or not a "commit" call should be made when the [visibilityState](https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState) of the course page changes to `"hidden"`. This functionality helps to ensure that tracking data is saved whenever the user switches to another tab or minimises the browser window - and is only available in [browsers that support the Page Visibility API](http://caniuse.com/#search=page%20visibility). The default is `true`. +>>**_exitStateIfIncomplete** (string): Determines the 'exit state' (`cmi.core.exit` in SCORM 1.2, `cmi.exit` in SCORM 2004) to set if the course hasn't been completed. The default behaviour will cause the exit state to be set to an empty string for SCORM 1.2 courses, or `"suspend"` for SCORM 2004 courses. The default behaviour should be left in place unless you are confident you know what you are doing! + +>>**_exitStateIfComplete** (string): Determines the 'exit state' (`cmi.core.exit` in SCORM 1.2, `cmi.exit` in SCORM 2004) to set when the course has been completed. The default behaviour will cause the exit state to be set to an empty string for SCORM 1.2 courses, or `"normal"` for SCORM 2004 courses. The default behaviour should be left in place unless you are confident you know what you are doing! Note: if you are using SCORM 2004, you can set this to `"suspend"` to prevent the LMS from clearing all progress tracking when a previously-completed course is re-launched by the learner. +
Back to Top
### Running a course without tracking while Spoor is installed diff --git a/example.json b/example.json index 0a72ca09..d1bc5340 100644 --- a/example.json +++ b/example.json @@ -22,7 +22,9 @@ "_timedCommitFrequency": 10, "_maxCommitRetries": 5, "_commitRetryDelay": 2000, - "_commitOnVisibilityChangeHidden": true + "_commitOnVisibilityChangeHidden": true, + "_exitStateIfIncomplete": "auto", + "_exitStateIfComplete": "auto" } } } diff --git a/js/adapt-contrib-spoor.js b/js/adapt-contrib-spoor.js index cb4ec465..0f8b299b 100644 --- a/js/adapt-contrib-spoor.js +++ b/js/adapt-contrib-spoor.js @@ -59,25 +59,33 @@ define([ scorm.setVersion(settings._scormVersion || "1.2"); - if(settings.hasOwnProperty("_suppressErrors")) { + if(settings._suppressErrors) { scorm.suppressErrors = settings._suppressErrors; } - if(settings.hasOwnProperty("_commitOnStatusChange")) { + if(settings._commitOnStatusChange) { scorm.commitOnStatusChange = settings._commitOnStatusChange; } - if(settings.hasOwnProperty("_timedCommitFrequency")) { + if(_.isFinite(settings._timedCommitFrequency)) { scorm.timedCommitFrequency = settings._timedCommitFrequency; } - if(settings.hasOwnProperty("_maxCommitRetries")) { + if(_.isFinite(settings._maxCommitRetries)) { scorm.maxCommitRetries = settings._maxCommitRetries; } - if(settings.hasOwnProperty("_commitRetryDelay")) { + if(_.isFinite(settings._commitRetryDelay)) { scorm.commitRetryDelay = settings._commitRetryDelay; } + + if ("_exitStateIfIncomplete" in settings) { + scorm.exitStateIfIncomplete = settings._exitStateIfIncomplete; + } + + if ("_exitStateIfComplete" in settings) { + scorm.exitStateIfComplete = settings._exitStateIfComplete; + } } else { /** * force use of SCORM 1.2 by default - some LMSes (SABA/Kallidus for instance) present both APIs to the SCO and, if given the choice, diff --git a/js/scorm/wrapper.js b/js/scorm/wrapper.js index dd15fbc3..702e9e7e 100644 --- a/js/scorm/wrapper.js +++ b/js/scorm/wrapper.js @@ -38,6 +38,15 @@ define ([ * not currently used - but you could include in an error message to show when data was last saved */ this.lastCommitSuccessTime = null; + /** + * The exit state to use when course isn't completed yet + */ + this.exitStateIfIncomplete = "auto"; + /** + * The exit state to use when the course has been completed/passed + */ + this.exitStateIfComplete = "auto"; + this.timedCommitIntervalID = null; this.retryCommitTimeoutID = null; @@ -50,6 +59,10 @@ define ([ this.logger = Logger.getInstance(); this.scorm = pipwerks.SCORM; + /** + * Prevent the Pipwerks SCORM API wrapper's handling of the exit status + */ + this.scorm.handleExitMode = false; this.suppressErrors = false; @@ -80,13 +93,6 @@ define ([ ScormWrapper.prototype.setVersion = function(value) { this.logger.debug("ScormWrapper::setVersion: " + value); this.scorm.version = value; - /** - * stop the pipwerks code from setting cmi.core.exit to suspend/logout when targeting SCORM 1.2. - * there doesn't seem to be any tangible benefit to doing this in 1.2 and it can actually cause problems with some LMSes - * (e.g. setting it to 'logout' apparently causes Plateau to log the user completely out of the LMS!) - * It needs to be on for SCORM 2004 though, otherwise the LMS might not restore the suspend_data - */ - this.scorm.handleExitMode = this.isSCORM2004(); }; ScormWrapper.prototype.initialize = function() { @@ -303,10 +309,10 @@ define ([ if (this.isSCORM2004()) { this.scorm.set("cmi.session_time", this.convertToSCORM2004Time(this.endTime.getTime() - this.startTime.getTime())); - } - else { + this.scorm.set("cmi.exit", this.getExitState()); + } else { this.scorm.set("cmi.core.session_time", this.convertToSCORM12Time(this.endTime.getTime() - this.startTime.getTime())); - this.scorm.set("cmi.core.exit", ""); + this.scorm.set("cmi.core.exit", this.getExitState()); } // api no longer available from this point @@ -716,5 +722,17 @@ define ([ return response.join(','); }; + ScormWrapper.prototype.getExitState = function() { + var completionStatus = this.scorm.data.completionStatus; + var isIncomplete = completionStatus === 'incomplete' || completionStatus === 'not attempted'; + var exitState = isIncomplete ? this.exitStateIfIncomplete : this.exitStateIfComplete; + + if (exitState !== 'auto') return exitState; + + if (this.isSCORM2004()) return (isIncomplete ? 'suspend' : 'normal'); + + return ''; + }; + return ScormWrapper; }); diff --git a/properties.schema b/properties.schema index 5c79c0ca..4c0f181f 100644 --- a/properties.schema +++ b/properties.schema @@ -176,6 +176,48 @@ "inputType": "Text", "validators": ["required"], "help": "Sets the 'identifier' attribute in the imsmanifest.xml" + }, + "_exitStateIfIncomplete": { + "type": "string", + "required": false, + "default": "auto", + "title": "Exit state if incomplete", + "enum": ["auto", "suspend", "normal", ""], + "inputType": { + "type": "Select", + "options": [ + "auto", + "suspend", + "normal", + { + "val": "", + "label": "'' (empty string)" + } + ] + }, + "validators": [], + "help": "What exit status to use if the course is incomplete." + }, + "_exitStateIfComplete": { + "type": "string", + "required": false, + "default": "auto", + "title": "Exit state if complete", + "enum": ["auto", "suspend", "normal", ""], + "inputType": { + "type": "Select", + "options": [ + "auto", + "suspend", + "normal", + { + "val": "", + "label": "'' (empty string)" + } + ] + }, + "validators": [], + "help": "What exit status to use if the course is complete." } } }