diff --git a/package-lock.json b/package-lock.json
index e9b7d5e9..e962612b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@oat-sa/tao-test-runner-qti",
- "version": "2.23.4",
+ "version": "2.23.4-1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 5879f988..ca5084e7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@oat-sa/tao-test-runner-qti",
- "version": "2.23.4",
+ "version": "2.23.4-1",
"description": "TAO Test Runner QTI implementation",
"files": [
"dist",
diff --git a/src/provider/qti.js b/src/provider/qti.js
index ed052e8d..d1ffb19f 100644
--- a/src/provider/qti.js
+++ b/src/provider/qti.js
@@ -379,7 +379,7 @@ var qtiProvider = {
stopwatch.init();
stopwatch.spread(this, 'tick');
- const timerClientMode = config.options.timer && config.options.timer.restoreTimerFromClient;
+ const isTimerClientMode = () => config.options.timer && config.options.timer.restoreTimerFromClient;
/*
* Install behavior on events
@@ -562,13 +562,13 @@ var qtiProvider = {
this.trigger('enableitem enablenav');
})
.on('disableitem', function() {
- if (timerClientMode) {
+ if (isTimerClientMode()) {
stopwatch.stop();
}
this.trigger('disabletools');
})
.on('enableitem', function() {
- if (timerClientMode) {
+ if (isTimerClientMode()) {
stopwatch.start();
}
this.trigger('enabletools');
diff --git a/src/proxy/cache/assetPreloader.js b/src/proxy/cache/assetPreloader.js
new file mode 100644
index 00000000..c1c11bdd
--- /dev/null
+++ b/src/proxy/cache/assetPreloader.js
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; under version 2
+ * of the License (non-upgradable).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (c) 2021 Open Assessment Technologies SA
+ */
+
+import preloaders from 'taoQtiTest/runner/proxy/cache/preloaders/assets/preloaders';
+import preloaderManagerFactory from 'taoQtiTest/runner/proxy/cache/preloaderManager';
+
+/**
+ * @callback assetPreloaderManagerAction
+ * @param {string} type - The type of asset to preload
+ * @param {string} url - the url of the asset to load/unload
+ * @param {string} [sourceUrl] - the unresolved URL (used to index)
+ * @param {string} [itemIdentifier] - the id of the item the asset belongs to
+ */
+
+/**
+ * @callback assetPreloaderAction
+ * @param {string} url - the url of the asset to load/unload
+ * @param {string} [sourceUrl] - the unresolved URL (used to index)
+ * @param {string} [itemIdentifier] - the id of the item the asset belongs to
+ */
+
+/**
+ * @typedef {object} assetPreloaderManager
+ * @property {string} name - The name of the preloader
+ * @property {assetPreloaderManagerAction} loaded - Tells whether an asset is loaded or not
+ * @property {assetPreloaderManagerAction} load - Preload an asset
+ * @property {assetPreloaderManagerAction} unload - Unload an asset
+ */
+
+/**
+ * @typedef {object} assetPreloader
+ * @property {string} name - The name of the preloader
+ * @property {assetPreloaderAction} loaded - Tells whether an asset is loaded or not
+ * @property {assetPreloaderAction} load - Preload an asset
+ * @property {assetPreloaderAction} unload - Unload an asset
+ */
+
+/**
+ * Manages the preloading of assets
+ * @function assetPreloaderFactory
+ * @param assetManager - A reference to the assetManager
+ * @return {assetPreloaderManager}
+ */
+const assetPreloaderFactory = preloaderManagerFactory();
+preloaders.forEach(preloader => assetPreloaderFactory.registerProvider(preloader.name, preloader));
+
+export default assetPreloaderFactory;
diff --git a/src/proxy/cache/interactionPreloader.js b/src/proxy/cache/interactionPreloader.js
new file mode 100644
index 00000000..b6b9bda2
--- /dev/null
+++ b/src/proxy/cache/interactionPreloader.js
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; under version 2
+ * of the License (non-upgradable).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (c) 2021 Open Assessment Technologies SA
+ */
+
+import preloaders from 'taoQtiTest/runner/proxy/cache/preloaders/interactions/preloaders';
+import preloaderManagerFactory from 'taoQtiTest/runner/proxy/cache/preloaderManager';
+
+/**
+ * @callback interactionPreloaderManagerAction
+ * @param {string} type - The type of asset to preload
+ * @param {object} interaction - The interaction
+ * @param {object} itemData - The item data
+ * @param {string} itemIdentifier - the id of the item the interaction belongs to
+ */
+
+/**
+ * @callback interactionPreloaderAction
+ * @param {object} interaction - The interaction
+ * @param {object} itemData - The item data
+ * @param {string} itemIdentifier - the id of the item the interaction belongs to
+ */
+
+/**
+ * @typedef {object} interactionPreloaderManager
+ * @property {string} name - The name of the preloader
+ * @property {interactionPreloaderManagerAction} loaded - Tells whether an interaction is loaded or not
+ * @property {interactionPreloaderManagerAction} load - Preload an interaction
+ * @property {interactionPreloaderManagerAction} unload - Unload an interaction
+ */
+
+/**
+ * @typedef {object} interactionPreloader
+ * @property {string} name - The name of the preloader
+ * @property {interactionPreloaderAction} loaded - Tells whether an interaction is loaded or not
+ * @property {interactionPreloaderAction} load - Preload an interaction
+ * @property {interactionPreloaderAction} unload - Unload an interaction
+ */
+
+/**
+ * Manages the preloading of assets
+ * @function assetPreloaderFactory
+ * @param assetManager - A reference to the assetManager
+ * @return {assetPreloaderManager}
+ */
+const interactionPreloaderFactory = preloaderManagerFactory();
+preloaders.forEach(preloader => interactionPreloaderFactory.registerProvider(preloader.name, preloader));
+
+export default interactionPreloaderFactory;
diff --git a/src/proxy/cache/itemPreloader.js b/src/proxy/cache/itemPreloader.js
index 590b7776..24345911 100644
--- a/src/proxy/cache/itemPreloader.js
+++ b/src/proxy/cache/itemPreloader.js
@@ -25,45 +25,26 @@ import _ from 'lodash';
import loggerFactory from 'core/logger';
import qtiItemRunner from 'taoQtiItem/runner/qtiItemRunner';
import getAssetManager from 'taoQtiTest/runner/config/assetManager';
+import assetPreloaderFactory from 'taoQtiTest/runner/proxy/cache/assetPreloader';
+import interactionPreloaderFactory from 'taoQtiTest/runner/proxy/cache/interactionPreloader';
import urlUtil from 'util/url';
-var logger = loggerFactory('taoQtiTest/runner/proxy/cache/itemPreloader');
-
-/**
- * Test the support of possible `` values.
- * @param {String} feature - the value to test
- * @returns {Boolean}
- */
-var relSupport = function relSupport(feature) {
- var fakeLink = document.createElement('link');
- try {
- if (fakeLink.relList && _.isFunction(fakeLink.relList.supports)) {
- return fakeLink.relList.supports(feature);
- }
- } catch (err) {
- return false;
- }
-};
-
/**
- * Does the current env supports ``
+ * @type {logger}
+ * @private
*/
-var supportPreload = relSupport('preload');
-
-/**
- * Does the current env supports ``
- */
-var supportPrefetch = relSupport('prefetch');
+const logger = loggerFactory('taoQtiTest/runner/proxy/cache/itemPreloader');
/**
* Check if the given item object matches the expectations
- * @param {Object} item
- * @param {String} item.itemIdentifier - the item identifier
- * @param {String} item.baseUrl - item baseUrl
- * @param {Object} item.itemData.assets - assets per types : img : ['url1', 'url2' ]
- * @returns {Boolean}
+ * @param {object} item
+ * @param {string} item.itemIdentifier - the item identifier
+ * @param {string} item.baseUrl - item baseUrl
+ * @param {object} item.itemData.assets - assets per types : img : ['url1', 'url2' ]
+ * @returns {boolean}
+ * @private
*/
-var isItemObjectValid = function isItemObjectValid(item) {
+const isItemObjectValid = item => {
return (
_.isPlainObject(item) &&
_.isString(item.baseUrl) &&
@@ -73,152 +54,64 @@ var isItemObjectValid = function isItemObjectValid(item) {
);
};
+/**
+ * Sets a flag onto an item
+ * @param {object} item - The item to flag
+ * @param {string} flag - The flag name to set
+ */
+const setItemFlag = (item, flag) => {
+ item.flags = item.flags || {};
+ item.flags[flag] = true;
+};
+
+/**
+ * Extracts the list of interactions from the item
+ * @param {object} itemData
+ * @returns {object[]}
+ */
+const getItemInteractions = itemData => {
+ const interactions = [];
+ if (itemData.data && itemData.data.body && itemData.data.body.elements) {
+ _.forEach(itemData.data.body.elements, elements => interactions.push(elements));
+ }
+ return interactions;
+};
+
/**
* Create an instance of an item preloader
- * @param {Object} options
- * @param {String} options.testId - the unique identifier of the test instance, required to get the asset manager
+ * @param {object} options
+ * @param {string} options.testId - the unique identifier of the test instance, required to get the asset manager
* @returns {itemPreloader}
* @throws {TypeError} if the testId is not defined
*/
-var itemPreloaderFactory = function itemPreloaderFactory(options) {
- //this is the test asset manager, referenced under options.testId
- var testAssetManager;
-
+function itemPreloaderFactory(options) {
//we also have a specific instance of the asset manager to
//resolve assets of a next item (we can't use the test asset manager).
- var preloadAssetManager = getAssetManager('item-preloader');
-
- //keep references to preloaded images attached
- //in order to prevent garbage collection of cached images
- var images = {};
-
- //keep references to preloaded audio blobs
- var audioBlobs = {};
-
- /**
- * Asset loaders per supported asset types
- */
- var loaders = {
- /**
- * Preload images, using the in memory Image object
- * @param {String} url - the url of the image to preload
- * @param {String} sourceUrl - the unresolved URL (used to index)
- * @param {String} itemIdentifier - the id of the item the asset belongs to
- */
- img: function preloadImage(url, sourceUrl, itemIdentifier) {
- images[itemIdentifier] = images[itemIdentifier] || {};
- if ('Image' in window && !images[itemIdentifier][sourceUrl]) {
- images[itemIdentifier][sourceUrl] = new Image();
- images[itemIdentifier][sourceUrl].src = url;
- }
- },
-
- /**
- * Preload stylesheets
- * @param {String} url - the url of the css to preload
- */
- css: function preloadCss(url) {
- var link = document.createElement('link');
- if (supportPreload) {
- link.setAttribute('rel', 'preload');
- link.setAttribute('as', 'style');
- } else if (supportPrefetch) {
- link.setAttribute('rel', 'prefetch');
- link.setAttribute('as', 'style');
- } else {
- link.disabled = true;
- link.setAttribute('rel', 'stylesheet');
- link.setAttribute('type', 'text/css');
- }
- link.setAttribute('data-preload', true);
- link.setAttribute('href', url);
-
- document.querySelector('head').appendChild(link);
- },
-
- /**
- * Preload audio files : save the blobs for later use in the asset manager
- * @param {String} url - the url of the audio file to preload
- * @param {String} sourceUrl - the unresolved URL (used to index)
- * @param {String} itemIdentifier - the id of the item the asset belongs to
- */
- audio: function preloadAudio(url, sourceUrl, itemIdentifier) {
- var request;
- audioBlobs[itemIdentifier] = audioBlobs[itemIdentifier] || {};
- if (typeof audioBlobs[itemIdentifier][sourceUrl] === 'undefined') {
- //direct XHR to benefit from the "blob" response type
- request = new XMLHttpRequest();
- request.open('GET', url, true);
- request.responseType = 'blob';
- request.onload = function onRequestLoad() {
- if (this.status === 200) {
- //save the blob, directly
- audioBlobs[itemIdentifier][sourceUrl] = this.response;
- }
- };
- //ignore failed requests, best effort only
- request.send();
- }
- }
- };
-
- /**
- * Asset unloaders per supported asset types
- */
- var unloaders = {
- /**
- * Remove images ref so they can be garbage collected
- * @param {String} url - the url of the image to unload
- * @param {String} sourceUrl - the unresolved URL (used to index)
- * @param {String} itemIdentifier - the id of the item the asset belongs to
- */
- img: function unloadImage(url, sourceUrl, itemIdentifier) {
- if (images[itemIdentifier]) {
- images[itemIdentifier] = _.omit(images[itemIdentifier], sourceUrl);
- }
- },
-
- /**
- * Remove prefteched CSS link tag
- * @param {String} url - the url of the css to unload
- */
- css: function unloadCss(url) {
- var link = document.querySelector(`head link[data-preload][href="${url}"]`);
- if (link) {
- document.querySelector('head').removeChild(link);
- }
- },
-
- /**
- * Remove loaded audio files
- * @param {String} url - the url of the css to unload
- * @param {String} sourceUrl - the unresolved URL
- * @param {String} itemIdentifier - the id of the item the asset belongs to
- */
- audio: function unloadAudio(url, sourceUrl, itemIdentifier) {
- if (audioBlobs[itemIdentifier]) {
- audioBlobs[itemIdentifier] = _.omit(audioBlobs[itemIdentifier], sourceUrl);
- }
- }
- };
+ const preloadAssetManager = getAssetManager('item-preloader');
/**
* Resolves assets URLS using the assetManager
- * @param {String} baseUrl
- * @param {Object} assets - as [ type : [urls] ]
+ * @param {object} item
+ * @param {string} item.itemIdentifier - the item identifier
+ * @param {string} item.baseUrl - item baseUrl
+ * @param {string} item.itemData.type - type of item
+ * @param {object} item.itemData.data - item data
+ * @param {object} item.itemData.assets - assets per types : img : ['url1', 'url2' ]
* @returns {Promise