');
+ const $container = $('.main-container', section.selected.panel);
+ const { rootClassUri, id: resourceUri } = actionContext;
+ translationFormFactory($container, { rootClassUri, resourceUri, allowDeletion: true })
+ .on('edit', (id, language) => {
+ return actionManager.exec('test-authoring', {
+ id,
+ language,
+ rootClassUri,
+ originResourceUri: resourceUri,
+ translation: true,
+ actionParams: ['originResourceUri', 'language', 'translation']
+ });
+ })
+ .on('delete', function onDelete(id, language) {
+ return translationService.deleteTranslation(resourceUri, language).then(() => {
+ feedback().success(__('Translation deleted'));
+ return this.refresh();
+ });
+ })
+ .on('error', error => {
+ logger.error(error);
+ feedback().error(__('An error occurred while processing your request.'));
+ });
+ });
+
binder.register('testPreview', function testPreview(actionContext) {
const config = module.config();
const previewerConfig = Object.fromEntries(
diff --git a/views/js/loader/taoTests.min.js b/views/js/loader/taoTests.min.js
index 4183c9d1..2c7c0dd2 100644
--- a/views/js/loader/taoTests.min.js
+++ b/views/js/loader/taoTests.min.js
@@ -1,2 +1,2 @@
-define("taoTests/controller/routes",[],function(){"use strict";return{Tests:{deps:"controller/tests/action",actions:{editTest:"controller/tests/editTest"}},TestImport:{actions:{index:"controller/testImport/index"}}}}),define("taoTests/util/provider/itemClassProvider",["jquery","i18n","util/url"],function($,__,urlUtils){"use strict";var providers={listTests(data){return new Promise(function(resolve,reject){$.ajax({url:urlUtils.route("getItemClasses","SaSItems","taoItems"),data:{q:data.q,page:data.page},type:"GET",dataType:"JSON"}).done(function(tests){tests?resolve(tests):reject(new Error(__("Unable to load tests")))}).fail(function(){reject(new Error(__("Unable to load tests")))})})}};return providers}),define("taoTests/util/form/itemClassSelectorForm",["jquery","i18n","ui/filter","ui/feedback"],function($,__,filterFactory,feedback){"use strict";return{createSelectorInput(_ref){let{$filterContainer,$inputElement,dataProvider,inputPlaceholder=__("Select the item destination class"),inputLabel=__("Select Item Destination")}=_ref;return filterFactory($filterContainer,{placeholder:inputPlaceholder,label:inputLabel,width:"64%",quietMillis:1e3}).on("change",function(selection){$inputElement.val(selection)}).on("request",function(params){dataProvider.list(params.data).then(function(data){params.success(data)}).catch(function(err){params.error(err),feedback().error(err)})}).render("<%- text %>")},setupTaoLocalForm($form,providers){const $filterContainer=$(".item-select-container",$form),$inputElement=$("#itemClassDestination",$form);this.createSelectorInput({$filterContainer,$inputElement,dataProvider:{list:providers.listTests}})}}}),define("taoTests/controller/testImport/index",["jquery","taoTests/util/provider/itemClassProvider","taoTests/util/form/itemClassSelectorForm"],function($,itemClassProvider,itemClassSelectorForm){"use strict";return{start(){const $form=$("#import");itemClassSelectorForm.setupTaoLocalForm($form,itemClassProvider)}}}),define("taoTests/previewer/factory",["lodash","context","module","core/providerLoader","core/providerRegistry","core/logger"],function(_,context,module,providerLoaderFactory,providerRegistry){"use strict";function previewerFactory(type,uri,config){return config=Object.assign({},module.config(),config),providerLoaderFactory().addList(config.previewers).load(context.bundle).then(function(providers){providers.forEach(function(provider){previewerFactory.registerProvider(provider.name,provider)})}).then(function(){return previewerFactory.getProvider(type)}).then(function(provider){return provider.init(uri,config)})}return providerRegistry(previewerFactory,function validateProvider(provider){if("function"!=typeof provider.init)throw new TypeError("The previewer provider MUST have a init() method");return!0})}),define("taoTests/controller/tests/action",["i18n","layout/actions/binder","uri","ui/feedback","core/logger","taoTests/previewer/factory","module"],function(__,binder,uri,feedback,loggerFactory,previewerFactory,module){"use strict";const logger=loggerFactory("taoTests/controller/action");binder.register("testPreview",function testPreview(actionContext){const config=module.config(),previewerConfig=Object.fromEntries(Object.entries({readOnly:!1,fullPage:!0,pluginsOptions:config.pluginsOptions}).filter(_ref2=>{let[key,value]=_ref2;return value!==void 0})),getProvider=id=>{if(!id||!config.providers)return config.provider;const previewerId=parseInt(`${id}`.split("-").pop(),10)||0;return config.providers[previewerId]?config.providers[previewerId].id:config.provider};previewerFactory(getProvider(this.id)||"qtiTest",uri.decode(actionContext.uri),previewerConfig).catch(err=>{logger.error(err),feedback().error(__("Test Preview is not installed, please contact to your administrator."))})})}),define("taoTests/controller/tests/editTest",["jquery","ui/lock","module","layout/actions"],function($,lock,module,actions){"use strict";return{start(){const config=module.config(),maxButtons=10,getPreviewId=idx=>`test-preview${idx?`-${idx}`:""}`,previewActions=[];for(let i=0;i{previewAction.state.disabled=!config.isPreviewEnabled}),actions.updateState(),$("#lock-box").each(function(){lock($(this)).register()})}}}),define("taoTests/loader/taoTests.bundle",function(){}),define("taoTests/loader/taoTests.min",["taoItems/loader/taoItems.min"],function(){});
+define("taoTests/controller/routes",[],function(){"use strict";return{Tests:{deps:"controller/tests/action",actions:{editTest:"controller/tests/editTest"}},TestImport:{actions:{index:"controller/testImport/index"}}}}),define("taoTests/util/provider/itemClassProvider",["jquery","i18n","util/url"],function($,__,urlUtils){"use strict";var providers={listTests(data){return new Promise(function(resolve,reject){$.ajax({url:urlUtils.route("getItemClasses","SaSItems","taoItems"),data:{q:data.q,page:data.page},type:"GET",dataType:"JSON"}).done(function(tests){tests?resolve(tests):reject(new Error(__("Unable to load tests")))}).fail(function(){reject(new Error(__("Unable to load tests")))})})}};return providers}),define("taoTests/util/form/itemClassSelectorForm",["jquery","i18n","ui/filter","ui/feedback"],function($,__,filterFactory,feedback){"use strict";return{createSelectorInput(_ref){let{$filterContainer,$inputElement,dataProvider,inputPlaceholder=__("Select the item destination class"),inputLabel=__("Select Item Destination")}=_ref;return filterFactory($filterContainer,{placeholder:inputPlaceholder,label:inputLabel,width:"64%",quietMillis:1e3}).on("change",function(selection){$inputElement.val(selection)}).on("request",function(params){dataProvider.list(params.data).then(function(data){params.success(data)}).catch(function(err){params.error(err),feedback().error(err)})}).render("<%- text %>")},setupTaoLocalForm($form,providers){const $filterContainer=$(".item-select-container",$form),$inputElement=$("#itemClassDestination",$form);this.createSelectorInput({$filterContainer,$inputElement,dataProvider:{list:providers.listTests}})}}}),define("taoTests/controller/testImport/index",["jquery","taoTests/util/provider/itemClassProvider","taoTests/util/form/itemClassSelectorForm"],function($,itemClassProvider,itemClassSelectorForm){"use strict";return{start(){const $form=$("#import");itemClassSelectorForm.setupTaoLocalForm($form,itemClassProvider)}}}),define("taoTests/previewer/factory",["lodash","context","module","core/providerLoader","core/providerRegistry","core/logger"],function(_,context,module,providerLoaderFactory,providerRegistry){"use strict";function previewerFactory(type,uri,config){return config=Object.assign({},module.config(),config),providerLoaderFactory().addList(config.previewers).load(context.bundle).then(function(providers){providers.forEach(function(provider){previewerFactory.registerProvider(provider.name,provider)})}).then(function(){return previewerFactory.getProvider(type)}).then(function(provider){return provider.init(uri,config)})}return providerRegistry(previewerFactory,function validateProvider(provider){if("function"!=typeof provider.init)throw new TypeError("The previewer provider MUST have a init() method");return!0})}),define("taoTests/controller/tests/action",["i18n","module","uri","layout/actions","layout/actions/binder","layout/section","form/translation","services/translation","ui/feedback","core/logger","taoTests/previewer/factory"],function(__,module,uri,actionManager,binder,section,translationFormFactory,translationService,feedback,loggerFactory,previewerFactory){"use strict";const logger=loggerFactory("taoTests/controller/action");binder.register("translateTest",function(actionContext){section.current().updateContentBlock("");const $container=$(".main-container",section.selected.panel),{rootClassUri,id:resourceUri}=actionContext;translationFormFactory($container,{rootClassUri,resourceUri,allowDeletion:!0}).on("edit",(id,language)=>actionManager.exec("test-authoring",{id,language,rootClassUri,originResourceUri:resourceUri,translation:!0,actionParams:["originResourceUri","language","translation"]})).on("delete",function onDelete(id,language){return translationService.deleteTranslation(resourceUri,language).then(()=>(feedback().success(__("Translation deleted")),this.refresh()))}).on("error",error=>{logger.error(error),feedback().error(__("An error occurred while processing your request."))})}),binder.register("testPreview",function testPreview(actionContext){const config=module.config(),previewerConfig=Object.fromEntries(Object.entries({readOnly:!1,fullPage:!0,pluginsOptions:config.pluginsOptions}).filter(_ref2=>{let[key,value]=_ref2;return value!==void 0})),getProvider=id=>{if(!id||!config.providers)return config.provider;const previewerId=parseInt(`${id}`.split("-").pop(),10)||0;return config.providers[previewerId]?config.providers[previewerId].id:config.provider};previewerFactory(getProvider(this.id)||"qtiTest",uri.decode(actionContext.uri),previewerConfig).catch(err=>{logger.error(err),feedback().error(__("Test Preview is not installed, please contact to your administrator."))})})}),define("taoTests/controller/tests/editTest",["jquery","ui/lock","module","layout/actions"],function($,lock,module,actions){"use strict";return{start(){const config=module.config(),maxButtons=10,getPreviewId=idx=>`test-preview${idx?`-${idx}`:""}`,previewActions=[];for(let i=0;i{previewAction.state.disabled=!config.isPreviewEnabled}),actions.updateState(),$("#lock-box").each(function(){lock($(this)).register()})}}}),define("taoTests/loader/taoTests.bundle",function(){}),define("taoTests/loader/taoTests.min",["taoItems/loader/taoItems.min"],function(){});
//# sourceMappingURL=taoTests.min.js.map
\ No newline at end of file
diff --git a/views/js/loader/taoTests.min.js.map b/views/js/loader/taoTests.min.js.map
index 88d8a924..02e392ee 100644
--- a/views/js/loader/taoTests.min.js.map
+++ b/views/js/loader/taoTests.min.js.map
@@ -1 +1 @@
-{"version":3,"names":["define","Tests","deps","actions","editTest","TestImport","index","$","__","urlUtils","providers","listTests","data","Promise","resolve","reject","ajax","url","route","q","page","type","dataType","done","tests","Error","fail","filterFactory","feedback","createSelectorInput","_ref","$filterContainer","$inputElement","dataProvider","inputPlaceholder","inputLabel","placeholder","label","width","quietMillis","on","selection","val","params","list","then","success","catch","err","error","render","setupTaoLocalForm","$form","itemClassProvider","itemClassSelectorForm","start","_","context","module","providerLoaderFactory","providerRegistry","previewerFactory","uri","config","Object","assign","addList","previewers","load","bundle","forEach","provider","registerProvider","name","getProvider","init","validateProvider","TypeError","binder","loggerFactory","logger","register","testPreview","actionContext","previewerConfig","fromEntries","entries","readOnly","fullPage","pluginsOptions","filter","_ref2","key","value","id","previewerId","parseInt","split","pop","decode","lock","maxButtons","getPreviewId","idx","previewActions","i","action","getBy","push","previewAction","state","disabled","isPreviewEnabled","updateState","each"],"sources":["/github/workspace/tao/views/build/config-wrap-start-default.js","../controller/routes.js","../util/provider/itemClassProvider.js","../util/form/itemClassSelectorForm.js","../controller/testImport/index.js","../previewer/factory.js","../controller/tests/action.js","../controller/tests/editTest.js","module-create.js","/github/workspace/tao/views/build/config-wrap-end-default.js"],"sourcesContent":["\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2014-2020 (original work) Open Assessment Technologies SA\n */\n\n//@see http://forge.taotesting.com/projects/tao/wiki/Front_js\ndefine('taoTests/controller/routes',[],function(){\n 'use strict';\n return {\n 'Tests' : {\n 'deps' : 'controller/tests/action',\n 'actions' : {\n 'editTest' : 'controller/tests/editTest'\n }\n },\n 'TestImport' : {\n 'actions' : {\n 'index' : 'controller/testImport/index'\n }\n },\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA;\n */\ndefine('taoTests/util/provider/itemClassProvider',['jquery', 'i18n', 'util/url'], function ($, __, urlUtils) {\n 'use strict';\n\n var providers = {\n /**\n * List available tests\n * @param {Object} data\n * @returns {Promise}\n */\n listTests(data) {\n return new Promise(function (resolve, reject) {\n $.ajax({\n //Find and setup class list for items\n url: urlUtils.route('getItemClasses', 'SaSItems', 'taoItems'), data: {\n q: data.q, page: data.page\n }, type: 'GET', dataType: 'JSON'\n }).done(function (tests) {\n if (tests) {\n resolve(tests);\n } else {\n reject(new Error(__('Unable to load tests')));\n }\n }).fail(function () {\n reject(new Error(__('Unable to load tests')));\n });\n });\n }\n };\n\n return providers;\n\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);\n *\n */\ndefine('taoTests/util/form/itemClassSelectorForm',[\n 'jquery',\n 'i18n',\n 'ui/filter',\n 'ui/feedback'\n], function ($, __, filterFactory, feedback) {\n 'use strict';\n\n return {\n /**\n * Enhances a hidden form field, rendering a text input with filter, autocomplete and dropdown\n * @param {Object} options\n * @param {jQuery} options.$filterContainer\n * @param {jQuery} options.$inputElement\n * @param {taskQueueButton} options.taskButton - button which submits the form\n * @param {Function} options.dataProvider - provider function which returns a Promise\n * @param {String} options.inputPlaceholder\n * @param {String} options.inputLabel\n * @returns {filter} component which manages the form input\n */\n createSelectorInput({\n $filterContainer,\n $inputElement,\n dataProvider,\n inputPlaceholder = __('Select the item destination class'),\n inputLabel = __('Select Item Destination')\n }) {\n return filterFactory($filterContainer, {\n placeholder: inputPlaceholder,\n label: inputLabel,\n width: '64%',\n quietMillis: 1000\n })\n .on('change', function(selection) {\n $inputElement.val(selection);\n })\n .on('request', function(params) {\n dataProvider\n .list(params.data)\n .then(function(data) {\n params.success(data);\n })\n .catch(function(err) {\n params.error(err);\n feedback().error(err);\n });\n })\n .render('<%- text %>');\n },\n\n /**\n * Set up the wizard form for publishing a TAO Local delivery\n * @param {jQuery} $form\n * @param {Object} providers - contains function(s) for fetching data\n */\n setupTaoLocalForm($form, providers) {\n const $filterContainer = $('.item-select-container', $form);\n const $inputElement = $('#itemClassDestination', $form);\n\n // Enhanced selector input for tests:\n this.createSelectorInput({\n $filterContainer,\n $inputElement,\n dataProvider: {\n list: providers.listTests\n }\n });\n }\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA;\n *\n */\ndefine('taoTests/controller/testImport/index',[\n 'jquery',\n 'taoTests/util/provider/itemClassProvider',\n 'taoTests/util/form/itemClassSelectorForm',\n], function ($, itemClassProvider, itemClassSelectorForm) {\n 'use strict';\n\n return {\n start() {\n const $form = $('#import');\n itemClassSelectorForm.setupTaoLocalForm($form, itemClassProvider);\n }\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2020 (original work) Open Assessment Technologies SA ;\n */\n/**\n * @author Hanna Dzmitryieva \n */\ndefine('taoTests/previewer/factory',[\n 'lodash',\n 'context',\n 'module',\n 'core/providerLoader',\n 'core/providerRegistry',\n 'core/logger'\n], function (\n _,\n context,\n module,\n providerLoaderFactory,\n providerRegistry\n) {\n 'use strict';\n\n /**\n * Loads and display the test previewer\n * @param {String} type - The type of previewer\n * @param {String} uri - The URI of the test to load\n * @param {Object} [config] - Some config entries\n * @param {String} [config.fullPage] - Force the previewer to occupy the full window.\n * @param {String} [config.readOnly] - Do not allow to modify the previewed test.\n * @param {Object} [config.previewers] - Optionally load static adapters. By default take them from the module's config.\n * @returns {Promise}\n */\n function previewerFactory(type, uri, config) {\n config = Object.assign({}, module.config(), config);\n return providerLoaderFactory()\n .addList(config.previewers)\n .load(context.bundle)\n .then(function (providers) {\n providers.forEach(function (provider) {\n previewerFactory.registerProvider(provider.name, provider);\n });\n })\n .then(function () {\n return previewerFactory.getProvider(type);\n })\n .then(function (provider) {\n return provider.init(uri, config);\n });\n }\n\n return providerRegistry(previewerFactory, function validateProvider(provider) {\n if (typeof provider.init !== 'function') {\n throw new TypeError('The previewer provider MUST have a init() method');\n }\n return true;\n });\n});\n\n","/*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2014 - 2020 (original work) Open Assessment Technologies SA\n *\n */\ndefine('taoTests/controller/tests/action',[\n 'i18n',\n 'layout/actions/binder',\n 'uri',\n 'ui/feedback',\n 'core/logger',\n 'taoTests/previewer/factory',\n 'module'\n], function (__, binder, uri, feedback, loggerFactory, previewerFactory, module) {\n 'use strict';\n\n const logger = loggerFactory('taoTests/controller/action');\n\n binder.register('testPreview', function testPreview(actionContext) {\n const config = module.config();\n const previewerConfig = Object.fromEntries(\n Object.entries({\n readOnly: false,\n fullPage: true,\n pluginsOptions: config.pluginsOptions\n }).filter(([key, value]) => value !== undefined)\n );\n\n const getProvider = id => {\n if (!id || !config.providers) {\n return config.provider;\n }\n const previewerId = parseInt(`${id}`.split('-').pop(), 10) || 0;\n if (!config.providers[previewerId]) {\n return config.provider;\n }\n return config.providers[previewerId].id;\n };\n\n previewerFactory(getProvider(this.id) || 'qtiTest', uri.decode(actionContext.uri), previewerConfig).catch(\n err => {\n logger.error(err);\n feedback().error(__('Test Preview is not installed, please contact to your administrator.'));\n }\n );\n });\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2015-2024 (original work) Open Assessment Technologies SA;\n */\n\n/**\n * Test edition controller\n *\n */\ndefine('taoTests/controller/tests/editTest',['jquery', 'ui/lock', 'module', 'layout/actions'], function ($, lock, module, actions) {\n 'use strict';\n\n return {\n /**\n * Controller's entrypoint\n */\n start() {\n const config = module.config();\n const maxButtons = 10; // arbitrary value for the max number of buttons\n\n const getPreviewId = idx => `test-preview${idx ? `-${idx}` : ''}`;\n const previewActions = [];\n for (let i = 0; i < maxButtons; i++) {\n const action = actions.getBy(getPreviewId(i));\n if (!action) {\n break;\n }\n previewActions.push(action);\n }\n previewActions.forEach(previewAction => {\n previewAction.state.disabled = !config.isPreviewEnabled;\n });\n actions.updateState();\n\n $('#lock-box').each(function () {\n lock($(this)).register();\n });\n }\n };\n});\n\n","\ndefine(\"taoTests/loader/taoTests.bundle\", function(){});\n","define(\"taoTests/loader/taoTests.min\", [\"taoItems/loader/taoItems.min\"], function(){});\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"names":["define","Tests","deps","actions","editTest","TestImport","index","$","__","urlUtils","providers","listTests","data","Promise","resolve","reject","ajax","url","route","q","page","type","dataType","done","tests","Error","fail","filterFactory","feedback","createSelectorInput","_ref","$filterContainer","$inputElement","dataProvider","inputPlaceholder","inputLabel","placeholder","label","width","quietMillis","on","selection","val","params","list","then","success","catch","err","error","render","setupTaoLocalForm","$form","itemClassProvider","itemClassSelectorForm","start","_","context","module","providerLoaderFactory","providerRegistry","previewerFactory","uri","config","Object","assign","addList","previewers","load","bundle","forEach","provider","registerProvider","name","getProvider","init","validateProvider","TypeError","actionManager","binder","section","translationFormFactory","translationService","loggerFactory","logger","register","actionContext","current","updateContentBlock","$container","selected","panel","rootClassUri","id","resourceUri","allowDeletion","language","exec","originResourceUri","translation","actionParams","onDelete","deleteTranslation","refresh","testPreview","previewerConfig","fromEntries","entries","readOnly","fullPage","pluginsOptions","filter","_ref2","key","value","previewerId","parseInt","split","pop","decode","lock","maxButtons","getPreviewId","idx","previewActions","i","action","getBy","push","previewAction","state","disabled","isPreviewEnabled","updateState","each"],"sources":["/Users/oat/Projects/terre/tr-enterprise/files/delivery/tao/views/build/config-wrap-start-default.js","../controller/routes.js","../util/provider/itemClassProvider.js","../util/form/itemClassSelectorForm.js","../controller/testImport/index.js","../previewer/factory.js","../controller/tests/action.js","../controller/tests/editTest.js","module-create.js","/Users/oat/Projects/terre/tr-enterprise/files/delivery/tao/views/build/config-wrap-end-default.js"],"sourcesContent":["\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2014-2020 (original work) Open Assessment Technologies SA\n */\n\n//@see http://forge.taotesting.com/projects/tao/wiki/Front_js\ndefine('taoTests/controller/routes',[],function(){\n 'use strict';\n return {\n 'Tests' : {\n 'deps' : 'controller/tests/action',\n 'actions' : {\n 'editTest' : 'controller/tests/editTest'\n }\n },\n 'TestImport' : {\n 'actions' : {\n 'index' : 'controller/testImport/index'\n }\n },\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA;\n */\ndefine('taoTests/util/provider/itemClassProvider',['jquery', 'i18n', 'util/url'], function ($, __, urlUtils) {\n 'use strict';\n\n var providers = {\n /**\n * List available tests\n * @param {Object} data\n * @returns {Promise}\n */\n listTests(data) {\n return new Promise(function (resolve, reject) {\n $.ajax({\n //Find and setup class list for items\n url: urlUtils.route('getItemClasses', 'SaSItems', 'taoItems'), data: {\n q: data.q, page: data.page\n }, type: 'GET', dataType: 'JSON'\n }).done(function (tests) {\n if (tests) {\n resolve(tests);\n } else {\n reject(new Error(__('Unable to load tests')));\n }\n }).fail(function () {\n reject(new Error(__('Unable to load tests')));\n });\n });\n }\n };\n\n return providers;\n\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);\n *\n */\ndefine('taoTests/util/form/itemClassSelectorForm',[\n 'jquery',\n 'i18n',\n 'ui/filter',\n 'ui/feedback'\n], function ($, __, filterFactory, feedback) {\n 'use strict';\n\n return {\n /**\n * Enhances a hidden form field, rendering a text input with filter, autocomplete and dropdown\n * @param {Object} options\n * @param {jQuery} options.$filterContainer\n * @param {jQuery} options.$inputElement\n * @param {taskQueueButton} options.taskButton - button which submits the form\n * @param {Function} options.dataProvider - provider function which returns a Promise\n * @param {String} options.inputPlaceholder\n * @param {String} options.inputLabel\n * @returns {filter} component which manages the form input\n */\n createSelectorInput({\n $filterContainer,\n $inputElement,\n dataProvider,\n inputPlaceholder = __('Select the item destination class'),\n inputLabel = __('Select Item Destination')\n }) {\n return filterFactory($filterContainer, {\n placeholder: inputPlaceholder,\n label: inputLabel,\n width: '64%',\n quietMillis: 1000\n })\n .on('change', function(selection) {\n $inputElement.val(selection);\n })\n .on('request', function(params) {\n dataProvider\n .list(params.data)\n .then(function(data) {\n params.success(data);\n })\n .catch(function(err) {\n params.error(err);\n feedback().error(err);\n });\n })\n .render('<%- text %>');\n },\n\n /**\n * Set up the wizard form for publishing a TAO Local delivery\n * @param {jQuery} $form\n * @param {Object} providers - contains function(s) for fetching data\n */\n setupTaoLocalForm($form, providers) {\n const $filterContainer = $('.item-select-container', $form);\n const $inputElement = $('#itemClassDestination', $form);\n\n // Enhanced selector input for tests:\n this.createSelectorInput({\n $filterContainer,\n $inputElement,\n dataProvider: {\n list: providers.listTests\n }\n });\n }\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2024 (original work) Open Assessment Technologies SA;\n *\n */\ndefine('taoTests/controller/testImport/index',[\n 'jquery',\n 'taoTests/util/provider/itemClassProvider',\n 'taoTests/util/form/itemClassSelectorForm',\n], function ($, itemClassProvider, itemClassSelectorForm) {\n 'use strict';\n\n return {\n start() {\n const $form = $('#import');\n itemClassSelectorForm.setupTaoLocalForm($form, itemClassProvider);\n }\n };\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2020 (original work) Open Assessment Technologies SA ;\n */\n/**\n * @author Hanna Dzmitryieva \n */\ndefine('taoTests/previewer/factory',[\n 'lodash',\n 'context',\n 'module',\n 'core/providerLoader',\n 'core/providerRegistry',\n 'core/logger'\n], function (\n _,\n context,\n module,\n providerLoaderFactory,\n providerRegistry\n) {\n 'use strict';\n\n /**\n * Loads and display the test previewer\n * @param {String} type - The type of previewer\n * @param {String} uri - The URI of the test to load\n * @param {Object} [config] - Some config entries\n * @param {String} [config.fullPage] - Force the previewer to occupy the full window.\n * @param {String} [config.readOnly] - Do not allow to modify the previewed test.\n * @param {Object} [config.previewers] - Optionally load static adapters. By default take them from the module's config.\n * @returns {Promise}\n */\n function previewerFactory(type, uri, config) {\n config = Object.assign({}, module.config(), config);\n return providerLoaderFactory()\n .addList(config.previewers)\n .load(context.bundle)\n .then(function (providers) {\n providers.forEach(function (provider) {\n previewerFactory.registerProvider(provider.name, provider);\n });\n })\n .then(function () {\n return previewerFactory.getProvider(type);\n })\n .then(function (provider) {\n return provider.init(uri, config);\n });\n }\n\n return providerRegistry(previewerFactory, function validateProvider(provider) {\n if (typeof provider.init !== 'function') {\n throw new TypeError('The previewer provider MUST have a init() method');\n }\n return true;\n });\n});\n\n","/*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2014 - 2020 (original work) Open Assessment Technologies SA\n *\n */\ndefine('taoTests/controller/tests/action',[\n 'i18n',\n 'module',\n 'uri',\n 'layout/actions',\n 'layout/actions/binder',\n 'layout/section',\n 'form/translation',\n 'services/translation',\n 'ui/feedback',\n 'core/logger',\n 'taoTests/previewer/factory'\n], function (\n __,\n module,\n uri,\n actionManager,\n binder,\n section,\n translationFormFactory,\n translationService,\n feedback,\n loggerFactory,\n previewerFactory\n) {\n 'use strict';\n\n const logger = loggerFactory('taoTests/controller/action');\n\n binder.register('translateTest', function (actionContext) {\n section.current().updateContentBlock('');\n const $container = $('.main-container', section.selected.panel);\n const { rootClassUri, id: resourceUri } = actionContext;\n translationFormFactory($container, { rootClassUri, resourceUri, allowDeletion: true })\n .on('edit', (id, language) => {\n return actionManager.exec('test-authoring', {\n id,\n language,\n rootClassUri,\n originResourceUri: resourceUri,\n translation: true,\n actionParams: ['originResourceUri', 'language', 'translation']\n });\n })\n .on('delete', function onDelete(id, language) {\n return translationService.deleteTranslation(resourceUri, language).then(() => {\n feedback().success(__('Translation deleted'));\n return this.refresh();\n });\n })\n .on('error', error => {\n logger.error(error);\n feedback().error(__('An error occurred while processing your request.'));\n });\n });\n\n binder.register('testPreview', function testPreview(actionContext) {\n const config = module.config();\n const previewerConfig = Object.fromEntries(\n Object.entries({\n readOnly: false,\n fullPage: true,\n pluginsOptions: config.pluginsOptions\n }).filter(([key, value]) => value !== undefined)\n );\n\n const getProvider = id => {\n if (!id || !config.providers) {\n return config.provider;\n }\n const previewerId = parseInt(`${id}`.split('-').pop(), 10) || 0;\n if (!config.providers[previewerId]) {\n return config.provider;\n }\n return config.providers[previewerId].id;\n };\n\n previewerFactory(getProvider(this.id) || 'qtiTest', uri.decode(actionContext.uri), previewerConfig).catch(\n err => {\n logger.error(err);\n feedback().error(__('Test Preview is not installed, please contact to your administrator.'));\n }\n );\n });\n});\n\n","/**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2015-2024 (original work) Open Assessment Technologies SA;\n */\n\n/**\n * Test edition controller\n *\n */\ndefine('taoTests/controller/tests/editTest',['jquery', 'ui/lock', 'module', 'layout/actions'], function ($, lock, module, actions) {\n 'use strict';\n\n return {\n /**\n * Controller's entrypoint\n */\n start() {\n const config = module.config();\n const maxButtons = 10; // arbitrary value for the max number of buttons\n\n const getPreviewId = idx => `test-preview${idx ? `-${idx}` : ''}`;\n const previewActions = [];\n for (let i = 0; i < maxButtons; i++) {\n const action = actions.getBy(getPreviewId(i));\n if (!action) {\n break;\n }\n previewActions.push(action);\n }\n previewActions.forEach(previewAction => {\n previewAction.state.disabled = !config.isPreviewEnabled;\n });\n actions.updateState();\n\n $('#lock-box').each(function () {\n lock($(this)).register();\n });\n }\n };\n});\n\n","\ndefine(\"taoTests/loader/taoTests.bundle\", function(){});\n","define(\"taoTests/loader/taoTests.min\", [\"taoItems/loader/taoItems.min\"], function(){});\n"],"mappings":"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"}
\ No newline at end of file
diff --git a/views/js/loader/taoTestsRunner.min.js.map b/views/js/loader/taoTestsRunner.min.js.map
index 1c3ba3a1..c30aee2d 100644
--- a/views/js/loader/taoTestsRunner.min.js.map
+++ b/views/js/loader/taoTestsRunner.min.js.map
@@ -1 +1 @@
-{"version":3,"names":["define","_","areaBroker$1","Object","prototype","hasOwnProperty","call","requireAreas","areaBroker","partial","dataHolderFactory","map","Map","defaultObjects","forEach","entry","set","pluginFactory","plugin","partialRight","hostName","moment","uuid","momentTimezone_min","probeOverseerFactory","runner","collectEvent","probe","eventNs","name","probeHandler","now","data","id","type","timestamp","format","timezone","tz","timeZone","capture","context","apply","concat","slice","arguments","overseer","push","latency","collectLatencyEvent","events","eventName","listen","indexOf","on","startHandler","marker","stopHandler","last","args","findLast","immutableQueue","startEvents","stopEvents","queueStorage","probes","queue","writing","Promise","resolve","started","getStorage","getTestStore","getStore","then","newStorage","resetStorage","isPlainObject","isFunction","init","TypeError","add","isString","isEmpty","some","val","length","isArray","getQueue","storage","getItem","getProbes","setItem","flush","flushed","start","savedQueue","stop","removeHandler","off","removeItem","guess","Array","eventifier","providerRegistry","testRunnerFactory","providerName","providerRun","method","_len","_key","provider","pluginRun","execStack","getPlugins","all","reportError","err","trigger","dataHolder","pluginFactories","config","plugins","states","ready","render","finish","destroy","itemStates","getProvider","proxy","probeOverseer","testStore","getDataHolder","getAreaBroker","getName","setState","after","catch","loadItem","itemRef","itemData","setItemState","renderItem","unloadItem","omit","disableItem","getItemState","enableItem","setTestContext","setTestMap","getConfig","getOptions","options","getPlugin","getPluginsConfig","getPluginConfig","pluginName","pluginsConfig","loadAreaBroker","getProxy","loadProxy","Error","error","install","getProbeOverseer","loadProbeOverseer","loadTestStore","getPluginStore","loadedStore","reject","getState","active","getPersistentState","state","setPersistentState","stored","loaded","disabled","getTestData","get","setTestData","testData","getTestContext","testContext","getTestMap","testMap","loadDataHolder","next","scope","previous","jump","position","skip","direction","ref","exit","why","pause","resume","timeout","timer","destroyCleanUp","clear","validateProvider","async","delegator","tokenHandlerFactory","connectivity","proxyFactory","proxyName","getParams","params","mergedParams","merge","extraCallParams","getMiddlewares","list","middlewares","applyMiddlewares","request","response","command","middleware","series","status","delegate","fnName","_slice","initialized","includes","delegateProxy","communicator","communicatorPromise","testDataHolder","proxyAdapter","initConfig","defaults","_defaults","tokenHandler","onlineStatus","isOnline","use","each","cb","destroyCommunicator","setOnline","isOffline","setOffline","source","isConnectivityError","isObject","code","sent","getTokenHandler","hasCommunicator","getCommunicator","self","loadCommunicator","before","e","open","channel","handler","communicatorInstance","noop","send","message","addCallActionParams","sendVariables","variables","deferred","callTestAction","action","uri","submitItem","callItemAction","telemetry","signal","messages","msg","wrapper","pluginWrapper","loggerFactory","providerLoader","pluginLoader","itemRunner","loadTestRunnerProviders","providers","loadFromBundle","loadAndRegisterProvider","providersToLoad","target","registerProvider","addList","load","loadedProviders","registration","runnerProviders","itemRunnerProviders","register","communicatorProviders","proxyProviders","logger","warn","keys","providerType","debug","results","reduce","acc","value","assign","sampleProxy","component","runnerFactory","Handlebars","runnerComponentTpl","asString","html","Template","$","validateTestRunnerConfiguration","requiredProperties","property","join","getSelectedProvider","typeProviders","runnerComponentFactory","container","template","runnerComponent","getOption","getRunner","setTemplate","hide","runnerConfig","renderTo","getElement","defer","show","spread","destroying","removeAllListeners","depth0","helpers","partials","compilerInfo","runnerComponentApi","configuration","document","createElement","classList","append","remove","store","testStoreLoader","testId","preselectedBackend","testMode","volatiles","changeTracking","isStoreModeUnified","isUndefined","selectStoreMode","result","modes","unified","storeName","trackChange","isBoolean","isUnified","loadStore","keyPattern","RegExp","storeKey","key","getItems","entries","transform","test","replace","setVolatile","clearVolatileIfStoreChange","storeId","shouldClear","getIdentifier","savedStoreId","info","clearVolatileStores","clearing","storeInstance","startChangeTracking","hasChanges","resetChanges","legacyStoreExp","removeStore","removeAll","getStorageIdentifier","legacyPrefixes","fragmented","getAll","validate","prefix","foundStores"],"sources":["/github/workspace/tao/views/build/config-wrap-start-default.js","../runner/areaBroker.js","../runner/dataHolder.js","../runner/plugin.js","../runner/probeOverseer.js","../runner/runner.js","../runner/proxy.js","../runner/providerLoader.js","../runner/proxy/sample.js","../runner/runnerComponent.js","../runner/runnerComponentSimple.js","../runner/testStore.js","module-create.js","/github/workspace/tao/views/build/config-wrap-end-default.js"],"sourcesContent":["\n","define('taoTests/runner/areaBroker',['lodash', 'ui/areaBroker'], function (_, areaBroker$1) { 'use strict';\n\n _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;\n areaBroker$1 = areaBroker$1 && Object.prototype.hasOwnProperty.call(areaBroker$1, 'default') ? areaBroker$1['default'] : areaBroker$1;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technlogies SA\n *\n */\n var requireAreas = ['content',\n //where the content is renderer, for example an item\n 'toolbox',\n //the place to add arbitrary tools, like a zoom, a comment box, etc.\n 'navigation',\n //the navigation controls like next, previous, skip\n 'control',\n //the control center of the test, progress, timers, etc.\n 'header',\n //the area that could contains the test titles\n 'panel' //a panel to add more advanced GUI (item review, navigation pane, etc.)\n ];\n\n /**\n * Creates an area broker with the required areas for the test runner.\n *\n * @see ui/areaBroker\n *\n * @param {jQueryElement|HTMLElement|String} $container - the main container\n * @param {Object} mapping - keys are the area names, values are jQueryElement\n * @returns {broker} the broker\n * @throws {TypeError} without a valid container\n */\n var areaBroker = _.partial(areaBroker$1, requireAreas);\n\n return areaBroker;\n\n});\n\n","define('taoTests/runner/dataHolder',[],function () { 'use strict';\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2017-2019 (original work) Open Assessment Technlogies SA\n *\n */\n\n /**\n * Holds the test runner data.\n *\n * @example\n * var holder = holder();\n * holder.get('testMap');\n *\n * @author Bertrand Chevrier \n */\n\n /**\n * @type {String[]} the list of default objects to create\n */\n const defaultObjects = ['testContext', 'testMap'];\n\n /**\n * Creates a new data holder,\n * with default entries.\n *\n * @returns {Map} the holder\n */\n function dataHolderFactory() {\n var map = new Map();\n defaultObjects.forEach(function (entry) {\n map.set(entry, {});\n });\n return map;\n }\n\n return dataHolderFactory;\n\n});\n\n","define('taoTests/runner/plugin',['lodash', 'core/plugin'], function (_, pluginFactory) { 'use strict';\n\n _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;\n pluginFactory = pluginFactory && Object.prototype.hasOwnProperty.call(pluginFactory, 'default') ? pluginFactory['default'] : pluginFactory;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n\n /**\n * A pluginFactory configured for the test runner\n * @returns {Function} the preconfigured plugin factory\n */\n var plugin = _.partialRight(pluginFactory, {\n //alias getHost to getTestRunner\n hostName: 'testRunner'\n });\n\n return plugin;\n\n});\n\n","define('taoTests/runner/probeOverseer',['lodash', 'moment', 'lib/uuid', 'lib/moment-timezone.min'], function (_, moment, uuid, momentTimezone_min) { 'use strict';\n\n _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;\n moment = moment && Object.prototype.hasOwnProperty.call(moment, 'default') ? moment['default'] : moment;\n uuid = uuid && Object.prototype.hasOwnProperty.call(uuid, 'default') ? uuid['default'] : uuid;\n\n /*\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technlogies SA\n *\n */\n var timeZone = moment.tz.guess();\n var slice = Array.prototype.slice;\n\n /**\n * Create the overseer intance\n * @param {runner} runner - a instance of a test runner\n * @returns {probeOverseer} the new probe overseer\n * @throws TypeError if something goes wrong\n */\n function probeOverseerFactory(runner) {\n // the created instance\n var overseer;\n\n // the list of registered probes\n var probes = [];\n\n //temp queue\n var queue = [];\n\n //immutable queue which will not be flushed\n var immutableQueue = [];\n\n /**\n * @type {Storage} to store the collected events\n */\n var queueStorage;\n\n /**\n * @type {Promise} Promises chain to avoid write collisions\n */\n var writing = Promise.resolve();\n\n //is the overseer started\n var started = false;\n\n /**\n * Get the storage instance\n * @returns {Promise} that resolves with the storage\n */\n var getStorage = function getStorage() {\n if (queueStorage) {\n return Promise.resolve(queueStorage);\n }\n return runner.getTestStore().getStore('test-probe').then(function (newStorage) {\n queueStorage = newStorage;\n return Promise.resolve(queueStorage);\n });\n };\n\n /**\n * Unset the storage instance\n */\n var resetStorage = function resetStorage() {\n queueStorage = null;\n };\n\n /**\n * Register the collection event of a probe against a runner\n * @param {Object} probe - a valid probe\n */\n function collectEvent(probe) {\n var eventNs = `.probe-${probe.name}`;\n\n //event handler registered to collect data\n var probeHandler = function probeHandler() {\n var now = moment();\n var data = {\n id: uuid(12, 16),\n type: probe.name,\n timestamp: now.format('x') / 1000,\n timezone: now.tz(timeZone).format('Z')\n };\n if (typeof probe.capture === 'function') {\n data.context = probe.capture.apply(probe, [runner].concat(slice.call(arguments)));\n }\n overseer.push(data);\n };\n\n //fallback\n if (probe.latency) {\n return collectLatencyEvent(probe);\n }\n _.forEach(probe.events, function (eventName) {\n var listen = eventName.indexOf('.') > 0 ? eventName : eventName + eventNs;\n runner.on(listen, _.partial(probeHandler, eventName));\n });\n }\n function collectLatencyEvent(probe) {\n var eventNs = `.probe-${probe.name}`;\n\n //start event handler registered to collect data\n var startHandler = function startHandler() {\n var now = moment();\n var data = {\n id: uuid(12, 16),\n marker: 'start',\n type: probe.name,\n timestamp: now.format('x') / 1000,\n timezone: now.tz(timeZone).format('Z')\n };\n if (typeof probe.capture === 'function') {\n data.context = probe.capture.apply(probe, [runner].concat(slice.call(arguments)));\n }\n overseer.push(data);\n };\n\n //stop event handler registered to collect data\n var stopHandler = function stopHandler() {\n var now = moment();\n var last;\n var data = {\n type: probe.name,\n timestamp: now.format('x') / 1000,\n timezone: now.tz(timeZone).format('Z')\n };\n var args = slice.call(arguments);\n last = _.findLast(immutableQueue, {\n type: probe.name,\n marker: 'start'\n });\n if (last && !_.findLast(immutableQueue, {\n type: probe.name,\n marker: 'end',\n id: last.id\n })) {\n data.id = last.id;\n data.marker = 'end';\n if (typeof probe.capture === 'function') {\n data.context = probe.capture.apply(probe, [runner].concat(args));\n }\n overseer.push(data);\n }\n };\n\n //fallback\n if (!probe.latency) {\n return collectEvent(probe);\n }\n _.forEach(probe.startEvents, function (eventName) {\n var listen = eventName.indexOf('.') > 0 ? eventName : eventName + eventNs;\n runner.on(listen, _.partial(startHandler, eventName));\n });\n _.forEach(probe.stopEvents, function (eventName) {\n var listen = eventName.indexOf('.') > 0 ? eventName : eventName + eventNs;\n runner.on(listen, _.partial(stopHandler, eventName));\n });\n }\n\n //argument validation\n if (!_.isPlainObject(runner) || !_.isFunction(runner.init) || !_.isFunction(runner.on)) {\n throw new TypeError('Please set a test runner');\n }\n\n /**\n * @typedef {probeOverseer}\n */\n overseer = {\n /**\n * Add a new probe\n * @param {Object} probe\n * @param {String} probe.name - the probe name\n * @param {Boolean} [probe.latency = false] - simple or latency mode\n * @param {String[]} [probe.events] - the list of events to listen (simple mode)\n * @param {String[]} [probe.startEvents] - the list of events to mark the start (lantency mode)\n * @param {String[]} [probe.stopEvents] - the list of events to mark the end (latency mode)\n * @param {Function} [probe.capture] - lambda fn to define the data context, it receive the test runner and the event parameters\n * @returns {probeOverseer} chains\n * @throws TypeError if the probe is not well formatted\n */\n add: function add(probe) {\n // probe structure strict validation\n\n if (!_.isPlainObject(probe)) {\n throw new TypeError('A probe is a plain object');\n }\n if (!_.isString(probe.name) || _.isEmpty(probe.name)) {\n throw new TypeError('A probe must have a name');\n }\n if (probes.some(val => val.name === probe.name)) {\n throw new TypeError('A probe with this name is already regsitered');\n }\n if (probe.latency) {\n if (_.isString(probe.startEvents) && !_.isEmpty(probe.startEvents)) {\n probe.startEvents = [probe.startEvents];\n }\n if (_.isString(probe.stopEvents) && !_.isEmpty(probe.stopEvents)) {\n probe.stopEvents = [probe.stopEvents];\n }\n if (!probe.startEvents.length || !probe.stopEvents.length) {\n throw new TypeError('Latency based probes must have startEvents and stopEvents defined');\n }\n\n //if already started we register the events on addition\n if (started) {\n collectLatencyEvent(probe);\n }\n } else {\n if (_.isString(probe.events) && !_.isEmpty(probe.events)) {\n probe.events = [probe.events];\n }\n if (!_.isArray(probe.events) || probe.events.length === 0) {\n throw new TypeError('A probe must define events');\n }\n\n //if already started we register the events on addition\n if (started) {\n collectEvent(probe);\n }\n }\n probes.push(probe);\n return this;\n },\n /**\n * Get the time entries queue\n * @returns {Promise} with the data in parameterj\n */\n getQueue: function getQueue() {\n return getStorage().then(function (storage) {\n return storage.getItem('queue');\n });\n },\n /**\n * Get the list of defined probes\n * @returns {Object[]} the probes collection\n */\n getProbes: function getProbes() {\n return probes;\n },\n /**\n * Push a time entry to the queue\n * @param {Object} entry - the time entry\n */\n push: function push(entry) {\n getStorage().then(function (storage) {\n //ensure the queue is pushed to the store consistently and atomically\n writing = writing.then(function () {\n queue.push(entry);\n immutableQueue.push(entry);\n return storage.setItem('queue', queue);\n });\n });\n },\n /**\n * Flush the queue and get the entries\n * @returns {Promise} with the data in parameter\n */\n flush: function flush() {\n return new Promise(function (resolve) {\n getStorage().then(function (storage) {\n writing = writing.then(function () {\n return storage.getItem('queue').then(function (flushed) {\n queue = [];\n return storage.setItem('queue', queue).then(function () {\n resolve(flushed);\n });\n });\n });\n });\n });\n },\n /**\n * Start the probes\n * @returns {Promise} once started\n */\n start: function start() {\n return getStorage().then(function (storage) {\n return storage.getItem('queue').then(function (savedQueue) {\n if (_.isArray(savedQueue)) {\n queue = savedQueue;\n immutableQueue = savedQueue;\n }\n _.forEach(probes, collectEvent);\n started = true;\n });\n });\n },\n /**\n * Stop the probes\n * Be carefull, stop will also clear the store and the queue\n * @returns {Promise} once stopped\n */\n stop: function stop() {\n started = false;\n _.forEach(probes, function (probe) {\n var eventNs = `.probe-${probe.name}`;\n var removeHandler = function removeHandler(eventName) {\n runner.off(eventName + eventNs);\n };\n _.forEach(probe.startEvents, removeHandler);\n _.forEach(probe.stopEvents, removeHandler);\n _.forEach(probe.events, removeHandler);\n });\n queue = [];\n immutableQueue = [];\n return getStorage().then(function (storage) {\n return storage.removeItem('queue').then(resetStorage);\n });\n }\n };\n return overseer;\n }\n\n return probeOverseerFactory;\n\n});\n\n","define('taoTests/runner/runner',['lodash', 'core/eventifier', 'core/providerRegistry', 'taoTests/runner/dataHolder'], function (_, eventifier, providerRegistry, dataHolderFactory) { 'use strict';\n\n _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;\n eventifier = eventifier && Object.prototype.hasOwnProperty.call(eventifier, 'default') ? eventifier['default'] : eventifier;\n providerRegistry = providerRegistry && Object.prototype.hasOwnProperty.call(providerRegistry, 'default') ? providerRegistry['default'] : providerRegistry;\n dataHolderFactory = dataHolderFactory && Object.prototype.hasOwnProperty.call(dataHolderFactory, 'default') ? dataHolderFactory['default'] : dataHolderFactory;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2015-2020 (original work) Open Assessment Technologies SA ;\n */\n\n /**\n * Builds an instance of the QTI test runner\n *\n * @param {String} providerName\n * @param {Function[]} pluginFactories\n * @param {Object} config\n * @param {String} config.serviceCallId - the identifier of the test session\n * @param {String} [config.testDefinition] - the identifier of the test definition\n * @param {String} [config.testCompilation] - the identifier of the compiled test\n * @param {Object} config.options - the test runner configuration options\n * @param {Object} config.options.plugins - the plugins configuration\n * @param {jQueryElement} [config.renderTo] - the dom element that is going to holds the test content (item, rubick, etc)\n * @returns {runner}\n */\n function testRunnerFactory(providerName) {\n let pluginFactories = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];\n let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n /**\n * @type {Object} The test runner instance\n */\n let runner;\n\n /**\n * @type {Map} Contains the test runner data\n */\n let dataHolder;\n\n /**\n * @type {Object} the registered plugins\n */\n const plugins = {};\n\n /**\n * @type {Object} the test of the runner\n */\n const states = {\n init: false,\n ready: false,\n render: false,\n finish: false,\n destroy: false\n };\n\n /**\n * @type {Object} keeps the states of the items\n */\n let itemStates = {};\n\n /**\n * The selected test runner provider\n */\n const provider = testRunnerFactory.getProvider(providerName);\n\n /**\n * Keep the area broker instance\n * @see taoTests/runner/areaBroker\n */\n let areaBroker;\n\n /**\n * Keep the proxy instance\n * @see taoTests/runner/proxy\n */\n let proxy;\n\n /**\n * Keep the instance of the probes overseer\n * @see taoTests/runner/probeOverseer\n */\n let probeOverseer;\n\n /**\n * Keep the instance of a testStore\n * @see taoTests/runner/testStore\n */\n let testStore;\n\n /**\n * Run a method of the provider (by delegation)\n *\n * @param {String} method - the method to run\n * @param {...} args - rest parameters given to the provider method\n * @returns {Promise} so provider can do async stuffs\n */\n function providerRun(method) {\n for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n return new Promise(resolve => {\n if (!_.isFunction(provider[method])) {\n return resolve();\n }\n return resolve(provider[method].apply(runner, args));\n });\n }\n\n /**\n * Run a method in all plugins\n *\n * @param {String} method - the method to run\n * @returns {Promise} once that resolve when all plugins are done\n */\n function pluginRun(method) {\n var execStack = [];\n _.forEach(runner.getPlugins(), plugin => {\n if (_.isFunction(plugin[method])) {\n execStack.push(plugin[method]());\n }\n });\n return Promise.all(execStack);\n }\n\n /**\n * Trigger error event\n * @param {Error|String} err - the error\n * @fires runner#error\n */\n function reportError(err) {\n runner.trigger('error', err);\n }\n\n /**\n * Defines the test runner\n *\n * @type {runner}\n */\n runner = eventifier({\n /**\n * Initialize the runner\n * - instantiate the plugins\n * - provider init\n * - plugins init\n * - call render\n * @fires runner#init\n * @returns {runner} chains\n */\n init() {\n if (!dataHolder) {\n dataHolder = this.getDataHolder();\n }\n\n //instantiate the plugins first\n _.forEach(pluginFactories, pluginFactory => {\n const plugin = pluginFactory(runner, this.getAreaBroker());\n plugins[plugin.getName()] = plugin;\n });\n providerRun('install').then(_.partial(providerRun, 'loadPersistentStates')).then(_.partial(pluginRun, 'install')).then(_.partial(providerRun, 'init')).then(_.partial(pluginRun, 'init')).then(() => {\n this.setState('init', true).off('init.internal').after('init.internal', () => this.render()).trigger('init');\n }).catch(reportError);\n return this;\n },\n /**\n * Render the runner\n * - provider render\n * - plugins render\n * @fires runner#render\n * @fires runner#ready\n * @returns {runner} chains\n */\n render() {\n providerRun('render').then(() => pluginRun('render')).then(() => {\n this.setState('ready', true).trigger('render').trigger('ready');\n }).catch(reportError);\n return this;\n },\n /**\n * Load an item\n * - provider loadItem, resolve or return the itemData\n * - plugins loadItem\n * - call renderItem\n * @param {*} itemRef - something that let you identify the item to load\n * @fires runner#loaditem\n * @returns {runner} chains\n */\n loadItem(itemRef) {\n providerRun('loadItem', itemRef).then(itemData => {\n this.setItemState(itemRef, 'loaded', true).off('loaditem.internal').after('loaditem.internal', () => this.renderItem(itemRef, itemData)).trigger('loaditem', itemRef, itemData);\n }).catch(reportError);\n return this;\n },\n /**\n * Render an item\n * - provider renderItem\n * - plugins renderItem\n * @param {Object} itemRef\n * @param {Object} itemData - the loaded item data\n * @fires runner#renderitem\n * @returns {runner} chains\n */\n renderItem(itemRef, itemData) {\n providerRun('renderItem', itemRef, itemData).then(() => {\n this.setItemState(itemRef, 'ready', true).trigger('renderitem', itemRef, itemData);\n }).catch(reportError);\n return this;\n },\n /**\n * Unload an item (for example to destroy the item)\n * - provider unloadItem\n * - plugins unloadItem\n * @param {*} itemRef - something that let you identify the item to unload\n * @fires runner#unloaditem\n * @returns {runner} chains\n */\n unloadItem(itemRef) {\n providerRun('unloadItem', itemRef).then(() => {\n itemStates = _.omit(itemStates, itemRef);\n this.trigger('unloaditem', itemRef);\n }).catch(reportError);\n return this;\n },\n /**\n * Disable an item\n * - provider disableItem\n * @param {*} itemRef - something that let you identify the item\n * @fires runner#disableitem\n * @returns {runner} chains\n */\n disableItem(itemRef) {\n if (!this.getItemState(itemRef, 'disabled')) {\n providerRun('disableItem', itemRef).then(() => {\n this.setItemState(itemRef, 'disabled', true).trigger('disableitem', itemRef);\n }).catch(reportError);\n }\n return this;\n },\n /**\n * Enable an item\n * - provider enableItem\n * @param {*} itemRef - something that let you identify the item\n * @fires runner#disableitem\n * @returns {runner} chains\n */\n enableItem(itemRef) {\n if (this.getItemState(itemRef, 'disabled')) {\n providerRun('enableItem', itemRef).then(() => {\n this.setItemState(itemRef, 'disabled', false).trigger('enableitem', itemRef);\n }).catch(reportError);\n }\n return this;\n },\n /**\n * When the test is terminated\n * - provider finish\n * - plugins finsh\n * @fires runner#finish\n * @returns {runner} chains\n */\n finish() {\n providerRun('finish').then(() => pluginRun('finish')).then(() => {\n this.setState('finish', true).trigger('finish');\n }).catch(reportError);\n return this;\n },\n /**\n * Flushes the runner\n * - provider flush\n * - plugins flush\n * @fires runner#flush\n * @returns {runner} chains\n */\n flush() {\n providerRun('flush').then(() => pluginRun('flush')).then(() => {\n this.setState('flush', true).trigger('flush');\n }).catch(reportError);\n return this;\n },\n /**\n * Destroy\n * - provider destroy\n * - plugins destroy\n * @fires runner#destroy\n * @returns {runner} chains\n */\n destroy() {\n providerRun('destroy').then(() => pluginRun('destroy')).then(() => {\n if (proxy) {\n return proxy.destroy();\n }\n }).then(() => {\n this.setTestContext({}).setTestMap({}).setState('destroy', true).trigger('destroy');\n }).catch(reportError);\n return this;\n },\n /**\n * Get the whole test runner configuration\n * @returns {Object} the config\n */\n getConfig() {\n return config || {};\n },\n /**\n * Get the options from the configuration parameters, (feature flags, parameter values, etc.)\n *\n * Alias to getConfig().options\n *\n * In deprecated mode, this is initialized through getTestData (after /init)\n *\n * @returns {Object} the configuration options\n */\n getOptions() {\n return this.getConfig().options || {};\n },\n /**\n * Get the runner pugins\n * @returns {plugin[]} the plugins\n */\n getPlugins() {\n return plugins;\n },\n /**\n * Get a plugin\n * @param {String} name - the plugin name\n * @returns {plugin} the plugin\n */\n getPlugin(name) {\n return plugins[name];\n },\n /**\n * Get the configuration of the plugins\n *\n * Alias to getConfig().options.plugins\n *\n * In deprecated mode, this is initialized through getTestData (after /init)\n *\n * @returns {Object} the configuration options\n */\n getPluginsConfig() {\n return this.getOptions().plugins || {};\n },\n /**\n * Get the configuration of a given plugin\n *\n * In deprecated mode, this is initialized through getTestData (after /init)\n *\n * @param {String} pluginName - the name of the plugin\n * @returns {Object} the configuration options of the plugin\n */\n getPluginConfig(pluginName) {\n if (pluginName && plugins[pluginName]) {\n const pluginsConfig = this.getPluginsConfig();\n if (pluginsConfig[pluginName]) {\n return pluginsConfig[pluginName];\n }\n }\n return {};\n },\n /**\n * Get the area broker, load it if not present\n *\n * @returns {areaBroker} the areaBroker\n */\n getAreaBroker() {\n if (!areaBroker) {\n areaBroker = provider.loadAreaBroker.call(this);\n }\n return areaBroker;\n },\n /**\n * Get the proxy, load it if not present\n *\n * @returns {proxy} the proxy\n */\n getProxy() {\n if (!proxy) {\n if (!_.isFunction(provider.loadProxy)) {\n throw new Error('The provider does not have a loadProxy method');\n }\n proxy = provider.loadProxy.call(this);\n proxy.on('error', error => this.trigger('error', error));\n proxy.install(this.getDataHolder());\n }\n return proxy;\n },\n /**\n * Get the probeOverseer, and load it if not present\n *\n * @returns {probeOverseer} the probe overseer\n */\n getProbeOverseer() {\n if (!probeOverseer && _.isFunction(provider.loadProbeOverseer)) {\n probeOverseer = provider.loadProbeOverseer.call(this);\n }\n return probeOverseer;\n },\n /**\n * Get the testStore, and load it if not present\n *\n * @returns {testStore} the testStore instance\n */\n getTestStore() {\n if (!testStore && _.isFunction(provider.loadTestStore)) {\n testStore = provider.loadTestStore.call(this);\n }\n return testStore;\n },\n /**\n * Get a plugin store.\n * It's a convenience method that calls testStore.getStore\n * @param {String} name - the name of store, usually the plugin name.\n *\n * @returns {Promise} the plugin store\n */\n getPluginStore(name) {\n const loadedStore = this.getTestStore();\n if (!loadedStore || !_.isFunction(loadedStore.getStore)) {\n return Promise.reject(new Error('Please configure a testStore via loadTestStore to be able to get a plugin store'));\n }\n return this.getTestStore().getStore(name);\n },\n /**\n * Check a runner state\n *\n * @param {String} name - the state name\n * @returns {Boolean} if active, false if not set\n */\n getState(name) {\n return !!states[name];\n },\n /**\n * Define a runner state\n *\n * @param {String} name - the state name\n * @param {Boolean} active - is the state active\n * @returns {runner} chains\n * @throws {TypeError} if the state name is not a valid string\n */\n setState(name, active) {\n if (!_.isString(name) || _.isEmpty(name)) {\n throw new TypeError('The state must have a name');\n }\n states[name] = !!active;\n return this;\n },\n /**\n * Checks a runner persistent state\n * - provider getPersistentState\n *\n * @param {String} name - the state name\n * @returns {Boolean} if active, false if not set\n */\n getPersistentState(name) {\n let state;\n if (_.isFunction(provider.getPersistentState)) {\n state = provider.getPersistentState.call(runner, name);\n }\n return !!state;\n },\n /**\n * Defines a runner persistent state\n * - provider setPersistentState\n *\n * @param {String} name - the state name\n * @param {Boolean} active - is the state active\n * @returns {Promise} Returns a promise that:\n * - will be resolved once the state is fully stored\n * - will be rejected if any error occurs or if the state name is not a valid string\n */\n setPersistentState(name, active) {\n let stored;\n if (!_.isString(name) || _.isEmpty(name)) {\n stored = Promise.reject(new TypeError('The state must have a name'));\n } else {\n stored = providerRun('setPersistentState', name, !!active);\n }\n stored.catch(reportError);\n return stored;\n },\n /**\n * Check an item state\n *\n * @param {*} itemRef - something that let you identify the item\n * @param {String} name - the state name\n * @returns {Boolean} if active, false if not set\n *\n * @throws {TypeError} if there is no itemRef nor name\n */\n getItemState(itemRef, name) {\n if (_.isEmpty(itemRef) || _.isEmpty(name)) {\n throw new TypeError('The state is identified by an itemRef and a name');\n }\n return !!(itemStates[itemRef] && itemStates[itemRef][name]);\n },\n /**\n * Check an item state\n *\n * @param {*} itemRef - something that let you identify the item\n * @param {String} name - the state name\n * @param {Boolean} active - is the state active\n * @returns {runner} chains\n *\n * @throws {TypeError} if there is no itemRef nor name\n */\n setItemState(itemRef, name, active) {\n if (_.isEmpty(itemRef) || _.isEmpty(name)) {\n throw new TypeError('The state is identified by an itemRef and a name');\n }\n itemStates[itemRef] = itemStates[itemRef] || {\n loaded: false,\n ready: false,\n disabled: false\n };\n itemStates[itemRef][name] = !!active;\n return this;\n },\n /**\n * Get the test data/definition\n * @deprecated\n * @returns {Object} the test data\n */\n getTestData() {\n return dataHolder && dataHolder.get('testData');\n },\n /**\n * Set the test data/definition\n * @deprecated\n * @param {Object} testData - the test data\n * @returns {runner} chains\n */\n setTestData(testData) {\n if (dataHolder && _.isPlainObject(testData)) {\n dataHolder.set('testData', testData);\n }\n return this;\n },\n /**\n * Get the test context/state\n * @returns {Object} the test context\n */\n getTestContext() {\n return dataHolder && dataHolder.get('testContext');\n },\n /**\n * Set the test context/state\n * @param {Object} testContext - the context to set\n * @returns {runner} chains\n */\n setTestContext(testContext) {\n if (dataHolder && _.isPlainObject(testContext)) {\n dataHolder.set('testContext', testContext);\n }\n return this;\n },\n /**\n * Get the test items map\n * @returns {Object} the test map\n */\n getTestMap() {\n return dataHolder && dataHolder.get('testMap');\n },\n /**\n * Set the test items map\n * @param {Object} testMap - the map to set\n * @returns {runner} chains\n */\n setTestMap(testMap) {\n if (dataHolder && _.isPlainObject(testMap)) {\n dataHolder.set('testMap', testMap);\n }\n return this;\n },\n /**\n * Get the data holder\n * @returns {dataHolder}\n */\n getDataHolder() {\n if (!dataHolder) {\n if (_.isFunction(provider.loadDataHolder)) {\n dataHolder = provider.loadDataHolder.call(this);\n } else {\n dataHolder = dataHolderFactory();\n }\n }\n return dataHolder;\n },\n /**\n * Move next alias\n * @param {String|*} [scope] - the movement scope\n * @fires runner#move\n * @returns {runner} chains\n */\n next(scope) {\n if (_.isFunction(provider.next)) {\n return providerRun('next', scope);\n }\n\n //backward compat\n this.trigger('move', 'next', scope);\n return this;\n },\n /**\n * Move previous alias\n * @param {String|*} [scope] - the movement scope\n * @fires runner#move\n * @returns {runner} chains\n */\n previous(scope) {\n if (_.isFunction(provider.previous)) {\n return providerRun('previous', scope);\n }\n\n //backward compat\n this.trigger('move', 'previous', scope);\n return this;\n },\n /**\n * Move to alias\n * @param {String|Number} position - where to jump\n * @param {String|*} [scope] - the movement scope\n * @fires runner#move\n * @returns {runner} chains\n */\n jump(position, scope) {\n if (_.isFunction(provider.jump)) {\n return providerRun('jump', position, scope);\n }\n\n //backward compat\n this.trigger('move', 'jump', scope, position);\n return this;\n },\n /**\n * Skip alias\n * @param {String|*} [scope] - the movement scope\n * @param {String|*} [direction] - next/previous/jump\n * @param {Number|*} [ref] - the item ref\n * @fires runner#skip\n * @returns {runner} chains\n */\n skip(scope, direction, ref) {\n if (_.isFunction(provider.skip)) {\n return providerRun('skip', scope, direction, ref);\n }\n\n //backward compat\n this.trigger('skip', scope, direction, ref);\n return this;\n },\n /**\n * Exit the test\n * @param {String|*} [why] - reason the test is exited\n * @fires runner#exit\n * @returns {runner} chains\n */\n exit(why) {\n if (_.isFunction(provider.exit)) {\n return providerRun('exit', why);\n }\n\n //backward compat\n this.trigger('exit', why);\n return this;\n },\n /**\n * Pause the current execution\n * @fires runner#pause\n * @returns {runner} chains\n */\n pause() {\n if (_.isFunction(provider.pause)) {\n if (!this.getState('pause')) {\n this.setState('pause', true);\n return providerRun('pause');\n }\n return Promise.resolve();\n }\n\n //backward compat\n if (!this.getState('pause')) {\n this.setState('pause', true).trigger('pause');\n }\n return this;\n },\n /**\n * Resume a paused test\n * @fires runner#pause\n * @returns {runner} chains\n */\n resume() {\n if (_.isFunction(provider.resume)) {\n if (this.getState('pause')) {\n this.setState('pause', false);\n return providerRun('resume');\n }\n return Promise.resolve();\n }\n\n //backward compat\n if (this.getState('pause') === true) {\n this.setState('pause', false).trigger('resume');\n }\n return this;\n },\n /**\n * Notify a test timeout\n * @param {String} scope - The scope where the timeout occurred\n * @param {String} ref - The reference to the place where the timeout occurred\n * @param {Object} [timer] - The timer's descriptor, if any\n * @fires runner#timeout\n * @returns {runner} chains\n */\n timeout(scope, ref, timer) {\n if (_.isFunction(provider.timeout)) {\n return providerRun('timeout', scope, ref, timer);\n }\n\n //backward compat\n this.trigger('timeout', scope, ref, timer);\n return this;\n }\n });\n runner.on('move', function () {\n this.trigger(...arguments);\n }).after('destroy', function destroyCleanUp() {\n if (dataHolder) {\n dataHolder.clear();\n }\n areaBroker = null;\n proxy = null;\n probeOverseer = null;\n testStore = null;\n });\n return runner;\n }\n\n //bind the provider registration capabilities to the testRunnerFactory\n var runner = providerRegistry(testRunnerFactory, function validateProvider(provider) {\n //mandatory methods\n if (!_.isFunction(provider.loadAreaBroker)) {\n throw new TypeError('The runner provider MUST have a method that returns an areaBroker');\n }\n return true;\n });\n\n return runner;\n\n});\n\n","define('taoTests/runner/proxy',['lodash', 'async', 'core/delegator', 'core/eventifier', 'core/providerRegistry', 'core/tokenHandler', 'core/connectivity'], function (_, async, delegator, eventifier, providerRegistry, tokenHandlerFactory, connectivity) { 'use strict';\n\n _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;\n async = async && Object.prototype.hasOwnProperty.call(async, 'default') ? async['default'] : async;\n delegator = delegator && Object.prototype.hasOwnProperty.call(delegator, 'default') ? delegator['default'] : delegator;\n eventifier = eventifier && Object.prototype.hasOwnProperty.call(eventifier, 'default') ? eventifier['default'] : eventifier;\n providerRegistry = providerRegistry && Object.prototype.hasOwnProperty.call(providerRegistry, 'default') ? providerRegistry['default'] : providerRegistry;\n tokenHandlerFactory = tokenHandlerFactory && Object.prototype.hasOwnProperty.call(tokenHandlerFactory, 'default') ? tokenHandlerFactory['default'] : tokenHandlerFactory;\n connectivity = connectivity && Object.prototype.hasOwnProperty.call(connectivity, 'default') ? connectivity['default'] : connectivity;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2016-2019 (original work) Open Assessment Technologies SA ;\n */\n var _defaults = {};\n var _slice = [].slice;\n\n /**\n * Defines a proxy bound to a particular adapter\n *\n * @param {String} proxyName - The name of the proxy adapter to use in the returned proxy instance\n * @param {Object} [config] - Some optional config depending of implementation,\n * this object will be forwarded to the proxy adapter\n * @returns {proxy} - The proxy instance, bound to the selected proxy adapter\n */\n function proxyFactory(proxyName, config) {\n var proxy, delegateProxy, communicator, communicatorPromise;\n var testDataHolder;\n var extraCallParams = {};\n var proxyAdapter = proxyFactory.getProvider(proxyName);\n var initConfig = _.defaults(config || {}, _defaults);\n var tokenHandler = tokenHandlerFactory();\n var middlewares = {};\n var initialized = false;\n var onlineStatus = connectivity.isOnline();\n\n /**\n * Gets parameters merged with extra parameters\n * @param {Object} [params]\n * @returns {Object}\n */\n function getParams(params) {\n var mergedParams = _.merge({}, params, extraCallParams);\n extraCallParams = {};\n return mergedParams;\n }\n\n /**\n * Gets the aggregated list of middlewares for a particular queue name\n * @param {String} queue - The name of the queue to get\n * @returns {Array}\n */\n function getMiddlewares(queue) {\n var list = middlewares[queue] || [];\n if (middlewares.all) {\n list = list.concat(middlewares.all);\n }\n return list;\n }\n\n /**\n * Applies the list of registered middlewares onto the received response\n * @param {Object} request - The request descriptor\n * @param {String} request.command - The name of the requested command\n * @param {Object} request.params - The map of provided parameters\n * @param {Object} response The response descriptor\n * @param {String} response.status The status of the response, can be either 'success' or 'error'\n * @param {Object} response.data The full response data\n * @returns {Promise}\n */\n function applyMiddlewares(request, response) {\n // wrap each middleware to provide parameters\n var list = _.map(getMiddlewares(request.command), function (middleware) {\n return function (next) {\n middleware(request, response, next);\n };\n });\n\n // apply each middleware in series, then resolve or reject the promise\n return new Promise(function (resolve, reject) {\n async.series(list, function (err) {\n // handle implicit error from response descriptor\n if (!err && 'error' === response.status) {\n err = response.data;\n }\n if (err) {\n reject(err);\n } else {\n proxy.trigger('receive', response.data, 'proxy');\n resolve(response.data);\n }\n });\n });\n }\n\n /**\n * Delegates the call to the proxy implementation and apply the middleware.\n *\n * @param {String} fnName - The name of the delegated method to call\n * @returns {Promise} - The delegated method must return a promise\n * @private\n * @throws Error\n */\n function delegate(fnName) {\n var request = {\n command: fnName,\n params: _slice.call(arguments, 1)\n };\n if (!initialized && !['install', 'init'].includes(fnName)) {\n return Promise.reject(new Error('Proxy is not properly initialized or has been destroyed!'));\n }\n return delegateProxy.apply(null, arguments).then(function (data) {\n // If the delegate call succeed the proxy is initialized.\n // Place this set here to avoid to wrap the init() into another promise.\n initialized = true;\n\n // handle successful request\n return applyMiddlewares(request, {\n status: 'success',\n data: data\n });\n }).catch(function (data) {\n // handle failed request\n return applyMiddlewares(request, {\n status: 'error',\n data: data\n });\n });\n }\n\n /**\n * Defines the test runner proxy\n * @typedef {proxy}\n */\n proxy = eventifier({\n /**\n * Add a middleware\n * @param {String} [command] The command queue in which add the middleware (default: 'all')\n * @param {Function...} callback - A middleware callback. Must accept 3 parameters: request, response, next.\n * @returns {proxy}\n */\n use: function use(command) {\n var queue = command && _.isString(command) ? command : 'all';\n var list = middlewares[queue] || [];\n middlewares[queue] = list;\n _.each(arguments, function (cb) {\n if (_.isFunction(cb)) {\n list.push(cb);\n }\n });\n return this;\n },\n /**\n * Install the proxy.\n * This step let's attach some features before the proxy reallys starts (before init).\n *\n * @param {Map} dataHolder - the test runner data holder\n * @returns {*}\n */\n install: function install(dataHolder) {\n if (dataHolder) {\n testDataHolder = dataHolder;\n }\n return delegate('install', initConfig);\n },\n /**\n * Initializes the proxy\n * @param {Object} [params] - An optional list of parameters\n * @returns {Promise} - Returns a promise. The proxy will be fully initialized on resolve.\n * Any error will be provided if rejected.\n * @fires init\n */\n init: function init(params) {\n /**\n * @event proxy#init\n * @param {Promise} promise\n * @param {Object} config\n * @param {Object} params\n */\n return delegate('init', initConfig, getParams(params));\n },\n /**\n * Uninstalls the proxy\n * @returns {Promise} - Returns a promise. The proxy will be fully uninstalled on resolve.\n * Any error will be provided if rejected.\n * @fires destroy\n */\n destroy: function destroy() {\n /**\n * @event proxy#destroy\n * @param {Promise} promise\n */\n return delegate('destroy').then(function () {\n // The proxy is now destroyed. A call to init() is mandatory to be able to use it again.\n initialized = false;\n\n // a communicator has been invoked and...\n if (communicatorPromise) {\n return new Promise(function (resolve, reject) {\n function destroyCommunicator() {\n communicator.destroy().then(resolve).catch(reject);\n }\n communicatorPromise\n // ... has been loaded successfully, then destroy it\n .then(function () {\n destroyCommunicator();\n })\n // ...has failed to be loaded, maybe no need to destroy it\n .catch(function () {\n if (communicator) {\n destroyCommunicator();\n } else {\n resolve();\n }\n });\n });\n }\n });\n },\n /**\n * Get the map that holds the test data\n * @returns {Map|Object} the dataHolder\n */\n getDataHolder: function getDataHolder() {\n return testDataHolder;\n },\n /**\n * Set the proxy as online\n * @returns {proxy} chains\n * @fires {proxy#reconnect}\n */\n setOnline: function setOnline() {\n if (this.isOffline()) {\n onlineStatus = true;\n this.trigger('reconnect');\n }\n return this;\n },\n /**\n * Set the proxy as offline\n * @param {String} [source] - source of the connectivity change\n * @returns {proxy} chains\n * @fires {proxy#disconnect}\n */\n setOffline: function setOffline(source) {\n if (this.isOnline()) {\n onlineStatus = false;\n this.trigger('disconnect', source);\n }\n return this;\n },\n /**\n * Are we online ?\n * @returns {Boolean}\n */\n isOnline: function isOnline() {\n return onlineStatus;\n },\n /**\n * Are we offline\n * @returns {Boolean}\n */\n isOffline: function isOffline() {\n return !onlineStatus;\n },\n /**\n * For the proxy a connection error is an error object with\n * source 'network', a 0 code and a false sent attribute.\n *\n * @param {Error|Object} err - the error to verify\n * @returns {Boolean} true if a connection error.\n */\n isConnectivityError: function isConnectivityError(err) {\n return _.isObject(err) && err.source === 'network' && err.code === 0 && err.sent === false;\n },\n /**\n * Gets the security token handler\n * @returns {tokenHandler}\n */\n getTokenHandler: function getTokenHandler() {\n return tokenHandler;\n },\n /**\n * Checks if a communication channel has been requested.\n * @returns {Boolean}\n */\n hasCommunicator: function hasCommunicator() {\n return !!communicatorPromise;\n },\n /**\n * Gets access to the communication channel, load it if not present\n * @returns {Promise} Returns a promise that will resolve the communication channel\n */\n getCommunicator: function getCommunicator() {\n var self = this;\n if (!initialized) {\n return Promise.reject(new Error('Proxy is not properly initialized or has been destroyed!'));\n }\n if (!communicatorPromise) {\n communicatorPromise = new Promise(function (resolve, reject) {\n if (_.isFunction(proxyAdapter.loadCommunicator)) {\n communicator = proxyAdapter.loadCommunicator.call(self);\n if (communicator) {\n communicator.before('error', function (e, err) {\n if (self.isConnectivityError(err)) {\n self.setOffline('communicator');\n }\n }).on('error', function (err) {\n self.trigger('error', err);\n }).on('receive', function (response) {\n self.setOnline();\n self.trigger('receive', response, 'communicator');\n }).init().then(function () {\n return communicator.open().then(function () {\n resolve(communicator);\n }).catch(reject);\n }).catch(reject);\n } else {\n reject(new Error('No communicator has been set up!'));\n }\n } else {\n reject(new Error('The proxy provider does not have a loadCommunicator method'));\n }\n });\n }\n return communicatorPromise;\n },\n /**\n * Registers a listener on a particular channel\n * @param {String} name - The name of the channel to listen\n * @param {Function} handler - The listener callback\n * @returns {proxy}\n * @throws TypeError if the name is missing or the handler is not a callback\n */\n channel: function channel(name, handler) {\n if (!_.isString(name) || name.length <= 0) {\n throw new TypeError('A channel must have a name');\n }\n if (!_.isFunction(handler)) {\n throw new TypeError('A handler must be attached to a channel');\n }\n this.getCommunicator().then(function (communicatorInstance) {\n communicatorInstance.channel(name, handler);\n })\n // just an empty catch to avoid any error to be displayed in the console when the communicator is not enabled\n .catch(_.noop);\n this.on(`channel-${name}`, handler);\n return this;\n },\n /**\n * Sends an messages through the communication implementation.\n * @param {String} channel - The name of the communication channel to use\n * @param {Object} message - The message to send\n * @returns {Promise} The delegated provider's method must return a promise\n */\n send: function send(channel, message) {\n return this.getCommunicator().then(function (communicatorInstance) {\n return communicatorInstance.send(channel, message);\n });\n },\n /**\n * Add extra parameters that will be added to the init or the next callTestAction or callItemAction\n * This enables plugins to place parameters for next calls\n * @param {Object} params - the extra parameters\n * @returns {proxy}\n */\n addCallActionParams: function addCallActionParams(params) {\n if (_.isPlainObject(params)) {\n _.merge(extraCallParams, params);\n }\n return this;\n },\n /**\n * Gets the test definition data\n * @deprecated\n *\n * @returns {Promise} - Returns a promise. The test definition data will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires getTestData\n */\n getTestData: function getTestData() {\n /**\n * @event proxy#getTestData\n * @param {Promise} promise\n */\n return delegate('getTestData');\n },\n /**\n * Gets the test context\n * @returns {Promise} - Returns a promise. The context object will be provided on resolve.\n * Any error will be provided if rejected.\n */\n getTestContext: function getTestContext() {\n /**\n * @event proxy#getTestContext\n * @param {Promise} promise\n */\n return delegate('getTestContext');\n },\n /**\n * Gets the test map\n * @returns {Promise} - Returns a promise. The test map object will be provided on resolve.\n * Any error will be provided if rejected.\n */\n getTestMap: function getTestMap() {\n /**\n * @event proxy#getTestMap\n * @param {Promise} promise\n */\n return delegate('getTestMap');\n },\n /**\n * Sends the test variables\n * @param {Object} variables\n * @param {Boolean} deferred whether action can be scheduled (put into queue) to be sent in a bunch of actions later (default: false).\n * @returns {Promise} - Returns a promise. The result of the request will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires sendVariables\n */\n sendVariables: function sendVariables(variables, deferred) {\n /**\n * @event proxy#sendVariables\n * @param {Promise} promise\n */\n return delegate('sendVariables', variables, deferred);\n },\n /**\n * Calls an action related to the test\n * @param {String} action - The name of the action to call\n * @param {Object} [params] - Some optional parameters to join to the call\n * @param {Boolean} deferred whether action can be scheduled (put into queue) to be sent in a bunch of actions later.\n * @returns {Promise} - Returns a promise. The result of the request will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires callTestAction\n */\n callTestAction: function callTestAction(action, params, deferred) {\n /**\n * @event proxy#callTestAction\n * @param {Promise} promise\n * @param {String} action\n * @param {Object} params\n */\n return delegate('callTestAction', action, getParams(params), deferred);\n },\n /**\n * Gets an item definition by its URI, also gets its current state\n * @param {String} uri - The URI of the item to get\n * @param {Object} [params] - addtional params to be appended\n * @returns {Promise} - Returns a promise. The item data will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires getItem\n */\n getItem: function getItem(uri, params) {\n /**\n * @event proxy#getItem\n * @param {Promise} promise\n * @param {String} uri\n */\n return delegate('getItem', uri, params);\n },\n /**\n * Submits the state and the response of a particular item\n * @param {String} uri - The URI of the item to update\n * @param {Object} state - The state to submit\n * @param {Object} response - The response object to submit\n * @param {Object} [params] - addtional params to be appended\n * @returns {Promise} - Returns a promise. The result of the request will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires submitItem\n */\n submitItem: function submitItem(uri, state, response, params) {\n /**\n * @event proxy#submitItem\n * @param {Promise} promise\n * @param {String} uri\n * @param {Object} state\n * @param {Object} response\n */\n return delegate('submitItem', uri, state, response, getParams(params));\n },\n /**\n * Calls an action related to a particular item\n * @param {String} uri - The URI of the item for which call the action\n * @param {String} action - The name of the action to call\n * @param {Object} [params] - Some optional parameters to join to the call\n * @param {Boolean} deferred whether action can be scheduled (put into queue) to be sent in a bunch of actions later.\n * @returns {Promise} - Returns a promise. The result of the request will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires callItemAction\n */\n callItemAction: function callItemAction(uri, action, params, deferred) {\n /**\n * @event proxy#callItemAction\n * @param {Promise} promise\n * @param {String} uri\n * @param {String} action\n * @param {Object} params\n */\n return delegate('callItemAction', uri, action, getParams(params), deferred);\n },\n /**\n * Sends a telemetry signal\n * @param {String} uri - The URI of the item for which sends the telemetry signal\n * @param {String} signal - The name of the signal to send\n * @param {Object} [params] - Some optional parameters to join to the signal\n * @returns {Promise} - Returns a promise. The result of the request will be provided on resolve.\n * Any error will be provided if rejected.\n * @fires telemetry\n */\n telemetry: function telemetry(uri, signal, params) {\n /**\n * @event proxy#telemetry\n * @param {Promise} promise\n * @param {String} uri\n * @param {String} signal\n * @param {Object} params\n */\n return delegate('telemetry', uri, signal, params);\n }\n });\n\n //listen for connectivty changes\n connectivity.on('offline', function () {\n proxy.setOffline('device');\n }).on('online', function () {\n proxy.setOnline();\n });\n\n // catch platform messages that come outside of the communicator component, then each is dispatched to the right channel\n proxy.on('message', function (channel, message) {\n this.trigger(`channel-${channel}`, message);\n }).use(function (request, response, next) {\n if (response.data && response.data.messages) {\n // receive server messages\n _.forEach(response.data.messages, function (msg) {\n if (msg.channel) {\n proxy.trigger('message', msg.channel, msg.message);\n } else {\n proxy.trigger('message', 'malformed', msg);\n }\n });\n }\n next();\n })\n //detect failing request and change the online status\n .use(function (request, response, next) {\n if (proxy.isConnectivityError(response.data)) {\n proxy.setOffline('request');\n } else if (response.data && response.data.sent === true) {\n proxy.setOnline();\n }\n next();\n });\n delegateProxy = delegator(proxy, proxyAdapter, {\n name: 'proxy',\n wrapper: function pluginWrapper(response) {\n return Promise.resolve(response);\n }\n });\n return proxy;\n }\n var proxy = providerRegistry(proxyFactory);\n\n return proxy;\n\n});\n\n","define('taoTests/runner/providerLoader',['core/logger', 'core/providerLoader', 'core/pluginLoader', 'core/communicator', 'taoTests/runner/runner', 'taoTests/runner/proxy', 'taoItems/runner/api/itemRunner'], function (loggerFactory, providerLoader, pluginLoader, communicator, runner, proxy, itemRunner) { 'use strict';\n\n loggerFactory = loggerFactory && Object.prototype.hasOwnProperty.call(loggerFactory, 'default') ? loggerFactory['default'] : loggerFactory;\n providerLoader = providerLoader && Object.prototype.hasOwnProperty.call(providerLoader, 'default') ? providerLoader['default'] : providerLoader;\n pluginLoader = pluginLoader && Object.prototype.hasOwnProperty.call(pluginLoader, 'default') ? pluginLoader['default'] : pluginLoader;\n communicator = communicator && Object.prototype.hasOwnProperty.call(communicator, 'default') ? communicator['default'] : communicator;\n runner = runner && Object.prototype.hasOwnProperty.call(runner, 'default') ? runner['default'] : runner;\n proxy = proxy && Object.prototype.hasOwnProperty.call(proxy, 'default') ? proxy['default'] : proxy;\n itemRunner = itemRunner && Object.prototype.hasOwnProperty.call(itemRunner, 'default') ? itemRunner['default'] : itemRunner;\n\n /**\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU General Public License\n * as published by the Free Software Foundation; under version 2\n * of the License (non-upgradable).\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n *\n * Copyright (c) 2019 Open Assessment Technologies SA ;\n */\n const logger = loggerFactory('taoTests/runner/loader');\n\n /**\n * @typedef {Object} provider - A provider is an object exposing a list of methods with respect to the API managed by the target.\n * @property {String} name - The name of the provider. It should be unique among all.\n * @property {Function} init - Each provider much expose at least a method `init()`\n * @property {Function} ... - Any other method the target is expecting\n */\n\n /**\n * Load the providers that match the registration\n * @param {Object} providers\n * @param {provider|provider[]} providers.runner\n * @param {provider|provider[]} [providers.proxy]\n * @param {provider|provider[]} [providers.communicator]\n * @param {provider|provider[]} [providers.plugins]\n * @param {Boolean} loadFromBundle - does the loader load the modules from the sources (dev mode) or the bundles\n * @returns {Promise