@oat-sa/tao-test-runner-qti
Version:
TAO Test Runner QTI implementation
306 lines (289 loc) • 11.5 kB
JavaScript
define(['lodash', 'i18n', 'ui/hider', 'util/shortcut', 'util/namespace', 'taoTests/runner/plugin', 'taoQtiTest/runner/helpers/map', 'ui/keyNavigation/navigator', 'ui/keyNavigation/navigableDomElement', 'taoQtiTest/runner/plugins/tools/apipTextToSpeech/textToSpeech', 'taoQtiTest/runner/plugins/tools/apipTextToSpeech/ttsApipDataProvider'], function (_, __, hider, shortcut, namespaceHelper, pluginFactory, mapHelper, keyNavigator, navigableDomElement, ttsComponentFactory, ttsApipDataProvider) { 'use strict';
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
__ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
shortcut = shortcut && Object.prototype.hasOwnProperty.call(shortcut, 'default') ? shortcut['default'] : shortcut;
namespaceHelper = namespaceHelper && Object.prototype.hasOwnProperty.call(namespaceHelper, 'default') ? namespaceHelper['default'] : namespaceHelper;
pluginFactory = pluginFactory && Object.prototype.hasOwnProperty.call(pluginFactory, 'default') ? pluginFactory['default'] : pluginFactory;
mapHelper = mapHelper && Object.prototype.hasOwnProperty.call(mapHelper, 'default') ? mapHelper['default'] : mapHelper;
keyNavigator = keyNavigator && Object.prototype.hasOwnProperty.call(keyNavigator, 'default') ? keyNavigator['default'] : keyNavigator;
navigableDomElement = navigableDomElement && Object.prototype.hasOwnProperty.call(navigableDomElement, 'default') ? navigableDomElement['default'] : navigableDomElement;
ttsComponentFactory = ttsComponentFactory && Object.prototype.hasOwnProperty.call(ttsComponentFactory, 'default') ? ttsComponentFactory['default'] : ttsComponentFactory;
ttsApipDataProvider = ttsApipDataProvider && Object.prototype.hasOwnProperty.call(ttsApipDataProvider, 'default') ? ttsApipDataProvider['default'] : ttsApipDataProvider;
/**
* 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-2019 (original work) Open Assessment Technologies SA;
*
* @author Anton Tsymuk <anton@taotesting.com>
*/
const pluginName = 'apiptts';
const actionPrefix = `tool-${pluginName}-`;
/**
* Returns the configured plugin
*/
var plugin = pluginFactory({
name: pluginName,
/**
* Initialize the plugin (called during runner's init)
*/
init() {
const testRunner = this.getTestRunner();
const testRunnerOptions = testRunner.getOptions();
const pluginShortcuts = (testRunnerOptions.shortcuts || {})[this.getName()] || {};
let ttsComponent;
let ttsApipData;
const createNavigationGroup = () => {
const $container = testRunner.getAreaBroker().getContainer();
const $navigationGroupElement = this.button.getElement();
const groupNavigationId = `${pluginName}_navigation_group`;
const $navigationElements = $container.find(ttsApipData.map(_ref => {
let {
selector
} = _ref;
return selector;
}).join(', '));
this.navigationGroup = keyNavigator({
id: groupNavigationId,
group: $navigationGroupElement,
elements: navigableDomElement.createFromDoms($navigationElements.add($navigationGroupElement)),
propagateTab: false,
loop: true,
keepState: true
}).on('tab', () => {
if (ttsComponent.is('sfhMode')) {
this.navigationGroup.next();
testRunner.trigger(`${actionPrefix}next`);
}
}).on('shift+tab', () => {
if (ttsComponent.is('sfhMode')) {
this.navigationGroup.previous();
testRunner.trigger(`${actionPrefix}previous`);
}
}).on('activate', () => {
if (ttsComponent.is('sfhMode')) {
testRunner.trigger(`${actionPrefix}togglePlayback`);
}
}).on('blur', () => {
setTimeout(() => {
if (!this.navigationGroup.isFocused()) {
this.navigationGroup.focus();
}
}, 0);
}).setCursorAt($navigationElements.length);
ttsComponent.on('next finish', () => {
if (ttsComponent.is('sfhMode')) {
const $currentElement = this.navigationGroup.getCursor().navigable.getElement();
const {
selector
} = ttsComponent.getCurrentItem() || {};
if (!selector || !$currentElement.is(selector)) {
this.navigationGroup.next();
}
}
});
};
/**
* Creates the tts component on demand
* @returns {textToSpeech}
*/
const getTTSComponent = () => {
if (!ttsComponent) {
const $container = testRunner.getAreaBroker().getContainer();
ttsComponent = ttsComponentFactory($container, {}).on('close', () => {
if (this.getState('active')) {
testRunner.trigger(`${actionPrefix}toggle`);
}
}).hide();
}
return ttsComponent;
};
/**
* Checks if the plugin is currently available.
* To be activated with the special category x-tao-option-apiptts
*
* @returns {Boolean}
*/
const isConfigured = () => mapHelper.hasItemCategory(testRunner.getTestMap(), testRunner.getTestContext().itemIdentifier, 'apiptts', true);
/**
* Is plugin activated ? if not, then we hide the plugin
*/
const togglePlugin = () => {
if (isConfigured()) {
this.show();
} else {
this.hide();
}
};
/**
* Show the plugin panel
*
* @fires plugin-open.apiptts
*/
const enablePlugin = () => {
createNavigationGroup();
this.button.turnOn();
this.setState('active', true);
this.trigger('open');
if (ttsComponent.is('hidden')) {
ttsComponent.show();
}
};
/**
* Hide the plugin panel
*
* @fires plugin-close.apiptts
*/
const disablePlugin = () => {
if (this.getState('active')) {
this.navigationGroup.blur();
this.navigationGroup.destroy();
this.setState('active', false);
this.button.turnOff();
this.trigger('close');
if (ttsComponent && !ttsComponent.is('hidden')) {
ttsComponent.close();
ttsComponent.hide();
}
}
};
/**
* Shows/hides the plugin
*/
const toggleTool = () => {
if (this.getState('enabled')) {
if (this.getState('active')) {
disablePlugin();
this.setState('sleep', true);
} else {
enablePlugin();
this.setState('sleep', false);
}
}
};
// Add plugin button to toolbox
this.button = this.getAreaBroker().getToolbox().createEntry({
className: `${this.getName()}-plugin`,
control: this.getName(),
icon: 'headphones',
text: __('Text To Speech'),
title: __('Enable text to speech')
});
// Handle plugin button click
this.button.on('click', e => {
e.preventDefault();
testRunner.trigger(`${actionPrefix}toggle`);
});
// Register plugin shortcuts
if (testRunnerOptions.allowShortcuts) {
_.forEach(pluginShortcuts, (command, key) => {
shortcut.add(namespaceHelper.namespaceAll(command, pluginName, true), () => {
if (key === 'spaceTogglePlayback' && ttsComponent && ttsComponent.is('sfhMode')) {
return;
}
const eventKey = key.endsWith('TogglePlayback') ? 'togglePlayback' : key;
testRunner.trigger(actionPrefix + eventKey);
}, {
avoidInput: true
});
});
}
// Hide plugin by default
togglePlugin();
this.disable();
this.hide();
//update plugin state based on changes
testRunner.on('loaditem', () => {
togglePlugin();
this.disable();
}).on('enabletools renderitem', () => {
this.enable();
}).on('disabletools unloaditem', () => {
disablePlugin();
this.disable();
}).on(`${actionPrefix}toggle`, () => {
if (isConfigured()) {
toggleTool();
}
}).on(`${actionPrefix}togglePlayback`, () => {
if (this.getState('enabled')) {
if (this.getState('active')) {
if (ttsComponent.is('sfhMode')) {
const $currentElement = this.navigationGroup.getCursor().navigable.getElement();
const {
selector
} = ttsComponent.getCurrentItem() || {};
if (!$currentElement.is(selector)) {
if (this.button.getElement()[0] !== $currentElement[0]) {
$currentElement.trigger('click');
}
return;
}
}
ttsComponent.togglePlayback();
}
}
}).on('renderitem', () => {
if (!isConfigured()) {
return;
}
ttsApipData = ttsApipDataProvider(testRunner.itemRunner.getData().apipAccessibility || {}).map(apipItemData => Object.assign({}, apipItemData, {
url: testRunner.itemRunner.assetManager.resolve(apipItemData.url)
}));
if (!ttsApipData.length) {
disablePlugin();
this.hide();
return;
}
getTTSComponent().setMediaContentData(ttsApipData);
this.show();
if (!this.getState('sleep')) {
this.setState('enabled', true);
toggleTool();
}
});
},
/**
* Called during the runner's destroy phase
*/
destroy() {
shortcut.remove(`.${this.getName()}`);
},
/**
* Enable the button
*/
enable() {
this.button.enable();
},
/**
* Disable the button
*/
disable() {
this.button.disable();
},
/**
* Show the button
*/
show() {
this.button.show();
},
/**
* Hide the button
*/
hide() {
this.button.hide();
}
});
return plugin;
});