Adding new selfReportSessionTime setting

This commit is contained in:
Jonathan Putney
2020-06-26 14:50:33 -04:00
parent aa03f6affb
commit ca94ee9fb5
12 changed files with 643 additions and 500 deletions

View File

@@ -52,6 +52,7 @@ The APIs include several settings to customize the functionality of each API:
| `autoProgress` | false | true/false | In case Sequencing is being used, you can tell the API to automatically throw the `SequenceNext` event.|
| `logLevel` | 4 | int<br><br>1 => DEBUG<br>2 => INFO<br>3 => WARN<br>4 => ERROR<br>5 => NONE | By default, the APIs only log error messages. |
| `mastery_override` | false | true/false | (SCORM 1.2) Used to override a module's `cmi.core.lesson_status` so that a pass/fail is determined based on a mastery score and the user's raw score, rather than using whatever status is provided by the module. An example of this would be if a module is published using a `Complete/Incomplete` final status, but the LMS always wants to receive a `Passed/Failed` for quizzes, then we can use this setting to override the given final status. |
| `selfReportSessionTime` | false | true/false | Should the API override the default `session_time` reported by the module? Useful when modules don't properly report time. |
| `responseHandler` | function | | A function to properly tranform the response from the LMS to the correct format. The APIs expect the result from the LMS to be in the following format (errorCode is optional): `{ "result": true, "errorCode": 0 }` |
## Initial Values

93
dist/scorm-again.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

