diff --git a/src/BaseAPI.js b/src/BaseAPI.js index 5cedf11..539973c 100644 --- a/src/BaseAPI.js +++ b/src/BaseAPI.js @@ -14,8 +14,6 @@ const api_constants = { LOG_LEVEL_NONE: 5, }; -let _self; - /** * Base API class for AICC, SCORM 1.2, and SCORM 2004. Should be considered * abstract, and never initialized on it's own. @@ -31,14 +29,13 @@ export default class BaseAPI { * @param {object} error_codes */ constructor(error_codes) { - _self = this; - _self.currentState = api_constants.STATE_NOT_INITIALIZED; - _self.apiLogLevel = api_constants.LOG_LEVEL_ERROR; - _self.lastErrorCode = 0; - _self.listenerArray = []; + this.currentState = api_constants.STATE_NOT_INITIALIZED; + this.apiLogLevel = api_constants.LOG_LEVEL_ERROR; + this.lastErrorCode = 0; + this.listenerArray = []; - _self.#timeout = null; - _self.#error_codes = error_codes; + this.#timeout = null; + this.#error_codes = error_codes; } /** @@ -54,20 +51,20 @@ export default class BaseAPI { terminationMessage?: String) { let returnValue = api_constants.SCORM_FALSE; - if (_self.isInitialized()) { - _self.throwSCORMError(_self.#error_codes.INITIALIZED, initializeMessage); - } else if (_self.isTerminated()) { - _self.throwSCORMError(_self.#error_codes.TERMINATED, terminationMessage); + if (this.isInitialized()) { + this.throwSCORMError(this.#error_codes.INITIALIZED, initializeMessage); + } else if (this.isTerminated()) { + this.throwSCORMError(this.#error_codes.TERMINATED, terminationMessage); } else { - _self.currentState = api_constants.STATE_INITIALIZED; - _self.lastErrorCode = 0; + this.currentState = api_constants.STATE_INITIALIZED; + this.lastErrorCode = 0; returnValue = api_constants.SCORM_TRUE; - _self.processListeners(callbackName); + this.processListeners(callbackName); } - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); - _self.clearSCORMError(returnValue); + this.clearSCORMError(returnValue); return returnValue; } @@ -83,18 +80,18 @@ export default class BaseAPI { checkTerminated: boolean) { let returnValue = api_constants.SCORM_FALSE; - if (_self.checkState(checkTerminated, - _self.#error_codes.TERMINATION_BEFORE_INIT, - _self.#error_codes.MULTIPLE_TERMINATION)) { - if (checkTerminated) _self.lastErrorCode = 0; - _self.currentState = api_constants.STATE_TERMINATED; + if (this.checkState(checkTerminated, + this.#error_codes.TERMINATION_BEFORE_INIT, + this.#error_codes.MULTIPLE_TERMINATION)) { + if (checkTerminated) this.lastErrorCode = 0; + this.currentState = api_constants.STATE_TERMINATED; returnValue = api_constants.SCORM_TRUE; - _self.processListeners(callbackName); + this.processListeners(callbackName); } - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); - _self.clearSCORMError(returnValue); + this.clearSCORMError(returnValue); return returnValue; } @@ -113,17 +110,17 @@ export default class BaseAPI { CMIElement: String) { let returnValue = ''; - if (_self.checkState(checkTerminated, - _self.#error_codes.RETRIEVE_BEFORE_INIT, - _self.#error_codes.RETRIEVE_AFTER_TERM)) { - if (checkTerminated) _self.lastErrorCode = 0; - returnValue = _self.getCMIValue(CMIElement); - _self.processListeners(callbackName, CMIElement); + if (this.checkState(checkTerminated, + this.#error_codes.RETRIEVE_BEFORE_INIT, + this.#error_codes.RETRIEVE_AFTER_TERM)) { + if (checkTerminated) this.lastErrorCode = 0; + returnValue = this.getCMIValue(CMIElement); + this.processListeners(callbackName, CMIElement); } - _self.apiLog(callbackName, CMIElement, ': returned: ' + returnValue, + this.apiLog(callbackName, CMIElement, ': returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); - _self.clearSCORMError(returnValue); + this.clearSCORMError(returnValue); return returnValue; } @@ -144,17 +141,17 @@ export default class BaseAPI { value) { let returnValue = ''; - if (_self.checkState(checkTerminated, _self.#error_codes.STORE_BEFORE_INIT, - _self.#error_codes.STORE_AFTER_TERM)) { - if (checkTerminated) _self.lastErrorCode = 0; - returnValue = _self.setCMIValue(CMIElement, value); - _self.processListeners(callbackName, CMIElement, value); + if (this.checkState(checkTerminated, this.#error_codes.STORE_BEFORE_INIT, + this.#error_codes.STORE_AFTER_TERM)) { + if (checkTerminated) this.lastErrorCode = 0; + returnValue = this.setCMIValue(CMIElement, value); + this.processListeners(callbackName, CMIElement, value); } - _self.apiLog(callbackName, CMIElement, + this.apiLog(callbackName, CMIElement, ': ' + value + ': result: ' + returnValue, api_constants.LOG_LEVEL_INFO); - _self.clearSCORMError(returnValue); + this.clearSCORMError(returnValue); return returnValue; } @@ -170,16 +167,16 @@ export default class BaseAPI { checkTerminated: boolean) { let returnValue = api_constants.SCORM_FALSE; - if (_self.checkState(checkTerminated, _self.#error_codes.COMMIT_BEFORE_INIT, - _self.#error_codes.COMMIT_AFTER_TERM)) { - if (checkTerminated) _self.lastErrorCode = 0; + if (this.checkState(checkTerminated, this.#error_codes.COMMIT_BEFORE_INIT, + this.#error_codes.COMMIT_AFTER_TERM)) { + if (checkTerminated) this.lastErrorCode = 0; returnValue = api_constants.SCORM_TRUE; - _self.processListeners(callbackName); + this.processListeners(callbackName); } - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); - _self.clearSCORMError(returnValue); + this.clearSCORMError(returnValue); return returnValue; } @@ -190,11 +187,11 @@ export default class BaseAPI { * @return {string} */ getLastError(callbackName: String) { - const returnValue = String(_self.lastErrorCode); + const returnValue = String(this.lastErrorCode); - _self.processListeners(callbackName); + this.processListeners(callbackName); - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); return returnValue; @@ -211,11 +208,11 @@ export default class BaseAPI { let returnValue = ''; if (CMIErrorCode !== null && CMIErrorCode !== '') { - returnValue = _self.getLmsErrorMessageDetails(CMIErrorCode); - _self.processListeners(callbackName); + returnValue = this.getLmsErrorMessageDetails(CMIErrorCode); + this.processListeners(callbackName); } - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); return returnValue; @@ -232,11 +229,11 @@ export default class BaseAPI { let returnValue = ''; if (CMIErrorCode !== null && CMIErrorCode !== '') { - returnValue = _self.getLmsErrorMessageDetails(CMIErrorCode, true); - _self.processListeners(callbackName); + returnValue = this.getLmsErrorMessageDetails(CMIErrorCode, true); + this.processListeners(callbackName); } - _self.apiLog(callbackName, null, 'returned: ' + returnValue, + this.apiLog(callbackName, null, 'returned: ' + returnValue, api_constants.LOG_LEVEL_INFO); return returnValue; @@ -254,11 +251,11 @@ export default class BaseAPI { checkTerminated: boolean, beforeInitError: number, afterTermError?: number) { - if (_self.isNotInitialized()) { - _self.throwSCORMError(beforeInitError); + if (this.isNotInitialized()) { + this.throwSCORMError(beforeInitError); return false; - } else if (checkTerminated && _self.isTerminated()) { - _self.throwSCORMError(afterTermError); + } else if (checkTerminated && this.isTerminated()) { + this.throwSCORMError(afterTermError); return false; } @@ -278,9 +275,9 @@ export default class BaseAPI { CMIElement: String, logMessage: String, messageLevel: number) { - logMessage = _self.formatMessage(functionName, CMIElement, logMessage); + logMessage = this.formatMessage(functionName, CMIElement, logMessage); - if (messageLevel >= _self.apiLogLevel) { + if (messageLevel >= this.apiLogLevel) { switch (messageLevel) { case api_constants.LOG_LEVEL_ERROR: console.error(logMessage); @@ -402,8 +399,8 @@ export default class BaseAPI { const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`; const invalidErrorCode = scorm2004 ? - _self.#error_codes.UNDEFINED_DATA_MODEL : - _self.#error_codes.GENERAL; + this.#error_codes.UNDEFINED_DATA_MODEL : + this.#error_codes.GENERAL; for (let i = 0; i < structure.length; i++) { const attribute = structure[i]; @@ -411,15 +408,15 @@ export default class BaseAPI { if (i === structure.length - 1) { if (scorm2004 && (attribute.substr(0, 8) === '{target=') && (typeof refObject._isTargetValid == 'function')) { - _self.throwSCORMError(_self.#error_codes.READ_ONLY_ELEMENT); + this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT); } else if (!{}.hasOwnProperty.call(refObject, attribute)) { - _self.throwSCORMError(invalidErrorCode, invalidErrorMessage); + this.throwSCORMError(invalidErrorCode, invalidErrorMessage); } else { - if (_self.stringContains(CMIElement, '.correct_responses')) { - _self.validateCorrectResponse(CMIElement, value); + if (this.stringContains(CMIElement, '.correct_responses')) { + this.validateCorrectResponse(CMIElement, value); } - if (!scorm2004 || _self.lastErrorCode === 0) { + if (!scorm2004 || this.lastErrorCode === 0) { refObject[attribute] = value; returnValue = api_constants.SCORM_TRUE; } @@ -427,7 +424,7 @@ export default class BaseAPI { } else { refObject = refObject[attribute]; if (!refObject) { - _self.throwSCORMError(invalidErrorCode, invalidErrorMessage); + this.throwSCORMError(invalidErrorCode, invalidErrorMessage); break; } @@ -441,10 +438,10 @@ export default class BaseAPI { if (item) { refObject = item; } else { - const newChild = _self.getChildElement(CMIElement, value); + const newChild = this.getChildElement(CMIElement, value); if (!newChild) { - _self.throwSCORMError(invalidErrorCode, invalidErrorMessage); + this.throwSCORMError(invalidErrorCode, invalidErrorMessage); } else { refObject.childArray.push(newChild); refObject = newChild; @@ -459,7 +456,7 @@ export default class BaseAPI { } if (returnValue === api_constants.SCORM_FALSE) { - _self.apiLog(methodName, null, + this.apiLog(methodName, null, `There was an error setting the value for: ${CMIElement}, value of: ${value}`, api_constants.LOG_LEVEL_WARNING); } @@ -512,7 +509,7 @@ export default class BaseAPI { if (!scorm2004) { if (i === structure.length - 1) { if (!{}.hasOwnProperty.call(refObject, attribute)) { - _self.throwSCORMError(101, + this.throwSCORMError(101, 'getCMIValue did not find a value for: ' + CMIElement); } } @@ -523,7 +520,7 @@ export default class BaseAPI { substr(8, String(attribute).length - 9); return refObject._isTargetValid(target); } else if (!{}.hasOwnProperty.call(refObject, attribute)) { - _self.throwSCORMError(401, + this.throwSCORMError(401, 'The data model element passed to GetValue (' + CMIElement + ') is not a valid SCORM data model element.'); return ''; @@ -536,9 +533,9 @@ export default class BaseAPI { if (refObject === null || refObject === undefined) { if (!scorm2004) { if (attribute === '_children') { - _self.throwSCORMError(202); + this.throwSCORMError(202); } else if (attribute === '_count') { - _self.throwSCORMError(203); + this.throwSCORMError(203); } } return ''; @@ -553,7 +550,7 @@ export default class BaseAPI { * @return {boolean} */ isInitialized() { - return _self.currentState === api_constants.STATE_INITIALIZED; + return this.currentState === api_constants.STATE_INITIALIZED; } /** @@ -562,7 +559,7 @@ export default class BaseAPI { * @return {boolean} */ isNotInitialized() { - return _self.currentState === api_constants.STATE_NOT_INITIALIZED; + return this.currentState === api_constants.STATE_NOT_INITIALIZED; } /** @@ -571,7 +568,7 @@ export default class BaseAPI { * @return {boolean} */ isTerminated() { - return _self.currentState === api_constants.STATE_TERMINATED; + return this.currentState === api_constants.STATE_TERMINATED; } /** @@ -595,7 +592,7 @@ export default class BaseAPI { CMIElement = listenerName.replace(functionName + '.', ''); } - _self.listenerArray.push({ + this.listenerArray.push({ functionName: functionName, CMIElement: CMIElement, callback: callback, @@ -611,8 +608,8 @@ export default class BaseAPI { * @param {*} value */ processListeners(functionName: String, CMIElement: String, value: any) { - for (let i = 0; i < _self.listenerArray.length; i++) { - const listener = _self.listenerArray[i]; + for (let i = 0; i < this.listenerArray.length; i++) { + const listener = this.listenerArray[i]; const functionsMatch = listener.functionName === functionName; const listenerHasCMIElement = !!listener.CMIElement; const CMIElementsMatch = listener.CMIElement === CMIElement; @@ -631,13 +628,13 @@ export default class BaseAPI { */ throwSCORMError(errorNumber: number, message: String) { if (!message) { - message = _self.getLmsErrorMessageDetails(errorNumber); + message = this.getLmsErrorMessageDetails(errorNumber); } - _self.apiLog('throwSCORMError', null, errorNumber + ': ' + message, + this.apiLog('throwSCORMError', null, errorNumber + ': ' + message, api_constants.LOG_LEVEL_ERROR); - _self.lastErrorCode = String(errorNumber); + this.lastErrorCode = String(errorNumber); } /** @@ -647,7 +644,7 @@ export default class BaseAPI { */ clearSCORMError(success: String) { if (success !== api_constants.SCORM_FALSE) { - _self.lastErrorCode = 0; + this.lastErrorCode = 0; } } @@ -658,7 +655,7 @@ export default class BaseAPI { * @param {string} CMIElement */ loadFromJSON(json, CMIElement) { - if (!_self.isNotInitialized()) { + if (!this.isNotInitialized()) { console.error( 'loadFromJSON can only be called before the call to lmsInitialize.'); return; @@ -673,13 +670,13 @@ export default class BaseAPI { if (value['childArray']) { for (let i = 0; i < value['childArray'].length; i++) { - _self.loadFromJSON(value['childArray'][i], + this.loadFromJSON(value['childArray'][i], currentCMIElement + '.' + i); } } else if (value.constructor === Object) { - _self.loadFromJSON(value, currentCMIElement); + this.loadFromJSON(value, currentCMIElement); } else { - _self.setCMIValue(currentCMIElement, value); + this.setCMIValue(currentCMIElement, value); } } } @@ -691,68 +688,28 @@ export default class BaseAPI { * @return {string} */ renderCMIToJSON() { - const cmi = _self.cmi; + const cmi = this.cmi; // Do we want/need to return fields that have no set value? // return JSON.stringify({ cmi }, (k, v) => v === undefined ? null : v, 2); return JSON.stringify({cmi}); } - /** - * Check if the value matches the proper format. If not, throw proper error code. - * - * @param {string} value - * @param {string} regexPattern - * @return {boolean} - */ - checkValidFormat(value: String, regexPattern: String) { - const formatRegex = new RegExp(regexPattern); - if (!value || !value.match(formatRegex)) { - _self.throwSCORMError(_self.#error_codes.TYPE_MISMATCH); - return false; - } - return true; - } - - /** - * Check if the value matches the proper range. If not, throw proper error code. - * - * @param {*} value - * @param {string} rangePattern - * @return {boolean} - */ - checkValidRange(value: any, rangePattern: String) { - const ranges = rangePattern.split('#'); - value = value * 1.0; - if (value >= ranges[0]) { - if ((ranges[1] === '*') || (value <= ranges[1])) { - _self.clearSCORMError(api_constants.SCORM_TRUE); - return true; - } else { - _self.throwSCORMError(_self.#error_codes.VALUE_OUT_OF_RANGE); - return false; - } - } else { - _self.throwSCORMError(_self.#error_codes.VALUE_OUT_OF_RANGE); - return false; - } - } - /** * Throws a SCORM error * * @param {number} when - the number of milliseconds to wait before committing */ scheduleCommit(when: number) { - _self.#timeout = new ScheduledCommit(this, when); + this.#timeout = new ScheduledCommit(this, when); } /** * Clears and cancels any currently scheduled commits */ clearScheduledCommit() { - if (_self.#timeout) { - _self.#timeout.cancel(); - _self.#timeout = null; + if (this.#timeout) { + this.#timeout.cancel(); + this.#timeout = null; } } } @@ -771,26 +728,26 @@ class ScheduledCommit { * @param {number} when */ constructor(API: any, when: number) { - _self.#API = API; - _self.#timeout = setTimeout(_self.#wrapper, when); + this.#API = API; + this.#timeout = setTimeout(this.#wrapper, when); } /** * Cancel any currently scheduled commit */ cancel() { - _self.#cancelled = true; - if (_self.#timeout) { - clearTimeout(_self.#timeout); + this.#cancelled = true; + if (this.#timeout) { + clearTimeout(this.#timeout); } } /** * Wrap the API commit call to check if the call has already been cancelled */ - #wrapper = () => { - if (!_self.#cancelled) { - _self.#API.commit(); + #wrapper() { + if (!this.#cancelled) { + this.#API.commit(); } - }; + } } diff --git a/src/Scorm2004API.js b/src/Scorm2004API.js index f6c72d7..3b2c1dd 100644 --- a/src/Scorm2004API.js +++ b/src/Scorm2004API.js @@ -18,12 +18,11 @@ import {valid_languages} from './constants/language_constants'; import {scorm2004_regex} from './regex'; const constants = scorm2004_constants; -let _self; /** * API class for SCORM 2004 */ -class Scorm2004API extends BaseAPI { +export default class Scorm2004API extends BaseAPI { #version: '1.0'; /** @@ -32,19 +31,18 @@ class Scorm2004API extends BaseAPI { constructor() { super(scorm2004_error_codes); - _self = this; - _self.cmi = new CMI(_self); - _self.adl = new ADL(_self); + this.cmi = new CMI(this); + this.adl = new ADL(this); // Rename functions to match 2004 Spec and expose to modules - _self.Initialize = _self.lmsInitialize; - _self.Terminate = _self.lmsTerminate; - _self.GetValue = _self.lmsGetValue; - _self.SetValue = _self.lmsSetValue; - _self.Commit = _self.lmsCommit; - _self.GetLastError = _self.lmsGetLastError; - _self.GetErrorString = _self.lmsGetErrorString; - _self.GetDiagnostic = _self.lmsGetDiagnostic; + this.Initialize = this.lmsInitialize; + this.Terminate = this.lmsTerminate; + this.GetValue = this.lmsGetValue; + this.SetValue = this.lmsSetValue; + this.Commit = this.lmsCommit; + this.GetLastError = this.lmsGetLastError; + this.GetErrorString = this.lmsGetErrorString; + this.GetDiagnostic = this.lmsGetDiagnostic; } /** @@ -59,14 +57,14 @@ class Scorm2004API extends BaseAPI { * @return {string} bool */ lmsInitialize() { - return _self.initialize('Initialize'); + return this.initialize('Initialize'); } /** * @return {string} bool */ lmsTerminate() { - return _self.terminate('Terminate', true); + return this.terminate('Terminate', true); } /** @@ -74,7 +72,7 @@ class Scorm2004API extends BaseAPI { * @return {string} */ lmsGetValue(CMIElement) { - return _self.getValue('GetValue', true, CMIElement); + return this.getValue('GetValue', true, CMIElement); } /** @@ -83,7 +81,7 @@ class Scorm2004API extends BaseAPI { * @return {string} */ lmsSetValue(CMIElement, value) { - return _self.setValue('SetValue', true, CMIElement, value); + return this.setValue('SetValue', true, CMIElement, value); } /** @@ -92,7 +90,7 @@ class Scorm2004API extends BaseAPI { * @return {string} bool */ lmsCommit() { - return _self.commit('Commit'); + return this.commit('Commit'); } /** @@ -101,7 +99,7 @@ class Scorm2004API extends BaseAPI { * @return {string} */ lmsGetLastError() { - return _self.getLastError('GetLastError'); + return this.getLastError('GetLastError'); } /** @@ -111,7 +109,7 @@ class Scorm2004API extends BaseAPI { * @return {string} */ lmsGetErrorString(CMIErrorCode) { - return _self.getErrorString('GetErrorString', CMIErrorCode); + return this.getErrorString('GetErrorString', CMIErrorCode); } /** @@ -121,7 +119,7 @@ class Scorm2004API extends BaseAPI { * @return {string} */ lmsGetDiagnostic(CMIErrorCode) { - return _self.getDiagnostic('GetDiagnostic', CMIErrorCode); + return this.getDiagnostic('GetDiagnostic', CMIErrorCode); } /** @@ -131,7 +129,7 @@ class Scorm2004API extends BaseAPI { * @param {any} value */ setCMIValue(CMIElement, value) { - _self._commonSetCMIValue('SetValue', true, CMIElement, value); + this._commonSetCMIValue('SetValue', true, CMIElement, value); } /** @@ -144,23 +142,23 @@ class Scorm2004API extends BaseAPI { getChildElement(CMIElement, value) { let newChild; - if (_self.stringContains(CMIElement, 'cmi.objectives')) { + if (this.stringContains(CMIElement, 'cmi.objectives')) { newChild = new CMIObjectivesObject(this); - } else if (_self.stringContains(CMIElement, '.correct_responses')) { + } else if (this.stringContains(CMIElement, '.correct_responses')) { const parts = CMIElement.split('.'); const index = Number(parts[2]); - const interaction = _self.cmi.interactions.childArray[index]; + const interaction = this.cmi.interactions.childArray[index]; if (typeof interaction.type === 'undefined') { - _self.throwSCORMError(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); + this.throwSCORMError(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { const interaction_type = interaction.type; const interaction_count = interaction.correct_responses._count; if (interaction_type === 'choice') { - for (let i = 0; i < interaction_count && _self.lastErrorCode === + for (let i = 0; i < interaction_count && this.lastErrorCode === 0; i++) { const response = interaction.correct_responses.childArray[i]; if (response.pattern === value) { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); } } } @@ -174,22 +172,22 @@ class Scorm2004API extends BaseAPI { } if (nodes.length > 0 && nodes.length <= response_type.max) { - _self.#checkCorrectResponseValue(interaction_type, nodes, value); + this.#checkCorrectResponseValue(interaction_type, nodes, value); } else if (nodes.length > response_type.max) { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, 'Data Model Element Pattern Too Long'); } } - if (_self.lastErrorCode === 0) { + if (this.lastErrorCode === 0) { newChild = new CMIInteractionsCorrectResponsesObject(this); } - } else if (_self.stringContains(CMIElement, '.objectives')) { + } else if (this.stringContains(CMIElement, '.objectives')) { newChild = new CMIInteractionsObjectivesObject(this); - } else if (_self.stringContains(CMIElement, 'cmi.interactions')) { + } else if (this.stringContains(CMIElement, 'cmi.interactions')) { newChild = new CMIInteractionsObject(this); - } else if (_self.stringContains(CMIElement, 'cmi.comments_from_learner')) { + } else if (this.stringContains(CMIElement, 'cmi.comments_from_learner')) { newChild = new CMICommentsFromLearnerObject(this); - } else if (_self.stringContains(CMIElement, 'cmi.comments_from_lms')) { + } else if (this.stringContains(CMIElement, 'cmi.comments_from_lms')) { newChild = new CMICommentsFromLMSObject(this); } @@ -205,15 +203,15 @@ class Scorm2004API extends BaseAPI { const parts = CMIElement.split('.'); const index = Number(parts[2]); const pattern_index = Number(parts[4]); - const interaction = _self.cmi.interactions.childArray[index]; + const interaction = this.cmi.interactions.childArray[index]; const interaction_type = interaction.type; const interaction_count = interaction.correct_responses._count; if (interaction_type === 'choice') { - for (let i = 0; i < interaction_count && _self.lastErrorCode === 0; i++) { + for (let i = 0; i < interaction_count && this.lastErrorCode === 0; i++) { const response = interaction.correct_responses.childArray[i]; if (response.pattern === value) { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); } } } @@ -229,26 +227,26 @@ class Scorm2004API extends BaseAPI { } if (nodes.length > 0 && nodes.length <= response_type.max) { - _self.#checkCorrectResponseValue(interaction_type, nodes, value); + this.#checkCorrectResponseValue(interaction_type, nodes, value); } else if (nodes.length > response_type.max) { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, 'Data Model Element Pattern Too Long'); } - if (_self.lastErrorCode === 0 && + if (this.lastErrorCode === 0 && (!response_type.duplicate || - !_self.#checkDuplicatedPattern(interaction.correct_responses, + !this.#checkDuplicatedPattern(interaction.correct_responses, pattern_index, value)) || - (_self.lastErrorCode === 0 && value === '')) { + (this.lastErrorCode === 0 && value === '')) { // do nothing, we want the inverse } else { - if (_self.lastErrorCode === 0) { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, + if (this.lastErrorCode === 0) { + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, 'Data Model Element Pattern Already Exists'); } } } else { - _self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, 'Data Model Element Collection Limit Reached'); } } @@ -260,7 +258,7 @@ class Scorm2004API extends BaseAPI { * @return {*} */ getCMIValue(CMIElement) { - return _self._commonGetCMIValue('GetValue', true, CMIElement); + return this._commonGetCMIValue('GetValue', true, CMIElement); } /** @@ -311,10 +309,10 @@ class Scorm2004API extends BaseAPI { #checkCorrectResponseValue = (interaction_type, nodes, value) => { const response = correct_responses[interaction_type]; const formatRegex = new RegExp(response.format); - for (let i = 0; i < nodes.length && _self.lastErrorCode === 0; i++) { + for (let i = 0; i < nodes.length && this.lastErrorCode === 0; i++) { if (interaction_type.match( '^(fill-in|long-fill-in|matching|performance|sequencing)$')) { - nodes[i] = _self.#removeCorrectResponsePrefixes(nodes[i]); + nodes[i] = this.#removeCorrectResponsePrefixes(nodes[i]); } if (response.delimiter2 !== undefined) { @@ -322,30 +320,30 @@ class Scorm2004API extends BaseAPI { if (values.length === 2) { const matches = values[0].match(formatRegex); if (!matches) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } else { if (!values[1].match(new RegExp(response.format2))) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } } else { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } else { const matches = nodes[i].match(formatRegex); if ((!matches && value !== '') || (!matches && interaction_type === 'true-false')) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } else { if (interaction_type === 'numeric' && nodes.length > 1) { if (Number(nodes[0]) > Number(nodes[1])) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } else { if (nodes[i] !== '' && response.unique) { - for (let j = 0; j < i && _self.lastErrorCode === 0; j++) { + for (let j = 0; j < i && this.lastErrorCode === 0; j++) { if (nodes[i] === nodes[j]) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } } @@ -377,7 +375,7 @@ class Scorm2004API extends BaseAPI { const lang = langMatches[3]; if (lang !== undefined && lang.length > 0) { if (valid_languages[lang.toLowerCase()] === undefined) { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } } @@ -386,7 +384,7 @@ class Scorm2004API extends BaseAPI { case 'case_matters': if (!seenLang && !seenOrder && !seenCase) { if (matches[3] !== 'true' && matches[3] !== 'false') { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } @@ -395,7 +393,7 @@ class Scorm2004API extends BaseAPI { case 'order_matters': if (!seenCase && !seenLang && !seenOrder) { if (matches[3] !== 'true' && matches[3] !== 'false') { - _self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); + this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); } } @@ -417,8 +415,8 @@ class Scorm2004API extends BaseAPI { */ replaceWithAnotherScormAPI(newAPI) { // Data Model - _self.cmi = newAPI.cmi; - _self.adl = newAPI.adl; + this.cmi = newAPI.cmi; + this.adl = newAPI.adl; } /** @@ -427,8 +425,8 @@ class Scorm2004API extends BaseAPI { * @return {string} ISO8601 Duration */ getCurrentTotalTime() { - const totalTime = _self.cmi.total_time; - const sessionTime = _self.cmi.session_time; + const totalTime = this.cmi.total_time; + const sessionTime = this.cmi.session_time; return Util.addTwoDurations(totalTime, sessionTime, scorm2004_regex.CMITimespan); diff --git a/src/cmi/aicc_cmi.js b/src/cmi/aicc_cmi.js index f090e3f..aba8ea9 100644 --- a/src/cmi/aicc_cmi.js +++ b/src/cmi/aicc_cmi.js @@ -3,31 +3,38 @@ import {BaseCMI, CMIArray, CMIScore} from './common'; import {aicc_constants} from '../constants/api_constants'; import {aicc_regex} from '../regex'; import {scorm12_error_codes} from '../constants/error_codes'; +import {ValidationError} from '../exceptions'; +import {check12ValidFormat} from './scorm12_cmi'; +import {throwReadOnlyError} from './scorm12_cmi'; +import {throwWriteOnlyError} from './scorm12_cmi'; const constants = aicc_constants; const regex = aicc_regex; -/** - * Sets a READ_ONLY error on the API - * @param {AICC} API - */ -function throwReadOnlyError(API) { - API.throwSCORMError(scorm12_error_codes.READ_ONLY_ELEMENT); -} - /** * CMI Class for AICC */ export class CMI extends Scorm12CMI.CMI { /** * Constructor for AICC CMI object - * @param {AICC} API + * @param {boolean} initialized */ - constructor(API) { - super(API, constants.cmi_children); + constructor(initialized: boolean) { + super(constants.cmi_children); - this.student_data = new AICCCMIStudentData(API); - this.evaluation = new CMIEvaluation(API); + if (initialized) this.initialize(); + + this.student_data = new AICCCMIStudentData(); + this.evaluation = new CMIEvaluation(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.student_data?.initialize(); + this.evaluation?.initialize(); } } @@ -37,12 +44,19 @@ export class CMI extends Scorm12CMI.CMI { class CMIEvaluation extends BaseCMI { /** * Constructor for AICC Evaluation object - * @param {AICC} API */ - constructor(API) { - super(API); + constructor() { + super(); - this.comments = new CMIEvaluationComments(API); + this.comments = new CMIEvaluationComments(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.comments?.initialize(); } } @@ -52,10 +66,9 @@ class CMIEvaluation extends BaseCMI { class CMIEvaluationComments extends CMIArray { /** * Constructor for AICC Evaluation Comments object - * @param {AICC} API */ - constructor(API) { - super(API, constants.comments_children, + constructor() { + super(constants.comments_children, scorm12_error_codes.INVALID_SET_VALUE); } } @@ -66,12 +79,19 @@ class CMIEvaluationComments extends CMIArray { class AICCCMIStudentData extends Scorm12CMI.CMIStudentData { /** * Constructor for AICC StudentData object - * @param {AICC} API */ - constructor(API) { - super(API, constants.student_data_children); + constructor() { + super(constants.student_data_children); - this.tries = new CMITries(API); + this.tries = new CMITries(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.tries?.initialize(); } #tries_during_lesson = ''; @@ -86,13 +106,13 @@ class AICCCMIStudentData extends Scorm12CMI.CMIStudentData { /** * Setter for #tries_during_lesson. Sets an error if trying to set after - * API initialization. + * initialization. * @param {string} tries_during_lesson */ set tries_during_lesson(tries_during_lesson) { - this.API.isNotInitialized() ? + !this.initialized ? this.#tries_during_lesson = tries_during_lesson : - throwReadOnlyError(this.API); + throwReadOnlyError(); } } @@ -102,10 +122,9 @@ class AICCCMIStudentData extends Scorm12CMI.CMIStudentData { export class CMITries extends CMIArray { /** * Constructor for inline Tries Array class - * @param {AICC} API */ - constructor(API) { - super(API, aicc_constants.tries_children); + constructor() { + super(aicc_constants.tries_children); } } @@ -115,12 +134,19 @@ export class CMITries extends CMIArray { export class CMITriesObject extends BaseCMI { /** * Constructor for AICC Tries object - * @param {AICC} API */ - constructor(API) { - super(API); + constructor() { + super(); - this.score = new CMIScore(API); + this.score = new CMIScore(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.score?.initialize(); } #status = ''; @@ -139,7 +165,7 @@ export class CMITriesObject extends BaseCMI { * @param {string} status */ set status(status) { - if (this.API.checkValidFormat(status, regex.CMIStatus2)) { + if (check12ValidFormat(status, regex.CMIStatus2)) { this.#status = status; } } @@ -157,7 +183,7 @@ export class CMITriesObject extends BaseCMI { * @param {string} time */ set time(time) { - if (this.API.checkValidFormat(time, regex.CMITime)) { + if (check12ValidFormat(time, regex.CMITime)) { this.#time = time; } } @@ -169,10 +195,9 @@ export class CMITriesObject extends BaseCMI { export class CMIEvaluationCommentsObject extends BaseCMI { /** * Constructor for Evaluation Comments - * @param {AICC} API */ - constructor(API) { - super(API); + constructor() { + super(); } #content = ''; @@ -192,7 +217,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI { * @param {string} content */ set content(content) { - if (this.API.checkValidFormat(content, regex.CMIString256)) { + if (check12ValidFormat(content, regex.CMIString256)) { this.#content = content; } } @@ -210,7 +235,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI { * @param {string} location */ set location(location) { - if (this.API.checkValidFormat(location, regex.CMIString256)) { + if (check12ValidFormat(location, regex.CMIString256)) { this.#location = location; } } @@ -228,7 +253,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI { * @param {string} time */ set time(time) { - if (this.API.checkValidFormat(time, regex.CMITime)) { + if (check12ValidFormat(time, regex.CMITime)) { this.#time = time; } } @@ -240,10 +265,9 @@ export class CMIEvaluationCommentsObject extends BaseCMI { export class NAV extends BaseCMI { /** * Constructor for NAV object - * @param {AICC} API */ - constructor(API) { - super(API); + constructor() { + super(); } #event = ''; @@ -253,9 +277,7 @@ export class NAV extends BaseCMI { * @return {string} */ get event() { - return (!this.jsonString) ? - this.API.throwSCORMError(scorm12_error_codes.WRITE_ONLY_ELEMENT) : - this.#event; + return (!this.jsonString) ? throwWriteOnlyError() : this.#event; } /** @@ -263,7 +285,7 @@ export class NAV extends BaseCMI { * @param {string} event */ set event(event) { - if (this.API.checkValidFormat(event, regex.NAVEvent)) { + if (check12ValidFormat(event, regex.NAVEvent)) { this.#event = event; } } diff --git a/src/cmi/common.js b/src/cmi/common.js index 11ee0ee..6fdda4a 100644 --- a/src/cmi/common.js +++ b/src/cmi/common.js @@ -1,20 +1,68 @@ // @flow import {scorm12_constants} from '../constants/api_constants'; import {scorm12_error_codes} from '../constants/error_codes'; +import {ValidationError} from '../exceptions'; + +/** + * Check if the value matches the proper format. If not, throw proper error code. + * + * @param {string} value + * @param {string} regexPattern + * @param {number} errorCode + * @return {boolean} + */ +export function checkValidFormat( + value: String, regexPattern: String, errorCode: number) { + const formatRegex = new RegExp(regexPattern); + if (!value || !value.match(formatRegex)) { + throw new ValidationError(errorCode); + } + return true; +} + +/** + * Check if the value matches the proper range. If not, throw proper error code. + * + * @param {*} value + * @param {string} rangePattern + * @param {number} errorCode + * @return {boolean} + */ +export function checkValidRange( + value: any, rangePattern: String, errorCode: number) { + const ranges = rangePattern.split('#'); + value = value * 1.0; + if (value >= ranges[0]) { + if ((ranges[1] === '*') || (value <= ranges[1])) { + return true; + } else { + throw new ValidationError(errorCode); + } + } else { + throw new ValidationError(errorCode); + } +} /** * Base class for API cmi objects */ export class BaseCMI { jsonString = false; - API; + #initialized = false; /** - * Constructor for base cmi - * @param {BaseAPI} API + * Getter for #initialized + * @return {boolean} */ - constructor(API: any) { - this.API = API; + get initialized() { + return this.#initialized; + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + this.#initialized = true; } } @@ -24,29 +72,48 @@ export class BaseCMI { export class CMIScore extends BaseCMI { /** * Constructor for *.score - * @param {BaseAPI} API * @param {string} score_children * @param {string} score_range + * @param {string} max * @param {number} invalidErrorCode + * @param {number} invalidTypeCode + * @param {number} invalidRangeCode */ - constructor(API, score_children?, score_range?, invalidErrorCode) { - super(API); + constructor( + { + score_children, + score_range, + max, + invalidErrorCode, + invalidTypeCode, + invalidRangeCode, + }) { + super(); this.#_children = score_children ? score_children : scorm12_constants.score_children; this.#_score_range = score_range ? score_range : false; + this.#max = max ? max : '100'; this.#_invalid_error_code = invalidErrorCode ? invalidErrorCode : scorm12_error_codes.INVALID_SET_VALUE; + this.#_invalid_type_code = invalidTypeCode ? + invalidTypeCode : + scorm12_error_codes.TYPE_MISMATCH; + this.#_invalid_range_code = invalidRangeCode ? + invalidRangeCode : + scorm12_error_codes.VALUE_OUT_OF_RANGE; } #_children; #_score_range; #_invalid_error_code; + #_invalid_type_code; + #_invalid_range_code; #raw = ''; #min = ''; - #max = '100'; + #max; /** * Getter for _children @@ -63,7 +130,7 @@ export class CMIScore extends BaseCMI { * @private */ set _children(_children) { - this.API.throwSCORMError(this.#_invalid_error_code); + throw new ValidationError(this.#_invalid_error_code); } /** @@ -79,9 +146,11 @@ export class CMIScore extends BaseCMI { * @param {string} raw */ set raw(raw) { - if (this.API.checkValidFormat(raw, scorm12_constants.CMIDecimal) && + if (checkValidFormat(raw, scorm12_constants.CMIDecimal, + this.#_invalid_type_code) && (!this.#_score_range || - this.API.checkValidRange(raw, this.#_score_range))) { + checkValidRange(raw, this.#_score_range, + this.#_invalid_range_code))) { this.#raw = raw; } } @@ -99,9 +168,11 @@ export class CMIScore extends BaseCMI { * @param {string} min */ set min(min) { - if (this.API.checkValidFormat(min, scorm12_constants.CMIDecimal) && + if (checkValidFormat(min, scorm12_constants.CMIDecimal, + this.#_invalid_type_code) && (!this.#_score_range || - this.API.checkValidRange(min, this.#_score_range))) { + checkValidRange(min, this.#_score_range, + this.#_invalid_range_code))) { this.#min = min; } } @@ -119,9 +190,11 @@ export class CMIScore extends BaseCMI { * @param {string} max */ set max(max) { - if (this.API.checkValidFormat(max, scorm12_constants.CMIDecimal) && + if (checkValidFormat(max, scorm12_constants.CMIDecimal, + this.#_invalid_type_code) && (!this.#_score_range || - this.API.checkValidRange(max, this.#_score_range))) { + checkValidRange(max, this.#_score_range, + this.#_invalid_range_code))) { this.#max = max; } } @@ -145,12 +218,11 @@ export class CMIScore extends BaseCMI { export class CMIArray extends BaseCMI { /** * Constructor cmi *.n arrays - * @param {BaseAPI} API * @param {string} children * @param {number} errorCode */ - constructor({API, children, errorCode}) { - super(API); + constructor({children, errorCode}) { + super(); this.#_children = children; this.#errorCode = errorCode; this.childArray = []; @@ -174,7 +246,7 @@ export class CMIArray extends BaseCMI { * @private */ set _children(_children) { - this.API.throwSCORMError(this.#errorCode); + throw new ValidationError(this.#errorCode); } /** @@ -192,7 +264,7 @@ export class CMIArray extends BaseCMI { * @private */ set _count(_count) { - this.API.throwSCORMError(this.#errorCode); + throw new ValidationError(this.#errorCode); } /** diff --git a/src/cmi/scorm12_cmi.js b/src/cmi/scorm12_cmi.js index f8ef781..2a98e91 100644 --- a/src/cmi/scorm12_cmi.js +++ b/src/cmi/scorm12_cmi.js @@ -1,34 +1,60 @@ // @flow -import {BaseCMI, CMIArray, CMIScore} from './common'; +import { + BaseCMI, + checkValidFormat, + checkValidRange, + CMIArray, + CMIScore, +} from './common'; import {scorm12_constants} from '../constants/api_constants'; import {scorm12_error_codes} from '../constants/error_codes'; import {scorm12_regex} from '../regex'; +import {ValidationError} from '../exceptions'; const constants = scorm12_constants; const regex = scorm12_regex; /** * Helper method for throwing Read Only error - * @param {Scorm12API} API */ -function throwReadOnlyError(API) { - API.throwSCORMError(scorm12_error_codes.READ_ONLY_ELEMENT); +export function throwReadOnlyError() { + throw new ValidationError(scorm12_error_codes.READ_ONLY_ELEMENT); } /** * Helper method for throwing Write Only error - * @param {Scorm12API} API */ -function throwWriteOnlyError(API) { - API.throwSCORMError(scorm12_error_codes.WRITE_ONLY_ELEMENT); +export function throwWriteOnlyError() { + throw new ValidationError(scorm12_error_codes.WRITE_ONLY_ELEMENT); } /** * Helper method for throwing Invalid Set error - * @param {Scorm12API} API */ -function throwInvalidValueError(API) { - API.throwSCORMError(scorm12_error_codes.INVALID_SET_VALUE); +function throwInvalidValueError() { + throw new ValidationError(scorm12_error_codes.INVALID_SET_VALUE); +} + +/** + * Helper method, no reason to have to pass the same error codes every time + * @param {*} value + * @param {string} regexPattern + * @return {boolean} + */ +export function check12ValidFormat(value: String, regexPattern: String) { + return checkValidFormat(value, regexPattern, + scorm12_error_codes.TYPE_MISMATCH); +} + +/** + * Helper method, no reason to have to pass the same error codes every time + * @param {*} value + * @param {string} rangePattern + * @return {boolean} + */ +export function check12ValidRange(value: any, rangePattern: String) { + return checkValidRange(value, rangePattern, + scorm12_error_codes.VALUE_OUT_OF_RANGE); } /** @@ -46,21 +72,33 @@ export class CMI extends BaseCMI { /** * Constructor for the SCORM 1.2 cmi object - * @param {Scorm12API} API * @param {string} cmi_children - * @param {string} student_data + * @param {(CMIStudentData|AICCCMIStudentData)} student_data + * @param {boolean} initialized */ - constructor(API, cmi_children, student_data) { - super(API); + constructor(cmi_children, student_data, initialized: boolean) { + super(); + + if (initialized) this.initialize(); this.#_children = cmi_children ? cmi_children : constants.cmi_children; - this.core = new CMICore(API); - this.objectives = new CMIObjectives(API); - this.student_data = student_data ? - student_data : - new CMIStudentData(API); - this.student_preference = new CMIStudentPreference(API); - this.interactions = new CMIInteractions(API); + this.core = new CMICore(); + this.objectives = new CMIObjectives(); + this.student_data = student_data ? student_data : new CMIStudentData(); + this.student_preference = new CMIStudentPreference(); + this.interactions = new CMIInteractions(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.core?.initialize(); + this.objectives?.initialize(); + this.student_data?.initialize(); + this.student_preference?.initialize(); + this.interactions?.initialize(); } /** @@ -112,7 +150,7 @@ export class CMI extends BaseCMI { * @private */ set _version(_version) { - throwInvalidValueError(this.API); + throwInvalidValueError(); } /** @@ -130,7 +168,7 @@ export class CMI extends BaseCMI { * @private */ set _children(_children) { - throwInvalidValueError(this.API); + throwInvalidValueError(); } /** @@ -146,7 +184,7 @@ export class CMI extends BaseCMI { * @param {string} suspend_data */ set suspend_data(suspend_data) { - if (this.API.checkValidFormat(suspend_data, regex.CMIString4096)) { + if (check12ValidFormat(suspend_data, regex.CMIString4096)) { this.#suspend_data = suspend_data; } } @@ -160,13 +198,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #launch_data. Can only be called before API initialization. + * Setter for #launch_data. Can only be called before initialization. * @param {string} launch_data */ set launch_data(launch_data) { - this.API.isNotInitialized() ? - this.#launch_data = launch_data : - throwReadOnlyError(this.API); + !this.initialized ? this.#launch_data = launch_data : throwReadOnlyError(); } /** @@ -182,7 +218,7 @@ export class CMI extends BaseCMI { * @param {string} comments */ set comments(comments) { - if (this.API.checkValidFormat(comments, regex.CMIString4096)) { + if (check12ValidFormat(comments, regex.CMIString4096)) { this.#comments = comments; } } @@ -196,13 +232,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #comments_from_lms. Can only be called before API initialization. + * Setter for #comments_from_lms. Can only be called before initialization. * @param {string} comments_from_lms */ set comments_from_lms(comments_from_lms) { - this.API.isNotInitialized() ? - this.#comments_from_lms = comments_from_lms : - throwReadOnlyError(this.API); + !this.initialized ? this.#comments_from_lms = comments_from_lms : throwReadOnlyError(); } } @@ -212,13 +246,26 @@ export class CMI extends BaseCMI { class CMICore extends BaseCMI { /** * Constructor for cmi.core - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); - this.score = new Scorm12CMIScore(API, constants.score_children, - regex.score_range); + this.score = new Scorm12CMIScore( + { + score_children: constants.score_children, + score_range: regex.score_range, + invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE, + invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH, + invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE, + }); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.score?.initialize(); } #_children = constants.core_children; @@ -248,7 +295,7 @@ class CMICore extends BaseCMI { * @private */ set _children(_children) { - throwInvalidValueError(this.API); + throwInvalidValueError(); } /** @@ -260,13 +307,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #student_id. Can only be called before API initialization. + * Setter for #student_id. Can only be called before initialization. * @param {string} student_id */ set student_id(student_id) { - this.API.isNotInitialized() ? - this.#student_id = student_id : - throwReadOnlyError(this.API); + !this.initialized ? this.#student_id = student_id : throwReadOnlyError(); } /** @@ -278,13 +323,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #student_name. Can only be called before API initialization. + * Setter for #student_name. Can only be called before initialization. * @param {string} student_name */ set student_name(student_name) { - this.API.isNotInitialized() ? - this.#student_name = student_name : - throwReadOnlyError(this.API); + !this.initialized ? this.#student_name = student_name : throwReadOnlyError(); } /** @@ -300,7 +343,7 @@ class CMICore extends BaseCMI { * @param {string} lesson_location */ set lesson_location(lesson_location) { - if (this.API.checkValidFormat(lesson_location, regex.CMIString256)) { + if (check12ValidFormat(lesson_location, regex.CMIString256)) { this.#lesson_location = lesson_location; } } @@ -314,13 +357,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #credit. Can only be called before API initialization. + * Setter for #credit. Can only be called before initialization. * @param {string} credit */ set credit(credit) { - this.API.isNotInitialized() ? - this.#credit = credit : - throwReadOnlyError(this.API); + !this.initialized ? this.#credit = credit : throwReadOnlyError(); } /** @@ -336,7 +377,7 @@ class CMICore extends BaseCMI { * @param {string} lesson_status */ set lesson_status(lesson_status) { - if (this.API.checkValidFormat(lesson_status, regex.CMIStatus)) { + if (check12ValidFormat(lesson_status, regex.CMIStatus)) { this.#lesson_status = lesson_status; } } @@ -350,13 +391,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #entry. Can only be called before API initialization. + * Setter for #entry. Can only be called before initialization. * @param {string} entry */ set entry(entry) { - this.API.isNotInitialized() ? - this.#entry = entry : - throwReadOnlyError(this.API); + !this.initialized ? this.#entry = entry : throwReadOnlyError(); } /** @@ -368,13 +407,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #total_time. Can only be called before API initialization. + * Setter for #total_time. Can only be called before initialization. * @param {string} total_time */ set total_time(total_time) { - this.API.isNotInitialized() ? - this.#total_time = total_time : - throwReadOnlyError(this.API); + !this.initialized ? this.#total_time = total_time : throwReadOnlyError(); } /** @@ -386,13 +423,11 @@ class CMICore extends BaseCMI { } /** - * Setter for #lesson_mode. Can only be called before API initialization. + * Setter for #lesson_mode. Can only be called before initialization. * @param {string} lesson_mode */ set lesson_mode(lesson_mode) { - this.API.isNotInitialized() ? - this.#lesson_mode = lesson_mode : - throwReadOnlyError(this.API); + !this.initialized ? this.#lesson_mode = lesson_mode : throwReadOnlyError(); } /** @@ -400,7 +435,7 @@ class CMICore extends BaseCMI { * @return {*} */ get exit() { - return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#exit; + return (!this.jsonString) ? throwWriteOnlyError() : this.#exit; } /** @@ -408,7 +443,7 @@ class CMICore extends BaseCMI { * @param {string} exit */ set exit(exit) { - if (this.API.checkValidFormat(exit, regex.CMIExit)) { + if (check12ValidFormat(exit, regex.CMIExit)) { this.#exit = exit; } } @@ -418,9 +453,7 @@ class CMICore extends BaseCMI { * @return {*} */ get session_time() { - return (!this.jsonString) ? - throwWriteOnlyError(this.API) : - this.#session_time; + return (!this.jsonString) ? throwWriteOnlyError() : this.#session_time; } /** @@ -428,7 +461,7 @@ class CMICore extends BaseCMI { * @param {string} session_time */ set session_time(session_time) { - if (this.API.checkValidFormat(session_time, regex.CMITimespan)) { + if (check12ValidFormat(session_time, regex.CMITimespan)) { this.#session_time = session_time; } } @@ -478,11 +511,9 @@ class CMICore extends BaseCMI { class CMIObjectives extends CMIArray { /** * Constructor for cmi.objectives - * @param {Scorm12API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.objectives_children, errorCode: scorm12_error_codes.INVALID_SET_VALUE, }); @@ -500,11 +531,10 @@ export class CMIStudentData extends BaseCMI { /** * Constructor for cmi.student_data - * @param {Scorm12API} API * @param {string} student_data_children */ - constructor(API, student_data_children) { - super(API); + constructor(student_data_children) { + super(); this.#_children = student_data_children ? student_data_children : @@ -526,7 +556,7 @@ export class CMIStudentData extends BaseCMI { * @private */ set _children(_children) { - throwInvalidValueError(this.API); + throwInvalidValueError(); } /** @@ -538,13 +568,11 @@ export class CMIStudentData extends BaseCMI { } /** - * Setter for #master_score. Can only be called before API initialization. + * Setter for #master_score. Can only be called before initialization. * @param {string} mastery_score */ set mastery_score(mastery_score) { - this.API.isNotInitialized() ? - this.#mastery_score = mastery_score : - throwReadOnlyError(this.API); + !this.initialized ? this.#mastery_score = mastery_score : throwReadOnlyError(); } /** @@ -556,13 +584,11 @@ export class CMIStudentData extends BaseCMI { } /** - * Setter for #max_time_allowed. Can only be called before API initialization. + * Setter for #max_time_allowed. Can only be called before initialization. * @param {string} max_time_allowed */ set max_time_allowed(max_time_allowed) { - this.API.isNotInitialized() ? - this.#max_time_allowed = max_time_allowed : - throwReadOnlyError(this.API); + !this.initialized ? this.#max_time_allowed = max_time_allowed : throwReadOnlyError(); } /** @@ -574,13 +600,11 @@ export class CMIStudentData extends BaseCMI { } /** - * Setter for #time_limit_action. Can only be called before API initialization. + * Setter for #time_limit_action. Can only be called before initialization. * @param {string} time_limit_action */ set time_limit_action(time_limit_action) { - this.API.isNotInitialized() ? - this.#time_limit_action = time_limit_action : - throwReadOnlyError(this.API); + !this.initialized ? this.#time_limit_action = time_limit_action : throwReadOnlyError(); } /** @@ -612,10 +636,9 @@ export class CMIStudentData extends BaseCMI { class CMIStudentPreference extends BaseCMI { /** * Constructor for cmi.student_preference - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); } #_children = constants.student_preference_children; @@ -639,7 +662,7 @@ class CMIStudentPreference extends BaseCMI { * @private */ set _children(_children) { - throwInvalidValueError(this.API); + throwInvalidValueError(); } /** @@ -655,8 +678,8 @@ class CMIStudentPreference extends BaseCMI { * @param {string} audio */ set audio(audio) { - if (this.API.checkValidFormat(audio, regex.CMISInteger) && - this.API.checkValidRange(audio, regex.audio_range)) { + if (check12ValidFormat(audio, regex.CMISInteger) && + check12ValidRange(audio, regex.audio_range)) { this.#audio = audio; } } @@ -674,7 +697,7 @@ class CMIStudentPreference extends BaseCMI { * @param {string} language */ set language(language) { - if (this.API.checkValidFormat(language, regex.CMIString256)) { + if (check12ValidFormat(language, regex.CMIString256)) { this.#language = language; } } @@ -692,8 +715,8 @@ class CMIStudentPreference extends BaseCMI { * @param {string} speed */ set speed(speed) { - if (this.API.checkValidFormat(speed, regex.CMISInteger) && - this.API.checkValidRange(speed, regex.speed_range)) { + if (check12ValidFormat(speed, regex.CMISInteger) && + check12ValidRange(speed, regex.speed_range)) { this.#speed = speed; } } @@ -711,8 +734,8 @@ class CMIStudentPreference extends BaseCMI { * @param {string} text */ set text(text) { - if (this.API.checkValidFormat(text, regex.CMISInteger) && - this.API.checkValidRange(text, regex.text_range)) { + if (check12ValidFormat(text, regex.CMISInteger) && + check12ValidRange(text, regex.text_range)) { this.#text = text; } } @@ -748,11 +771,9 @@ class CMIStudentPreference extends BaseCMI { class CMIInteractions extends CMIArray { /** * Constructor for cmi.interactions - * @param {Scorm12API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.interactions_children, errorCode: scorm12_error_codes.INVALID_SET_VALUE, }); @@ -765,23 +786,29 @@ class CMIInteractions extends CMIArray { export class CMIInteractionsObject extends BaseCMI { /** * Constructor for cmi.interactions.n object - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); this.objectives = new CMIArray({ - API: API, - errorCode: 402, + errorCode: scorm12_error_codes.INVALID_SET_VALUE, children: constants.objectives_children, }); this.correct_responses = new CMIArray({ - API: API, - errorCode: 402, + errorCode: scorm12_error_codes.INVALID_SET_VALUE, children: constants.correct_responses_children, }); } + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.objectives?.initialize(); + this.correct_responses?.initialize(); + } + #id: ''; #time: ''; #type: ''; @@ -795,7 +822,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get id() { - return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#id; + return (!this.jsonString) ? throwWriteOnlyError() : this.#id; } /** @@ -803,7 +830,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { + if (check12ValidFormat(id, regex.CMIIdentifier)) { this.#id = id; } } @@ -813,7 +840,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get time() { - return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#time; + return (!this.jsonString) ? throwWriteOnlyError() : this.#time; } /** @@ -821,7 +848,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} time */ set time(time) { - if (this.API.checkValidFormat(time, regex.CMITime)) { + if (check12ValidFormat(time, regex.CMITime)) { this.#time = time; } } @@ -831,7 +858,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get type() { - return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#type; + return (!this.jsonString) ? throwWriteOnlyError() : this.#type; } /** @@ -839,7 +866,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} type */ set type(type) { - if (this.API.checkValidFormat(type, regex.CMIType)) { + if (check12ValidFormat(type, regex.CMIType)) { this.#type = type; } } @@ -850,7 +877,7 @@ export class CMIInteractionsObject extends BaseCMI { */ get weighting() { return (!this.jsonString) ? - throwWriteOnlyError(this.API) : + throwWriteOnlyError() : this.#weighting; } @@ -859,8 +886,8 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} weighting */ set weighting(weighting) { - if (this.API.checkValidFormat(weighting, regex.CMIDecimal) && - this.API.checkValidRange(weighting, regex.weighting_range)) { + if (check12ValidFormat(weighting, regex.CMIDecimal) && + check12ValidRange(weighting, regex.weighting_range)) { this.#weighting = weighting; } } @@ -870,9 +897,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get student_response() { - return (!this.jsonString) ? - throwWriteOnlyError(this.API) : - this.#student_response; + return (!this.jsonString) ? throwWriteOnlyError() : this.#student_response; } /** @@ -880,7 +905,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} student_response */ set student_response(student_response) { - if (this.API.checkValidFormat(student_response, regex.CMIFeedback)) { + if (check12ValidFormat(student_response, regex.CMIFeedback)) { this.#student_response = student_response; } } @@ -890,9 +915,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get result() { - return (!this.jsonString) ? - throwWriteOnlyError(this.API) : - this.#result; + return (!this.jsonString) ? throwWriteOnlyError() : this.#result; } /** @@ -900,7 +923,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} result */ set result(result) { - if (this.API.checkValidFormat(result, regex.CMIResult)) { + if (check12ValidFormat(result, regex.CMIResult)) { this.#result = result; } } @@ -910,9 +933,7 @@ export class CMIInteractionsObject extends BaseCMI { * @return {*} */ get latency() { - return (!this.jsonString) ? - throwWriteOnlyError(this.API) : - this.#latency; + return (!this.jsonString) ? throwWriteOnlyError() : this.#latency; } /** @@ -920,7 +941,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} latency */ set latency(latency) { - if (this.API.checkValidFormat(latency, regex.CMITimespan)) { + if (check12ValidFormat(latency, regex.CMITimespan)) { this.#latency = latency; } } @@ -966,12 +987,11 @@ export class CMIInteractionsObject extends BaseCMI { export class CMIObjectivesObject extends BaseCMI { /** * Constructor for cmi.objectives.n - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); - this.score = new Scorm12CMIScore(API); + this.score = new Scorm12CMIScore(); } #id: ''; @@ -990,7 +1010,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { + if (check12ValidFormat(id, regex.CMIIdentifier)) { this.#id = id; } } @@ -1008,7 +1028,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} status */ set status(status) { - if (this.API.checkValidFormat(status, regex.CMIStatus2)) { + if (check12ValidFormat(status, regex.CMIStatus2)) { this.#status = status; } } @@ -1041,10 +1061,9 @@ export class CMIObjectivesObject extends BaseCMI { export class CMIInteractionsObjectivesObject extends BaseCMI { /** * Constructor for cmi.interactions.n.objectives.n - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); } #id: ''; @@ -1062,7 +1081,7 @@ export class CMIInteractionsObjectivesObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { + if (check12ValidFormat(id, regex.CMIIdentifier)) { this.#id = id; } } @@ -1091,10 +1110,9 @@ export class CMIInteractionsObjectivesObject extends BaseCMI { export class CMIInteractionsCorrectResponsesObject extends BaseCMI { /** * Constructor for cmi.interactions.correct_responses.n - * @param {Scorm12API} API */ - constructor(API) { - super(API); + constructor() { + super(); } #pattern: ''; @@ -1112,7 +1130,7 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI { * @param {string} pattern */ set pattern(pattern) { - if (this.API.checkValidFormat(pattern, regex.CMIFeedback)) { + if (check12ValidFormat(pattern, regex.CMIFeedback)) { this.#pattern = pattern; } } diff --git a/src/cmi/scorm2004_cmi.js b/src/cmi/scorm2004_cmi.js index 94ac398..e429854 100644 --- a/src/cmi/scorm2004_cmi.js +++ b/src/cmi/scorm2004_cmi.js @@ -1,56 +1,67 @@ // @flow -import {BaseCMI, CMIArray, CMIScore} from './common'; +import { + BaseCMI, + checkValidFormat, + checkValidRange, + CMIArray, + CMIScore, +} from './common'; import {scorm2004_constants} from '../constants/api_constants'; import {scorm2004_regex} from '../regex'; import {scorm2004_error_codes} from '../constants/error_codes'; import {learner_responses} from '../constants/response_constants'; +import {ValidationError} from '../exceptions'; const constants = scorm2004_constants; const regex = scorm2004_regex; /** * Helper method for throwing Read Only error - * @param {Scorm2004API} API */ -function throwReadOnlyError(API) { - API.throwSCORMError(scorm2004_error_codes.READ_ONLY_ELEMENT); +function throwReadOnlyError() { + throw new ValidationError(scorm2004_error_codes.READ_ONLY_ELEMENT); } /** * Helper method for throwing Write Only error - * @param {Scorm2004API} API */ -function throwWriteOnlyError(API) { - API.throwSCORMError(scorm2004_error_codes.WRITE_ONLY_ELEMENT); +function throwWriteOnlyError() { + throw new ValidationError(scorm2004_error_codes.WRITE_ONLY_ELEMENT); } /** * Helper method for throwing Type Mismatch error - * @param {Scorm2004API} API */ -function throwTypeMismatchError(API) { - API.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); +function throwTypeMismatchError() { + throw new ValidationError(scorm2004_error_codes.TYPE_MISMATCH); +} + +/** + * Helper method, no reason to have to pass the same error codes every time + * @param {*} value + * @param {string} regexPattern + * @return {boolean} + */ +function check2004ValidFormat(value: String, regexPattern: String) { + return checkValidFormat(value, regexPattern, + scorm2004_error_codes.TYPE_MISMATCH); +} + +/** + * Helper method, no reason to have to pass the same error codes every time + * @param {*} value + * @param {string} rangePattern + * @return {boolean} + */ +function check2004ValidRange(value: any, rangePattern: String) { + return checkValidRange(value, rangePattern, + scorm2004_error_codes.VALUE_OUT_OF_RANGE); } /** * Class representing cmi object for SCORM 2004 */ export class CMI extends BaseCMI { - /** - * Constructor for the SCORM 2004 cmi object - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - - this.learner_preference = new CMILearnerPreference(API); - this.score = new Scorm2004CMIScore(API); - this.comments_from_learner = new CMICommentsFromLearner(API); - this.comments_from_lms = new CMICommentsFromLMS(API); - this.interactions = new CMIInteractions(API); - this.objectives = new CMIObjectives(API); - } - #_version = '1.0'; #_children = constants.cmi_children; #completion_status = 'unknown'; @@ -72,6 +83,36 @@ export class CMI extends BaseCMI { #time_limit_action = 'continue,no message'; #total_time = '0'; + /** + * Constructor for the SCORM 2004 cmi object + * @param {boolean} initialized + */ + constructor(initialized: boolean) { + super(); + + if (initialized) this.initialize(); + + this.learner_preference = new CMILearnerPreference(); + this.score = new Scorm2004CMIScore(); + this.comments_from_learner = new CMICommentsFromLearner(); + this.comments_from_lms = new CMICommentsFromLMS(); + this.interactions = new CMIInteractions(); + this.objectives = new CMIObjectives(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.learner_preference?.initialize(); + this.score?.initialize(); + this.comments_from_learner?.initialize(); + this.comments_from_lms?.initialize(); + this.interactions?.initialize(); + this.objectives?.initialize(); + } + /** * Getter for #_version * @return {string} @@ -87,7 +128,7 @@ export class CMI extends BaseCMI { * @private */ set _version(_version) { - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -105,7 +146,7 @@ export class CMI extends BaseCMI { * @private */ set _children(_children) { - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -121,7 +162,7 @@ export class CMI extends BaseCMI { * @param {string} completion_status */ set completion_status(completion_status) { - if (this.API.checkValidFormat(completion_status, regex.CMICStatus)) { + if (check2004ValidFormat(completion_status, regex.CMICStatus)) { this.#completion_status = completion_status; } } @@ -135,13 +176,13 @@ export class CMI extends BaseCMI { } /** - * Setter for #completion_threshold. Can only be called before API initialization. + * Setter for #completion_threshold. Can only be called before initialization. * @param {string} completion_threshold */ set completion_threshold(completion_threshold) { - this.API.isNotInitialized() ? + !this.initialized ? this.#completion_threshold = completion_threshold : - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -153,13 +194,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #credit. Can only be called before API initialization. + * Setter for #credit. Can only be called before initialization. * @param {string} credit */ set credit(credit) { - this.API.isNotInitialized() ? - this.#credit = credit : - throwReadOnlyError(this.API); + !this.initialized ? this.#credit = credit : throwReadOnlyError(); } /** @@ -171,13 +210,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #entry. Can only be called before API initialization. + * Setter for #entry. Can only be called before initialization. * @param {string} entry */ set entry(entry) { - this.API.isNotInitialized() ? - this.#entry = entry : - throwReadOnlyError(this.API); + !this.initialized ? this.#entry = entry : throwReadOnlyError(); } /** @@ -193,7 +230,7 @@ export class CMI extends BaseCMI { * @param {string} exit */ set exit(exit) { - if (this.API.checkValidFormat(exit, regex.CMIExit)) { + if (check2004ValidFormat(exit, regex.CMIExit)) { this.#exit = exit; } } @@ -207,13 +244,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #launch_data. Can only be called before API initialization. + * Setter for #launch_data. Can only be called before initialization. * @param {string} launch_data */ set launch_data(launch_data) { - this.API.isNotInitialized() ? - this.#launch_data = launch_data : - throwReadOnlyError(this.API); + !this.initialized ? this.#launch_data = launch_data : throwReadOnlyError(); } /** @@ -225,13 +260,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #learner_id. Can only be called before API initialization. + * Setter for #learner_id. Can only be called before initialization. * @param {string} learner_id */ set learner_id(learner_id) { - this.API.isNotInitialized() ? - this.#learner_id = learner_id : - throwReadOnlyError(this.API); + !this.initialized ? this.#learner_id = learner_id : throwReadOnlyError(); } /** @@ -243,13 +276,13 @@ export class CMI extends BaseCMI { } /** - * Setter for #learner_name. Can only be called before API initialization. + * Setter for #learner_name. Can only be called before initialization. * @param {string} learner_name */ set learner_name(learner_name) { - this.API.isNotInitialized() ? + !this.initialized ? this.#learner_name = learner_name : - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -265,7 +298,7 @@ export class CMI extends BaseCMI { * @param {string} location */ set location(location) { - if (this.API.checkValidFormat(location, regex.CMIString1000)) { + if (check2004ValidFormat(location, regex.CMIString1000)) { this.#location = location; } } @@ -279,13 +312,13 @@ export class CMI extends BaseCMI { } /** - * Setter for #max_time_allowed. Can only be called before API initialization. + * Setter for #max_time_allowed. Can only be called before initialization. * @param {string} max_time_allowed */ set max_time_allowed(max_time_allowed) { - this.API.isNotInitialized() ? + !this.initialized ? this.#max_time_allowed = max_time_allowed : - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -297,13 +330,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #mode. Can only be called before API initialization. + * Setter for #mode. Can only be called before initialization. * @param {string} mode */ set mode(mode) { - this.API.isNotInitialized() ? - this.#mode = mode : - throwReadOnlyError(this.API); + !this.initialized ? this.#mode = mode : throwReadOnlyError(); } /** @@ -319,8 +350,8 @@ export class CMI extends BaseCMI { * @param {string} progress_measure */ set progress_measure(progress_measure) { - if (this.API.checkValidFormat(progress_measure, regex.CMIDecimal) && - this.API.checkValidRange(progress_measure, regex.progress_range)) { + if (check2004ValidFormat(progress_measure, regex.CMIDecimal) && + check2004ValidRange(progress_measure, regex.progress_range)) { this.#progress_measure = progress_measure; } } @@ -334,13 +365,13 @@ export class CMI extends BaseCMI { } /** - * Setter for #scaled_passing_score. Can only be called before API initialization. + * Setter for #scaled_passing_score. Can only be called before initialization. * @param {string} scaled_passing_score */ set scaled_passing_score(scaled_passing_score) { - this.API.isNotInitialized() ? + !this.initialized ? this.#scaled_passing_score = scaled_passing_score : - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -348,9 +379,7 @@ export class CMI extends BaseCMI { * @return {string} */ get session_time() { - return (!this.jsonString) ? - this.API.throwSCORMError(405) : - this.#session_time; + return (!this.jsonString) ? throwWriteOnlyError() : this.#session_time; } /** @@ -358,7 +387,7 @@ export class CMI extends BaseCMI { * @param {string} session_time */ set session_time(session_time) { - if (this.API.checkValidFormat(session_time, regex.CMITimespan)) { + if (check2004ValidFormat(session_time, regex.CMITimespan)) { this.#session_time = session_time; } } @@ -376,7 +405,7 @@ export class CMI extends BaseCMI { * @param {string} success_status */ set success_status(success_status) { - if (this.API.checkValidFormat(success_status, regex.CMISStatus)) { + if (check2004ValidFormat(success_status, regex.CMISStatus)) { this.#success_status = success_status; } } @@ -394,7 +423,7 @@ export class CMI extends BaseCMI { * @param {string} suspend_data */ set suspend_data(suspend_data) { - if (this.API.checkValidFormat(suspend_data, regex.CMIString64000)) { + if (check2004ValidFormat(suspend_data, regex.CMIString64000)) { this.#suspend_data = suspend_data; } } @@ -408,13 +437,13 @@ export class CMI extends BaseCMI { } /** - * Setter for #time_limit_action. Can only be called before API initialization. + * Setter for #time_limit_action. Can only be called before initialization. * @param {string} time_limit_action */ set time_limit_action(time_limit_action) { - this.API.isNotInitialized() ? + !this.initialized ? this.#time_limit_action = time_limit_action : - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -426,13 +455,11 @@ export class CMI extends BaseCMI { } /** - * Setter for #total_time. Can only be called before API initialization. + * Setter for #total_time. Can only be called before initialization. * @param {string} total_time */ set total_time(total_time) { - this.API.isNotInitialized() ? - this.#total_time = total_time : - throwReadOnlyError(this.API); + !this.initialized ? this.#total_time = total_time : throwReadOnlyError(); } /** @@ -504,20 +531,19 @@ export class CMI extends BaseCMI { * Class for SCORM 2004's cmi.learner_preference object */ class CMILearnerPreference extends BaseCMI { - /** - * Constructor for cmi.learner_preference - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - } - #_children = constants.student_preference_children; #audio_level = '1'; #language = ''; #delivery_speed = '1'; #audio_captioning = '0'; + /** + * Constructor for cmi.learner_preference + */ + constructor() { + super(); + } + /** * Getter for #_children * @return {string} @@ -533,7 +559,7 @@ class CMILearnerPreference extends BaseCMI { * @private */ set _children(_children) { - throwReadOnlyError(this.API); + throwReadOnlyError(); } /** @@ -549,8 +575,8 @@ class CMILearnerPreference extends BaseCMI { * @param {string} audio_level */ set audio_level(audio_level) { - if (this.API.checkValidFormat(audio_level, regex.CMIDecimal) && - this.API.checkValidRange(audio_level, regex.audio_range)) { + if (check2004ValidFormat(audio_level, regex.CMIDecimal) && + check2004ValidRange(audio_level, regex.audio_range)) { this.#audio_level = audio_level; } } @@ -568,7 +594,7 @@ class CMILearnerPreference extends BaseCMI { * @param {string} language */ set language(language) { - if (this.API.checkValidFormat(language, regex.CMILang)) { + if (check2004ValidFormat(language, regex.CMILang)) { this.#language = language; } } @@ -586,8 +612,8 @@ class CMILearnerPreference extends BaseCMI { * @param {string} delivery_speed */ set delivery_speed(delivery_speed) { - if (this.API.checkValidFormat(delivery_speed, regex.CMIDecimal) && - this.API.checkValidRange(delivery_speed, regex.speed_range)) { + if (check2004ValidFormat(delivery_speed, regex.CMIDecimal) && + check2004ValidRange(delivery_speed, regex.speed_range)) { this.#delivery_speed = delivery_speed; } } @@ -605,8 +631,8 @@ class CMILearnerPreference extends BaseCMI { * @param {string} audio_captioning */ set audio_captioning(audio_captioning) { - if (this.API.checkValidFormat(audio_captioning, regex.CMISInteger) && - this.API.checkValidRange(audio_captioning, regex.text_range)) { + if (check2004ValidFormat(audio_captioning, regex.CMISInteger) && + check2004ValidRange(audio_captioning, regex.text_range)) { this.#audio_captioning = audio_captioning; } } @@ -642,11 +668,9 @@ class CMILearnerPreference extends BaseCMI { class CMIInteractions extends CMIArray { /** * Constructor for cmi.objectives Array - * @param {Scorm2004API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.objectives_children, errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, }); @@ -659,11 +683,9 @@ class CMIInteractions extends CMIArray { class CMIObjectives extends CMIArray { /** * Constructor for cmi.objectives Array - * @param {Scorm2004API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.objectives_children, errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, }); @@ -676,11 +698,9 @@ class CMIObjectives extends CMIArray { class CMICommentsFromLMS extends CMIArray { /** * Constructor for cmi.comments_from_lms Array - * @param {Scorm2004API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.comments_children, errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, }); @@ -693,11 +713,9 @@ class CMICommentsFromLMS extends CMIArray { class CMICommentsFromLearner extends CMIArray { /** * Constructor for cmi.comments_from_learner Array - * @param {Scorm2004API} API */ - constructor(API) { + constructor() { super({ - API: API, children: constants.comments_children, errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, }); @@ -708,25 +726,6 @@ class CMICommentsFromLearner extends CMIArray { * Class for SCORM 2004's cmi.interaction.n object */ export class CMIInteractionsObject extends BaseCMI { - /** - * Constructor for cmi.interaction.n - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - - this.objectives = new CMIArray({ - API: API, - errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, - children: constants.objectives_children, - }); - this.correct_responses = new CMIArray({ - API: API, - errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, - children: constants.correct_responses_children, - }); - } - #id = ''; #type = ''; #timestamp = ''; @@ -736,6 +735,31 @@ export class CMIInteractionsObject extends BaseCMI { #latency = ''; #description = ''; + /** + * Constructor for cmi.interaction.n + */ + constructor() { + super(); + + this.objectives = new CMIArray({ + errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, + children: constants.objectives_children, + }); + this.correct_responses = new CMIArray({ + errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT, + children: constants.correct_responses_children, + }); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.objectives?.initialize(); + this.correct_responses?.initialize(); + } + /** * Getter for #id * @return {string} @@ -749,7 +773,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMILongIdentifier)) { + if (check2004ValidFormat(id, regex.CMILongIdentifier)) { this.#id = id; } } @@ -767,7 +791,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} type */ set type(type) { - if (this.API.checkValidFormat(type, regex.CMIType)) { + if (check2004ValidFormat(type, regex.CMIType)) { this.#type = type; } } @@ -785,7 +809,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} timestamp */ set timestamp(timestamp) { - if (this.API.checkValidFormat(timestamp, regex.CMITime)) { + if (check2004ValidFormat(timestamp, regex.CMITime)) { this.#timestamp = timestamp; } } @@ -803,7 +827,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} weighting */ set weighting(weighting) { - if (this.API.checkValidFormat(weighting, regex.CMIDecimal)) { + if (check2004ValidFormat(weighting, regex.CMIDecimal)) { this.#weighting = weighting; } } @@ -823,7 +847,8 @@ export class CMIInteractionsObject extends BaseCMI { */ set learner_response(learner_response) { if (typeof this.type === 'undefined') { - this.API.throwSCORMError(this.API.error.DEPENDENCY_NOT_ESTABLISHED); + throw new ValidationError( + scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { let nodes = []; const response_type = learner_responses[this.type]; @@ -835,29 +860,28 @@ export class CMIInteractionsObject extends BaseCMI { if ((nodes.length > 0) && (nodes.length <= response_type.max)) { const formatRegex = new RegExp(response_type.format); - for (let i = 0; (i < nodes.length) && - (this.API.lastErrorCode === 0); i++) { + for (let i = 0; i < nodes.length; i++) { if (typeof response_type.delimiter2 !== 'undefined') { const values = nodes[i].split(response_type.delimiter2); if (values.length === 2) { if (!values[0].match(formatRegex)) { - throwTypeMismatchError(this.API); + throwTypeMismatchError(); } else { if (!values[1].match(new RegExp(response_type.format2))) { - throwTypeMismatchError(this.API); + throwTypeMismatchError(); } } } else { - throwTypeMismatchError(this.API); + throwTypeMismatchError(); } } else { if (!nodes[i].match(formatRegex)) { - throwTypeMismatchError(this.API); + throwTypeMismatchError(); } else { if (nodes[i] !== '' && response_type.unique) { - for (let j = 0; (j < i) && this.API.lastErrorCode === 0; j++) { + for (let j = 0; j < i; j++) { if (nodes[i] === nodes[j]) { - throwTypeMismatchError(this.API); + throwTypeMismatchError(); } } } @@ -865,7 +889,7 @@ export class CMIInteractionsObject extends BaseCMI { } } } else { - this.API.throwSCORMError(this.API.error.GENERAL_SET_FAILURE); + throw new ValidationError(scorm2004_error_codes.GENERAL_SET_FAILURE); } } } @@ -883,7 +907,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} result */ set result(result) { - if (this.API.checkValidFormat(result, regex.CMIResult)) { + if (check2004ValidFormat(result, regex.CMIResult)) { this.#result = result; } } @@ -901,7 +925,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} latency */ set latency(latency) { - if (this.API.checkValidFormat(latency, regex.CMITimespan)) { + if (check2004ValidFormat(latency, regex.CMITimespan)) { this.#latency = latency; } } @@ -919,7 +943,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} description */ set description(description) { - if (this.API.checkValidFormat(description, regex.CMILangString250)) { + if (check2004ValidFormat(description, regex.CMILangString250)) { this.#description = description; } } @@ -965,22 +989,29 @@ export class CMIInteractionsObject extends BaseCMI { * Class for SCORM 2004's cmi.objectives.n object */ export class CMIObjectivesObject extends BaseCMI { - /** - * Constructor for cmi.objectives.n - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - - this.score = new Scorm2004CMIScore(API); - } - #id = ''; #success_status = 'unknown'; #completion_status = 'unknown'; #progress_measure = ''; #description = ''; + /** + * Constructor for cmi.objectives.n + */ + constructor() { + super(); + + this.score = new Scorm2004CMIScore(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.score?.initialize(); + } + /** * Getter for #id * @return {string} @@ -994,7 +1025,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMILongIdentifier)) { + if (check2004ValidFormat(id, regex.CMILongIdentifier)) { this.#id = id; } } @@ -1012,7 +1043,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} success_status */ set success_status(success_status) { - if (this.API.checkValidFormat(success_status, regex.CMISStatus)) { + if (check2004ValidFormat(success_status, regex.CMISStatus)) { this.#success_status = success_status; } } @@ -1030,7 +1061,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} completion_status */ set completion_status(completion_status) { - if (this.API.checkValidFormat(completion_status, regex.CMICStatus)) { + if (check2004ValidFormat(completion_status, regex.CMICStatus)) { this.#completion_status = completion_status; } } @@ -1048,8 +1079,8 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} progress_measure */ set progress_measure(progress_measure) { - if (this.API.checkValidFormat(progress_measure, regex.CMIDecimal) && - this.API.checkValidRange(progress_measure, regex.progress_range)) { + if (check2004ValidFormat(progress_measure, regex.CMIDecimal) && + check2004ValidRange(progress_measure, regex.progress_range)) { this.#progress_measure = progress_measure; } } @@ -1067,7 +1098,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} description */ set description(description) { - if (this.API.checkValidFormat(description, regex.CMILangString250)) { + if (check2004ValidFormat(description, regex.CMILangString250)) { this.#description = description; } } @@ -1103,18 +1134,22 @@ export class CMIObjectivesObject extends BaseCMI { * Class for SCORM 2004's cmi *.score object */ class Scorm2004CMIScore extends CMIScore { + #scaled = ''; + /** * Constructor for cmi *.score - * @param {Scorm2004API} API */ - constructor(API) { - super(API, constants.score_children); - - this.max = ''; + constructor() { + super( + { + score_children: constants.score_children, + max: '', + invalidErrorCode: scorm2004_error_codes.INVALID_SET_VALUE, + invalidTypeCode: scorm2004_error_codes.TYPE_MISMATCH, + invalidRangeCode: scorm2004_error_codes.VALUE_OUT_OF_RANGE, + }); } - #scaled = ''; - /** * Getter for #scaled * @return {string} @@ -1128,8 +1163,8 @@ class Scorm2004CMIScore extends CMIScore { * @param {string} scaled */ set scaled(scaled) { - if (this.API.checkValidFormat(scaled, regex.CMIDecimal) && - this.API.checkValidRange(scaled, regex.scaled_range)) { + if (check2004ValidFormat(scaled, regex.CMIDecimal) && + check2004ValidRange(scaled, regex.scaled_range)) { this.#scaled = scaled; } } @@ -1163,18 +1198,17 @@ class Scorm2004CMIScore extends CMIScore { * Class representing SCORM 2004's cmi.comments_from_learner.n object */ export class CMICommentsFromLearnerObject extends BaseCMI { - /** - * Constructor for cmi.comments_from_learner.n - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - } - #comment = ''; #location = ''; #timestamp = ''; + /** + * Constructor for cmi.comments_from_learner.n + */ + constructor() { + super(); + } + /** * Getter for #comment * @return {string} @@ -1188,7 +1222,7 @@ export class CMICommentsFromLearnerObject extends BaseCMI { * @param {string} comment */ set comment(comment) { - if (this.API.checkValidFormat(comment, regex.CMILangString4000)) { + if (check2004ValidFormat(comment, regex.CMILangString4000)) { this.#comment = comment; } } @@ -1206,7 +1240,7 @@ export class CMICommentsFromLearnerObject extends BaseCMI { * @param {string} location */ set location(location) { - if (this.API.checkValidFormat(location, regex.CMIString250)) { + if (check2004ValidFormat(location, regex.CMIString250)) { this.#location = location; } } @@ -1224,7 +1258,7 @@ export class CMICommentsFromLearnerObject extends BaseCMI { * @param {string} timestamp */ set timestamp(timestamp) { - if (this.API.checkValidFormat(timestamp, regex.CMITime)) { + if (check2004ValidFormat(timestamp, regex.CMITime)) { this.#timestamp = timestamp; } } @@ -1236,40 +1270,33 @@ export class CMICommentsFromLearnerObject extends BaseCMI { export class CMICommentsFromLMSObject extends CMICommentsFromLearnerObject { /** * Constructor for cmi.comments_from_lms.n - * @param {Scorm2004API} API */ - constructor(API) { - super(API); + constructor() { + super(); } /** - * Setter for #comment. Can only be called before API initialization. + * Setter for #comment. Can only be called before initialization. * @param {string} comment */ set comment(comment) { - this.API.isNotInitialized() ? - this.comment = comment : - throwReadOnlyError(this.API); + !this.initialized ? this.comment = comment : throwReadOnlyError(); } /** - * Setter for #location. Can only be called before API initialization. + * Setter for #location. Can only be called before initialization. * @param {string} location */ set location(location) { - this.API.isNotInitialized() ? - this.location = location : - throwReadOnlyError(this.API); + !this.initialized ? this.location = location : throwReadOnlyError(); } /** - * Setter for #timestamp. Can only be called before API initialization. + * Setter for #timestamp. Can only be called before initialization. * @param {string} timestamp */ set timestamp(timestamp) { - this.API.isNotInitialized() ? - this.timestamp = timestamp : - throwReadOnlyError(this.API); + !this.initialized ? this.timestamp = timestamp : throwReadOnlyError(); } } @@ -1277,16 +1304,15 @@ export class CMICommentsFromLMSObject extends CMICommentsFromLearnerObject { * Class representing SCORM 2004's cmi.interactions.n.objectives.n object */ export class CMIInteractionsObjectivesObject extends BaseCMI { + #id = ''; + /** * Constructor for cmi.interactions.n.objectives.n - * @param {Scorm2004API} API */ - constructor(API) { - super(API); + constructor() { + super(); } - #id = ''; - /** * Getter for #id * @return {string} @@ -1300,7 +1326,7 @@ export class CMIInteractionsObjectivesObject extends BaseCMI { * @param {string} id */ set id(id) { - if (this.API.checkValidFormat(id, regex.CMILongIdentifier)) { + if (check2004ValidFormat(id, regex.CMILongIdentifier)) { this.#id = id; } } @@ -1310,16 +1336,15 @@ export class CMIInteractionsObjectivesObject extends BaseCMI { * Class representing SCORM 2004's cmi.interactions.n.correct_responses.n object */ export class CMIInteractionsCorrectResponsesObject extends BaseCMI { + #pattern = ''; + /** * Constructor for cmi.interactions.n.correct_responses.n - * @param {Scorm2004API} API */ - constructor(API) { - super(API); + constructor() { + super(); } - #pattern = ''; - /** * Getter for #pattern * @return {string} @@ -1333,7 +1358,7 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI { * @param {string} pattern */ set pattern(pattern) { - if (this.API.checkValidFormat(pattern, regex.CMIFeedback)) { + if (check2004ValidFormat(pattern, regex.CMIFeedback)) { this.#pattern = pattern; } } @@ -1345,131 +1370,19 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI { export class ADL extends BaseCMI { /** * Constructor for adl - * @param {Scorm2004API} API */ - constructor(API) { - super(API); + constructor() { + super(); - this.nav = new class extends BaseCMI { - /** - * Constructor for adl.nav - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); + this.nav = new ADLNav(); + } - this.request_valid = new class extends BaseCMI { - /** - * Constructor for adl.nav.request_valid - * @param {Scorm2004API} API - */ - constructor(API) { - super(API); - } - - #continue = 'unknown'; - #previous = 'unknown'; - - /** - * Getter for #continue - * @return {string} - */ - get continue() { - return this.#continue; - } - - /** - * Setter for #continue. Just throws an error. - * @param {*} _ - */ - set continue(_) { - throwReadOnlyError(this.API); - } - - /** - * Getter for #previous - * @return {string} - */ - get previous() { - return this.#previous; - } - - /** - * Setter for #previous. Just throws an error. - * @param {*} _ - */ - set previous(_) { - throwReadOnlyError(this.API); - } - - choice = class { - _isTargetValid = (_target) => 'unknown'; - }(); - - jump = class { - _isTargetValid = (_target) => 'unknown'; - }(); - - /** - * toJSON for adl.nav.request_valid - * - * @return { - * { - * previous: string, - * continue: string - * } - * } - */ - toJSON() { - this.jsonString = true; - const result = { - 'previous': this.previous, - 'continue': this.continue, - }; - delete this.jsonString; - return result; - } - }(API); - } - - #request = '_none_'; - - /** - * Getter for #request - * @return {string} - */ - get request() { - return this.#request; - } - - /** - * Setter for #request - * @param {string} request - */ - set request(request) { - if (this.API.checkValidFormat(request, regex.NAVEvent)) { - this.#request = request; - } - } - - /** - * toJSON for adl.nav - * - * @return { - * { - * request: string - * } - * } - */ - toJSON() { - this.jsonString = true; - const result = { - 'request': this.request, - }; - delete this.jsonString; - return result; - } - }(API); + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.nav?.initialize(); } /** @@ -1491,3 +1404,136 @@ export class ADL extends BaseCMI { return result; } } + +/** + * Class representing SCORM 2004's adl.nav object + */ +class ADLNav extends BaseCMI { + #request = '_none_'; + + /** + * Constructor for adl.nav + */ + constructor() { + super(); + + this.request_valid = new ADLNavRequestValid(); + } + + /** + * Called when the API has been initialized after the CMI has been created + */ + initialize() { + super.initialize(); + this.request_valid?.initialize(); + } + + /** + * Getter for #request + * @return {string} + */ + get request() { + return this.#request; + } + + /** + * Setter for #request + * @param {string} request + */ + set request(request) { + if (check2004ValidFormat(request, regex.NAVEvent)) { + this.#request = request; + } + } + + /** + * toJSON for adl.nav + * + * @return { + * { + * request: string + * } + * } + */ + toJSON() { + this.jsonString = true; + const result = { + 'request': this.request, + }; + delete this.jsonString; + return result; + } +} + +/** + * Class representing SCORM 2004's adl.nav.request_valid object + */ +class ADLNavRequestValid extends BaseCMI { + #continue = 'unknown'; + #previous = 'unknown'; + choice = class { + _isTargetValid = (_target) => 'unknown'; + }; + jump = class { + _isTargetValid = (_target) => 'unknown'; + }; + + /** + * Constructor for adl.nav.request_valid + */ + constructor() { + super(); + } + + /** + * Getter for #continue + * @return {string} + */ + get continue() { + return this.#continue; + } + + /** + * Setter for #continue. Just throws an error. + * @param {*} _ + */ + set continue(_) { + throwReadOnlyError(); + } + + /** + * Getter for #previous + * @return {string} + */ + get previous() { + return this.#previous; + } + + /** + * Setter for #previous. Just throws an error. + * @param {*} _ + */ + set previous(_) { + throwReadOnlyError(); + } + + /** + * toJSON for adl.nav.request_valid + * + * @return { + * { + * previous: string, + * continue: string + * } + * } + */ + toJSON() { + this.jsonString = true; + const result = { + 'previous': this.previous, + 'continue': this.continue, + }; + delete this.jsonString; + return result; + } +} diff --git a/src/exceptions.js b/src/exceptions.js new file mode 100644 index 0000000..c1dc089 --- /dev/null +++ b/src/exceptions.js @@ -0,0 +1,33 @@ +// @flow + +/** + * Data Validation Exception + */ +export class ValidationError extends Error { + /** + * Constructor to take in an error message and code + * @param {number} errorCode + */ + constructor(errorCode: number) { + super(errorCode); + this.#errorCode = errorCode; + } + + #errorCode; + + /** + * Getter for #errorCode + * @return {number} + */ + get errorCode() { + return this.#errorCode; + } + + /** + * Trying to override the default Error message + * @return {string} + */ + get message() { + return this.#errorCode + ''; + } +} diff --git a/test/cmi/aicc_cmi.spec.js b/test/cmi/aicc_cmi.spec.js index 0975e8f..f2dd508 100644 --- a/test/cmi/aicc_cmi.spec.js +++ b/test/cmi/aicc_cmi.spec.js @@ -1,29 +1,27 @@ -import {expect, assert} from 'chai'; +import {expect} from 'chai'; import {describe, it, beforeEach, afterEach} from 'mocha'; -import AICC from '../../src/AICC'; import {aicc_constants} from '../../src/constants/api_constants'; import {scorm12_error_codes} from '../../src/constants/error_codes'; +import {CMI} from '../../src/cmi/aicc_cmi'; -let API; +let cmi; const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should be able to write upto ${limit} characters to ${fieldName}`, () => { - eval(`API.${fieldName} = 'x'.repeat(${limit})`); - expect(0).to.equal(API.lastErrorCode); + expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)). + to.not.throw(); }); it(`Should fail to write more than ${limit} characters to ${fieldName}`, () => { - eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`); - expect(scorm12_error_codes.TYPE_MISMATCH + ''). - to. - equal(API.lastErrorCode); + expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)). + to.throw(scorm12_error_codes.TYPE_MISMATCH + ''); }); }); }; @@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { const checkInvalidSet = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should fail to write to ${fieldName}`, () => { - eval(`API.${fieldName} = 'xxx'`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.INVALID_SET_VALUE + ''); + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm12_error_codes.INVALID_SET_VALUE + ''); }); }); }; @@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => { const checkReadOnly = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should fail to write to ${fieldName}`, () => { - eval(`API.${fieldName} = 'xxx'`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.READ_ONLY_ELEMENT + ''); + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + ''); }); }); }; @@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => { const checkRead = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); }); }; @@ -69,12 +63,12 @@ const checkRead = ({fieldName, expectedValue = ''}) => { const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)). + to.not.throw(); }); }); }; @@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should fail to read from ${fieldName}`, () => { - eval(`API.${fieldName}`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + ''); + expect(() => eval(`${fieldName}`)). + to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + ''); }); it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); }); }); }; @@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { const checkWrite = ({fieldName, valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); }); }); }; @@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} if ({}.hasOwnProperty.call(validValues, idx)) { it(`Should successfully write '${validValues[idx]}' to ${fieldName}`, () => { - eval(`API.${fieldName} = '${validValues[idx]}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${validValues[idx]}'`)). + to.not.throw(); }); } } @@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} if ({}.hasOwnProperty.call(invalidValues, idx)) { it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`, () => { - eval(`API.${fieldName} = '${invalidValues[idx]}'`); - expect(API.lastErrorCode).to.equal(expectedError + ''); + expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)). + to.throw(expectedError + ''); }); } } @@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} describe('AICC CMI Tests', () => { describe('CMI Spec Tests', () => { - beforeEach('Create the API object', () => { - API = new AICC(); - API.lmsInitialize(); - }); - afterEach('Destroy API object', () => { - API = null; - }); - - it('lmsInitialize should create CMI object', () => { - assert(API.cmi !== undefined, 'CMI object is created'); - }); - - it('Exporting CMI to JSON produces proper Object', () => { - expect( - JSON.parse(API.renderCMIToJSON()).cmi?.core !== undefined, - ).to.be.true; - }); - describe('Pre-Initialize Tests', () => { beforeEach('Create the API object', () => { - API = new AICC(); + cmi = new CMI(); }); afterEach('Destroy API object', () => { - API = null; + cmi = null; }); /** @@ -409,11 +381,11 @@ describe('AICC CMI Tests', () => { describe('Post-Initialize Tests', () => { beforeEach('Create the API object', () => { - API = new AICC(); - API.lmsInitialize(); + cmi = new CMI(); + cmi.initialize(); }); afterEach('Destroy API object', () => { - API = null; + cmi = null; }); /** diff --git a/test/cmi/scorm12_cmi.spec.js b/test/cmi/scorm12_cmi.spec.js index bb73333..df3cd75 100644 --- a/test/cmi/scorm12_cmi.spec.js +++ b/test/cmi/scorm12_cmi.spec.js @@ -1,29 +1,27 @@ -import {expect, assert} from 'chai'; +import {expect} from 'chai'; import {describe, it, beforeEach, afterEach} from 'mocha'; -import Scorm12API from '../../src/Scorm12API'; import {scorm12_constants} from '../../src/constants/api_constants'; import {scorm12_error_codes} from '../../src/constants/error_codes'; +import {CMI} from '../../src/cmi/scorm12_cmi'; -let API; +let cmi; const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should be able to write upto ${limit} characters to ${fieldName}`, () => { - eval(`API.${fieldName} = 'x'.repeat(${limit})`); - expect(0).to.equal(API.lastErrorCode); + expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)). + to.not.throw(); }); it(`Should fail to write more than ${limit} characters to ${fieldName}`, () => { - eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`); - expect(scorm12_error_codes.TYPE_MISMATCH + ''). - to. - equal(API.lastErrorCode); + expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)). + to.throw(scorm12_error_codes.TYPE_MISMATCH + ''); }); }); }; @@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { const checkInvalidSet = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should fail to write to ${fieldName}`, () => { - eval(`API.${fieldName} = 'xxx'`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.INVALID_SET_VALUE + ''); + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm12_error_codes.INVALID_SET_VALUE + ''); }); }); }; @@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => { const checkReadOnly = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should fail to write to ${fieldName}`, () => { - eval(`API.${fieldName} = 'xxx'`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.READ_ONLY_ELEMENT + ''); + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + ''); }); }); }; @@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => { const checkRead = ({fieldName, expectedValue = ''}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); }); }; @@ -69,12 +63,12 @@ const checkRead = ({fieldName, expectedValue = ''}) => { const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => { - expect(eval(`API.${fieldName}`)).to.equal(expectedValue); + expect(eval(`${fieldName}`)).to.equal(expectedValue); }); it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)). + to.not.throw(); }); }); }; @@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should fail to read from ${fieldName}`, () => { - eval(`API.${fieldName}`); - expect(API.lastErrorCode). - to. - equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + ''); + expect(() => eval(`${fieldName}`)). + to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + ''); }); it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); }); }); }; @@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { const checkWrite = ({fieldName, valueToTest = 'xxx'}) => { describe(`Field: ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => { - eval(`API.${fieldName} = '${valueToTest}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); }); }); }; @@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} if ({}.hasOwnProperty.call(validValues, idx)) { it(`Should successfully write '${validValues[idx]}' to ${fieldName}`, () => { - eval(`API.${fieldName} = '${validValues[idx]}'`); - expect(API.lastErrorCode).to.equal(0); + expect(() => eval(`${fieldName} = '${validValues[idx]}'`)). + to.not.throw(); }); } } @@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} if ({}.hasOwnProperty.call(invalidValues, idx)) { it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`, () => { - eval(`API.${fieldName} = '${invalidValues[idx]}'`); - expect(API.lastErrorCode).to.equal(expectedError + ''); + expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)). + to.throw(expectedError + ''); }); } } @@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues} describe('SCORM 1.2 CMI Tests', () => { describe('CMI Spec Tests', () => { - beforeEach('Create the API object', () => { - API = new Scorm12API(); - API.lmsInitialize(); - }); - afterEach('Destroy API object', () => { - API = null; - }); - - it('lmsInitialize should create CMI object', () => { - assert(API.cmi !== undefined, 'CMI object is created'); - }); - - it('Exporting CMI to JSON produces proper Object', () => { - expect( - JSON.parse(API.renderCMIToJSON()).cmi?.core !== undefined, - ).to.be.true; - }); - describe('Pre-Initialize Tests', () => { beforeEach('Create the API object', () => { - API = new Scorm12API(); + cmi = new CMI(); }); afterEach('Destroy API object', () => { - API = null; + cmi = null; }); /** @@ -408,11 +380,11 @@ describe('SCORM 1.2 CMI Tests', () => { describe('Post-Initialize Tests', () => { beforeEach('Create the API object', () => { - API = new Scorm12API(); - API.lmsInitialize(); + cmi = new CMI(); + cmi.initialize(); }); afterEach('Destroy API object', () => { - API = null; + cmi = null; }); /** diff --git a/test/cmi/scorm2004_cmi.spec.js b/test/cmi/scorm2004_cmi.spec.js new file mode 100644 index 0000000..dab42fd --- /dev/null +++ b/test/cmi/scorm2004_cmi.spec.js @@ -0,0 +1,156 @@ +import {expect} from 'chai'; +import {describe, it, beforeEach, afterEach} from 'mocha'; +import {scorm2004_error_codes} from '../../src/constants/error_codes'; +import {scorm2004_constants} from '../../src/constants/api_constants'; +import {CMI} from '../../src/cmi/scorm2004_cmi'; + +let cmi; + +const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should be able to read from ${fieldName}`, () => { + expect(eval(`${fieldName}`)).to.equal(expectedValue); + }); + + it(`Should be able to write upto ${limit} characters to ${fieldName}`, + () => { + expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)). + to.not.throw(); + }); + + it(`Should fail to write more than ${limit} characters to ${fieldName}`, + () => { + expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)). + to.throw(scorm2004_error_codes.TYPE_MISMATCH + ''); + }); + }); +}; + +const checkInvalidSet = ({fieldName, expectedValue = ''}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should be able to read from ${fieldName}`, () => { + expect(eval(`${fieldName}`)).to.equal(expectedValue); + }); + + it(`Should fail to write to ${fieldName}`, () => { + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm2004_error_codes.INVALID_SET_VALUE + ''); + }); + }); +}; + +const checkReadOnly = ({fieldName, expectedValue = ''}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should be able to read from ${fieldName}`, () => { + expect(eval(`${fieldName}`)).to.equal(expectedValue); + }); + + it(`Should fail to write to ${fieldName}`, () => { + expect(() => eval(`${fieldName} = 'xxx'`)). + to.throw(scorm2004_error_codes.READ_ONLY_ELEMENT + ''); + }); + }); +}; + +const checkRead = ({fieldName, expectedValue = ''}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should be able to read from ${fieldName}`, () => { + expect(eval(`${fieldName}`)).to.equal(expectedValue); + }); + }); +}; + +const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should be able to read from ${fieldName}`, () => { + expect(eval(`${fieldName}`)).to.equal(expectedValue); + }); + + it(`Should successfully write to ${fieldName}`, () => { + expect(() => eval(`${fieldName} = '${valueToTest}'`)). + to.not.throw(); + }); + }); +}; + +const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should fail to read from ${fieldName}`, () => { + expect(() => eval(`${fieldName}`)). + to.throw(scorm2004_error_codes.WRITE_ONLY_ELEMENT + ''); + }); + + it(`Should successfully write to ${fieldName}`, () => { + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); + }); + }); +}; + +const checkWrite = ({fieldName, valueToTest = 'xxx'}) => { + describe(`Field: ${fieldName}`, () => { + it(`Should successfully write to ${fieldName}`, () => { + expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw(); + }); + }); +}; + +const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}) => { + describe(`Field: ${fieldName}`, () => { + for (const idx in validValues) { + if ({}.hasOwnProperty.call(validValues, idx)) { + it(`Should successfully write '${validValues[idx]}' to ${fieldName}`, + () => { + expect(() => eval(`${fieldName} = '${validValues[idx]}'`)). + to.not.throw(); + }); + } + } + + for (const idx in invalidValues) { + if ({}.hasOwnProperty.call(invalidValues, idx)) { + it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`, + () => { + expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)). + to.throw(expectedError + ''); + }); + } + } + }); +}; + +describe('SCORM 2004 CMI Tests', () => { + describe('CMI Spec Tests', () => { + describe('Pre-Initialize Tests', () => { + beforeEach('Create the API object', () => { + cmi = new CMI(); + }); + afterEach('Destroy API object', () => { + cmi = null; + }); + + /** + * Base CMI Properties + */ + checkReadOnly({fieldName: 'cmi._version', expectedValue: '1.0'}); + checkReadOnly({ + fieldName: 'cmi._children', + expectedValue: scorm2004_constants.cmi_children, + }); + checkValidValues({ + fieldName: 'cmi.completion_status', + expectedError: scorm2004_error_codes.TYPE_MISMATCH, + validValues: [ + 'completed', + 'incomplete', + 'not attempted', + 'unknown', + ], + invalidValues: [ + 'complete', + 'passed', + 'failed', + ], + }); + }); + }); +});