Skip to content

Commit

Permalink
Fix: Reworked and removed offline_API_wrapper.js (fixes adaptlearning…
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverfoster authored Mar 8, 2023
1 parent 0733063 commit 75f15a6
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 209 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ Determines whether the `cmi.completion_status` is set to "completed" if the asse
#### \_showCookieLmsResetButton (boolean):
Determines whether a reset button will be available to relaunch the course and optionally clear tracking data (scorm_test_harness.html only). The default is `false`.

#### \_shouldPersistCookieLMSData (boolean):
Determines whether to persist the cookie data over browser sessions (scorm_test_harness.html only). The default is `true`.

<div float align=right><a href="#top">Back to Top</a></div>

## Notes
Expand Down
3 changes: 2 additions & 1 deletion example.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"_exitStateIfComplete": "auto",
"_setCompletedWhenFailed": true
},
"_showCookieLmsResetButton": false
"_showCookieLmsResetButton": false,
"_shouldPersistCookieLMSData": true
}

// to be added to course/en/course.json (note: you only need to add the ones you want to change/translate)
Expand Down
2 changes: 2 additions & 0 deletions js/adapt-contrib-spoor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import ScormWrapper from './scorm/wrapper';
import StatefulSession from './adapt-stateful-session';
import OfflineStorage from './adapt-offlineStorage-scorm';
import offlineStorage from 'core/js/offlineStorage';
import { shouldStart as shouldStartCookieLMS, start as startCookieLMS } from './scorm/cookieLMS';