929
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "scorm-again",
"version": "1.1.4",
"version": "1.2.0",
"description": "A modern SCORM JavaScript run-time library for AICC, SCORM 1.2, and SCORM 2004",
"main": "dist/scorm-again.min.js",
"directories": {
@@ -21,7 +21,7 @@
"babelify": "^10.0.0",
"browserify": "^16.5.1",
"chai": "^4.2.0",
"eslint": "^6.8.0",
"eslint": "^7.3.1",
"eslint-config-google": "^0.14.0",
"eslint-plugin-import": "^2.21.2",
"grunt": "^1.1.0",
@@ -33,7 +33,7 @@
"jsdoc-babel": "^0.5.0",
"minifyify": "^7.3.5",
"minimist": "^1.2.5",
"mocha": "^7.1.2",
"mocha": "^8.0.1",
"mocha-junit-reporter": "^2.0.0",
"mochawesome": "^6.1.1",
"nyc": "^15.1.0"

View File

@@ -23,6 +23,7 @@ export default class BaseAPI {
commitRequestDataType: 'application/json;charset=UTF-8',
autoProgress: false,
logLevel: global_constants.LOG_LEVEL_ERROR,
selfReportSessionTime: false,
responseHandler: function(xhr) {
let result;
if (typeof xhr !== 'undefined') {
@@ -62,6 +63,7 @@ export default class BaseAPI {
this.settings = settings;
this.apiLogLevel = this.settings.logLevel;
this.selfReportSessionTime = this.settings.selfReportSessionTime;
}
/**
@@ -82,6 +84,10 @@ export default class BaseAPI {
} else if (this.isTerminated()) {
this.throwSCORMError(this.#error_codes.TERMINATED, terminationMessage);
} else {
if (this.selfReportSessionTime) {
this.cmi.setStartTime();
}
this.currentState = global_constants.STATE_INITIALIZED;
this.lastErrorCode = 0;
returnValue = global_constants.SCORM_TRUE;
@@ -132,7 +138,7 @@ export default class BaseAPI {
this.throwSCORMError(result.errorCode);
}
returnValue = result.result ?
result.result : global_constants.SCORM_FALSE;
result.result : global_constants.SCORM_FALSE;
if (checkTerminated) this.lastErrorCode = 0;
@@ -256,7 +262,7 @@ export default class BaseAPI {
this.throwSCORMError(result.errorCode);
}
returnValue = result.result ?
result.result : global_constants.SCORM_FALSE;
result.result : global_constants.SCORM_FALSE;
this.apiLog(callbackName, 'HttpRequest', ' Result: ' + returnValue,
global_constants.LOG_LEVEL_DEBUG);
@@ -452,9 +458,9 @@ export default class BaseAPI {
*/
_checkObjectHasProperty(refObject, attribute: String) {
return Object.hasOwnProperty.call(refObject, attribute) ||
Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(refObject), attribute) ||
(attribute in refObject);
Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(refObject), attribute) ||
(attribute in refObject);
}
/**
@@ -518,15 +524,15 @@ 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 ?
this.#error_codes.UNDEFINED_DATA_MODEL :
this.#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];
if (i === structure.length - 1) {
if (scorm2004 && (attribute.substr(0, 8) === '{target=') &&
(typeof refObject._isTargetValid == 'function')) {
(typeof refObject._isTargetValid == 'function')) {
this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT);
} else if (!this._checkObjectHasProperty(refObject, attribute)) {
this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
@@ -632,8 +638,8 @@ export default class BaseAPI {
const uninitializedErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) has not been initialized.`;
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
const invalidErrorCode = scorm2004 ?
this.#error_codes.UNDEFINED_DATA_MODEL :
this.#error_codes.GENERAL;
this.#error_codes.UNDEFINED_DATA_MODEL :
this.#error_codes.GENERAL;
for (let i = 0; i < structure.length; i++) {
attribute = structure[i];
@@ -647,7 +653,7 @@ export default class BaseAPI {
}
} else {
if ((String(attribute).substr(0, 8) === '{target=') &&
(typeof refObject._isTargetValid == 'function')) {
(typeof refObject._isTargetValid == 'function')) {
const target = String(attribute).
substr(8, String(attribute).length - 9);
return refObject._isTargetValid(target);
@@ -767,8 +773,11 @@ export default class BaseAPI {
const functionsMatch = listener.functionName === functionName;
const listenerHasCMIElement = !!listener.CMIElement;
let CMIElementsMatch = false;
if (CMIElement && listener.CMIElement && listener.CMIElement.substring(listener.CMIElement.length - 1) === '*') {
CMIElementsMatch = CMIElement.indexOf(listener.CMIElement.substring(0, listener.CMIElement.length - 1)) === 0;
if (CMIElement && listener.CMIElement &&
listener.CMIElement.substring(listener.CMIElement.length - 1) ===
'*') {
CMIElementsMatch = CMIElement.indexOf(listener.CMIElement.substring(0,
listener.CMIElement.length - 1)) === 0;
} else {
CMIElementsMatch = listener.CMIElement === CMIElement;
}

View File

@@ -62,6 +62,7 @@ export function checkValidRange(
export class BaseCMI {
jsonString = false;
#initialized = false;
#start_time;
/**
* Constructor for BaseCMI, just marks the class as abstract
@@ -80,12 +81,28 @@ export class BaseCMI {
return this.#initialized;
}
/**
* Getter for #start_time
* @return {Number}
*/
get start_time() {
return this.#start_time;
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
this.#initialized = true;
}
/**
* Called when the player should override the 'session_time' provided by
* the module
*/
setStartTime() {
this.#start_time = new Date().getTime();
}
}
/**

View File

@@ -11,6 +11,7 @@ import ErrorCodes from '../constants/error_codes';
import Regex from '../constants/regex';
import {ValidationError} from '../exceptions';
import * as Utilities from '../utilities';
import * as Util from '../utilities';
const scorm12_constants = APIConstants.scorm12;
const scorm12_regex = Regex.scorm12;
@@ -254,7 +255,7 @@ export class CMI extends BaseCMI {
* @return {string}
*/
getCurrentTotalTime() {
return this.core.getCurrentTotalTime();
return this.core.getCurrentTotalTime(this.start_time);
}
}
@@ -508,13 +509,21 @@ class CMICore extends BaseCMI {
/**
* Adds the current session time to the existing total time.
*
* @param {Number} start_time
* @return {string}
*/
getCurrentTotalTime() {
getCurrentTotalTime(start_time: Number) {
let sessionTime = this.#session_time;
const startTime = start_time;
if (typeof startTime !== 'undefined' || startTime === null) {
const seconds = new Date().getTime() - startTime;
sessionTime = Util.getSecondsAsHHMMSS(seconds / 1000);
}
return Utilities.addHHMMSSTimeStrings(
this.#total_time,
this.#session_time,
sessionTime,
new RegExp(scorm12_regex.CMITimespan),
);
}

View File

@@ -431,7 +431,8 @@ export class CMI extends BaseCMI {
* @param {string} suspend_data
*/
set suspend_data(suspend_data) {
if (check2004ValidFormat(suspend_data, scorm2004_regex.CMIString64000, true)) {
if (check2004ValidFormat(suspend_data, scorm2004_regex.CMIString64000,
true)) {
this.#suspend_data = suspend_data;
}
}
@@ -476,9 +477,17 @@ export class CMI extends BaseCMI {
* @return {string} ISO8601 Duration
*/
getCurrentTotalTime() {
let sessionTime = this.#session_time;
const startTime = this.start_time;
if (typeof startTime !== 'undefined' || startTime === null) {
const seconds = new Date().getTime() - startTime;
sessionTime = Util.getSecondsAsISODuration(seconds / 1000);
}
return Util.addTwoDurations(
this.#total_time,
this.#session_time,
sessionTime,
scorm2004_regex.CMITimespan,
);
}
@@ -966,7 +975,8 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} description
*/
set description(description) {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250, true)) {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
true)) {
this.#description = description;
}
}
@@ -1121,7 +1131,8 @@ export class CMIObjectivesObject extends BaseCMI {
* @param {string} description
*/
set description(description) {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250, true)) {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
true)) {
this.#description = description;
}
}
@@ -1257,7 +1268,8 @@ export class CMICommentsObject extends BaseCMI {
if (this.initialized && this.#readOnlyAfterInit) {
throwReadOnlyError();
} else {
if (check2004ValidFormat(comment, scorm2004_regex.CMILangString4000, true)) {
if (check2004ValidFormat(comment, scorm2004_regex.CMILangString4000,
true)) {
this.#comment = comment;
}
}

View File

@@ -317,9 +317,6 @@ describe('SCORM 1.2 API Tests', () => {
cmiExport.cmi.core.total_time,
).to.equal('36:34:55');
});
});
describe('renderCommitCMI()', () => {
it('if the user passes, should calculate total time when terminateCommit passed',
() => {
const scorm12API = api();

View File

@@ -7,8 +7,8 @@ import {scorm2004_values} from './field_values';
const scorm2004_error_codes = ErrorCodes.scorm2004;
const api = (startingData) => {
const API = new Scorm2004API();
const api = (settings = {}, startingData = {}) => {
const API = new Scorm2004API(settings);
API.apiLogLevel = 1;
if (startingData) {
API.startingData = startingData;