diff --git a/js/scormWrapper.js b/js/scormWrapper.js index 67f49821..80a8b627 100644 --- a/js/scormWrapper.js +++ b/js/scormWrapper.js @@ -1,969 +1,832 @@ -define (function(require) { - - /* - IMPORTANT: This wrapper uses the Pipwerks SCORM wrapper and should therefore support both SCORM 1.2 and 2004. Ensure any changes support both versions. - */ - - var ScormWrapper = function() - { - /* configuration */ - this.setCompletedWhenFailed = true;// this only applies to SCORM 2004 - /** - * whether to commit each time there's a change to lesson_status or not - */ - this.commitOnStatusChange = true; - /** - * allows you to deactivate tracking to cmi.interactions - useful in instances where the code to detect when cmi.interactions are - * are supported isn't working due to inconsistent LMS behaviour - */ - this.disableInteractionTracking = false; - /** - * how frequently (in minutes) to commit automatically. set to 0 to disable. - */ - this.timedCommitFrequency = 10; - /** - * how many times to retry if a commit fails - */ - this.maxCommitRetries = 5; - /** - * time (in milliseconds) to wait between retries - */ - this.commitRetryDelay = 1000; - - /** - * prevents commit from being called if there's already a 'commit retry' pending. - */ - this.commitRetryPending = false; - /** - * how many times we've done a 'commit retry' - */ - this.commitRetries = 0; - /** - * not currently used - but you could include in an error message to show when data was last saved - */ - this.lastCommitSuccessTime = null; - - this.timedCommitIntervalID = null; - this.retryCommitTimeoutID = null; - this.logOutputWin = null; - this.startTime = null; - this.endTime = null; - - this.lmsConnected = false; - this.finishCalled = false; - - this.logger = Logger.getInstance(); - this.scorm = pipwerks.SCORM; - - this.registeredViews = []; - - if (window.__debug) - this.showDebugWindow(); - - /** - * and also stop it from setting cmi.core.exit to suspend/logout. there doesn't seem to be any tangible benefit to doing this... - * it can actually cause problems with some LMSes (e.g. setting 'logout' apparently causes Plateau to log out completely!) - * you can always switch it back on for an individual course if you think it's necessary. - */ - this.scorm.handleExitMode = false; - }; - - // static - ScormWrapper.instance = null; - - /******************************* public methods *******************************/ - - // static -ScormWrapper.getInstance = function() { - if (ScormWrapper.instance === null) - ScormWrapper.instance = new ScormWrapper(); - return ScormWrapper.instance; - }; - -ScormWrapper.prototype.getVersion = function() { - return this.scorm.version; - }; - -ScormWrapper.prototype.setVersion = function(value) { - this.scorm.version = value; - }; - -ScormWrapper.prototype.registerView = function(_view) { - this.registeredViews[this.registeredViews.length] = _view; - }; - -ScormWrapper.prototype.updateViews = function() { - for (var i = 0; i < this.registeredViews.length; i++) { - this.registeredViews[i].update(this); - } - }; - -ScormWrapper.prototype.initialize = function() { - this.lmsConnected = this.scorm.init(); - - if (this.lmsConnected) { - this.startTime = new Date(); - - this.initTimedCommit(); - } - else { - this.handleError("Course could not connect to the LMS"); - } - - return this.lmsConnected; - }; - - /** - * allows you to check if this is the user's first ever 'session' of a SCO, even after the lesson_status has been set to 'incomplete' - */ - ScormWrapper.prototype.isFirstSession = function() { - return (this.getValue("cmi.core.entry") === "ab-initio"); - } - -ScormWrapper.prototype.setIncomplete = function() { - this.setValue(this.isSCORM2004() ? "cmi.completion_status" : "cmi.core.lesson_status", "incomplete"); - - if(this.commitOnStatusChange) this.commit(); - }; - -ScormWrapper.prototype.setCompleted = function() { - this.setValue(this.isSCORM2004() ? "cmi.completion_status" : "cmi.core.lesson_status", "completed"); - - if(this.commitOnStatusChange) this.commit(); - }; - -ScormWrapper.prototype.setPassed = function() { - if (this.isSCORM2004()) { - this.setValue("cmi.completion_status", "completed"); - this.setValue("cmi.success_status", "passed"); - } - else { - this.setValue("cmi.core.lesson_status", "passed"); - } - - if(this.commitOnStatusChange) this.commit(); - }; - -ScormWrapper.prototype.setFailed = function() { - if (this.isSCORM2004()) { - this.setValue("cmi.success_status", "failed"); - - if(this.setCompletedWhenFailed) - this.setValue("cmi.completion_status", "completed"); - } - else { - this.setValue("cmi.core.lesson_status", "failed"); - } - - if(this.commitOnStatusChange) this.commit(); - }; - -ScormWrapper.prototype.getStatus = function() { - var status = this.getValue(this.isSCORM2004() ? "cmi.completion_status" : "cmi.core.lesson_status"); - - switch(status.toLowerCase()) {// workaround for some LMSes (e.g. Arena) not adhering to the all-lowercase rule - case "passed": - case "completed": - case "incomplete": - case "failed": - case "browsed": - case "not attempted": - case "not_attempted":// mentioned in SCORM 2004 docs but not sure it ever gets used - case "unknown": //the SCORM 2004 version if not attempted - return status; - break; - default: - this.handleError("ScormWrapper::getStatus: invalid lesson status '" + status + "' received from LMS"); - return null; - } - }; - -ScormWrapper.prototype.getScore = function() { - return this.getValue(this.isSCORM2004() ? "cmi.score.raw" : "cmi.core.score.raw"); - }; - -ScormWrapper.prototype.setScore = function(_score, _minScore, _maxScore) { - if (this.isSCORM2004()) { - this.setValue("cmi.score.raw", _score) && this.setValue("cmi.score.min", _minScore) && this.setValue("cmi.score.max", _maxScore) && this.setValue("cmi.score.scaled", _score / 100); - } - else { - this.setValue("cmi.core.score.raw", _score); - - if(this.isSupported("cmi.core.score.min")) this.setValue("cmi.core.score.min", _minScore); - - if(this.isSupported("cmi.core.score.max")) this.setValue("cmi.core.score.max", _maxScore); - } - }; - -ScormWrapper.prototype.getLessonLocation = function() { - return this.getValue(this.isSCORM2004() ? "cmi.location" : "cmi.core.lesson_location"); - }; - -ScormWrapper.prototype.setLessonLocation = function(_location) { - this.setValue(this.isSCORM2004() ? "cmi.location" : "cmi.core.lesson_location", _location); - }; - -ScormWrapper.prototype.getSuspendData = function() { - return this.getValue("cmi.suspend_data"); - }; - -ScormWrapper.prototype.setSuspendData = function(_data) { - this.setValue("cmi.suspend_data", _data); - }; - -ScormWrapper.prototype.getStudentName = function() { - return this.getValue(this.isSCORM2004() ? "cmi.learner_name" : "cmi.core.student_name"); - }; - -ScormWrapper.prototype.commit = function() { - this.logger.debug("ScormWrapper::commit"); - - if (this.lmsConnected) { - if (this.commitRetryPending) { - this.logger.debug("ScormWrapper::commit: skipping this commit call as one is already pending."); - } - else { - if (this.scorm.save()) { - this.commitRetries = 0; - this.lastCommitSuccessTime = new Date(); - } - else { - if (this.commitRetries <= this.maxCommitRetries && !this.finishCalled) { - this.commitRetries++; - this.initRetryCommit(); - } - else { - var _errorCode = this.scorm.debug.getCode(); - - var _errorMsg = "Course could not commit data to the LMS"; - _errorMsg += "\nError " + _errorCode + ": " + this.scorm.debug.getInfo(_errorCode); - _errorMsg += "\nLMS Error Info: " + this.scorm.debug.getDiagnosticInfo(_errorCode); - - this.handleError(_errorMsg); - } - } - } - } - else { - this.handleError("Course is not connected to the LMS"); - } - }; - -ScormWrapper.prototype.finish = function() { - this.logger.debug("ScormWrapper::finish"); - - if (this.lmsConnected && !this.finishCalled) { - this.finishCalled = true; - - if(this.timedCommitIntervalID != null) { - window.clearInterval(this.timedCommitIntervalID); - } - - if(this.commitRetryPending) { - window.clearTimeout(this.retryCommitTimeoutID); - this.commitRetryPending = false; - } - - if (this.logOutputWin && !this.logOutputWin.closed) { - this.logOutputWin.close(); - } - - this.endTime = new Date(); - - if (this.isSCORM2004()) { - this.scorm.set("cmi.session_time", this.convertMilliSecondsToSCORM2004Time(this.endTime.getTime() - this.startTime.getTime())); - this.scorm.set("cmi.exit", "normal"); - } - else { - this.scorm.set("cmi.core.session_time", this.convertMilliSecondsToSCORMTime(this.endTime.getTime() - this.startTime.getTime())); - this.scorm.set("cmi.core.exit", ""); - } - - // api no longer available from this point - this.lmsConnected = false; - - if (!this.scorm.quit()) { - this.handleError("Course could not finish"); - } - } - else { - this.handleError("Course is not connected to the LMS"); - } - }; - -ScormWrapper.prototype.recordInteraction = function(strID, strResponse, strCorrect, strLatency, scormInteractionType) { - /* - * because some LMSes (e.g. Learning Core, Syntrio) - report cmi.interactions._count as being supported even though - * no other cmi.interactions data fields are... so this flag gives us an option to switch off tracking to - * cmi.interactions altogether in these rare instances. - */ - if(this.disableInteractionTracking === true) { - return; - } - - if(this.isSupported("cmi.interactions._count")) { - if (scormInteractionType === "choice") { - var responseIdentifiers = new Array(); - var answers = strResponse.split("#"); - - for (var i = 0; i < answers.length; i++) { - responseIdentifiers.push(new ResponseIdentifier(answers[i], answers[i])); - } - - this.recordMultipleChoiceInteraction(strID, responseIdentifiers, strCorrect, null, null, null, strLatency, null); - } - else if (scormInteractionType === "matching") { - var matchingResponses = new Array(); - var sourceTargetPairs = strResponse.split("#"); - var sourceTarget = null; - - for (var i = 0; i < sourceTargetPairs.length; i++) { - sourceTarget = sourceTargetPairs[i].split("."); - matchingResponses.push(new MatchingResponse(sourceTarget[0], sourceTarget[1])); - } - - this.recordMatchingInteraction(strID, matchingResponses, strCorrect, null, null, null, strLatency, null); - } - } - else { - this.logger.info("ScormWrapper::recordInteraction: cmi.interactions are not supported by this LMS..."); - } - } - - /****************************** private methods ******************************/ -ScormWrapper.prototype.getValue = function(_property) { - this.logger.debug("ScormWrapper::getValue: _property=" + _property); - - if(this.finishCalled) { - this.logger.debug("ScormWrapper::getValue: ignoring request as 'finish' has been called"); - return; - } - - if (this.lmsConnected) { - var _value = this.scorm.get(_property); - var _errorCode = this.scorm.debug.getCode(); - var _errorMsg = ""; - - if (_errorCode !== 0) { - if (_errorCode === 403) { - this.logger.warn("ScormWrapper::getValue: data model element not initialized"); - } - else { - _errorMsg += "Course could not get " + _property; - _errorMsg += "\nError Info: " + this.scorm.debug.getInfo(_errorCode); - _errorMsg += "\nLMS Error Info: " + this.scorm.debug.getDiagnosticInfo(_errorCode); - - this.handleError(_errorMsg); - } - } - this.logger.debug("ScormWrapper::getValue: returning " + _value); - return _value + ""; - } - else { - this.handleError("Course is not connected to the LMS"); - } - }; - -ScormWrapper.prototype.setValue = function(_property, _value) { - this.logger.debug("ScormWrapper::setValue: _property=" + _property + " _value=" + _value); - - if(this.finishCalled) { - this.logger.debug("ScormWrapper::setValue: ignoring request as 'finish' has been called"); - return; - } - - if (this.lmsConnected) { - var _success = this.scorm.set(_property, _value); - var _errorCode = this.scorm.debug.getCode(); - var _errorMsg = ""; - - if (!_success) { - /* - * Some LMSes have an annoying tendency to return false from a set call even when it actually worked fine. - * So, we should throw an error _only_ if there was a valid error code... - */ - if(_errorCode !== 0) { - _errorMsg += "Course could not set " + _property + " to " + _value; - _errorMsg += "\nError Info: " + this.scorm.debug.getInfo(_errorCode); - _errorMsg += "\nLMS Error Info: " + this.scorm.debug.getDiagnosticInfo(_errorCode); - - this.handleError(_errorMsg); - } - else { - this.logger.warn("ScormWrapper::setValue: LMS reported that the 'set' call failed but then said there was no error!"); - } - } - - return _success; - } - else { - this.handleError("Course is not connected to the LMS"); - } - }; - - /** - * used for checking any data field that is not 'LMS Mandatory' to see whether - * the LMS we're running on supports it or not. - * Note that the way this check is being performed means it wouldn't work for any element that is - * 'write only', but so far we've not had a requirement to check for any optional elements that are. - */ -ScormWrapper.prototype.isSupported = function(_property) { - this.logger.debug("ScormWrapper::isSupported: _property=" + _property); - - if(this.finishCalled) { - this.logger.debug("ScormWrapper::isSupported: ignoring request as 'finish' has been called"); - return; - } - - if (this.lmsConnected) { - var _value = this.scorm.get(_property); - var _errorCode = this.scorm.debug.getCode(); - - return (_errorCode === 401 ? false : true); - } - else { - this.handleError("Course is not connected to the LMS"); - return false; - } - }; - -ScormWrapper.prototype.initTimedCommit = function() { - this.logger.debug("ScormWrapper::initTimedCommit"); - - if(this.timedCommitFrequency > 0) { - var delay = this.timedCommitFrequency * (60 * 1000); - this.timedCommitIntervalID = window.setInterval(delegate(this, this.commit), delay); - } - }; - -ScormWrapper.prototype.initRetryCommit = function() { - this.logger.debug("ScormWrapper::initRetryCommit"); - - this.commitRetryPending = true;// stop anything else from calling commit until this is done - - this.retryCommitTimeoutID = window.setTimeout(delegate(this, this.doRetryCommit), this.commitRetryDelay); - }; - -ScormWrapper.prototype.doRetryCommit = function() { - this.logger.debug("ScormWrapper::doRetryCommit"); - - this.commitRetryPending = false; - - this.commit(); - }; - -ScormWrapper.prototype.handleError = function(_msg) { - this.logger.error(_msg); - - if ((!this.logOutputWin || this.logOutputWin.closed) && confirm("An error has occured:\n\n" + _msg + "\n\nPress 'OK' to view debug information to send to technical support.")) - this.showDebugWindow(); - }; - - ScormWrapper.prototype.createValidIdentifier = function(str) - { - str = this.trim(new String(str)); - - if (_.indexOf(str.toLowerCase(), "urn:") === 0) { - str = str.substr(4); - } - - // URNs may only contain the following characters: letters, numbers - ( ) + . : = @ ; $ _ ! * ' % - // if anything else is found, replace it with _ - str = str.replace(/[^\w\-\(\)\+\.\:\=\@\;\$\_\!\*\'\%]/g, "_"); - - return str; - }; - -ScormWrapper.prototype.createResponseIdentifier = function(strShort, strLong) { - - if (strShort.length != 1 || strShort.search(/\w/) < 0) { - strShort = ""; - } - else { - strShort = strShort.toLowerCase(); - } - - strLong = this.createValidIdentifier(strLong); - - return new ResponseIdentifier(strShort, strLong); - }; - -ScormWrapper.prototype.recordInteraction12 = function(strID, strResponse, bCorrect, strCorrectResponse, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, scormInteractionType, strAlternateResponse, strAlternateCorrectResponse) { - var bResult; - var bTempResult; - var interactionIndex; - var strResult; - - // in SCORM 1.2, add a new interaction rather than updating an old one, because some LMS vendors have misinterpreted the "write only" rule regarding interactions to mean "write once" - interactionIndex = this.getValue("cmi.interactions._count"); - - if (interactionIndex === "") { - interactionIndex = 0; - } - - if (bCorrect === true || bCorrect === "true" || bCorrect === "correct") { - strResult = "correct"; - } - else if (bCorrect === false || bCorrect === "false" || bCorrect === "wrong") { - strResult = "wrong"; - } - else if (bCorrect === "unanticipated") { - strResult = "unanticipated"; - } - else if (bCorrect === "neutral") { - strResult = "neutral"; - } - - bResult = this.setValue("cmi.interactions." + interactionIndex + ".id", strID); - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".type", scormInteractionType); - - bTempResult = this.setValue("cmi.interactions." + interactionIndex + ".student_response", strResponse); - - if (bTempResult === false) { - bTempResult = this.setValue("cmi.interactions." + interactionIndex + ".student_response", strAlternateResponse); - } - - bResult = bResult && bTempResult; - - if (strCorrectResponse !== undefined && strCorrectResponse !== null && strCorrectResponse !== "") { - bTempResult = this.setValue("cmi.interactions." + interactionIndex + ".correct_responses.0.pattern", strCorrectResponse); - if (bTempResult === false) { - bTempResult = this.setValue("cmi.interactions." + interactionIndex + ".correct_responses.0.pattern", strAlternateCorrectResponse); - } - - bResult = bResult && bTempResult; - } - - if (strResult !== undefined && strResult !== null && strResult !== "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".result", strResult); - } - - // ignore the description parameter in SCORM 1.2, there is nothing we can do with it - - if (intWeighting !== undefined && intWeighting !== null && intWeighting !== "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".weighting", intWeighting); - } - - if (intLatency !== undefined && intLatency !== null && intLatency !== "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".latency", this.convertMilliSecondsToSCORMTime(intLatency)); - } - - if (strLearningObjectiveID !== undefined && strLearningObjectiveID !== null && strLearningObjectiveID !== "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".objectives.0.id", strLearningObjectiveID); - } - - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".time", this.convertDateToCMITime(dtmTime)); - - return bResult; - }; - -ScormWrapper.prototype.recordInteraction2004 = function(strID, strResponse, bCorrect, strCorrectResponse, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, scormInteractionType) { - var bResult; - var interactionIndex; - var strResult; - - bCorrect = new String(bCorrect); - - interactionIndex = this.getValue("cmi.interactions._count"); - - if (interactionIndex === "") { - interactionIndex = 0; - } - - if (bCorrect === true || bCorrect === "true" || bCorrect === "correct") { - strResult = "correct"; - } - else if (bCorrect === false || bCorrect == "false" || bCorrect === "wrong") { - strResult = "incorrect"; - } - else if (bCorrect === "unanticipated") { - strResult = "unanticipated"; - } - else if (bCorrect === "neutral") { - strResult = "neutral"; - } - else { - strResult = ""; - } - - strID = this.createValidIdentifier(strID); - - bResult = this.setValue("cmi.interactions." + interactionIndex + ".id", strID); - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".type", scormInteractionType); - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".learner_response", strResponse); - - if (strResult != undefined && strResult != null && strResult != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".result", strResult); - } - - if (strCorrectResponse != undefined && strCorrectResponse != null && strCorrectResponse != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".correct_responses.0.pattern", strCorrectResponse); - } - - if (strDescription != undefined && strDescription != null && strDescription != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".description", strDescription); - } - - // ignore the description parameter in SCORM 1.2, there is nothing we can do with it - - if (intWeighting != undefined && intWeighting != null && intWeighting != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".weighting", intWeighting); - } - - if (intLatency != undefined && intLatency != null && intLatency != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".latency", this.convertMilliSecondsToSCORM2004Time(intLatency)); - } - - if (strLearningObjectiveID != undefined && strLearningObjectiveID != null && strLearningObjectiveID != "") { - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".objectives.0.id", strLearningObjectiveID); - } - - bResult = bResult && this.setValue("cmi.interactions." + interactionIndex + ".timestamp", this.convertDateToISO8601Timestamp(dtmTime)); - - return bResult; - }; - -ScormWrapper.prototype.recordMultipleChoiceInteraction = function(strID, response, blnCorrect, correctResponse, strDescription, intWeighting, intLatency, strLearningObjectiveID) { - var _responseArray = null; - var _correctResponseArray = null; - - if (response.constructor == String) { - _responseArray = new Array(this.createResponseIdentifier(response, response)); - } - else if (response.constructor == ResponseIdentifier) { - _responseArray = new Array(response); - } - else if (response.constructor == Array || response.constructor.toString().search("Array") > 0) { - _responseArray = response; - } - else if (window.console && response.constructor.toString() == "(Internal Function)" && response.length > 0) { - _responseArray = response; - } - else { - this.handleError("ScormWrapper::recordMultipleChoiceInteraction: response is not in the correct format"); - return false; - } - - if (correctResponse != null && correctResponse != undefined && correctResponse != "") { - if (correctResponse.constructor == String) { - _correctResponseArray = new Array(this.createResponseIdentifier(correctResponse, correctResponse)); - } - else if (correctResponse.constructor == ResponseIdentifier) { - _correctResponseArray = new Array(correctResponse); - } - else if (correctResponse.constructor == Array || correctResponse.constructor.toString().search("Array") > 0) { - _correctResponseArray = correctResponse; - } - else if (window.console && correctResponse.constructor.toString() == "(Internal Function)" && correctResponse.length > 0) { - _correctResponseArray = correctResponse; - } - else { - this.handleError("ScormWrapper::recordMultipleChoiceInteraction: correct response is not in the correct format"); - return false; - } - } - else { - _correctResponseArray = new Array(); - } - - var dtmTime = new Date(); - - var strResponse = ""; - var strResponseLong = ""; - - var strCorrectResponse = ""; - var strCorrectResponseLong = ""; - - for (var i = 0; i < _responseArray.length; i++) { - if (strResponse.length > 0) {strResponse += this.isSCORM2004() ? "[,]" : ",";} - if (strResponseLong.length > 0) {strResponseLong += ",";} - - strResponse += this.isSCORM2004() ? _responseArray[i].Long : _responseArray[i].Short; - strResponseLong += _responseArray[i].Long; - } - - for (var i = 0; i < _correctResponseArray.length; i++) { - if (strCorrectResponse.length > 0) {strCorrectResponse += this.isSCORM2004() ? "[,]" : ",";} - if (strCorrectResponseLong.length > 0) {strCorrectResponseLong += ",";} - - strCorrectResponse += this.isSCORM2004() ? _correctResponseArray[i].Long : _correctResponseArray[i].Short; - strCorrectResponseLong += _correctResponseArray[i].Long; - } - - if (this.isSCORM2004()) - return this.recordInteraction2004(strID, strResponse, blnCorrect, strCorrectResponse, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, "choice"); - - return this.recordInteraction12(strID, strResponseLong, blnCorrect, strCorrectResponseLong, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, "choice", strResponse, strCorrectResponse); - }; - -ScormWrapper.prototype.recordMatchingInteraction = function(strID, response, blnCorrect, correctResponse, strDescription, intWeighting, intLatency, strLearningObjectiveID) { - var _responseArray = null; - var _correctResponseArray = null; - - if (response.constructor == MatchingResponse) { - _responseArray = new Array(response); - } - else if (response.constructor == Array || response.constructor.toString().search("Array") > 0) { - _responseArray = response; - } - else if (window.console && response.constructor.toString() == "(Internal Function)" && response.length > 0) { - _responseArray = response; - } - else { - this.handleError("ScormWrapper::recordMatchingInteraction: response is not in the correct format"); - return false; - } - - if (correctResponse != null && correctResponse != undefined) { - if (correctResponse.constructor == MatchingResponse) { - _correctResponseArray = new Array(correctResponse); - } - else if (correctResponse.constructor == Array || correctResponse.constructor.toString().search("Array") > 0) { - _correctResponseArray = correctResponse; - } - else if (window.console && correctResponse.constructor.toString() == "(Internal Function)" && correctResponse.length > 0) { - _correctResponseArray = correctResponse; - } - else { - this.handleError("ScormWrapper::recordMatchingInteraction: correct response is not in the correct format"); - return false; - } - } - else { - _correctResponseArray = new Array(); - } - - var dtmTime = new Date(); - - var strResponse = ""; - var strResponseLong = ""; - - var strCorrectResponse = ""; - var strCorrectResponseLong = ""; - - for (var i = 0; i < _responseArray.length; i++) { - if (strResponse.length > 0) {strResponse += ",";} - if (strResponseLong.length > 0) {strResponseLong += this.isSCORM2004() ? "[,]" : ",";} - - strResponse += _responseArray[i].Source.Short + "." + _responseArray[i].Target.Short; - strResponseLong += _responseArray[i].Source.Long + (this.isSCORM2004() ? "[.]" : ".") + _responseArray[i].Target.Long; - } - - for (var i = 0; i < _correctResponseArray.length; i++) { - if (strCorrectResponse.length > 0) {strCorrectResponse += ",";} - if (strCorrectResponseLong.length > 0) {strCorrectResponseLong += this.isSCORM2004() ? "[,]" : ",";} - - strCorrectResponse += _correctResponseArray[i].Source.Short + "." + _correctResponseArray[i].Target.Short; - strCorrectResponseLong += _correctResponseArray[i].Source.Long + (this.isSCORM2004() ? "[.]" : ".") + _correctResponseArray[i].Target.Long; - } - - if (this.isSCORM2004()) - return this.recordInteraction2004(strID, strResponseLong, blnCorrect, strCorrectResponseLong, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, "matching"); - - return this.recordInteraction12(strID, strResponseLong, blnCorrect, strCorrectResponseLong, strDescription, intWeighting, intLatency, strLearningObjectiveID, dtmTime, "matching", strResponse, strCorrectResponse); - }; - -ScormWrapper.prototype.showDebugWindow = function() { - - if (this.logOutputWin && !this.logOutputWin.closed) { - this.logOutputWin.close(); - } - - this.logOutputWin = window.open("log_output.html", "Log", "width=600,height=300,status=no,scrollbars=yes,resize=yes,menubar=yes,toolbar=yes,location=yes,top=0,left=0"); - - if (this.logOutputWin) - this.logOutputWin.focus(); - - return; - }; - -ScormWrapper.prototype.convertMilliSecondsToSCORMTime = function(value) { - var h; - var m; - var s; - var ms; - var cs; - var CMITimeSpan; - - ms = value % 1000; - - s = ((value - ms) / 1000) % 60; - - m = ((value - ms - (s * 1000)) / 60000) % 60; - - h = (value - ms - (s * 1000) - (m * 60000)) / 3600000; - - if (h === 10000) { - h = 9999; - - m = (value - (h * 3600000)) / 60000; - if (m === 100) { - m = 99; - } - m = Math.floor(m); - - s = (value - (h * 3600000) - (m * 60000)) / 1000; - if (s === 100) { - s = 99; - } - s = Math.floor(s); - - ms = (value - (h * 3600000) - (m * 60000) - (s * 1000)); - } - - cs = Math.floor(ms / 10); - - CMITimeSpan = this.zeroPad(h, 4) + ":" + this.zeroPad(m, 2) + ":" + this.zeroPad(s, 2); - CMITimeSpan += "." + cs; - - if (h > 9999) { - CMITimeSpan = "9999:99:99"; - - CMITimeSpan += ".99"; - } - - return CMITimeSpan; - }; - -ScormWrapper.prototype.convertDateToCMITime = function(_value) { - var h; - var m; - var s; - - dtmDate = new Date(_value); - - h = dtmDate.getHours(); - m = dtmDate.getMinutes(); - s = dtmDate.getSeconds(); - - return this.zeroPad(h, 2) + ":" + this.zeroPad(m, 2) + ":" + this.zeroPad(s, 2); - }; - -ScormWrapper.prototype.convertMilliSecondsToSCORM2004Time = function(_value) { - var str = ""; - var cs; - var s; - var m; - var h; - var d; - var mo; // assumed to be an "average" month (a leap year every 4 years) = ((365*4) + 1) / 48 = 30.4375 days per month - var y; - - var HUNDREDTHS_PER_SECOND = 100; - var HUNDREDTHS_PER_MINUTE = HUNDREDTHS_PER_SECOND * 60; - var HUNDREDTHS_PER_HOUR = HUNDREDTHS_PER_MINUTE * 60; - var HUNDREDTHS_PER_DAY = HUNDREDTHS_PER_HOUR * 24; - var HUNDREDTHS_PER_MONTH = HUNDREDTHS_PER_DAY * (((365 * 4) + 1) / 48); - var HUNDREDTHS_PER_YEAR = HUNDREDTHS_PER_MONTH * 12; - - cs = Math.floor(_value / 10); - - y = Math.floor(cs / HUNDREDTHS_PER_YEAR); - cs -= (y * HUNDREDTHS_PER_YEAR); - - mo = Math.floor(cs / HUNDREDTHS_PER_MONTH); - cs -= (mo * HUNDREDTHS_PER_MONTH); - - d = Math.floor(cs / HUNDREDTHS_PER_DAY); - cs -= (d * HUNDREDTHS_PER_DAY); - - h = Math.floor(cs / HUNDREDTHS_PER_HOUR); - cs -= (h * HUNDREDTHS_PER_HOUR); - - m = Math.floor(cs / HUNDREDTHS_PER_MINUTE); - cs -= (m * HUNDREDTHS_PER_MINUTE); - - s = Math.floor(cs / HUNDREDTHS_PER_SECOND); - cs -= (s * HUNDREDTHS_PER_SECOND); - - if (y > 0) - str += y + "Y"; - if (mo > 0) - str += mo + "M"; - if (d > 0) - str += d + "D"; - - // check to see if we have any time before adding the "T" - if ((cs + s + m + h) > 0 ) { - - str += "T"; - - if (h > 0) - str += h + "H"; - - if (m > 0) - str += m + "M"; - - if ((cs + s) > 0) { - str += s; - - if (cs > 0) - str += "." + cs; - - str += "S"; - } - } - - if (str === "") - str = "0S"; - - str = "P" + str; - - return str; - }; - -ScormWrapper.prototype.convertDateToISO8601Timestamp = function(_value) { - var str; - - dtm = new Date(_value); - - var y = dtm.getFullYear(); - var mo = dtm.getMonth() + 1; - var d = dtm.getDate(); - var h = dtm.getHours(); - var m = dtm.getMinutes(); - var s = dtm.getSeconds(); - - mo = this.zeroPad(mo, 2); - d = this.zeroPad(d, 2); - h = this.zeroPad(h, 2); - m = this.zeroPad(m, 2); - s = this.zeroPad(s, 2); - - str = y + "-" + mo + "-" + d + "T" + h + ":" + m + ":" + s; - - return str; - }; - -ScormWrapper.prototype.zeroPad = function(intNum, intNumDigits) { - var strTemp; - var intLen; - var i; - - strTemp = new String(intNum); - intLen = strTemp.length; - - if (intLen > intNumDigits) { - strTemp = strTemp.substr(0, intNumDigits); - } - else { - for (i = intLen; i < intNumDigits; i++) - strTemp = "0" + strTemp; - } - - return strTemp; - }; - -ScormWrapper.prototype.trim = function(str) { - return str.replace(/^\s*|\s*$/g, ""); - }; - -ScormWrapper.prototype.isSCORM2004 = function() { - return this.scorm.version === "2004"; - }; - -function delegate(obj, func) { - { - return function() {return func.apply(obj, arguments); }; -} return ScormWrapper; -}); \ No newline at end of file +/* =========================================================== + +pipwerks SCORM Wrapper for JavaScript +v1.1.20140217 + +Created by Philip Hutchison, January 2008-2014 +https://github.com/pipwerks/scorm-api-wrapper + +Copyright (c) Philip Hutchison +MIT-style license: http://pipwerks.mit-license.org/ + +This wrapper works with both SCORM 1.2 and SCORM 2004. + +Inspired by APIWrapper.js, created by the ADL and +Concurrent Technologies Corporation, distributed by +the ADL (http://www.adlnet.gov/scorm). + +SCORM.API.find() and SCORM.API.get() functions based +on ADL code, modified by Mike Rustici +(http://www.scorm.com/resources/apifinder/SCORMAPIFinder.htm), +further modified by Philip Hutchison + +=============================================================== */ + + +var pipwerks = {}; //pipwerks 'namespace' helps ensure no conflicts with possible other "SCORM" variables +pipwerks.UTILS = {}; //For holding UTILS functions +pipwerks.debug = { isActive: true }; //Enable (true) or disable (false) for debug mode + +pipwerks.SCORM = { //Define the SCORM object + version: null, //Store SCORM version. + handleCompletionStatus: true, //Whether or not the wrapper should automatically handle the initial completion status + handleExitMode: true, //Whether or not the wrapper should automatically handle the exit mode + API: { handle: null, + isFound: false }, //Create API child object + connection: { isActive: false }, //Create connection child object + data: { completionStatus: null, + exitStatus: null }, //Create data child object + debug: {} //Create debug child object +}; + + + +/* -------------------------------------------------------------------------------- + pipwerks.SCORM.isAvailable + A simple function to allow Flash ExternalInterface to confirm + presence of JS wrapper before attempting any LMS communication. + + Parameters: none + Returns: Boolean (true) +----------------------------------------------------------------------------------- */ + +pipwerks.SCORM.isAvailable = function(){ + return true; +}; + + + +// ------------------------------------------------------------------------- // +// --- SCORM.API functions ------------------------------------------------- // +// ------------------------------------------------------------------------- // + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.API.find(window) + 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 +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.API.find = function(win){ + + var API = null, + findAttempts = 0, + findAttemptLimit = 500, + traceMsgPrefix = "SCORM.API.find", + trace = pipwerks.UTILS.trace, + scorm = pipwerks.SCORM; + + while ((!win.API && !win.API_1484_11) && + (win.parent) && + (win.parent != win) && + (findAttempts <= findAttemptLimit)){ + + findAttempts++; + win = win.parent; + + } + + //If SCORM version is specified by user, look for specific API + if(scorm.version){ + + switch(scorm.version){ + + case "2004" : + + if(win.API_1484_11){ + + API = win.API_1484_11; + + } else { + + trace(traceMsgPrefix +": SCORM version 2004 was specified by user, but API_1484_11 cannot be found."); + + } + + break; + + case "1.2" : + + if(win.API){ + + API = win.API; + + } else { + + trace(traceMsgPrefix +": SCORM version 1.2 was specified by user, but API cannot be found."); + + } + + break; + + } + + } else { //If SCORM version not specified by user, look for APIs + + if(win.API_1484_11) { //SCORM 2004-specific API. + + scorm.version = "2004"; //Set version + API = win.API_1484_11; + + } else if(win.API){ //SCORM 1.2-specific API + + scorm.version = "1.2"; //Set version + API = win.API; + + } + + } + + if(API){ + + trace(traceMsgPrefix +": API found. Version: " +scorm.version); + trace("API: " +API); + + } else { + + trace(traceMsgPrefix +": Error finding API. \nFind attempts: " +findAttempts +". \nFind attempt limit: " +findAttemptLimit); + + } + + return API; + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.API.get() + Looks for an object named API, 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 +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.API.get = function(){ + + var API = null, + win = window, + scorm = pipwerks.SCORM, + find = scorm.API.find, + trace = pipwerks.UTILS.trace; + + if(win.parent && win.parent != win){ + API = find(win.parent); + } + + if(!API && win.top.opener){ + API = find(win.top.opener); + } + + //Special handling for Plateau + //Thanks to Joseph Venditti for the patch + if(!API && win.top.opener && win.top.opener.document) { + API = find(win.top.opener.document); + } + + if(API){ + scorm.API.isFound = true; + } else { + trace("API.get failed: Can't find the API!"); + } + + return API; + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.API.getHandle() + Returns the handle to API object if it was previously set + + Parameters: None. + Returns: Object (the pipwerks.SCORM.API.handle variable). +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.API.getHandle = function() { + + var API = pipwerks.SCORM.API; + + if(!API.handle && !API.isFound){ + + API.handle = API.get(); + + } + + return API.handle; + +}; + + + +// ------------------------------------------------------------------------- // +// --- pipwerks.SCORM.connection functions --------------------------------- // +// ------------------------------------------------------------------------- // + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.connection.initialize() + Tells the LMS to initiate the communication session. + + Parameters: None + Returns: Boolean +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.connection.initialize = function(){ + + var success = false, + scorm = pipwerks.SCORM, + completionStatus = scorm.data.completionStatus, + trace = pipwerks.UTILS.trace, + makeBoolean = pipwerks.UTILS.StringToBoolean, + debug = scorm.debug, + traceMsgPrefix = "SCORM.connection.initialize "; + + trace("connection.initialize called."); + + if(!scorm.connection.isActive){ + + var API = scorm.API.getHandle(), + errorCode = 0; + + if(API){ + + switch(scorm.version){ + case "1.2" : success = makeBoolean(API.LMSInitialize("")); break; + case "2004": success = makeBoolean(API.Initialize("")); break; + } + + if(success){ + + //Double-check that connection is active and working before returning 'true' boolean + errorCode = debug.getCode(); + + if(errorCode !== null && errorCode === 0){ + + scorm.connection.isActive = true; + + if(scorm.handleCompletionStatus){ + + //Automatically set new launches to incomplete + completionStatus = scorm.status("get"); + + if(completionStatus){ + + switch(completionStatus){ + + //Both SCORM 1.2 and 2004 + case "not attempted": scorm.status("set", "incomplete"); break; + + //SCORM 2004 only + case "unknown" : scorm.status("set", "incomplete"); break; + + //Additional options, presented here in case you'd like to use them + //case "completed" : break; + //case "incomplete" : break; + //case "passed" : break; //SCORM 1.2 only + //case "failed" : break; //SCORM 1.2 only + //case "browsed" : break; //SCORM 1.2 only + + } + + } + + } + + } else { + + success = false; + trace(traceMsgPrefix +"failed. \nError code: " +errorCode +" \nError info: " +debug.getInfo(errorCode)); + + } + + } else { + + errorCode = debug.getCode(); + + if(errorCode !== null && errorCode !== 0){ + + trace(traceMsgPrefix +"failed. \nError code: " +errorCode +" \nError info: " +debug.getInfo(errorCode)); + + } else { + + trace(traceMsgPrefix +"failed: No response from server."); + + } + } + + } else { + + trace(traceMsgPrefix +"failed: API is null."); + + } + + } else { + + trace(traceMsgPrefix +"aborted: Connection already active."); + + } + + return success; + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.connection.terminate() + Tells the LMS to terminate the communication session + + Parameters: None + Returns: Boolean +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.connection.terminate = function(){ + + var success = false, + scorm = pipwerks.SCORM, + exitStatus = scorm.data.exitStatus, + completionStatus = scorm.data.completionStatus, + trace = pipwerks.UTILS.trace, + makeBoolean = pipwerks.UTILS.StringToBoolean, + debug = scorm.debug, + traceMsgPrefix = "SCORM.connection.terminate "; + + + if(scorm.connection.isActive){ + + var API = scorm.API.getHandle(), + errorCode = 0; + + if(API){ + + if(scorm.handleExitMode && !exitStatus){ + + if(completionStatus !== "completed" && completionStatus !== "passed"){ + + switch(scorm.version){ + case "1.2" : success = scorm.set("cmi.core.exit", "suspend"); break; + case "2004": success = scorm.set("cmi.exit", "suspend"); break; + } + + } else { + + switch(scorm.version){ + case "1.2" : success = scorm.set("cmi.core.exit", "logout"); break; + case "2004": success = scorm.set("cmi.exit", "normal"); break; + } + + } + + } + + //Ensure we persist the data + success = scorm.save(); + + if(success){ + + switch(scorm.version){ + case "1.2" : success = makeBoolean(API.LMSFinish("")); break; + case "2004": success = makeBoolean(API.Terminate("")); break; + } + + if(success){ + + scorm.connection.isActive = false; + + } else { + + errorCode = debug.getCode(); + trace(traceMsgPrefix +"failed. \nError code: " +errorCode +" \nError info: " +debug.getInfo(errorCode)); + + } + + } + + } else { + + trace(traceMsgPrefix +"failed: API is null."); + + } + + } else { + + trace(traceMsgPrefix +"aborted: Connection already terminated."); + + } + + return success; + +}; + + + +// ------------------------------------------------------------------------- // +// --- pipwerks.SCORM.data functions --------------------------------------- // +// ------------------------------------------------------------------------- // + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.data.get(parameter) + Requests information from the LMS. + + Parameter: parameter (string, name of the SCORM data model element) + Returns: string (the value of the specified data model element) +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.data.get = function(parameter){ + + var value = null, + scorm = pipwerks.SCORM, + trace = pipwerks.UTILS.trace, + debug = scorm.debug, + traceMsgPrefix = "SCORM.data.get(" +parameter +") "; + + if(scorm.connection.isActive){ + + var API = scorm.API.getHandle(), + errorCode = 0; + + if(API){ + + switch(scorm.version){ + case "1.2" : value = API.LMSGetValue(parameter); break; + case "2004": value = API.GetValue(parameter); break; + } + + errorCode = debug.getCode(); + + //GetValue returns an empty string on errors + //If value is an empty string, check errorCode to make sure there are no errors + if(value !== "" || errorCode === 0){ + + //GetValue is successful. + //If parameter is lesson_status/completion_status or exit status, let's + //grab the value and cache it so we can check it during connection.terminate() + switch(parameter){ + + case "cmi.core.lesson_status": + case "cmi.completion_status" : scorm.data.completionStatus = value; break; + + case "cmi.core.exit": + case "cmi.exit" : scorm.data.exitStatus = value; break; + + } + + } else { + + trace(traceMsgPrefix +"failed. \nError code: " +errorCode +"\nError info: " +debug.getInfo(errorCode)); + + } + + } else { + + trace(traceMsgPrefix +"failed: API is null."); + + } + + } else { + + trace(traceMsgPrefix +"failed: API connection is inactive."); + + } + + trace(traceMsgPrefix +" value: " +value); + + return String(value); + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.data.set() + Tells the LMS to assign the value to the named data model element. + Also stores the SCO's completion status in a variable named + pipwerks.SCORM.data.completionStatus. This variable is checked whenever + pipwerks.SCORM.connection.terminate() is invoked. + + Parameters: parameter (string). The data model element + value (string). The value for the data model element + Returns: Boolean +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.data.set = function(parameter, value){ + + var success = false, + scorm = pipwerks.SCORM, + trace = pipwerks.UTILS.trace, + makeBoolean = pipwerks.UTILS.StringToBoolean, + debug = scorm.debug, + traceMsgPrefix = "SCORM.data.set(" +parameter +") "; + + + if(scorm.connection.isActive){ + + var API = scorm.API.getHandle(), + errorCode = 0; + + if(API){ + + switch(scorm.version){ + case "1.2" : success = makeBoolean(API.LMSSetValue(parameter, value)); break; + case "2004": success = makeBoolean(API.SetValue(parameter, value)); break; + } + + if(success){ + + if(parameter === "cmi.core.lesson_status" || parameter === "cmi.completion_status"){ + + scorm.data.completionStatus = value; + + } + + } else { + + trace(traceMsgPrefix +"failed. \nError code: " +errorCode +". \nError info: " +debug.getInfo(errorCode)); + + } + + } else { + + trace(traceMsgPrefix +"failed: API is null."); + + } + + } else { + + trace(traceMsgPrefix +"failed: API connection is inactive."); + + } + + return success; + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.data.save() + Instructs the LMS to persist all data to this point in the session + + Parameters: None + Returns: Boolean +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.data.save = function(){ + + var success = false, + scorm = pipwerks.SCORM, + trace = pipwerks.UTILS.trace, + makeBoolean = pipwerks.UTILS.StringToBoolean, + traceMsgPrefix = "SCORM.data.save failed"; + + + if(scorm.connection.isActive){ + + var API = scorm.API.getHandle(); + + if(API){ + + switch(scorm.version){ + case "1.2" : success = makeBoolean(API.LMSCommit("")); break; + case "2004": success = makeBoolean(API.Commit("")); break; + } + + } else { + + trace(traceMsgPrefix +": API is null."); + + } + + } else { + + trace(traceMsgPrefix +": API connection is inactive."); + + } + + return success; + +}; + + +pipwerks.SCORM.status = function (action, status){ + + var success = false, + scorm = pipwerks.SCORM, + trace = pipwerks.UTILS.trace, + traceMsgPrefix = "SCORM.getStatus failed", + cmi = ""; + + if(action !== null){ + + switch(scorm.version){ + case "1.2" : cmi = "cmi.core.lesson_status"; break; + case "2004": cmi = "cmi.completion_status"; break; + } + + switch(action){ + + case "get": success = scorm.data.get(cmi); break; + + case "set": if(status !== null){ + + success = scorm.data.set(cmi, status); + + } else { + + success = false; + trace(traceMsgPrefix +": status was not specified."); + + } + + break; + + default : success = false; + trace(traceMsgPrefix +": no valid action was specified."); + + } + + } else { + + trace(traceMsgPrefix +": action was not specified."); + + } + + return success; + +}; + + +// ------------------------------------------------------------------------- // +// --- pipwerks.SCORM.debug functions -------------------------------------- // +// ------------------------------------------------------------------------- // + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.debug.getCode + Requests the error code for the current error state from the LMS + + Parameters: None + Returns: Integer (the last error code). +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.debug.getCode = function(){ + + var scorm = pipwerks.SCORM, + API = scorm.API.getHandle(), + trace = pipwerks.UTILS.trace, + code = 0; + + if(API){ + + switch(scorm.version){ + case "1.2" : code = parseInt(API.LMSGetLastError(), 10); break; + case "2004": code = parseInt(API.GetLastError(), 10); break; + } + + } else { + + trace("SCORM.debug.getCode failed: API is null."); + + } + + return code; + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.debug.getInfo() + "Used by a SCO to request the textual description for the error code + specified by the value of [errorCode]." + + Parameters: errorCode (integer). + Returns: String. +----------------------------------------------------------------------------- */ + +pipwerks.SCORM.debug.getInfo = function(errorCode){ + + var scorm = pipwerks.SCORM, + API = scorm.API.getHandle(), + trace = pipwerks.UTILS.trace, + result = ""; + + + if(API){ + + switch(scorm.version){ + case "1.2" : result = API.LMSGetErrorString(errorCode.toString()); break; + case "2004": result = API.GetErrorString(errorCode.toString()); break; + } + + } else { + + trace("SCORM.debug.getInfo failed: API is null."); + + } + + return String(result); + +}; + + +/* ------------------------------------------------------------------------- + pipwerks.SCORM.debug.getDiagnosticInfo + "Exists for LMS specific use. It allows the LMS to define additional + diagnostic information through the API Instance." + + Parameters: errorCode (integer). + Returns: String (Additional diagnostic information about the given error code). +---------------------------------------------------------------------------- */ + +pipwerks.SCORM.debug.getDiagnosticInfo = function(errorCode){ + + var scorm = pipwerks.SCORM, + API = scorm.API.getHandle(), + trace = pipwerks.UTILS.trace, + result = ""; + + if(API){ + + switch(scorm.version){ + case "1.2" : result = API.LMSGetDiagnostic(errorCode); break; + case "2004": result = API.GetDiagnostic(errorCode); break; + } + + } else { + + trace("SCORM.debug.getDiagnosticInfo failed: API is null."); + + } + + return String(result); + +}; + + +// ------------------------------------------------------------------------- // +// --- Shortcuts! ---------------------------------------------------------- // +// ------------------------------------------------------------------------- // + +// Because nobody likes typing verbose code. + +pipwerks.SCORM.init = pipwerks.SCORM.connection.initialize; +pipwerks.SCORM.get = pipwerks.SCORM.data.get; +pipwerks.SCORM.set = pipwerks.SCORM.data.set; +pipwerks.SCORM.save = pipwerks.SCORM.data.save; +pipwerks.SCORM.quit = pipwerks.SCORM.connection.terminate; + + + +// ------------------------------------------------------------------------- // +// --- pipwerks.UTILS functions -------------------------------------------- // +// ------------------------------------------------------------------------- // + + +/* ------------------------------------------------------------------------- + pipwerks.UTILS.StringToBoolean() + Converts 'boolean strings' into actual valid booleans. + + (Most values returned from the API are the strings "true" and "false".) + + Parameters: String + Returns: Boolean +---------------------------------------------------------------------------- */ + +pipwerks.UTILS.StringToBoolean = function(value){ + var t = typeof value; + switch(t){ + //typeof new String("true") === "object", so handle objects as string via fall-through. + //See https://github.com/pipwerks/scorm-api-wrapper/issues/3 + case "object": + case "string": return (/(true|1)/i).test(value); + case "number": return !!value; + case "boolean": return value; + case "undefined": return null; + default: return false; + } +}; + + + +/* ------------------------------------------------------------------------- + pipwerks.UTILS.trace() + Displays error messages when in debug mode. + + Parameters: msg (string) + Return: None +---------------------------------------------------------------------------- */ + +pipwerks.UTILS.trace = function(msg){ + + if(pipwerks.debug.isActive){ + + if(window.console && window.console.log){ + console.log(msg); + } else { + //alert(msg); + } + + } +}; \ No newline at end of file