class Spoor extends Backbone.Controller {

initialize() {
this.config = null;
if (shouldStartCookieLMS) startCookieLMS();
this.scorm = ScormWrapper.getInstance();
this.listenToOnce(Adapt, 'offlineStorage:prepare', this._prepare);
}
Expand Down
189 changes: 189 additions & 0 deletions js/scorm/cookieLMS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import Adapt from 'core/js/adapt';
import Cookies from 'libraries/js-cookie.js';

/** Start the mock API if window.ISCOOKIELMS exists and isn't null */
export const shouldStart = (Object.prototype.hasOwnProperty.call(window, 'ISCOOKIELMS') && window.ISCOOKIELMS !== null);

/** Store the data in a cookie if window.ISCOOKIELMS is true, otherwise setup the API without storing data. */
export const isStoringData = (window.ISCOOKIELMS === true);

export function createResetButton() {
const resetButtonStyle = '<style id="spoor-clear-button">.spoor-reset-button { position:fixed; right:0px; bottom:0px; } </style>';
const resetButton = '<button class="spoor-reset-button btn-text">Reset</button>';
$('body').append($(resetButtonStyle));
const $button = $(resetButton);
$('body').append($button);
$button.on('click', e => {
if (!e.shiftKey) {
Cookies.remove('_spoor');
alert('SCORM tracking cookie has been deleted! Tip: shift-click reset to preserve cookie.');
}
window.location = window.location.pathname;
});
}

export function configure() {
if (!isStoringData) return;
const spoorConfig = Adapt.config.get('_spoor');
if (spoorConfig?._showCookieLmsResetButton) createResetButton();
if (!spoorConfig?._shouldPersistCookieLMSData) {
Cookies.defaults = {
// uncomment this if you need the cookie to 'persist'. if left commented-out it will act as a 'session' cookie
// see https://github.com/js-cookie/js-cookie/tree/latest#expires
/* expires: 365, */
sameSite: 'strict'
};
}
}

export function postStorageWarning() {
if (postStorageWarning.__storageWarningTimeoutId !== null) return;
postStorageWarning.__storageWarningTimeoutId = setTimeout(() => {
const notificationMethod = (Adapt.config.get('_spoor')?._advancedSettings?._suppressErrors === true)
? console.error
: alert;
postStorageWarning.__storageWarningTimeoutId = null;
notificationMethod('Warning: possible cookie storage limit exceeded - tracking may malfunction');
}, 1000);
}

export function start () {

const GenericAPI = {

__offlineAPIWrapper: true,

store: function(force) {
if (!isStoringData) return;

if (!force && Cookies.get('_spoor') === undefined) return;

Cookies.set('_spoor', this.data);

// a length mismatch will most likely indicate cookie storage limit exceeded
if (Cookies.get('_spoor').length !== JSON.stringify(this.data).length) postStorageWarning();
},

fetch: function() {
if (!isStoringData) {
this.data = {};
return;
}

this.data = Cookies.getJSON('_spoor');

if (!this.data) {
this.data = {};
return false;
}

return true;
}

};

// SCORM 1.2 API
window.API = {

...GenericAPI,

LMSInitialize: function() {
configure();
if (!this.fetch()) {
this.data['cmi.core.lesson_status'] = 'not attempted';
this.data['cmi.suspend_data'] = '';
this.data['cmi.core.student_name'] = 'Surname, Sam';
this.data['cmi.core.student_id'] = 'sam.surname@example.org';
this.store(true);
}
return 'true';
},

LMSFinish: function() {
return 'true';
},

LMSGetValue: function(key) {
return this.data[key];
},

LMSSetValue: function(key, value) {
const str = 'cmi.interactions.';
if (key.indexOf(str) !== -1) return 'true';

this.data[key] = value;

this.store();
return 'true';
},

LMSCommit: function() {
return 'true';
},

LMSGetLastError: function() {
return 0;
},

LMSGetErrorString: function() {
return 'Fake error string.';
},

LMSGetDiagnostic: function() {
return 'Fake diagnostic information.';
}
};

// SCORM 2004 API
window.API_1484_11 = {

...GenericAPI,

Initialize: function() {
configure();
if (!this.fetch()) {
this.data['cmi.completion_status'] = 'not attempted';
this.data['cmi.suspend_data'] = '';
this.data['cmi.learner_name'] = 'Surname, Sam';
this.data['cmi.learner_id'] = 'sam.surname@example.org';
this.store(true);
}
return 'true';
},

Terminate: function() {
return 'true';
},

GetValue: function(key) {
return this.data[key];
},

SetValue: function(key, value) {
const str = 'cmi.interactions.';
if (key.indexOf(str) !== -1) return 'true';

this.data[key] = value;

this.store();
return 'true';
},

Commit: function() {
return 'true';
},

GetLastError: function() {
return 0;
},

GetErrorString: function() {
return 'Fake error string.';
},

GetDiagnostic: function() {
return 'Fake diagnostic information.';
}

};
}
9 changes: 9 additions & 0 deletions properties.schema
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,15 @@
"inputType": "Checkbox",
"validators": [],
"help": "If enabled, a reset button will be available to relaunch the course and optionally clear tracking data (scorm_test_harness.html only)."
},
"_shouldPersistCookieLMSData": {
"type": "boolean",
"required": false,
"default": true,
"title": "Persist cookie data (scorm_test_harness.html only)",
"inputType": "Checkbox",
"validators": [],
"help": "If enabled, the course data will persist over browser sessions (scorm_test_harness.html only)."
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions required/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
window.ADAPT_BUILD_TYPE = '@@build.type';
window.ISCOOKIELMS = false;
</script>
<script src="offline_API_wrapper.js"></script>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Adapt</title>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="adapt.css" type="text/css" rel="stylesheet">
<script src="libraries/modernizr.js"></script>
Expand All @@ -18,7 +17,7 @@
<body>
<div id="app">
<div id="wrapper">
<!-- Menu / page render here -->
<!-- Adapt menu / page render here -->
</div>
</div>
</body>
Expand Down
13 changes: 7 additions & 6 deletions required/index_lms.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
<head>
<script>
window.ADAPT_BUILD_TYPE = '@@build.type';
window.ISCOOKIELMS = null;
</script>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Adapt</title>
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="adapt.css" type="text/css" rel="stylesheet">
<script src="libraries/modernizr.js"></script>
<script src="adapt/js/scriptLoader.js"></script>
</head>

<body>
<div id="app">
<div id="wrapper">
<!-- Adapt menu / page renders here -->
</div>
</div>
<div id="app">
<div id="wrapper">
<!-- Adapt menu / page renders here -->
</div>
</div>
</body>

</html>
Loading

0 comments on commit 75f15a6

Please sign in to comment.