UNPKG

scorm-again

Version:

A modern SCORM JavaScript run-time library for SCORM 1.2 and SCORM 2004

1,611 lines (1,593 loc) 188 kB
this.AICC = (function () { 'use strict'; const global_constants = { SCORM_TRUE: "true", SCORM_FALSE: "false", STATE_NOT_INITIALIZED: 0, STATE_INITIALIZED: 1, STATE_TERMINATED: 2 }; const scorm12_constants = { // Children lists cmi_children: "core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions", core_children: "student_id,student_name,lesson_location,credit,lesson_status,entry,score,total_time,lesson_mode,exit,session_time", score_children: "raw,min,max", comments_children: "content,location,time", objectives_children: "id,score,status", correct_responses_children: "pattern", student_data_children: "mastery_score,max_time_allowed,time_limit_action", student_preference_children: "audio,language,speed,text", interactions_children: "id,objectives,time,type,correct_responses,weighting,student_response,result,latency", error_descriptions: { "101": { basicMessage: "General Exception", detailMessage: "No specific error code exists to describe the error." }, "201": { basicMessage: "Invalid argument error", detailMessage: "Indicates that an argument represents an invalid data model element or is otherwise incorrect." }, "202": { basicMessage: "Element cannot have children", detailMessage: 'Indicates that LMSGetValue was called with a data model element name that ends in "_children" for a data model element that does not support the "_children" suffix.' }, "203": { basicMessage: "Element not an array - cannot have count", detailMessage: 'Indicates that LMSGetValue was called with a data model element name that ends in "_count" for a data model element that does not support the "_count" suffix.' }, "301": { basicMessage: "Not initialized", detailMessage: "Indicates that an API call was made before the call to lmsInitialize." }, "401": { basicMessage: "Not implemented error", detailMessage: "The data model element indicated in a call to LMSGetValue or LMSSetValue is valid, but was not implemented by this LMS. SCORM 1.2 defines a set of data model elements as being optional for an LMS to implement." }, "402": { basicMessage: "Invalid set value, element is a keyword", detailMessage: 'Indicates that LMSSetValue was called on a data model element that represents a keyword (elements that end in "_children" and "_count").' }, "403": { basicMessage: "Element is read only", detailMessage: "LMSSetValue was called with a data model element that can only be read." }, "404": { basicMessage: "Element is write only", detailMessage: "LMSGetValue was called on a data model element that can only be written to." }, "405": { basicMessage: "Incorrect Data Type", detailMessage: "LMSSetValue was called with a value that is not consistent with the data format of the supplied data model element." }, "407": { basicMessage: "Element Value Out Of Range", detailMessage: "The numeric value supplied to a LMSSetValue call is outside of the numeric range allowed for the supplied data model element." }, "408": { basicMessage: "Data Model Dependency Not Established", detailMessage: "Some data model elements cannot be set until another data model element was set. This error condition indicates that the prerequisite element was not set before the dependent element." } } }; const aicc_constants = { ...scorm12_constants, ...{ cmi_children: "core,suspend_data,launch_data,comments,objectives,student_data,student_preference,interactions,evaluation", student_preference_children: "audio,language,lesson_type,speed,text,text_color,text_location,text_size,video,windows", student_data_children: "attempt_number,tries,mastery_score,max_time_allowed,time_limit_action", student_demographics_children: "city,class,company,country,experience,familiar_name,instructor_name,title,native_language,state,street_address,telephone,years_experience", tries_children: "time,status,score", attempt_records_children: "score,lesson_status", paths_children: "location_id,date,time,status,why_left,time_in_element" } }; const global_errors = { GENERAL: 101, INITIALIZATION_FAILED: 101, INITIALIZED: 101, TERMINATED: 101, TERMINATION_FAILURE: 101, TERMINATION_BEFORE_INIT: 101, MULTIPLE_TERMINATION: 101, RETRIEVE_BEFORE_INIT: 101, RETRIEVE_AFTER_TERM: 101, STORE_BEFORE_INIT: 101, STORE_AFTER_TERM: 101, COMMIT_BEFORE_INIT: 101, COMMIT_AFTER_TERM: 101, ARGUMENT_ERROR: 101, CHILDREN_ERROR: 101, COUNT_ERROR: 101, GENERAL_GET_FAILURE: 101, GENERAL_SET_FAILURE: 101, GENERAL_COMMIT_FAILURE: 101, UNDEFINED_DATA_MODEL: 101, UNIMPLEMENTED_ELEMENT: 101, VALUE_NOT_INITIALIZED: 101, INVALID_SET_VALUE: 101, READ_ONLY_ELEMENT: 101, WRITE_ONLY_ELEMENT: 101, TYPE_MISMATCH: 101, VALUE_OUT_OF_RANGE: 101, DEPENDENCY_NOT_ESTABLISHED: 101 }; const scorm12_errors$1 = { ...global_errors, RETRIEVE_BEFORE_INIT: 301, STORE_BEFORE_INIT: 301, COMMIT_BEFORE_INIT: 301, ARGUMENT_ERROR: 201, CHILDREN_ERROR: 202, COUNT_ERROR: 203, UNDEFINED_DATA_MODEL: 401, UNIMPLEMENTED_ELEMENT: 401, VALUE_NOT_INITIALIZED: 301, INVALID_SET_VALUE: 402, READ_ONLY_ELEMENT: 403, WRITE_ONLY_ELEMENT: 404, TYPE_MISMATCH: 405, VALUE_OUT_OF_RANGE: 407, DEPENDENCY_NOT_ESTABLISHED: 408 }; const scorm12_regex = { CMIString256: "^[\\s\\S]{0,255}$", CMIString4096: "^[\\s\\S]{0,4096}$", CMITime: "^(?:[01]\\d|2[0123]):(?:[012345]\\d):(?:[012345]\\d)$", CMITimespan: "^([0-9]{2,}):([0-9]{2}):([0-9]{2})(.[0-9]{1,2})?$", CMIInteger: "^\\d+$", CMISInteger: "^-?([0-9]+)$", CMIDecimal: "^-?([0-9]{0,3})(\\.[0-9]*)?$", CMIIdentifier: "^[\\u0021-\\u007E\\s]{0,255}$", // Allow storing larger responses for interactions // Some content packages may exceed the 255 character limit // defined in the SCORM 1.2 specification. The previous // expression truncated these values which resulted in // a "101: General Exception" being thrown when long // answers were supplied. To support these packages we // relax the limitation and accept any length string. CMIFeedback: "^.*$", // This must be redefined CMIIndex: "[._](\\d+).", // Vocabulary Data Type Definition CMIStatus: "^(passed|completed|failed|incomplete|browsed)$", CMIStatus2: "^(passed|completed|failed|incomplete|browsed|not attempted)$", CMIExit: "^(time-out|suspend|logout|)$", CMIType: "^(true-false|choice|fill-in|matching|performance|sequencing|likert|numeric)$", CMIResult: "^(correct|wrong|unanticipated|neutral|([0-9]{0,3})?(\\.[0-9]*)?)$", NAVEvent: "^(previous|continue|start|resumeAll|choice|jump|exit|exitAll|abandon|abandonAll|suspendAll|retry|retryAll|_none_)$", // Data ranges score_range: "0#100", audio_range: "-1#100", speed_range: "-100#100", weighting_range: "-100#100", text_range: "-1#1" }; const aicc_regex = { ...scorm12_regex, ...{ // AICC identifiers may contain letters, numbers, underscores, // periods, and hyphens up to 255 characters in length. // The previous expression only allowed "\w" characters which // excluded periods and hyphens. CMIIdentifier: "^[A-Za-z0-9._-]{1,255}$" } }; class BaseScormValidationError extends Error { constructor(CMIElement, errorCode) { super(`${CMIElement} : ${errorCode.toString()}`); this._errorCode = errorCode; Object.setPrototypeOf(this, BaseScormValidationError.prototype); } /** * Getter for _errorCode * @return {number} */ get errorCode() { return this._errorCode; } } class ValidationError extends BaseScormValidationError { /** * Constructor to take in an error message and code * @param {string} CMIElement * @param {number} errorCode * @param {string} errorMessage * @param {string} detailedMessage */ constructor(CMIElement, errorCode, errorMessage, detailedMessage) { super(CMIElement, errorCode); this._detailedMessage = ""; this.message = `${CMIElement} : ${errorMessage}`; this._errorMessage = errorMessage; if (detailedMessage) { this._detailedMessage = detailedMessage; } Object.setPrototypeOf(this, ValidationError.prototype); } /** * Getter for _errorMessage * @return {string} */ get errorMessage() { return this._errorMessage; } /** * Getter for _detailedMessage * @return {string} */ get detailedMessage() { return this._detailedMessage; } } const scorm12_errors = scorm12_constants.error_descriptions; class Scorm12ValidationError extends ValidationError { /** * Constructor to take in an error code * @param {string} CMIElement * @param {number} errorCode */ constructor(CMIElement, errorCode) { if ({}.hasOwnProperty.call(scorm12_errors, String(errorCode))) { super(CMIElement, errorCode, scorm12_errors[String(errorCode)]?.basicMessage || "Unknown error", scorm12_errors[String(errorCode)]?.detailMessage); } else { super(CMIElement, 101, scorm12_errors["101"]?.basicMessage ?? "General error", scorm12_errors["101"]?.detailMessage); } Object.setPrototypeOf(this, Scorm12ValidationError.prototype); } } class BaseCMI { /** * Constructor for BaseCMI * @param {string} cmi_element */ constructor(cmi_element) { this.jsonString = false; this._initialized = false; this._cmi_element = cmi_element; } /** * Getter for _initialized * @return {boolean} */ get initialized() { return this._initialized; } /** * Called when the API has been initialized after the CMI has been created */ initialize() { this._initialized = true; } } class BaseRootCMI extends BaseCMI { /** * Start time of the course * @type {number | undefined} * @protected */ get start_time() { return this._start_time; } /** * Setter for start_time. Can only be called once. */ setStartTime() { if (this._start_time === void 0) { this._start_time = (/* @__PURE__ */new Date()).getTime(); } else { throw new Error("Start time has already been set."); } } } const SECONDS_PER_MINUTE = 60; const SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; const getSecondsAsHHMMSS = memoize(totalSeconds => { if (!totalSeconds || totalSeconds <= 0) { return "00:00:00"; } const hours = Math.floor(totalSeconds / SECONDS_PER_HOUR); const dateObj = new Date(totalSeconds * 1e3); const minutes = dateObj.getUTCMinutes(); const seconds = dateObj.getSeconds(); const ms = totalSeconds % 1; let msStr = ""; if (countDecimals(ms) > 0) { if (countDecimals(ms) > 2) { msStr = ms.toFixed(2); } else { msStr = String(ms); } msStr = "." + msStr.split(".")[1]; } return (hours + ":" + minutes + ":" + seconds).replace(/\b\d\b/g, "0$&") + msStr; }); const getTimeAsSeconds = memoize((timeString, timeRegex) => { if (typeof timeString === "number" || typeof timeString === "boolean") { timeString = String(timeString); } if (typeof timeRegex === "string") { timeRegex = new RegExp(timeRegex); } if (!timeString) { return 0; } if (!timeString.match(timeRegex)) { if (/^\d+(?:\.\d+)?$/.test(timeString)) { return Number(timeString); } return 0; } const parts = timeString.split(":"); const hours = Number(parts[0]); const minutes = Number(parts[1]); const seconds = Number(parts[2]); return hours * 3600 + minutes * 60 + seconds; }, // Custom key function to handle RegExp objects which can't be stringified (timeString, timeRegex) => { const timeStr = typeof timeString === "string" ? timeString : String(timeString ?? ""); const regexStr = typeof timeRegex === "string" ? timeRegex : timeRegex?.toString() ?? ""; return `${timeStr}:${regexStr}`; }); function addHHMMSSTimeStrings(first, second, timeRegex) { if (typeof timeRegex === "string") { timeRegex = new RegExp(timeRegex); } return getSecondsAsHHMMSS(getTimeAsSeconds(first, timeRegex) + getTimeAsSeconds(second, timeRegex)); } function flatten(data) { const result = {}; function recurse(cur, prop) { if (Object(cur) !== cur) { result[prop] = cur; } else if (Array.isArray(cur)) { cur.forEach((item, i) => { recurse(item, `${prop}[${i}]`); }); if (cur.length === 0) result[prop] = []; } else { const keys = Object.keys(cur).filter(p => Object.prototype.hasOwnProperty.call(cur, p)); const isEmpty = keys.length === 0; keys.forEach(p => { recurse(cur[p], prop ? `${prop}.${p}` : p); }); if (isEmpty && prop) result[prop] = {}; } } recurse(data, ""); return result; } function unflatten(data) { if (Object(data) !== data || Array.isArray(data)) return data; const result = {}; const pattern = /\.?([^.[\]]+)|\[(\d+)]/g; Object.keys(data).filter(p => Object.prototype.hasOwnProperty.call(data, p)).forEach(p => { let cur = result; let prop = ""; const regex = new RegExp(pattern); Array.from({ length: p.match(new RegExp(pattern, "g"))?.length ?? 0 }, () => regex.exec(p)).forEach(m => { if (m) { cur = cur[prop] ?? (cur[prop] = m[2] ? [] : {}); prop = m[2] || m[1] || ""; } }); cur[prop] = data[p]; }); return result[""] ?? result; } function countDecimals(num) { if (Math.floor(num) === num || String(num)?.indexOf?.(".") < 0) return 0; const parts = num.toString().split(".")?.[1]; return parts?.length ?? 0; } function formatMessage(functionName, message, CMIElement) { const baseLength = 20; const paddedFunction = functionName.padEnd(baseLength); let messageString = `${paddedFunction}: `; if (CMIElement) { const CMIElementBaseLength = 70; messageString += CMIElement; messageString = messageString.padEnd(CMIElementBaseLength); } messageString += message ?? ""; return messageString; } function stringMatches(str, tester) { if (typeof str !== "string") { return false; } return new RegExp(tester).test(str); } function memoize(fn, keyFn) { const cache = /* @__PURE__ */new Map(); return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } const key = keyFn ? keyFn(...args) : JSON.stringify(args); return cache.has(key) ? cache.get(key) : (() => { const result = fn(...args); cache.set(key, result); return result; })(); }; } const checkValidFormat = memoize((CMIElement, value, regexPattern, errorCode, errorClass, allowEmptyString) => { if (typeof value !== "string") { return false; } const formatRegex = new RegExp(regexPattern); const matches = value.match(formatRegex); if (allowEmptyString && value === "") { return true; } if (value === void 0 || !matches || matches[0] === "") { throw new errorClass(CMIElement, errorCode); } return true; }, // Custom key function that excludes the error class from the cache key // since it can't be stringified and doesn't affect the validation result (CMIElement, value, regexPattern, errorCode, _errorClass, allowEmptyString) => { const valueKey = typeof value === "string" ? value : `[${typeof value}]`; return `${CMIElement}:${valueKey}:${regexPattern}:${errorCode}:${allowEmptyString || false}`; }); const checkValidRange = memoize((CMIElement, value, rangePattern, errorCode, errorClass) => { const ranges = rangePattern.split("#"); value = value * 1; if (ranges[0] && value >= ranges[0]) { if (ranges[1] && (ranges[1] === "*" || value <= ranges[1])) { return true; } else { throw new errorClass(CMIElement, errorCode); } } else { throw new errorClass(CMIElement, errorCode); } }, // Custom key function that excludes the error class from the cache key // since it can't be stringified and doesn't affect the validation result (CMIElement, value, rangePattern, errorCode, _errorClass) => `${CMIElement}:${value}:${rangePattern}:${errorCode}`); function check12ValidFormat(CMIElement, value, regexPattern, allowEmptyString) { return checkValidFormat(CMIElement, value, regexPattern, scorm12_errors$1.TYPE_MISMATCH, Scorm12ValidationError, allowEmptyString); } function check12ValidRange(CMIElement, value, rangePattern, allowEmptyString) { if (value === "") { throw new Scorm12ValidationError(CMIElement, scorm12_errors$1.VALUE_OUT_OF_RANGE); } return checkValidRange(CMIElement, value, rangePattern, scorm12_errors$1.VALUE_OUT_OF_RANGE, Scorm12ValidationError); } class ValidationService { /** * Validates a score property (raw, min, max) * * @param {string} CMIElement * @param {string} value - The value to validate * @param {string} decimalRegex - The regex pattern for decimal validation * @param {string | false} scoreRange - The range pattern for score validation, or false if no range validation is needed * @param {number} invalidTypeCode - The error code for invalid type * @param {number} invalidRangeCode - The error code for invalid range * @param {typeof BaseScormValidationError} errorClass - The error class to use for validation errors * @return {boolean} - True if validation passes, throws an error otherwise */ validateScore(CMIElement, value, decimalRegex, scoreRange, invalidTypeCode, invalidRangeCode, errorClass) { return checkValidFormat(CMIElement, value, decimalRegex, invalidTypeCode, errorClass) && (!scoreRange || checkValidRange(CMIElement, value, scoreRange, invalidRangeCode, errorClass)); } /** * Validates a SCORM 1.2 audio property * * @param {string} CMIElement * @param {string} value - The value to validate * @return {boolean} - True if validation passes, throws an error otherwise */ validateScorm12Audio(CMIElement, value) { return check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) && check12ValidRange(CMIElement, value, scorm12_regex.audio_range); } /** * Validates a SCORM 1.2 language property * * @param {string} CMIElement * @param {string} value - The value to validate * @return {boolean} - True if validation passes, throws an error otherwise */ validateScorm12Language(CMIElement, value) { return check12ValidFormat(CMIElement, value, scorm12_regex.CMIString256); } /** * Validates a SCORM 1.2 speed property * * @param {string} CMIElement * @param {string} value - The value to validate * @return {boolean} - True if validation passes, throws an error otherwise */ validateScorm12Speed(CMIElement, value) { return check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) && check12ValidRange(CMIElement, value, scorm12_regex.speed_range); } /** * Validates a SCORM 1.2 text property * * @param {string} CMIElement * @param {string} value - The value to validate * @return {boolean} - True if validation passes, throws an error otherwise */ validateScorm12Text(CMIElement, value) { return check12ValidFormat(CMIElement, value, scorm12_regex.CMISInteger) && check12ValidRange(CMIElement, value, scorm12_regex.text_range); } /** * Validates if a property is read-only * * @param {string} CMIElement * @param {boolean} initialized - Whether the object is initialized * @throws {BaseScormValidationError} - Throws an error if the object is initialized */ validateReadOnly(CMIElement, initialized) { if (initialized) { throw new Scorm12ValidationError(CMIElement, scorm12_errors$1.READ_ONLY_ELEMENT); } } } const validationService = new ValidationService(); class CMIScore extends BaseCMI { /** * Constructor for *.score * @param { * score_children: string, * score_range: string, * max: string, * invalidErrorCode: number, * invalidTypeCode: number, * invalidRangeCode: number, * decimalRegex: string, * errorClass: typeof BaseScormValidationError * } params */ constructor(params) { super(params.CMIElement); this._raw = ""; this._min = ""; this.__children = params.score_children || scorm12_constants.score_children; this.__score_range = !params.score_range ? false : scorm12_regex.score_range; this._max = params.max || params.max === "" ? params.max : "100"; this.__invalid_error_code = params.invalidErrorCode || scorm12_errors$1.INVALID_SET_VALUE; this.__invalid_type_code = params.invalidTypeCode || scorm12_errors$1.TYPE_MISMATCH; this.__invalid_range_code = params.invalidRangeCode || scorm12_errors$1.VALUE_OUT_OF_RANGE; this.__decimal_regex = params.decimalRegex || scorm12_regex.CMIDecimal; this.__error_class = params.errorClass; } /** * Called when the API has been reset */ reset() { this._initialized = false; } /** * Getter for _children * @return {string} */ get _children() { return this.__children; } /** * Setter for _children. Just throws an error. * @param {string} _children */ set _children(_children) { throw new this.__error_class(this._cmi_element + "._children", this.__invalid_error_code); } /** * Getter for _raw * @return {string} */ get raw() { return this._raw; } /** * Setter for _raw * @param {string} raw */ set raw(raw) { if (validationService.validateScore(this._cmi_element + ".raw", raw, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) { this._raw = raw; } } /** * Getter for _min * @return {string} */ get min() { return this._min; } /** * Setter for _min * @param {string} min */ set min(min) { if (validationService.validateScore(this._cmi_element + ".min", min, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) { this._min = min; } } /** * Getter for _max * @return {string} */ get max() { return this._max; } /** * Setter for _max * @param {string} max */ set max(max) { if (validationService.validateScore(this._cmi_element + ".max", max, this.__decimal_regex, this.__score_range, this.__invalid_type_code, this.__invalid_range_code, this.__error_class)) { this._max = max; } } /** * Getter for _score_range * @return {string | false} */ getScoreObject() { const scoreObject = {}; if (!Number.isNaN(Number.parseFloat(this.raw))) { scoreObject.raw = Number.parseFloat(this.raw); } if (!Number.isNaN(Number.parseFloat(this.min))) { scoreObject.min = Number.parseFloat(this.min); } if (!Number.isNaN(Number.parseFloat(this.max))) { scoreObject.max = Number.parseFloat(this.max); } return scoreObject; } /** * toJSON for *.score * @return { * { * min: string, * max: string, * raw: string * } * } */ toJSON() { this.jsonString = true; const result = { raw: this.raw, min: this.min, max: this.max }; this.jsonString = false; return result; } } class CMICore extends BaseCMI { /** * Constructor for `cmi.core` */ constructor() { super("cmi.core"); this.__children = scorm12_constants.core_children; this._student_id = ""; this._student_name = ""; this._lesson_location = ""; this._credit = ""; this._lesson_status = "not attempted"; this._entry = ""; this._total_time = ""; this._lesson_mode = "normal"; this._exit = ""; this._session_time = "00:00:00"; this._suspend_data = ""; this.score = new CMIScore({ CMIElement: "cmi.core.score", score_children: scorm12_constants.score_children, score_range: scorm12_regex.score_range, invalidErrorCode: scorm12_errors$1.INVALID_SET_VALUE, invalidTypeCode: scorm12_errors$1.TYPE_MISMATCH, invalidRangeCode: scorm12_errors$1.VALUE_OUT_OF_RANGE, errorClass: Scorm12ValidationError }); } /** * Called when the API has been initialized after the CMI has been created */ initialize() { super.initialize(); this.score?.initialize(); } /** * Called when the API has been reset */ reset() { this._initialized = false; this._exit = ""; this._entry = ""; this._session_time = "00:00:00"; this.score?.reset(); } /** * Getter for __children * @return {string} * @private */ get _children() { return this.__children; } /** * Setter for __children. Just throws an error. * @param {string} _children * @private */ set _children(_children) { throw new Scorm12ValidationError(this._cmi_element + "._children", scorm12_errors$1.INVALID_SET_VALUE); } /** * Getter for _student_id * @return {string} */ get student_id() { return this._student_id; } /** * Setter for _student_id. Can only be called before initialization. * @param {string} student_id */ set student_id(student_id) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".student_id", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._student_id = student_id; } } /** * Getter for _student_name * @return {string} */ get student_name() { return this._student_name; } /** * Setter for _student_name. Can only be called before initialization. * @param {string} student_name */ set student_name(student_name) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".student_name", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._student_name = student_name; } } /** * Getter for _lesson_location * @return {string} */ get lesson_location() { return this._lesson_location; } /** * Setter for _lesson_location * @param {string} lesson_location */ set lesson_location(lesson_location) { if (check12ValidFormat(this._cmi_element + ".lesson_location", lesson_location, scorm12_regex.CMIString256, true)) { this._lesson_location = lesson_location; } } /** * Getter for _credit * @return {string} */ get credit() { return this._credit; } /** * Setter for _credit. Can only be called before initialization. * @param {string} credit */ set credit(credit) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".credit", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._credit = credit; } } /** * Getter for _lesson_status * @return {string} */ get lesson_status() { return this._lesson_status; } /** * Setter for _lesson_status * @param {string} lesson_status */ set lesson_status(lesson_status) { if (this.initialized) { if (check12ValidFormat(this._cmi_element + ".lesson_status", lesson_status, scorm12_regex.CMIStatus)) { this._lesson_status = lesson_status; } } else { if (check12ValidFormat(this._cmi_element + ".lesson_status", lesson_status, scorm12_regex.CMIStatus2)) { this._lesson_status = lesson_status; } } } /** * Getter for _entry * @return {string} */ get entry() { return this._entry; } /** * Setter for _entry. Can only be called before initialization. * @param {string} entry */ set entry(entry) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".entry", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._entry = entry; } } /** * Getter for _total_time * @return {string} */ get total_time() { return this._total_time; } /** * Setter for _total_time. Can only be called before initialization. * @param {string} total_time */ set total_time(total_time) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".total_time", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._total_time = total_time; } } /** * Getter for _lesson_mode * @return {string} */ get lesson_mode() { return this._lesson_mode; } /** * Setter for _lesson_mode. Can only be called before initialization. * @param {string} lesson_mode */ set lesson_mode(lesson_mode) { if (this.initialized) { throw new Scorm12ValidationError(this._cmi_element + ".lesson_mode", scorm12_errors$1.READ_ONLY_ELEMENT); } else { this._lesson_mode = lesson_mode; } } /** * Getter for _exit. Should only be called during JSON export. * @return {string} */ get exit() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".exit", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._exit; } /** * Setter for _exit * @param {string} exit */ set exit(exit) { if (check12ValidFormat(this._cmi_element + ".exit", exit, scorm12_regex.CMIExit, true)) { this._exit = exit; } } /** * Getter for _session_time. Should only be called during JSON export. * @return {string} */ get session_time() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".session_time", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._session_time; } /** * Setter for _session_time * @param {string} session_time */ set session_time(session_time) { if (check12ValidFormat(this._cmi_element + ".session_time", session_time, scorm12_regex.CMITimespan)) { this._session_time = session_time; } } /** * Getter for _suspend_data * @return {string} */ get suspend_data() { return this._suspend_data; } /** * Setter for _suspend_data * @param {string} suspend_data */ set suspend_data(suspend_data) { if (check12ValidFormat(this._cmi_element + ".suspend_data", suspend_data, scorm12_regex.CMIString4096, true)) { this._suspend_data = suspend_data; } } /** * Adds the current session time to the existing total time. * @param {number} start_time * @return {string} */ getCurrentTotalTime(start_time) { let sessionTime = this._session_time; if (typeof start_time !== "undefined" && start_time !== null) { const seconds = (/* @__PURE__ */new Date()).getTime() - start_time; sessionTime = getSecondsAsHHMMSS(seconds / 1e3); } return addHHMMSSTimeStrings(this._total_time, sessionTime, new RegExp(scorm12_regex.CMITimespan)); } /** * toJSON for cmi.core * * @return { * { * student_name: string, * entry: string, * exit: string, * score: CMIScore, * student_id: string, * lesson_mode: string, * lesson_location: string, * lesson_status: string, * credit: string, * session_time: string * } * } */ toJSON() { this.jsonString = true; const result = { student_id: this.student_id, student_name: this.student_name, lesson_location: this.lesson_location, credit: this.credit, lesson_status: this.lesson_status, entry: this.entry, lesson_mode: this.lesson_mode, exit: this.exit, session_time: this.session_time, score: this.score }; this.jsonString = false; return result; } } class CMIArray extends BaseCMI { /** * Constructor cmi *.n arrays * @param {object} params */ constructor(params) { super(params.CMIElement); this.__children = params.children; this._errorCode = params.errorCode || scorm12_errors$1.GENERAL; this._errorClass = params.errorClass || BaseScormValidationError; this.childArray = []; } /** * Called when the API has been reset */ reset() { let wipe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this._initialized = false; if (wipe) { this.childArray = []; } else { for (let i = 0; i < this.childArray.length; i++) { this.childArray[i].reset(); } } } /** * Getter for _children * @return {string} */ get _children() { return this.__children; } /** * Setter for _children. Just throws an error. * @param {string} _children */ set _children(_children) { throw new this._errorClass(this._cmi_element + "._children", this._errorCode); } /** * Getter for _count * @return {number} */ get _count() { return this.childArray.length; } /** * Setter for _count. Just throws an error. * @param {number} _count */ set _count(_count) { throw new this._errorClass(this._cmi_element + "._count", this._errorCode); } /** * toJSON for *.n arrays * @return {object} */ toJSON() { this.jsonString = true; const result = {}; for (let i = 0; i < this.childArray.length; i++) { result[i + ""] = this.childArray[i]; } this.jsonString = false; return result; } } class CMIObjectives extends CMIArray { /** * Constructor for `cmi.objectives` */ constructor() { super({ CMIElement: "cmi.objectives", children: scorm12_constants.objectives_children, errorCode: scorm12_errors$1.INVALID_SET_VALUE, errorClass: Scorm12ValidationError }); } } class CMIObjectivesObject extends BaseCMI { /** * Constructor for cmi.objectives.n */ constructor() { super("cmi.objectives.n"); this._id = ""; this._status = ""; this.score = new CMIScore({ CMIElement: "cmi.objectives.n.score", score_children: scorm12_constants.score_children, score_range: scorm12_regex.score_range, invalidErrorCode: scorm12_errors$1.INVALID_SET_VALUE, invalidTypeCode: scorm12_errors$1.TYPE_MISMATCH, invalidRangeCode: scorm12_errors$1.VALUE_OUT_OF_RANGE, errorClass: Scorm12ValidationError }); } /** * Called when the API has been reset */ reset() { this._initialized = false; this._id = ""; this._status = ""; this.score?.reset(); } /** * Getter for _id * @return {string} */ get id() { return this._id; } /** * Setter for _id * @param {string} id */ set id(id) { if (check12ValidFormat(this._cmi_element + ".id", id, scorm12_regex.CMIIdentifier)) { this._id = id; } } /** * Getter for _status * @return {string} */ get status() { return this._status; } /** * Setter for _status * @param {string} status */ set status(status) { if (check12ValidFormat(this._cmi_element + ".status", status, scorm12_regex.CMIStatus2)) { this._status = status; } } /** * toJSON for cmi.objectives.n * @return { * { * id: string, * status: string, * score: CMIScore * } * } */ toJSON() { this.jsonString = true; const result = { id: this.id, status: this.status, score: this.score }; this.jsonString = false; return result; } } class CMIStudentData extends BaseCMI { /** * Constructor for cmi.student_data * @param {string} student_data_children */ constructor(student_data_children) { super("cmi.student_data"); this._mastery_score = ""; this._max_time_allowed = ""; this._time_limit_action = ""; this.__children = student_data_children ? student_data_children : scorm12_constants.student_data_children; } /** * Called when the API has been reset */ reset() { this._initialized = false; } /** * Getter for __children * @return {string} * @private */ get _children() { return this.__children; } /** * Setter for __children. Just throws an error. * @param {string} _children * @private */ set _children(_children) { throw new Scorm12ValidationError(this._cmi_element + "._children", scorm12_errors$1.INVALID_SET_VALUE); } /** * Getter for _master_score * @return {string} */ get mastery_score() { return this._mastery_score; } /** * Setter for _master_score. Can only be called before initialization. * @param {string} mastery_score */ set mastery_score(mastery_score) { validationService.validateReadOnly(this._cmi_element + ".mastery_score", this.initialized); this._mastery_score = mastery_score; } /** * Getter for _max_time_allowed * @return {string} */ get max_time_allowed() { return this._max_time_allowed; } /** * Setter for _max_time_allowed. Can only be called before initialization. * @param {string} max_time_allowed */ set max_time_allowed(max_time_allowed) { validationService.validateReadOnly(this._cmi_element + ".max_time_allowed", this.initialized); this._max_time_allowed = max_time_allowed; } /** * Getter for _time_limit_action * @return {string} */ get time_limit_action() { return this._time_limit_action; } /** * Setter for _time_limit_action. Can only be called before initialization. * @param {string} time_limit_action */ set time_limit_action(time_limit_action) { validationService.validateReadOnly(this._cmi_element + ".time_limit_action", this.initialized); this._time_limit_action = time_limit_action; } /** * toJSON for cmi.student_data * * @return { * { * max_time_allowed: string, * time_limit_action: string, * mastery_score: string * } * } */ toJSON() { this.jsonString = true; const result = { mastery_score: this.mastery_score, max_time_allowed: this.max_time_allowed, time_limit_action: this.time_limit_action }; this.jsonString = false; return result; } } class CMIStudentPreference extends BaseCMI { /** * Constructor for cmi.student_preference * @param {string} student_preference_children */ constructor(student_preference_children) { super("cmi.student_preference"); this._audio = ""; this._language = ""; this._speed = ""; this._text = ""; this.__children = student_preference_children ? student_preference_children : scorm12_constants.student_preference_children; } /** * Called when the API has been reset */ reset() { this._initialized = false; } /** * Getter for __children * @return {string} * @private */ get _children() { return this.__children; } /** * Setter for __children. Just throws an error. * @param {string} _children * @private */ set _children(_children) { throw new Scorm12ValidationError(this._cmi_element + "._children", scorm12_errors$1.INVALID_SET_VALUE); } /** * Getter for _audio * @return {string} */ get audio() { return this._audio; } /** * Setter for _audio * @param {string} audio */ set audio(audio) { if (validationService.validateScorm12Audio(this._cmi_element + ".audio", audio)) { this._audio = audio; } } /** * Getter for _language * @return {string} */ get language() { return this._language; } /** * Setter for _language * @param {string} language */ set language(language) { if (validationService.validateScorm12Language(this._cmi_element + ".language", language)) { this._language = language; } } /** * Getter for _speed * @return {string} */ get speed() { return this._speed; } /** * Setter for _speed * @param {string} speed */ set speed(speed) { if (validationService.validateScorm12Speed(this._cmi_element + ".speed", speed)) { this._speed = speed; } } /** * Getter for _text * @return {string} */ get text() { return this._text; } /** * Setter for _text * @param {string} text */ set text(text) { if (validationService.validateScorm12Text(this._cmi_element + ".text", text)) { this._text = text; } } /** * toJSON for cmi.student_preference * * @return { * { * audio: string, * language: string, * speed: string, * text: string * } * } */ toJSON() { this.jsonString = true; const result = { audio: this.audio, language: this.language, speed: this.speed, text: this.text }; this.jsonString = false; return result; } } class CMIInteractions extends CMIArray { /** * Constructor for `cmi.interactions` */ constructor() { super({ CMIElement: "cmi.interactions", children: scorm12_constants.interactions_children, errorCode: scorm12_errors$1.INVALID_SET_VALUE, errorClass: Scorm12ValidationError }); } } class CMIInteractionsObject extends BaseCMI { /** * Constructor for cmi.interactions.n object */ constructor() { super("cmi.interactions.n"); this._id = ""; this._time = ""; this._type = ""; this._weighting = ""; this._student_response = ""; this._result = ""; this._latency = ""; this.objectives = new CMIArray({ CMIElement: "cmi.interactions.n.objectives", errorCode: scorm12_errors$1.INVALID_SET_VALUE, errorClass: Scorm12ValidationError, children: scorm12_constants.objectives_children }); this.correct_responses = new CMIArray({ CMIElement: "cmi.interactions.correct_responses", errorCode: scorm12_errors$1.INVALID_SET_VALUE, errorClass: Scorm12ValidationError, children: scorm12_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(); } /** * Called when the API has been reset */ reset() { this._initialized = false; this._id = ""; this._time = ""; this._type = ""; this._weighting = ""; this._student_response = ""; this._result = ""; this._latency = ""; this.objectives?.reset(); this.correct_responses?.reset(); } /** * Getter for _id. Should only be called during JSON export. * @return {string} */ get id() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".id", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._id; } /** * Setter for _id * @param {string} id */ set id(id) { if (check12ValidFormat(this._cmi_element + ".id", id, scorm12_regex.CMIIdentifier)) { this._id = id; } } /** * Getter for _time. Should only be called during JSON export. * @return {string} */ get time() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".time", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._time; } /** * Setter for _time * @param {string} time */ set time(time) { if (check12ValidFormat(this._cmi_element + ".time", time, scorm12_regex.CMITime)) { this._time = time; } } /** * Getter for _type. Should only be called during JSON export. * @return {string} */ get type() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".type", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._type; } /** * Setter for _type * @param {string} type */ set type(type) { if (check12ValidFormat(this._cmi_element + ".type", type, scorm12_regex.CMIType)) { this._type = type; } } /** * Getter for _weighting. Should only be called during JSON export. * @return {string} */ get weighting() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".weighting", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._weighting; } /** * Setter for _weighting * @param {string} weighting */ set weighting(weighting) { if (check12ValidFormat(this._cmi_element + ".weighting", weighting, scorm12_regex.CMIDecimal) && check12ValidRange(this._cmi_element + ".weighting", weighting, scorm12_regex.weighting_range)) { this._weighting = weighting; } } /** * Getter for _student_response. Should only be called during JSON export. * @return {string} */ get student_response() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".student_response", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._student_response; } /** * Setter for _student_response * @param {string} student_response */ set student_response(student_response) { if (check12ValidFormat(this._cmi_element + ".student_response", student_response, scorm12_regex.CMIFeedback, true)) { this._student_response = student_response; } } /** * Getter for _result. Should only be called during JSON export. * @return {string} */ get result() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".result", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._result; } /** * Setter for _result * @param {string} result */ set result(result) { if (check12ValidFormat(this._cmi_element + ".result", result, scorm12_regex.CMIResult)) { this._result = result; } } /** * Getter for _latency. Should only be called during JSON export. * @return {string} */ get latency() { if (!this.jsonString) { throw new Scorm12ValidationError(this._cmi_element + ".latency", scorm12_errors$1.WRITE_ONLY_ELEMENT); } return this._latency; } /** * Setter for _latency * @param {string} latency */ set latency(latency) { if (check12ValidFormat(this._cmi_element + ".latency", latency, scorm12_regex.CMITimespan)) { this._latency = latency; } } /** * toJSON for cmi.interactions.n * * @return { * { * id: string, * time: string, * type: string, * weighting: string, * student_response: string, * result: string, * latency: string, * objectives: CMIArray, * correct_responses: CMIArray * } * } */ toJSON() { this.jsonString = true; const result = { id: this.id, time: this.time, type: this.type, weighting: this.weighting, student_response: this.student_response, result: this.result, latency: this.latency, objectives: this.objectives, correct_responses: this.correct_responses }; this.jsonString = false; return result; } } class CMIInteractionsObjectivesObject extends BaseCMI { /** * Constructor for cmi.interactions.n.objectives.n */ c