Adding new selfReportSessionTime setting
This commit is contained in:
@@ -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.|
|
| `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. |
|
| `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. |
|
| `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 }` |
|
| `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
|
## Initial Values
|
||||||
|
|||||||
93
dist/scorm-again.js
vendored
93
dist/scorm-again.js
vendored
File diff suppressed because one or more lines are too long
2
dist/scorm-again.js.map
vendored
2
dist/scorm-again.js.map
vendored
File diff suppressed because one or more lines are too long
14
dist/scorm-again.min.js
vendored
14
dist/scorm-again.min.js
vendored
File diff suppressed because one or more lines are too long
929
package-lock.json
generated
929
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "scorm-again",
|
"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",
|
"description": "A modern SCORM JavaScript run-time library for AICC, SCORM 1.2, and SCORM 2004",
|
||||||
"main": "dist/scorm-again.min.js",
|
"main": "dist/scorm-again.min.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"babelify": "^10.0.0",
|
"babelify": "^10.0.0",
|
||||||
"browserify": "^16.5.1",
|
"browserify": "^16.5.1",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.3.1",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-import": "^2.21.2",
|
"eslint-plugin-import": "^2.21.2",
|
||||||
"grunt": "^1.1.0",
|
"grunt": "^1.1.0",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"jsdoc-babel": "^0.5.0",
|
"jsdoc-babel": "^0.5.0",
|
||||||
"minifyify": "^7.3.5",
|
"minifyify": "^7.3.5",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"mocha": "^7.1.2",
|
"mocha": "^8.0.1",
|
||||||
"mocha-junit-reporter": "^2.0.0",
|
"mocha-junit-reporter": "^2.0.0",
|
||||||
"mochawesome": "^6.1.1",
|
"mochawesome": "^6.1.1",
|
||||||
"nyc": "^15.1.0"
|
"nyc": "^15.1.0"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default class BaseAPI {
|
|||||||
commitRequestDataType: 'application/json;charset=UTF-8',
|
commitRequestDataType: 'application/json;charset=UTF-8',
|
||||||
autoProgress: false,
|
autoProgress: false,
|
||||||
logLevel: global_constants.LOG_LEVEL_ERROR,
|
logLevel: global_constants.LOG_LEVEL_ERROR,
|
||||||
|
selfReportSessionTime: false,
|
||||||
responseHandler: function(xhr) {
|
responseHandler: function(xhr) {
|
||||||
let result;
|
let result;
|
||||||
if (typeof xhr !== 'undefined') {
|
if (typeof xhr !== 'undefined') {
|
||||||
@@ -62,6 +63,7 @@ export default class BaseAPI {
|
|||||||
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.apiLogLevel = this.settings.logLevel;
|
this.apiLogLevel = this.settings.logLevel;
|
||||||
|
this.selfReportSessionTime = this.settings.selfReportSessionTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,6 +84,10 @@ export default class BaseAPI {
|
|||||||
} else if (this.isTerminated()) {
|
} else if (this.isTerminated()) {
|
||||||
this.throwSCORMError(this.#error_codes.TERMINATED, terminationMessage);
|
this.throwSCORMError(this.#error_codes.TERMINATED, terminationMessage);
|
||||||
} else {
|
} else {
|
||||||
|
if (this.selfReportSessionTime) {
|
||||||
|
this.cmi.setStartTime();
|
||||||
|
}
|
||||||
|
|
||||||
this.currentState = global_constants.STATE_INITIALIZED;
|
this.currentState = global_constants.STATE_INITIALIZED;
|
||||||
this.lastErrorCode = 0;
|
this.lastErrorCode = 0;
|
||||||
returnValue = global_constants.SCORM_TRUE;
|
returnValue = global_constants.SCORM_TRUE;
|
||||||
@@ -132,7 +138,7 @@ export default class BaseAPI {
|
|||||||
this.throwSCORMError(result.errorCode);
|
this.throwSCORMError(result.errorCode);
|
||||||
}
|
}
|
||||||
returnValue = result.result ?
|
returnValue = result.result ?
|
||||||
result.result : global_constants.SCORM_FALSE;
|
result.result : global_constants.SCORM_FALSE;
|
||||||
|
|
||||||
if (checkTerminated) this.lastErrorCode = 0;
|
if (checkTerminated) this.lastErrorCode = 0;
|
||||||
|
|
||||||
@@ -256,7 +262,7 @@ export default class BaseAPI {
|
|||||||
this.throwSCORMError(result.errorCode);
|
this.throwSCORMError(result.errorCode);
|
||||||
}
|
}
|
||||||
returnValue = result.result ?
|
returnValue = result.result ?
|
||||||
result.result : global_constants.SCORM_FALSE;
|
result.result : global_constants.SCORM_FALSE;
|
||||||
|
|
||||||
this.apiLog(callbackName, 'HttpRequest', ' Result: ' + returnValue,
|
this.apiLog(callbackName, 'HttpRequest', ' Result: ' + returnValue,
|
||||||
global_constants.LOG_LEVEL_DEBUG);
|
global_constants.LOG_LEVEL_DEBUG);
|
||||||
@@ -452,9 +458,9 @@ export default class BaseAPI {
|
|||||||
*/
|
*/
|
||||||
_checkObjectHasProperty(refObject, attribute: String) {
|
_checkObjectHasProperty(refObject, attribute: String) {
|
||||||
return Object.hasOwnProperty.call(refObject, attribute) ||
|
return Object.hasOwnProperty.call(refObject, attribute) ||
|
||||||
Object.getOwnPropertyDescriptor(
|
Object.getOwnPropertyDescriptor(
|
||||||
Object.getPrototypeOf(refObject), attribute) ||
|
Object.getPrototypeOf(refObject), attribute) ||
|
||||||
(attribute in refObject);
|
(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 invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
||||||
const invalidErrorCode = scorm2004 ?
|
const invalidErrorCode = scorm2004 ?
|
||||||
this.#error_codes.UNDEFINED_DATA_MODEL :
|
this.#error_codes.UNDEFINED_DATA_MODEL :
|
||||||
this.#error_codes.GENERAL;
|
this.#error_codes.GENERAL;
|
||||||
|
|
||||||
for (let i = 0; i < structure.length; i++) {
|
for (let i = 0; i < structure.length; i++) {
|
||||||
const attribute = structure[i];
|
const attribute = structure[i];
|
||||||
|
|
||||||
if (i === structure.length - 1) {
|
if (i === structure.length - 1) {
|
||||||
if (scorm2004 && (attribute.substr(0, 8) === '{target=') &&
|
if (scorm2004 && (attribute.substr(0, 8) === '{target=') &&
|
||||||
(typeof refObject._isTargetValid == 'function')) {
|
(typeof refObject._isTargetValid == 'function')) {
|
||||||
this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT);
|
this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT);
|
||||||
} else if (!this._checkObjectHasProperty(refObject, attribute)) {
|
} else if (!this._checkObjectHasProperty(refObject, attribute)) {
|
||||||
this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
|
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 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 invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
|
||||||
const invalidErrorCode = scorm2004 ?
|
const invalidErrorCode = scorm2004 ?
|
||||||
this.#error_codes.UNDEFINED_DATA_MODEL :
|
this.#error_codes.UNDEFINED_DATA_MODEL :
|
||||||
this.#error_codes.GENERAL;
|
this.#error_codes.GENERAL;
|
||||||
|
|
||||||
for (let i = 0; i < structure.length; i++) {
|
for (let i = 0; i < structure.length; i++) {
|
||||||
attribute = structure[i];
|
attribute = structure[i];
|
||||||
@@ -647,7 +653,7 @@ export default class BaseAPI {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((String(attribute).substr(0, 8) === '{target=') &&
|
if ((String(attribute).substr(0, 8) === '{target=') &&
|
||||||
(typeof refObject._isTargetValid == 'function')) {
|
(typeof refObject._isTargetValid == 'function')) {
|
||||||
const target = String(attribute).
|
const target = String(attribute).
|
||||||
substr(8, String(attribute).length - 9);
|
substr(8, String(attribute).length - 9);
|
||||||
return refObject._isTargetValid(target);
|
return refObject._isTargetValid(target);
|
||||||
@@ -767,8 +773,11 @@ export default class BaseAPI {
|
|||||||
const functionsMatch = listener.functionName === functionName;
|
const functionsMatch = listener.functionName === functionName;
|
||||||
const listenerHasCMIElement = !!listener.CMIElement;
|
const listenerHasCMIElement = !!listener.CMIElement;
|
||||||
let CMIElementsMatch = false;
|
let CMIElementsMatch = false;
|
||||||
if (CMIElement && listener.CMIElement && listener.CMIElement.substring(listener.CMIElement.length - 1) === '*') {
|
if (CMIElement && listener.CMIElement &&
|
||||||
CMIElementsMatch = CMIElement.indexOf(listener.CMIElement.substring(0, listener.CMIElement.length - 1)) === 0;
|
listener.CMIElement.substring(listener.CMIElement.length - 1) ===
|
||||||
|
'*') {
|
||||||
|
CMIElementsMatch = CMIElement.indexOf(listener.CMIElement.substring(0,
|
||||||
|
listener.CMIElement.length - 1)) === 0;
|
||||||
} else {
|
} else {
|
||||||
CMIElementsMatch = listener.CMIElement === CMIElement;
|
CMIElementsMatch = listener.CMIElement === CMIElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export function checkValidRange(
|
|||||||
export class BaseCMI {
|
export class BaseCMI {
|
||||||
jsonString = false;
|
jsonString = false;
|
||||||
#initialized = false;
|
#initialized = false;
|
||||||
|
#start_time;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for BaseCMI, just marks the class as abstract
|
* Constructor for BaseCMI, just marks the class as abstract
|
||||||
@@ -80,12 +81,28 @@ export class BaseCMI {
|
|||||||
return this.#initialized;
|
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
|
* Called when the API has been initialized after the CMI has been created
|
||||||
*/
|
*/
|
||||||
initialize() {
|
initialize() {
|
||||||
this.#initialized = true;
|
this.#initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the player should override the 'session_time' provided by
|
||||||
|
* the module
|
||||||
|
*/
|
||||||
|
setStartTime() {
|
||||||
|
this.#start_time = new Date().getTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import ErrorCodes from '../constants/error_codes';
|
|||||||
import Regex from '../constants/regex';
|
import Regex from '../constants/regex';
|
||||||
import {ValidationError} from '../exceptions';
|
import {ValidationError} from '../exceptions';
|
||||||
import * as Utilities from '../utilities';
|
import * as Utilities from '../utilities';
|
||||||
|
import * as Util from '../utilities';
|
||||||
|
|
||||||
const scorm12_constants = APIConstants.scorm12;
|
const scorm12_constants = APIConstants.scorm12;
|
||||||
const scorm12_regex = Regex.scorm12;
|
const scorm12_regex = Regex.scorm12;
|
||||||
@@ -254,7 +255,7 @@ export class CMI extends BaseCMI {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getCurrentTotalTime() {
|
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.
|
* Adds the current session time to the existing total time.
|
||||||
*
|
* @param {Number} start_time
|
||||||
* @return {string}
|
* @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(
|
return Utilities.addHHMMSSTimeStrings(
|
||||||
this.#total_time,
|
this.#total_time,
|
||||||
this.#session_time,
|
sessionTime,
|
||||||
new RegExp(scorm12_regex.CMITimespan),
|
new RegExp(scorm12_regex.CMITimespan),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -431,7 +431,8 @@ export class CMI extends BaseCMI {
|
|||||||
* @param {string} suspend_data
|
* @param {string} suspend_data
|
||||||
*/
|
*/
|
||||||
set suspend_data(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;
|
this.#suspend_data = suspend_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,9 +477,17 @@ export class CMI extends BaseCMI {
|
|||||||
* @return {string} ISO8601 Duration
|
* @return {string} ISO8601 Duration
|
||||||
*/
|
*/
|
||||||
getCurrentTotalTime() {
|
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(
|
return Util.addTwoDurations(
|
||||||
this.#total_time,
|
this.#total_time,
|
||||||
this.#session_time,
|
sessionTime,
|
||||||
scorm2004_regex.CMITimespan,
|
scorm2004_regex.CMITimespan,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -966,7 +975,8 @@ export class CMIInteractionsObject extends BaseCMI {
|
|||||||
* @param {string} description
|
* @param {string} description
|
||||||
*/
|
*/
|
||||||
set description(description) {
|
set description(description) {
|
||||||
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250, true)) {
|
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
|
||||||
|
true)) {
|
||||||
this.#description = description;
|
this.#description = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1121,7 +1131,8 @@ export class CMIObjectivesObject extends BaseCMI {
|
|||||||
* @param {string} description
|
* @param {string} description
|
||||||
*/
|
*/
|
||||||
set description(description) {
|
set description(description) {
|
||||||
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250, true)) {
|
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
|
||||||
|
true)) {
|
||||||
this.#description = description;
|
this.#description = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1257,7 +1268,8 @@ export class CMICommentsObject extends BaseCMI {
|
|||||||
if (this.initialized && this.#readOnlyAfterInit) {
|
if (this.initialized && this.#readOnlyAfterInit) {
|
||||||
throwReadOnlyError();
|
throwReadOnlyError();
|
||||||
} else {
|
} else {
|
||||||
if (check2004ValidFormat(comment, scorm2004_regex.CMILangString4000, true)) {
|
if (check2004ValidFormat(comment, scorm2004_regex.CMILangString4000,
|
||||||
|
true)) {
|
||||||
this.#comment = comment;
|
this.#comment = comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,9 +317,6 @@ describe('SCORM 1.2 API Tests', () => {
|
|||||||
cmiExport.cmi.core.total_time,
|
cmiExport.cmi.core.total_time,
|
||||||
).to.equal('36:34:55');
|
).to.equal('36:34:55');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('renderCommitCMI()', () => {
|
|
||||||
it('if the user passes, should calculate total time when terminateCommit passed',
|
it('if the user passes, should calculate total time when terminateCommit passed',
|
||||||
() => {
|
() => {
|
||||||
const scorm12API = api();
|
const scorm12API = api();
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {scorm2004_values} from './field_values';
|
|||||||
|
|
||||||
const scorm2004_error_codes = ErrorCodes.scorm2004;
|
const scorm2004_error_codes = ErrorCodes.scorm2004;
|
||||||
|
|
||||||
const api = (startingData) => {
|
const api = (settings = {}, startingData = {}) => {
|
||||||
const API = new Scorm2004API();
|
const API = new Scorm2004API(settings);
|
||||||
API.apiLogLevel = 1;
|
API.apiLogLevel = 1;
|
||||||
if (startingData) {
|
if (startingData) {
|
||||||
API.startingData = startingData;
|
API.startingData = startingData;
|
||||||
|
|||||||
Reference in New Issue
Block a user