@oat-sa/tao-test-runner-qti
Version:
TAO Test Runner QTI implementation
229 lines (209 loc) • 9.12 kB
JavaScript
define(['jquery', 'lodash', 'taoTests/runner/plugin', 'ui/dialog/alert', 'ui/dialog/confirm', 'util/shortcut/registry', 'util/shortcut', 'util/namespace'], function ($, _, pluginFactory, dialogAlert, dialogConfirm, shortcutRegistry, globalShortcut, namespaceHelper) { 'use strict';
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
pluginFactory = pluginFactory && Object.prototype.hasOwnProperty.call(pluginFactory, 'default') ? pluginFactory['default'] : pluginFactory;
dialogAlert = dialogAlert && Object.prototype.hasOwnProperty.call(dialogAlert, 'default') ? dialogAlert['default'] : dialogAlert;
dialogConfirm = dialogConfirm && Object.prototype.hasOwnProperty.call(dialogConfirm, 'default') ? dialogConfirm['default'] : dialogConfirm;
shortcutRegistry = shortcutRegistry && Object.prototype.hasOwnProperty.call(shortcutRegistry, 'default') ? shortcutRegistry['default'] : shortcutRegistry;
globalShortcut = globalShortcut && Object.prototype.hasOwnProperty.call(globalShortcut, 'default') ? globalShortcut['default'] : globalShortcut;
namespaceHelper = namespaceHelper && Object.prototype.hasOwnProperty.call(namespaceHelper, 'default') ? namespaceHelper['default'] : namespaceHelper;
/**
* 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 ;
*/
/**
* 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
*/
var dialog = 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;
});
}
});
return dialog;
});