UNPKG

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

Version:
255 lines (233 loc) 8.77 kB
/** * 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) 2016 (original work) Open Assessment Technologies SA ; */ /** * Test Runner Content Plugin : Overlay * * @author Bertrand Chevrier <bertrand@taotesting.com> */ import $ from 'jquery'; import _ from 'lodash'; import pluginFactory from 'taoTests/runner/plugin'; import dialogAlert from 'ui/dialog/alert'; import dialogConfirm from 'ui/dialog/confirm'; import shortcutRegistry from 'util/shortcut/registry'; import globalShortcut from 'util/shortcut'; import namespaceHelper from 'util/namespace'; /** * The public name of the plugin * @type {String} */ var pluginName = 'dialog'; /** * The prefix of actions triggered through the event loop * @type {String} */ var actionPrefix = `tool-${pluginName}-`; /** * Some default options for the plugin * @type {Object} */ var defaultOptions = { alert: { focus: 'ok' }, confirm: { focus: 'ok' } }; /** * Returns the configured plugin */ export default pluginFactory({ name: pluginName, /** * Initialize the plugin (called during runner's init) */ init: function init() { var testRunner = this.getTestRunner(); var testOptions = testRunner.getOptions(); var pluginConfig = Object.assign({}, defaultOptions, this.getConfig()); var pluginShortcuts = (testOptions.shortcuts || {})[pluginName] || {}; var alerts = []; var confirms = []; var opened = []; var dialogShortcut = shortcutRegistry($('body'), { propagate: false, prevent: true }); /** * Closes a dialog with accept * @param {dialog} dialog - The instance of the dialog */ function closeAccept(dialog) { // TODO: improve the dialog implementation in order to provide a better API dialog.trigger('okbtn.modal').hide(); } /** * Closes a dialog with rejection * @param {dialog} dialog - The instance of the dialog */ function closeReject(dialog) { dialog.hide(); } /** * Closes the last opened dialog * @param {Boolean} accept Whether the dialog should be accepted or not * @param {String} [shortcut] The shortcut that caused the action */ function closeLast(accept, shortcut) { var handle = opened.length && opened[opened.length - 1]; if (handle) { handle.shortcut = shortcut; if (accept) { closeAccept(handle.dialog); } else { closeReject(handle.dialog); } } } /** * Add dialog on top of the provided stack * @param {String} namespace - The event namespace that scope the dialog * @param {Array} stack - The dialogs stack on which push the new instance * @param {Function} dialog - The constructor of the dialog * @param {String} message - The message to display * @param {Function} accept - The callback for accept * @param {Function} reject - The callback for reject * @param {Object} options - Dialog options */ function addHandle(namespace, stack, dialog, message, accept, reject, options) { var handle = { context: namespace, dialog: dialog(message, doAccept, doReject, options) }; function doAccept(e, reason) { if (_.isFunction(accept)) { accept(handle.shortcut || reason); } } function doReject(e, reason) { if (_.isFunction(reject)) { reject(handle.shortcut || reason); } } // prevents all registered shortcuts to be triggered // and brings back the dialog shortcuts globalShortcut.disable(); dialogShortcut.enable(); stack.push(handle); opened.push(handle); handle.dialog.focus(options.focus); handle.dialog.on('closed.modal', function() { removeHandle(stack, handle.dialog); removeHandle(opened, handle.dialog); // if all dialogs have been closed allows all registered shortcuts to be triggered // also disables the dialog shortcuts if (!opened.length) { globalShortcut.enable(); dialogShortcut.disable(); } }); } /** * Remove a dialog from the provided stack * @param {Array} stack - The dialogs stack from which remove the dialog instance * @param {dialog} dialog - The instance of the dialog */ function removeHandle(stack, dialog) { if (dialog) { _.remove(stack, function(handle) { if (handle && dialog === handle.dialog) { return true; } }); } } /** * Closes all dialogs within the provided stack * @param {String} namespace - The event namespace that scope the dialogs to close * @param {Boolean} accept - Whether (`true`) or not (`false`) to close the dialogs with accept * @param {Array} stack - The dialogs stack in which close the dialogs */ function closeDialogs(namespace, accept, stack) { if (stack) { _.forEach(stack, function(handle) { if (handle && (namespace === '@' || namespace === handle.context)) { if (accept) { closeAccept(handle.dialog); } else { closeReject(handle.dialog); } } }); } else { closeDialogs(namespace, accept, alerts); closeDialogs(namespace, accept, confirms); } } // starts with shortcuts disabled, prevents the TAB key to be used to move outside the dialog box dialogShortcut.disable().set('Tab Shift+Tab'); // handle the plugin's shortcuts if (testOptions.allowShortcuts) { _.forEach(pluginShortcuts, function(command, key) { dialogShortcut.add(namespaceHelper.namespaceAll(command, pluginName, true), function(e, shortcut) { // just fire the action using the event loop testRunner.trigger(actionPrefix + key, shortcut); }); }); } //change plugin state testRunner .before('alert.*', function(e, msg, accept, options) { addHandle( e.namespace, alerts, dialogAlert, msg, accept, accept, _.merge({}, pluginConfig.alert, options) ); }) .before('confirm.*', function(e, msg, accept, reject, options) { addHandle( e.namespace, confirms, dialogConfirm, msg, accept, reject, _.merge({}, pluginConfig.confirm, options) ); }) .before('closedialog.*', function(e, accept) { closeDialogs(e.namespace, accept); }) .on(`${actionPrefix}accept`, function(shortcut) { closeLast(true, shortcut); }) .on(`${actionPrefix}reject`, function(shortcut) { closeLast(false, shortcut); }) .on('destroy', function() { closeDialogs('.@'); dialogShortcut.clear(); dialogShortcut = null; }); } });