UNPKG

@oat-sa/tao-test-runner-qti

Version:
168 lines (156 loc) 8.09 kB
define(['i18n', 'taoTests/runner/plugin', 'taoQtiTest/runner/helpers/map', 'taoQtiTest/runner/helpers/currentItem', 'taoQtiTest/runner/plugins/navigation/next/dialogConfirmNext', 'taoQtiTest/runner/helpers/navigation'], function (__, pluginFactory, mapHelper, currentItemHelper, dialogConfirmNext, navigationHelper) { 'use strict'; __ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __; pluginFactory = pluginFactory && Object.prototype.hasOwnProperty.call(pluginFactory, 'default') ? pluginFactory['default'] : pluginFactory; mapHelper = mapHelper && Object.prototype.hasOwnProperty.call(mapHelper, 'default') ? mapHelper['default'] : mapHelper; currentItemHelper = currentItemHelper && Object.prototype.hasOwnProperty.call(currentItemHelper, 'default') ? currentItemHelper['default'] : currentItemHelper; dialogConfirmNext = dialogConfirmNext && Object.prototype.hasOwnProperty.call(dialogConfirmNext, 'default') ? dialogConfirmNext['default'] : dialogConfirmNext; navigationHelper = navigationHelper && Object.prototype.hasOwnProperty.call(navigationHelper, 'default') ? navigationHelper['default'] : navigationHelper; /** * 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) 2019 (original work) Open Assessment Technologies SA ; */ /** * Returns the configured plugin */ var linearNextItemWarning = pluginFactory({ name: 'linearNextItemWarning', /** * Initialize the plugin (called during runner's init) */ init() { const self = this; const testRunner = this.getTestRunner(); const testRunnerOptions = testRunner.getOptions(); const testStore = testRunner.getTestStore(); // we'll store user's checkbox choice in here testStore.setVolatile(self.getName()); /** * Retrieve the required categories of the current item * @returns {Object} the calculator categories */ function getNextItemCategories() { const testContext = testRunner.getTestContext(); const testMap = testRunner.getTestMap(); return { nextPartWarning: mapHelper.hasItemCategory(testMap, testContext.itemIdentifier, 'nextPartWarning', true), nextSectionWarning: mapHelper.hasItemCategory(testMap, testContext.itemIdentifier, 'nextSectionWarning', true) }; } /** * Provides different variants of message text * @param {String} action - 'next' or 'skip' * @returns {String} */ function getCustomNextMessage(action) { var customNextMessage; var itemPartiallyAnswered = currentItemHelper.isAnswered(testRunner, true); if (!itemPartiallyAnswered) { customNextMessage = __('Are you sure you want to go to the next item? You will not be able to go back and provide an answer.'); } else if (action === 'next') { customNextMessage = __('Are you sure you want to go to the next item? You will not be able to go back and change your answer.'); } else if (action === 'skip') { customNextMessage = __('Are you sure you want to clear your answer and go to the next item? You will not be able to go back and provide an answer.'); } else { // more generic message for default case: customNextMessage = __('Are you sure you want to go to the next item? You will not be able to go back.'); } return customNextMessage; } //plugin behavior /** * Checks configuration, shows a dialog asking to confirm the nav action * * @param {String} action - 'next' or 'skip' * @returns {Promise} - resolves if dialog accepted or not shown, rejects if dialog cancelled */ function doNextWarning(action) { testRunner.trigger('disablenav'); // Load testStore checkbox value (async) return testStore.getStore(self.getName()).then(function (store) { return store.getItem('dontShowLinearNextItemWarning').then(function (checkboxValue) { var checkboxParams = null; // Show the warning unless user has turned it off: if (checkboxValue !== true) { // Define checkbox only if enabled by config: if (testRunnerOptions.enableLinearNextItemWarningCheckbox) { checkboxParams = { checked: checkboxValue, submitChecked: function () { store.setItem('dontShowLinearNextItemWarning', true); }, submitUnchecked: function () { store.setItem('dontShowLinearNextItemWarning', false); } }; } return new Promise(function (resolve, reject) { // show special dialog: dialogConfirmNext(__('Go to the next item?'), getCustomNextMessage(action), resolve, // if the test taker accepts function cancel() { // if he refuses reject({ cancel: true }); }, checkboxParams); }); } }); }).catch(function (err) { // if the rejection is due to an error, rethrow it if (err && err instanceof Error) { throw err; } if (err && err.cancel === true) { testRunner.trigger('enablenav'); return Promise.reject(); // to cancel the move } }); } // Attach this plugin to 'next' & 'skip' events testRunner.on('init', function () { // Clear the stored checkbox value before each test: testStore.getStore(self.getName()).then(function (store) { store.setItem('dontShowLinearNextItemWarning', null); }); }).before('move skip', function (e, type, scope) { const context = testRunner.getTestContext(); const map = testRunner.getTestMap(); const item = testRunner.getCurrentItem(); const currentPart = testRunner.getCurrentPart(); const categories = getNextItemCategories(); const isLast = navigationHelper.isLast(map, context.itemIdentifier); if (currentPart && currentPart.isLinear) { // Do nothing if nextSection warning imminent: if (scope === 'section' && categories.nextSectionWarning) { return; // Do nothing if endOfPart warning imminent: } else if (categories.nextPartWarning) { return; // Do nothing if 'informational item': } else if (item.informational) { return; // Show dialog if conditions met: } else if (type === 'next' && !isLast && testRunnerOptions.forceEnableLinearNextItemWarning) { return doNextWarning('next'); } else if (e.name === 'skip' && !isLast && testRunnerOptions.forceEnableLinearNextItemWarning) { return doNextWarning('skip'); } } }); } }); return linearNextItemWarning; });