scorm-again 3.1.5 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cross-frame-api.js +6 -13
- package/dist/cross-frame-api.js.map +1 -1
- package/dist/cross-frame-api.min.js +1 -1
- package/dist/cross-frame-lms.js +2 -6
- package/dist/cross-frame-lms.js.map +1 -1
- package/dist/esm/scorm-again.js +48 -11
- package/dist/esm/scorm-again.js.map +1 -1
- package/dist/esm/scorm-again.min.js +1 -1
- package/dist/esm/scorm-again.min.js.map +1 -1
- package/dist/esm/scorm12.js +46 -10
- package/dist/esm/scorm12.js.map +1 -1
- package/dist/esm/scorm12.min.js +1 -1
- package/dist/esm/scorm12.min.js.map +1 -1
- package/dist/esm/scorm2004.js +48 -11
- package/dist/esm/scorm2004.js.map +1 -1
- package/dist/esm/scorm2004.min.js +1 -1
- package/dist/esm/scorm2004.min.js.map +1 -1
- package/dist/scorm-again.js +697 -1070
- package/dist/scorm-again.js.map +1 -1
- package/dist/scorm-again.min.js +1 -1
- package/dist/scorm12.js +293 -406
- package/dist/scorm12.js.map +1 -1
- package/dist/scorm12.min.js +1 -1
- package/dist/scorm2004.js +597 -911
- package/dist/scorm2004.js.map +1 -1
- package/dist/scorm2004.min.js +1 -1
- package/dist/types/BaseAPI.d.ts +1 -1
- package/dist/types/interfaces/services.d.ts +1 -1
- package/dist/types/services/AsynchronousHttpService.d.ts +1 -0
- package/dist/types/services/ErrorHandlingService.d.ts +4 -4
- package/dist/types/services/SynchronousHttpService.d.ts +1 -0
- package/dist/types/types/api_types.d.ts +4 -0
- package/dist/types/utilities/core.d.ts +2 -0
- package/package.json +4 -4
package/dist/esm/scorm12.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
const SECONDS_PER_MINUTE = 60;
|
|
2
2
|
const SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE;
|
|
3
|
+
const CORS_SAFELISTED_CONTENT_TYPES = [
|
|
4
|
+
"text/plain",
|
|
5
|
+
"application/x-www-form-urlencoded",
|
|
6
|
+
"multipart/form-data"
|
|
7
|
+
];
|
|
8
|
+
function isCorsSafelistedContentType(contentType) {
|
|
9
|
+
const essence = ((contentType || "").split(";")[0] ?? "").trim().toLowerCase();
|
|
10
|
+
return CORS_SAFELISTED_CONTENT_TYPES.includes(essence);
|
|
11
|
+
}
|
|
12
|
+
function isCrossOriginUrl(url) {
|
|
13
|
+
if (typeof location === "undefined" || !location || !location.origin) return false;
|
|
14
|
+
try {
|
|
15
|
+
return new URL(url, location.href).origin !== location.origin;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
3
20
|
const getSecondsAsHHMMSS = (totalSeconds) => {
|
|
4
21
|
if (!totalSeconds || totalSeconds <= 0) {
|
|
5
22
|
return "00:00:00";
|
|
@@ -530,8 +547,10 @@ const DefaultSettings = {
|
|
|
530
547
|
lmsCommitUrl: false,
|
|
531
548
|
dataCommitFormat: "json",
|
|
532
549
|
commitRequestDataType: "application/json;charset=UTF-8",
|
|
550
|
+
terminationCommitContentType: "text/plain;charset=UTF-8",
|
|
533
551
|
autoProgress: false,
|
|
534
552
|
logLevel: LogLevelEnum.ERROR,
|
|
553
|
+
uninitializedGetLogLevel: LogLevelEnum.WARN,
|
|
535
554
|
selfReportSessionTime: false,
|
|
536
555
|
alwaysSendTotalTime: false,
|
|
537
556
|
renderCommonCommitFields: false,
|
|
@@ -1061,7 +1080,9 @@ class AsynchronousHttpService {
|
|
|
1061
1080
|
*/
|
|
1062
1081
|
async performBeacon(url, params) {
|
|
1063
1082
|
const { body, contentType } = this._prepareRequestBody(params);
|
|
1064
|
-
const
|
|
1083
|
+
const beaconContentType = Array.isArray(params) ? contentType : this.settings.terminationCommitContentType;
|
|
1084
|
+
this._warnIfBeaconContentTypeUnsafe(url, beaconContentType);
|
|
1085
|
+
const beaconSuccess = navigator.sendBeacon(url, new Blob([body], { type: beaconContentType }));
|
|
1065
1086
|
return Promise.resolve({
|
|
1066
1087
|
status: beaconSuccess ? 200 : 0,
|
|
1067
1088
|
ok: beaconSuccess,
|
|
@@ -1075,6 +1096,14 @@ class AsynchronousHttpService {
|
|
|
1075
1096
|
})
|
|
1076
1097
|
});
|
|
1077
1098
|
}
|
|
1099
|
+
_warnIfBeaconContentTypeUnsafe(url, contentType) {
|
|
1100
|
+
if (isCrossOriginUrl(url) && !isCorsSafelistedContentType(contentType)) {
|
|
1101
|
+
this.settings.onLogMessage?.(
|
|
1102
|
+
LogLevelEnum.WARN,
|
|
1103
|
+
`sendBeacon to cross-origin URL with non-CORS-safelisted Content-Type "${contentType}" may be silently dropped by the browser (Beacon cannot preflight). Use fetch (useAsynchronousCommits + asyncModeBeaconBehavior:"never") for cross-origin JSON/auth on terminate.`
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1078
1107
|
/**
|
|
1079
1108
|
* Transforms the response from the LMS to a ResultObject
|
|
1080
1109
|
* @param {Response} response - The response from the LMS
|
|
@@ -1742,14 +1771,14 @@ class ErrorHandlingService {
|
|
|
1742
1771
|
* @param {string} message - The error message
|
|
1743
1772
|
* @throws {ValidationError} - If throwException is true, throws a ValidationError
|
|
1744
1773
|
*/
|
|
1745
|
-
throwSCORMError(CMIElement, errorNumber, message) {
|
|
1774
|
+
throwSCORMError(CMIElement, errorNumber, message, messageLevel = LogLevelEnum.ERROR) {
|
|
1746
1775
|
this._lastDiagnostic = message || "";
|
|
1747
1776
|
if (!message) {
|
|
1748
1777
|
message = this._getLmsErrorMessageDetails(errorNumber, true);
|
|
1749
1778
|
}
|
|
1750
1779
|
const formattedMessage = `SCORM Error ${errorNumber}: ${message}${CMIElement ? ` [Element: ${CMIElement}]` : ""}`;
|
|
1751
|
-
this._apiLog("throwSCORMError", errorNumber + ": " + message,
|
|
1752
|
-
this._loggingService.
|
|
1780
|
+
this._apiLog("throwSCORMError", errorNumber + ": " + message, messageLevel, CMIElement);
|
|
1781
|
+
this._loggingService.log(messageLevel, formattedMessage);
|
|
1753
1782
|
this._lastErrorCode = String(errorNumber);
|
|
1754
1783
|
}
|
|
1755
1784
|
/**
|
|
@@ -2748,15 +2777,22 @@ class SynchronousHttpService {
|
|
|
2748
2777
|
const handledPayload = metadata === void 0 ? this.settings.requestHandler(params) : this.settings.requestHandler(params, metadata);
|
|
2749
2778
|
const requestPayload = handledPayload ?? params;
|
|
2750
2779
|
const { body } = this._prepareRequestBody(requestPayload);
|
|
2751
|
-
const
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
);
|
|
2780
|
+
const beaconContentType = this.settings.terminationCommitContentType;
|
|
2781
|
+
this._warnIfBeaconContentTypeUnsafe(url, beaconContentType);
|
|
2782
|
+
const beaconSuccess = navigator.sendBeacon(url, new Blob([body], { type: beaconContentType }));
|
|
2755
2783
|
return {
|
|
2756
2784
|
result: beaconSuccess ? "true" : "false",
|
|
2757
2785
|
errorCode: beaconSuccess ? 0 : this.error_codes.GENERAL_COMMIT_FAILURE || 391
|
|
2758
2786
|
};
|
|
2759
2787
|
}
|
|
2788
|
+
_warnIfBeaconContentTypeUnsafe(url, contentType) {
|
|
2789
|
+
if (isCrossOriginUrl(url) && !isCorsSafelistedContentType(contentType)) {
|
|
2790
|
+
this.settings.onLogMessage?.(
|
|
2791
|
+
LogLevelEnum.WARN,
|
|
2792
|
+
`sendBeacon to cross-origin URL with non-CORS-safelisted Content-Type "${contentType}" may be silently dropped by the browser (Beacon cannot preflight). Use fetch (useAsynchronousCommits + asyncModeBeaconBehavior:"never") for cross-origin JSON/auth on terminate.`
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2760
2796
|
/**
|
|
2761
2797
|
* Performs a synchronous XMLHttpRequest
|
|
2762
2798
|
* @param {string} url - The URL to send the request to
|
|
@@ -3783,8 +3819,8 @@ class BaseAPI {
|
|
|
3783
3819
|
* // Throw a "not initialized" error
|
|
3784
3820
|
* this.throwSCORMError(301, "The API must be initialized before calling GetValue");
|
|
3785
3821
|
*/
|
|
3786
|
-
throwSCORMError(CMIElement, errorNumber, message) {
|
|
3787
|
-
this._errorHandlingService.throwSCORMError(CMIElement, errorNumber ?? 0, message);
|
|
3822
|
+
throwSCORMError(CMIElement, errorNumber, message, messageLevel) {
|
|
3823
|
+
this._errorHandlingService.throwSCORMError(CMIElement, errorNumber ?? 0, message, messageLevel);
|
|
3788
3824
|
}
|
|
3789
3825
|
/**
|
|
3790
3826
|
* Clears the last SCORM error code when an operation succeeds.
|