diff --git a/.eslintrc b/.eslintrc
index 5f391db569f..60868ebf751 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -20,6 +20,8 @@
'M': true,
'Y': true
},
+ // Warn about unused eslint-disable statements.
+ 'reportUnusedDisableDirectives': true,
'rules': {
// See http://eslint.org/docs/rules/ for all rules and explanations of all
// rules.
diff --git a/admin/tool/lp/amd/build/competency_rule.min.js.map b/admin/tool/lp/amd/build/competency_rule.min.js.map
index 863fade449a..728f4bc2040 100644
--- a/admin/tool/lp/amd/build/competency_rule.min.js.map
+++ b/admin/tool/lp/amd/build/competency_rule.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_rule.min.js","sources":["../src/competency_rule.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see .\n\n/**\n * Competency rule base module.\n *\n * @module tool_lp/competency_rule\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * Competency rule abstract class.\n *\n * Any competency rule should extend this object. The event 'change' should be\n * triggered on the instance when the configuration has changed. This will allow\n * the components using the rule to gather the config, or check its validity.\n *\n * this._triggerChange();\n *\n * @param {Tree} tree The competency tree.\n */\n var Rule = function(tree) {\n this._eventNode = $('
');\n this._ready = $.Deferred();\n this._tree = tree;\n };\n\n /** @property {Object} The current competency. */\n Rule.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n Rule.prototype._eventNode = null;\n /** @property {Promise} Resolved when the object is ready. */\n Rule.prototype._ready = null;\n /** @property {Tree} The competency tree. */\n Rule.prototype._tree = null;\n\n /**\n * Whether or not the current competency can be configured using this rule.\n *\n * @return {Boolean}\n * @method canConfig\n */\n Rule.prototype.canConfig = function() {\n return this._tree.hasChildren(this._competency.id);\n };\n\n /**\n * The config established by this rule.\n *\n * To override in subclasses when relevant.\n *\n * @return {String|null}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return null;\n };\n\n // eslint-disable-line valid-jsdoc\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n // eslint-enable-line valid-jsdoc\n Rule.prototype.getType = function() {\n throw new Error('Not implemented');\n };\n\n /**\n * The init process.\n *\n * Do not override this, instead override _load.\n *\n * @return {Promise} Revoled when the plugin is initialised.\n * @method init\n */\n Rule.prototype.init = function() {\n return this._load();\n };\n\n /**\n * Callback to inject the template.\n *\n * @returns {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function() {\n return $.Deferred().reject().promise();\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * Plugins should override this.\n *\n * @return {Boolean}\n * @method _isValid\n */\n Rule.prototype.isValid = function() {\n return false;\n };\n\n /**\n * Load the class.\n *\n * @return {Promise}\n * @method _load\n * @protected\n */\n Rule.prototype._load = function() {\n return $.when();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Rule.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Sets the current competency.\n *\n * @param {Competency} competency\n * @method setTargetCompetency\n */\n Rule.prototype.setTargetCompetency = function(competency) {\n this._competency = competency;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n Rule.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n /**\n * Trigger the change event.\n *\n * @method _triggerChange\n * @protected\n */\n Rule.prototype._triggerChange = function() {\n this._trigger('change', this);\n };\n\n return /** @alias module:tool_lp/competency_rule */ Rule;\n\n});\n"],"names":["define","$","Rule","tree","_eventNode","_ready","Deferred","_tree","prototype","_competency","canConfig","this","hasChildren","id","getConfig","getType","Error","init","_load","injectTemplate","reject","promise","isValid","when","on","type","handler","setTargetCompetency","competency","_trigger","data","trigger","_triggerChange"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,WAAW,SAASC,OAapBC,KAAO,SAASC,WACXC,WAAaH,EAAE,cACfI,OAASJ,EAAEK,gBACXC,MAAQJ,aAIjBD,KAAKM,UAAUC,YAAc,KAE7BP,KAAKM,UAAUJ,WAAa,KAE5BF,KAAKM,UAAUH,OAAS,KAExBH,KAAKM,UAAUD,MAAQ,KAQvBL,KAAKM,UAAUE,UAAY,kBAChBC,KAAKJ,MAAMK,YAAYD,KAAKF,YAAYI,KAWnDX,KAAKM,UAAUM,UAAY,kBAChB,MAWXZ,KAAKM,UAAUO,QAAU,iBACf,IAAIC,MAAM,oBAWpBd,KAAKM,UAAUS,KAAO,kBACXN,KAAKO,SAShBhB,KAAKM,UAAUW,eAAiB,kBACrBlB,EAAEK,WAAWc,SAASC,WAWjCnB,KAAKM,UAAUc,QAAU,kBACd,GAUXpB,KAAKM,UAAUU,MAAQ,kBACZjB,EAAEsB,QAUbrB,KAAKM,UAAUgB,GAAK,SAASC,KAAMC,cAC1BtB,WAAWoB,GAAGC,KAAMC,UAS7BxB,KAAKM,UAAUmB,oBAAsB,SAASC,iBACrCnB,YAAcmB,YAWvB1B,KAAKM,UAAUqB,SAAW,SAASJ,KAAMK,WAChC1B,WAAW2B,QAAQN,KAAM,CAACK,QASnC5B,KAAKM,UAAUwB,eAAiB,gBACvBH,SAAS,SAAUlB,OAGwBT"}
\ No newline at end of file
+{"version":3,"file":"competency_rule.min.js","sources":["../src/competency_rule.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see
.\n\n/**\n * Competency rule base module.\n *\n * @module tool_lp/competency_rule\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * Competency rule abstract class.\n *\n * Any competency rule should extend this object. The event 'change' should be\n * triggered on the instance when the configuration has changed. This will allow\n * the components using the rule to gather the config, or check its validity.\n *\n * this._triggerChange();\n *\n * @param {Tree} tree The competency tree.\n */\n var Rule = function(tree) {\n this._eventNode = $('
');\n this._ready = $.Deferred();\n this._tree = tree;\n };\n\n /** @property {Object} The current competency. */\n Rule.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n Rule.prototype._eventNode = null;\n /** @property {Promise} Resolved when the object is ready. */\n Rule.prototype._ready = null;\n /** @property {Tree} The competency tree. */\n Rule.prototype._tree = null;\n\n /**\n * Whether or not the current competency can be configured using this rule.\n *\n * @return {Boolean}\n * @method canConfig\n */\n Rule.prototype.canConfig = function() {\n return this._tree.hasChildren(this._competency.id);\n };\n\n /**\n * The config established by this rule.\n *\n * To override in subclasses when relevant.\n *\n * @return {String|null}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return null;\n };\n\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n Rule.prototype.getType = function() {\n throw new Error('Not implemented');\n };\n\n /**\n * The init process.\n *\n * Do not override this, instead override _load.\n *\n * @return {Promise} Revoled when the plugin is initialised.\n * @method init\n */\n Rule.prototype.init = function() {\n return this._load();\n };\n\n /**\n * Callback to inject the template.\n *\n * @returns {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function() {\n return $.Deferred().reject().promise();\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * Plugins should override this.\n *\n * @return {Boolean}\n * @method _isValid\n */\n Rule.prototype.isValid = function() {\n return false;\n };\n\n /**\n * Load the class.\n *\n * @return {Promise}\n * @method _load\n * @protected\n */\n Rule.prototype._load = function() {\n return $.when();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Rule.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Sets the current competency.\n *\n * @param {Competency} competency\n * @method setTargetCompetency\n */\n Rule.prototype.setTargetCompetency = function(competency) {\n this._competency = competency;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n Rule.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n /**\n * Trigger the change event.\n *\n * @method _triggerChange\n * @protected\n */\n Rule.prototype._triggerChange = function() {\n this._trigger('change', this);\n };\n\n return /** @alias module:tool_lp/competency_rule */ Rule;\n\n});\n"],"names":["define","$","Rule","tree","_eventNode","_ready","Deferred","_tree","prototype","_competency","canConfig","this","hasChildren","id","getConfig","getType","Error","init","_load","injectTemplate","reject","promise","isValid","when","on","type","handler","setTargetCompetency","competency","_trigger","data","trigger","_triggerChange"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,WAAW,SAASC,OAapBC,KAAO,SAASC,WACXC,WAAaH,EAAE,cACfI,OAASJ,EAAEK,gBACXC,MAAQJ,aAIjBD,KAAKM,UAAUC,YAAc,KAE7BP,KAAKM,UAAUJ,WAAa,KAE5BF,KAAKM,UAAUH,OAAS,KAExBH,KAAKM,UAAUD,MAAQ,KAQvBL,KAAKM,UAAUE,UAAY,kBAChBC,KAAKJ,MAAMK,YAAYD,KAAKF,YAAYI,KAWnDX,KAAKM,UAAUM,UAAY,kBAChB,MASXZ,KAAKM,UAAUO,QAAU,iBACf,IAAIC,MAAM,oBAWpBd,KAAKM,UAAUS,KAAO,kBACXN,KAAKO,SAShBhB,KAAKM,UAAUW,eAAiB,kBACrBlB,EAAEK,WAAWc,SAASC,WAWjCnB,KAAKM,UAAUc,QAAU,kBACd,GAUXpB,KAAKM,UAAUU,MAAQ,kBACZjB,EAAEsB,QAUbrB,KAAKM,UAAUgB,GAAK,SAASC,KAAMC,cAC1BtB,WAAWoB,GAAGC,KAAMC,UAS7BxB,KAAKM,UAAUmB,oBAAsB,SAASC,iBACrCnB,YAAcmB,YAWvB1B,KAAKM,UAAUqB,SAAW,SAASJ,KAAMK,WAChC1B,WAAW2B,QAAQN,KAAM,CAACK,QASnC5B,KAAKM,UAAUwB,eAAiB,gBACvBH,SAAS,SAAUlB,OAGwBT"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/src/competency_rule.js b/admin/tool/lp/amd/src/competency_rule.js
index 0784351a1e8..217ca35583d 100644
--- a/admin/tool/lp/amd/src/competency_rule.js
+++ b/admin/tool/lp/amd/src/competency_rule.js
@@ -71,14 +71,12 @@ define(['jquery'], function($) {
return null;
};
- // eslint-disable-line valid-jsdoc
/**
* Return the type of the module.
*
* @return {String}
* @method getType
*/
- // eslint-enable-line valid-jsdoc
Rule.prototype.getType = function() {
throw new Error('Not implemented');
};
diff --git a/admin/tool/moodlenet/amd/build/instance_form.min.js.map b/admin/tool/moodlenet/amd/build/instance_form.min.js.map
index 4d3ccc56ff7..821603c3ba9 100644
--- a/admin/tool/moodlenet/amd/build/instance_form.min.js.map
+++ b/admin/tool/moodlenet/amd/build/instance_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"instance_form.min.js","sources":["../src/instance_form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see
.\n\n/**\n * Our basic form manager for when a user either enters\n * their profile url or just wants to browse.\n *\n * This file is a mishmash of JS functions we need for both the standalone (M3.7, M3.8)\n * plugin & Moodle 3.9 functions. The 3.9 Functions have a base understanding that certain\n * things exist i.e. directory structures for templates. When this feature goes 3.9+ only\n * The goal is that we can quickly gut all AMD modules into bare JS files and use ES6 guidelines.\n * Till then this will have to do.\n *\n * @module tool_moodlenet/instance_form\n * @copyright 2020 Mathew May
\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['tool_moodlenet/validator',\n 'tool_moodlenet/selectors',\n 'core/loadingicon',\n 'core/templates',\n 'core/notification',\n 'jquery'],\n function(Validator,\n Selectors,\n LoadingIcon,\n Templates,\n Notification,\n $) {\n\n /**\n * Add the event listeners to our form.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} page The whole page element for our form area\n */\n var registerListenerEvents = function registerListenerEvents(page) {\n page.addEventListener('click', function(e) {\n\n // Our fake submit button / browse button.\n if (e.target.matches(Selectors.action.submit)) {\n var input = page.querySelector('[data-var=\"mnet-link\"]');\n var overlay = page.querySelector(Selectors.region.spinner);\n var validationArea = document.querySelector(Selectors.region.validationArea);\n\n overlay.classList.remove('d-none');\n var spinner = LoadingIcon.addIconToContainerWithPromise(overlay);\n Validator.validation(input)\n .then(function(result) {\n spinner.resolve();\n overlay.classList.add('d-none');\n if (result.result) {\n input.classList.remove('is-invalid'); // Just in case the class has been applied already.\n input.classList.add('is-valid');\n validationArea.innerText = result.message;\n validationArea.classList.remove('text-danger');\n validationArea.classList.add('text-success');\n // Give the user some time to see their input is valid.\n setTimeout(function() {\n window.location = result.domain;\n }, 1000);\n } else {\n input.classList.add('is-invalid');\n validationArea.innerText = result.message;\n validationArea.classList.add('text-danger');\n }\n return;\n }).catch();\n }\n });\n };\n\n /**\n * Given a user wishes to see the MoodleNet profile url form transition them there.\n *\n * @method chooserNavigateToMnet\n * @param {HTMLElement} showMoodleNet The chooser's area for ment\n * @param {Object} footerData Our footer object to render out\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n */\n var chooserNavigateToMnet = function(showMoodleNet, footerData, carousel, modal) {\n showMoodleNet.innerHTML = '';\n\n // Add a spinner.\n var spinnerPromise = LoadingIcon.addIconToContainer(showMoodleNet);\n\n // Used later...\n var transitionPromiseResolver = null;\n var transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n $.when(\n spinnerPromise,\n transitionPromise\n ).then(function() {\n Templates.replaceNodeContents(showMoodleNet, footerData.customcarouseltemplate, '');\n return;\n }).catch(Notification.exception);\n\n // We apply our handlers in here to minimise plugin dependency in the Chooser.\n registerListenerEvents(showMoodleNet);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', function() {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel(2);\n // eslint-disable-next-line max-len\n modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {}));\n };\n\n /**\n * Given a user no longer wishes to see the MoodleNet profile url form transition them from there.\n *\n * @method chooserNavigateFromMnet\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n * @param {Object} footerData Our footer object to render out\n */\n var chooserNavigateFromMnet = function(carousel, modal, footerData) {\n // Trigger the transition between 'pages'.\n carousel.carousel(0);\n modal.setFooter(footerData.customfootertemplate);\n };\n\n /**\n * Create the custom listener that would handle anything in the footer.\n *\n * @param {Event} e The event being triggered.\n * @param {Object} footerData The data generated from the exporter.\n * @param {Object} modal The chooser modal.\n */\n var footerClickListener = function(e, footerData, modal) {\n if (e.target.matches(Selectors.action.showMoodleNet) || e.target.closest(Selectors.action.showMoodleNet)) {\n e.preventDefault();\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n const showMoodleNet = carousel.find(Selectors.region.moodleNet)[0];\n\n chooserNavigateToMnet(showMoodleNet, footerData, carousel, modal);\n }\n // From the help screen go back to the module overview.\n if (e.target.matches(Selectors.action.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n\n chooserNavigateFromMnet(carousel, modal, footerData);\n }\n };\n\n return {\n footerClickListener: footerClickListener\n };\n});\n"],"names":["define","Validator","Selectors","LoadingIcon","Templates","Notification","$","chooserNavigateToMnet","showMoodleNet","footerData","carousel","modal","innerHTML","page","spinnerPromise","addIconToContainer","transitionPromiseResolver","transitionPromise","Promise","resolve","when","then","replaceNodeContents","customcarouseltemplate","catch","exception","addEventListener","e","target","matches","action","submit","input","querySelector","overlay","region","spinner","validationArea","document","classList","remove","addIconToContainerWithPromise","validation","result","add","innerText","message","setTimeout","window","location","domain","one","setFooter","render","footerClickListener","closest","preventDefault","getBody","find","moodleNet","closeOption","customfootertemplate","chooserNavigateFromMnet"],"mappings":";;;;;;;;;;;;;;AA8BAA,sCAAO,CAAC,2BACA,2BACA,mBACA,iBACA,oBACA,WACJ,SAASC,UACAC,UACAC,YACAC,UACAC,aACAC,OAqDLC,sBAAwB,SAASC,cAAeC,WAAYC,SAAUC,OACtEH,cAAcI,UAAY,OA9C+BC,KAiDrDC,eAAiBX,YAAYY,mBAAmBP,eAGhDQ,0BAA4B,KAC5BC,kBAAoB,IAAIC,SAAQC,UAChCH,0BAA4BG,WAGhCb,EAAEc,KACEN,eACAG,mBACFI,MAAK,WACCjB,UAAUkB,oBAAoBd,cAAeC,WAAWc,uBAAwB,OAErFC,MAAMnB,aAAaoB,YA/DmCZ,KAkElCL,eAjElBkB,iBAAiB,SAAS,SAASC,MAGhCA,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOC,QAAS,KACvCC,MAAQnB,KAAKoB,cAAc,0BAC3BC,QAAUrB,KAAKoB,cAAc/B,UAAUiC,OAAOC,SAC9CC,eAAiBC,SAASL,cAAc/B,UAAUiC,OAAOE,gBAE7DH,QAAQK,UAAUC,OAAO,cACrBJ,QAAUjC,YAAYsC,8BAA8BP,SACxDjC,UAAUyC,WAAWV,OAChBX,MAAK,SAASsB,QACXP,QAAQjB,UACRe,QAAQK,UAAUK,IAAI,UAClBD,OAAOA,QACPX,MAAMO,UAAUC,OAAO,cACvBR,MAAMO,UAAUK,IAAI,YACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUC,OAAO,eAChCH,eAAeE,UAAUK,IAAI,gBAE7BG,YAAW,WACPC,OAAOC,SAAWN,OAAOO,SAC1B,OAEHlB,MAAMO,UAAUK,IAAI,cACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUK,IAAI,mBAGtCpB,YAsCXd,SAASyC,IAAI,oBAAoB,WAC7BnC,+BAGJN,SAASA,SAAS,GAElBC,MAAMyC,UAAUhD,UAAUiD,OAAO,2CAA4C,YAwC1E,CACHC,oBAjBsB,SAAS3B,EAAGlB,WAAYE,UAC1CgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOtB,gBAAkBmB,EAAEC,OAAO2B,QAAQrD,UAAU4B,OAAOtB,eAAgB,CACtGmB,EAAE6B,uBACI9C,SAAWJ,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAC/DF,cAAgBE,SAASgD,KAAKxD,UAAUiC,OAAOwB,WAAW,GAEhEpD,sBAAsBC,cAAeC,WAAYC,SAAUC,UAG3DgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAO8B,aAAc,EAtB1B,SAASlD,SAAUC,MAAOF,YAEpDC,SAASA,SAAS,GAClBC,MAAMyC,UAAU3C,WAAWoD,sBAsBvBC,CAFiBxD,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAEnCC,MAAOF"}
\ No newline at end of file
+{"version":3,"file":"instance_form.min.js","sources":["../src/instance_form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see .\n\n/**\n * Our basic form manager for when a user either enters\n * their profile url or just wants to browse.\n *\n * This file is a mishmash of JS functions we need for both the standalone (M3.7, M3.8)\n * plugin & Moodle 3.9 functions. The 3.9 Functions have a base understanding that certain\n * things exist i.e. directory structures for templates. When this feature goes 3.9+ only\n * The goal is that we can quickly gut all AMD modules into bare JS files and use ES6 guidelines.\n * Till then this will have to do.\n *\n * @module tool_moodlenet/instance_form\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['tool_moodlenet/validator',\n 'tool_moodlenet/selectors',\n 'core/loadingicon',\n 'core/templates',\n 'core/notification',\n 'jquery'],\n function(Validator,\n Selectors,\n LoadingIcon,\n Templates,\n Notification,\n $) {\n\n /**\n * Add the event listeners to our form.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} page The whole page element for our form area\n */\n var registerListenerEvents = function registerListenerEvents(page) {\n page.addEventListener('click', function(e) {\n\n // Our fake submit button / browse button.\n if (e.target.matches(Selectors.action.submit)) {\n var input = page.querySelector('[data-var=\"mnet-link\"]');\n var overlay = page.querySelector(Selectors.region.spinner);\n var validationArea = document.querySelector(Selectors.region.validationArea);\n\n overlay.classList.remove('d-none');\n var spinner = LoadingIcon.addIconToContainerWithPromise(overlay);\n Validator.validation(input)\n .then(function(result) {\n spinner.resolve();\n overlay.classList.add('d-none');\n if (result.result) {\n input.classList.remove('is-invalid'); // Just in case the class has been applied already.\n input.classList.add('is-valid');\n validationArea.innerText = result.message;\n validationArea.classList.remove('text-danger');\n validationArea.classList.add('text-success');\n // Give the user some time to see their input is valid.\n setTimeout(function() {\n window.location = result.domain;\n }, 1000);\n } else {\n input.classList.add('is-invalid');\n validationArea.innerText = result.message;\n validationArea.classList.add('text-danger');\n }\n return;\n }).catch();\n }\n });\n };\n\n /**\n * Given a user wishes to see the MoodleNet profile url form transition them there.\n *\n * @method chooserNavigateToMnet\n * @param {HTMLElement} showMoodleNet The chooser's area for ment\n * @param {Object} footerData Our footer object to render out\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n */\n var chooserNavigateToMnet = function(showMoodleNet, footerData, carousel, modal) {\n showMoodleNet.innerHTML = '';\n\n // Add a spinner.\n var spinnerPromise = LoadingIcon.addIconToContainer(showMoodleNet);\n\n // Used later...\n var transitionPromiseResolver = null;\n var transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n $.when(\n spinnerPromise,\n transitionPromise\n ).then(function() {\n Templates.replaceNodeContents(showMoodleNet, footerData.customcarouseltemplate, '');\n return;\n }).catch(Notification.exception);\n\n // We apply our handlers in here to minimise plugin dependency in the Chooser.\n registerListenerEvents(showMoodleNet);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', function() {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel(2);\n modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {}));\n };\n\n /**\n * Given a user no longer wishes to see the MoodleNet profile url form transition them from there.\n *\n * @method chooserNavigateFromMnet\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n * @param {Object} footerData Our footer object to render out\n */\n var chooserNavigateFromMnet = function(carousel, modal, footerData) {\n // Trigger the transition between 'pages'.\n carousel.carousel(0);\n modal.setFooter(footerData.customfootertemplate);\n };\n\n /**\n * Create the custom listener that would handle anything in the footer.\n *\n * @param {Event} e The event being triggered.\n * @param {Object} footerData The data generated from the exporter.\n * @param {Object} modal The chooser modal.\n */\n var footerClickListener = function(e, footerData, modal) {\n if (e.target.matches(Selectors.action.showMoodleNet) || e.target.closest(Selectors.action.showMoodleNet)) {\n e.preventDefault();\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n const showMoodleNet = carousel.find(Selectors.region.moodleNet)[0];\n\n chooserNavigateToMnet(showMoodleNet, footerData, carousel, modal);\n }\n // From the help screen go back to the module overview.\n if (e.target.matches(Selectors.action.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n\n chooserNavigateFromMnet(carousel, modal, footerData);\n }\n };\n\n return {\n footerClickListener: footerClickListener\n };\n});\n"],"names":["define","Validator","Selectors","LoadingIcon","Templates","Notification","$","chooserNavigateToMnet","showMoodleNet","footerData","carousel","modal","innerHTML","page","spinnerPromise","addIconToContainer","transitionPromiseResolver","transitionPromise","Promise","resolve","when","then","replaceNodeContents","customcarouseltemplate","catch","exception","addEventListener","e","target","matches","action","submit","input","querySelector","overlay","region","spinner","validationArea","document","classList","remove","addIconToContainerWithPromise","validation","result","add","innerText","message","setTimeout","window","location","domain","one","setFooter","render","footerClickListener","closest","preventDefault","getBody","find","moodleNet","closeOption","customfootertemplate","chooserNavigateFromMnet"],"mappings":";;;;;;;;;;;;;;AA8BAA,sCAAO,CAAC,2BACA,2BACA,mBACA,iBACA,oBACA,WACJ,SAASC,UACAC,UACAC,YACAC,UACAC,aACAC,OAqDLC,sBAAwB,SAASC,cAAeC,WAAYC,SAAUC,OACtEH,cAAcI,UAAY,OA9C+BC,KAiDrDC,eAAiBX,YAAYY,mBAAmBP,eAGhDQ,0BAA4B,KAC5BC,kBAAoB,IAAIC,SAAQC,UAChCH,0BAA4BG,WAGhCb,EAAEc,KACEN,eACAG,mBACFI,MAAK,WACCjB,UAAUkB,oBAAoBd,cAAeC,WAAWc,uBAAwB,OAErFC,MAAMnB,aAAaoB,YA/DmCZ,KAkElCL,eAjElBkB,iBAAiB,SAAS,SAASC,MAGhCA,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOC,QAAS,KACvCC,MAAQnB,KAAKoB,cAAc,0BAC3BC,QAAUrB,KAAKoB,cAAc/B,UAAUiC,OAAOC,SAC9CC,eAAiBC,SAASL,cAAc/B,UAAUiC,OAAOE,gBAE7DH,QAAQK,UAAUC,OAAO,cACrBJ,QAAUjC,YAAYsC,8BAA8BP,SACxDjC,UAAUyC,WAAWV,OAChBX,MAAK,SAASsB,QACXP,QAAQjB,UACRe,QAAQK,UAAUK,IAAI,UAClBD,OAAOA,QACPX,MAAMO,UAAUC,OAAO,cACvBR,MAAMO,UAAUK,IAAI,YACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUC,OAAO,eAChCH,eAAeE,UAAUK,IAAI,gBAE7BG,YAAW,WACPC,OAAOC,SAAWN,OAAOO,SAC1B,OAEHlB,MAAMO,UAAUK,IAAI,cACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUK,IAAI,mBAGtCpB,YAsCXd,SAASyC,IAAI,oBAAoB,WAC7BnC,+BAGJN,SAASA,SAAS,GAClBC,MAAMyC,UAAUhD,UAAUiD,OAAO,2CAA4C,YAwC1E,CACHC,oBAjBsB,SAAS3B,EAAGlB,WAAYE,UAC1CgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOtB,gBAAkBmB,EAAEC,OAAO2B,QAAQrD,UAAU4B,OAAOtB,eAAgB,CACtGmB,EAAE6B,uBACI9C,SAAWJ,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAC/DF,cAAgBE,SAASgD,KAAKxD,UAAUiC,OAAOwB,WAAW,GAEhEpD,sBAAsBC,cAAeC,WAAYC,SAAUC,UAG3DgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAO8B,aAAc,EAtB1B,SAASlD,SAAUC,MAAOF,YAEpDC,SAASA,SAAS,GAClBC,MAAMyC,UAAU3C,WAAWoD,sBAsBvBC,CAFiBxD,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAEnCC,MAAOF"}
\ No newline at end of file
diff --git a/admin/tool/moodlenet/amd/src/instance_form.js b/admin/tool/moodlenet/amd/src/instance_form.js
index 59ab4e921c7..57b4cd50159 100644
--- a/admin/tool/moodlenet/amd/src/instance_form.js
+++ b/admin/tool/moodlenet/amd/src/instance_form.js
@@ -121,7 +121,6 @@ define(['tool_moodlenet/validator',
});
// Trigger the transition between 'pages'.
carousel.carousel(2);
- // eslint-disable-next-line max-len
modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {}));
};
diff --git a/lib/amd/build/local/aria/focuslock.min.js.map b/lib/amd/build/local/aria/focuslock.min.js.map
index f43298a4bae..4a579912cdb 100644
--- a/lib/amd/build/local/aria/focuslock.min.js.map
+++ b/lib/amd/build/local/aria/focuslock.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"focuslock.min.js","sources":["../../../src/local/aria/focuslock.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see .\n\n/**\n * Tab locking system.\n *\n * This is based on code and examples provided in the ARIA specification.\n * https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html\n *\n * @module core/local/aria/focuslock\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Selectors from './selectors';\n\nconst lockRegionStack = [];\nconst initialFocusElementStack = [];\nconst finalFocusElementStack = [];\n\nlet lastFocus = null;\nlet ignoreFocusChanges = false;\nlet isLocked = false;\n\n/**\n * The lock handler.\n *\n * This is the item that does a majority of the work.\n * The overall logic from this comes from the examles in the WCAG guidelines.\n *\n * The general idea is that if the focus is not held within by an Element within the lock region, then we replace focus\n * on the first element in the lock region. If the first element is the element previously selected prior to the\n * user-initiated focus change, then instead jump to the last element in the lock region.\n *\n * This gives us a solution which supports focus locking of any kind, which loops in both directions, and which\n * prevents the lock from escaping the modal entirely.\n *\n * @method\n * @param {Event} event The event from the focus change\n */\nconst lockHandler = event => {\n if (ignoreFocusChanges) {\n // The focus change was made by an internal call to set focus.\n return;\n }\n\n // Find the current lock region.\n let lockRegion = getCurrentLockRegion();\n while (lockRegion) {\n if (document.contains(lockRegion)) {\n break;\n }\n\n // The lock region does not exist.\n // Perhaps it was removed without being untrapped.\n untrapFocus();\n lockRegion = getCurrentLockRegion();\n }\n if (!lockRegion) {\n return;\n }\n\n if (lockRegion.contains(event.target)) {\n lastFocus = event.target;\n } else {\n focusFirstDescendant();\n if (lastFocus == document.activeElement) {\n focusLastDescendant();\n }\n lastFocus = document.activeElement;\n }\n};\n\n/**\n * Focus the first descendant of the current lock region.\n *\n * @method\n * @returns {Bool} Whether a node was focused\n */\nconst focusFirstDescendant = () => {\n const lockRegion = getCurrentLockRegion();\n\n // Grab all elements in the lock region and attempt to focus each element until one is focused.\n // We can capture most of this in the query selector, but some cases may still reject focus.\n // For example, a disabled text area cannot be focused, and it becomes difficult to provide a decent query selector\n // to capture this.\n // The use of Array.some just ensures that we stop as soon as we have a successful focus.\n const focusableElements = Array.from(lockRegion.querySelectorAll(Selectors.elements.focusable));\n\n // The lock region itself may be focusable. This is particularly true on Moodle's older dialogues.\n // We must include it in the calculation of descendants to ensure that looping works correctly.\n focusableElements.unshift(lockRegion);\n return focusableElements.some(focusableElement => attemptFocus(focusableElement));\n};\n\n/**\n * Focus the last descendant of the current lock region.\n *\n * @method\n * @returns {Bool} Whether a node was focused\n */\nconst focusLastDescendant = () => {\n const lockRegion = getCurrentLockRegion();\n\n // Grab all elements in the lock region, reverse them, and attempt to focus each element until one is focused.\n // We can capture most of this in the query selector, but some cases may still reject focus.\n // For example, a disabled text area cannot be focused, and it becomes difficult to provide a decent query selector\n // to capture this.\n // The use of Array.some just ensures that we stop as soon as we have a successful focus.\n const focusableElements = Array.from(lockRegion.querySelectorAll(Selectors.elements.focusable)).reverse();\n\n // The lock region itself may be focusable. This is particularly true on Moodle's older dialogues.\n // We must include it in the calculation of descendants to ensure that looping works correctly.\n focusableElements.push(lockRegion);\n return focusableElements.some(focusableElement => attemptFocus(focusableElement));\n};\n\n/**\n * Check whether the supplied focusTarget is actually focusable.\n * There are cases where a normally focusable element can reject focus.\n *\n * Note: This example is a wholesale copy of the WCAG example.\n *\n * @method\n * @param {HTMLElement} focusTarget\n * @returns {Bool}\n */\nconst isFocusable = focusTarget => {\n if (focusTarget.tabIndex > 0 || (focusTarget.tabIndex === 0 && focusTarget.getAttribute('tabIndex') !== null)) {\n return true;\n }\n\n if (focusTarget.disabled) {\n return false;\n }\n\n switch (focusTarget.nodeName) {\n case 'A':\n return !!focusTarget.href && focusTarget.rel != 'ignore';\n case 'INPUT':\n return focusTarget.type != 'hidden' && focusTarget.type != 'file';\n case 'BUTTON':\n case 'SELECT':\n case 'TEXTAREA':\n return true;\n default:\n return false;\n }\n};\n\n/**\n * Attempt to focus the supplied focusTarget.\n *\n * Note: This example is a heavily inspired by the WCAG example.\n *\n * @method\n * @param {HTMLElement} focusTarget\n * @returns {Bool} Whether focus was successful o rnot.\n */\nconst attemptFocus = focusTarget => {\n if (!isFocusable(focusTarget)) {\n return false;\n }\n\n // The ignoreFocusChanges variable prevents the focus event handler from interfering and entering a fight with itself.\n ignoreFocusChanges = true;\n\n try {\n focusTarget.focus();\n } catch (e) {\n // Ignore failures. We will just try to focus the next element in the list.\n // eslint-disable-line\n }\n\n ignoreFocusChanges = false;\n\n // If focus was successful the activeElement will be the one we focused.\n return (document.activeElement === focusTarget);\n};\n\n/**\n * Get the current lock region from the top of the stack.\n *\n * @method\n * @returns {HTMLElement}\n */\nconst getCurrentLockRegion = () => {\n return lockRegionStack[lockRegionStack.length - 1];\n};\n\n/**\n * Add a new lock region to the stack.\n *\n * @method\n * @param {HTMLElement} newLockRegion\n */\nconst addLockRegionToStack = newLockRegion => {\n if (newLockRegion === getCurrentLockRegion()) {\n return;\n }\n\n lockRegionStack.push(newLockRegion);\n const currentLockRegion = getCurrentLockRegion();\n\n // Append an empty div which can be focused just outside of the item locked.\n // This locks tab focus to within the tab region, and does not allow it to extend back into the window by\n // guaranteeing the existence of a tabable item after the lock region which can be focused but which will be caught\n // by the handler.\n const element = document.createElement('div');\n element.tabIndex = 0;\n element.style.position = 'fixed';\n element.style.top = 0;\n element.style.left = 0;\n\n const initialNode = element.cloneNode();\n currentLockRegion.parentNode.insertBefore(initialNode, currentLockRegion);\n initialFocusElementStack.push(initialNode);\n\n const finalNode = element.cloneNode();\n currentLockRegion.parentNode.insertBefore(finalNode, currentLockRegion.nextSibling);\n finalFocusElementStack.push(finalNode);\n};\n\n/**\n * Remove the top lock region from the stack.\n *\n * @method\n */\nconst removeLastLockRegionFromStack = () => {\n // Take the top element off the stack, and replce the current lockRegion value.\n lockRegionStack.pop();\n\n const finalNode = finalFocusElementStack.pop();\n if (finalNode) {\n // The final focus element may have been removed if it was part of a parent item.\n finalNode.remove();\n }\n\n const initialNode = initialFocusElementStack.pop();\n if (initialNode) {\n // The initial focus element may have been removed if it was part of a parent item.\n initialNode.remove();\n }\n};\n\n/**\n * Whether any region is left in the stack.\n *\n * @return {Bool}\n */\nconst hasTrappedRegionsInStack = () => {\n return !!lockRegionStack.length;\n};\n\n/**\n * Start trapping the focus and lock it to the specified newLockRegion.\n *\n * @method\n * @param {HTMLElement} newLockRegion The container to lock focus to\n */\nexport const trapFocus = newLockRegion => {\n // Update the lock region stack.\n // This allows us to support nesting.\n addLockRegionToStack(newLockRegion);\n\n if (!isLocked) {\n // Add the focus handler.\n document.addEventListener('focus', lockHandler, true);\n }\n\n // Attempt to focus on the first item in the lock region.\n if (!focusFirstDescendant()) {\n const currentLockRegion = getCurrentLockRegion();\n\n // No focusable descendants found in the region yet.\n // This can happen when the region is locked before content is generated.\n // Focus on the region itself for now.\n const originalRegionTabIndex = currentLockRegion.tabIndex;\n currentLockRegion.tabIndex = 0;\n attemptFocus(currentLockRegion);\n currentLockRegion.tabIndex = originalRegionTabIndex;\n }\n\n // Keep track of the last item focused.\n lastFocus = document.activeElement;\n\n isLocked = true;\n};\n\n/**\n * Stop trapping the focus.\n *\n * @method\n */\nexport const untrapFocus = () => {\n // Remove the top region from the stack.\n removeLastLockRegionFromStack();\n\n if (hasTrappedRegionsInStack()) {\n // The focus manager still has items in the stack.\n return;\n }\n\n document.removeEventListener('focus', lockHandler, true);\n\n lastFocus = null;\n ignoreFocusChanges = false;\n isLocked = false;\n};\n"],"names":["lockRegionStack","initialFocusElementStack","finalFocusElementStack","lastFocus","ignoreFocusChanges","isLocked","lockHandler","event","lockRegion","getCurrentLockRegion","document","contains","untrapFocus","target","focusFirstDescendant","activeElement","focusLastDescendant","focusableElements","Array","from","querySelectorAll","Selectors","elements","focusable","unshift","some","focusableElement","attemptFocus","reverse","push","focusTarget","tabIndex","getAttribute","disabled","nodeName","href","rel","type","isFocusable","focus","e","length","newLockRegion","currentLockRegion","element","createElement","style","position","top","left","initialNode","cloneNode","parentNode","insertBefore","finalNode","nextSibling","addLockRegionToStack","addEventListener","originalRegionTabIndex","pop","remove","removeLastLockRegionFromStack","removeEventListener"],"mappings":";;;;;;;;;;gLA2BMA,gBAAkB,GAClBC,yBAA2B,GAC3BC,uBAAyB,OAE3BC,UAAY,KACZC,oBAAqB,EACrBC,UAAW,QAkBTC,YAAcC,WACZH,8BAMAI,WAAaC,4BACVD,aACCE,SAASC,SAASH,aAMtBI,cACAJ,WAAaC,uBAEZD,aAIDA,WAAWG,SAASJ,MAAMM,QAC1BV,UAAYI,MAAMM,QAElBC,uBACIX,WAAaO,SAASK,eACtBC,sBAEJb,UAAYO,SAASK,iBAUvBD,qBAAuB,WACnBN,WAAaC,uBAObQ,kBAAoBC,MAAMC,KAAKX,WAAWY,iBAAiBC,mBAAUC,SAASC,mBAIpFN,kBAAkBO,QAAQhB,YACnBS,kBAAkBQ,MAAKC,kBAAoBC,aAAaD,qBAS7DV,oBAAsB,WAClBR,WAAaC,uBAObQ,kBAAoBC,MAAMC,KAAKX,WAAWY,iBAAiBC,mBAAUC,SAASC,YAAYK,iBAIhGX,kBAAkBY,KAAKrB,YAChBS,kBAAkBQ,MAAKC,kBAAoBC,aAAaD,qBA6C7DC,aAAeG,kBAhCDA,CAAAA,iBACZA,YAAYC,SAAW,GAA+B,IAAzBD,YAAYC,UAA2D,OAAzCD,YAAYE,aAAa,mBAC7E,KAGPF,YAAYG,gBACL,SAGHH,YAAYI,cACX,YACQJ,YAAYK,MAA2B,UAAnBL,YAAYM,QACxC,cAC0B,UAApBN,YAAYO,MAAwC,QAApBP,YAAYO,SAClD,aACA,aACA,kBACM,iBAEA,IAcVC,CAAYR,oBACN,EAIX1B,oBAAqB,MAGjB0B,YAAYS,QACd,MAAOC,WAKTpC,oBAAqB,EAGbM,SAASK,gBAAkBe,aASjCrB,qBAAuB,IAClBT,gBAAgBA,gBAAgByC,OAAS,sBAyE3BC,mBAhEIA,CAAAA,mBACrBA,gBAAkBjC,8BAItBT,gBAAgB6B,KAAKa,qBACfC,kBAAoBlC,uBAMpBmC,QAAUlC,SAASmC,cAAc,OACvCD,QAAQb,SAAW,EACnBa,QAAQE,MAAMC,SAAW,QACzBH,QAAQE,MAAME,IAAM,EACpBJ,QAAQE,MAAMG,KAAO,QAEfC,YAAcN,QAAQO,YAC5BR,kBAAkBS,WAAWC,aAAaH,YAAaP,mBACvD1C,yBAAyB4B,KAAKqB,mBAExBI,UAAYV,QAAQO,YAC1BR,kBAAkBS,WAAWC,aAAaC,UAAWX,kBAAkBY,aACvErD,uBAAuB2B,KAAKyB,YA2C5BE,CAAqBd,eAEhBrC,UAEDK,SAAS+C,iBAAiB,QAASnD,aAAa,IAI/CQ,uBAAwB,OACnB6B,kBAAoBlC,uBAKpBiD,uBAAyBf,kBAAkBZ,SACjDY,kBAAkBZ,SAAW,EAC7BJ,aAAagB,mBACbA,kBAAkBZ,SAAW2B,uBAIjCvD,UAAYO,SAASK,cAErBV,UAAW,SAQFO,YAAc,KAlEW,MAElCZ,gBAAgB2D,YAEVL,UAAYpD,uBAAuByD,MACrCL,WAEAA,UAAUM,eAGRV,YAAcjD,yBAAyB0D,MACzCT,aAEAA,YAAYU,UAuDhBC,GA7CS7D,gBAAgByC,SAoDzB/B,SAASoD,oBAAoB,QAASxD,aAAa,GAEnDH,UAAY,KACZC,oBAAqB,EACrBC,UAAW"}
\ No newline at end of file
+{"version":3,"file":"focuslock.min.js","sources":["../../../src/local/aria/focuslock.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle 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 Moodle. If not, see .\n\n/**\n * Tab locking system.\n *\n * This is based on code and examples provided in the ARIA specification.\n * https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html\n *\n * @module core/local/aria/focuslock\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Selectors from './selectors';\n\nconst lockRegionStack = [];\nconst initialFocusElementStack = [];\nconst finalFocusElementStack = [];\n\nlet lastFocus = null;\nlet ignoreFocusChanges = false;\nlet isLocked = false;\n\n/**\n * The lock handler.\n *\n * This is the item that does a majority of the work.\n * The overall logic from this comes from the examles in the WCAG guidelines.\n *\n * The general idea is that if the focus is not held within by an Element within the lock region, then we replace focus\n * on the first element in the lock region. If the first element is the element previously selected prior to the\n * user-initiated focus change, then instead jump to the last element in the lock region.\n *\n * This gives us a solution which supports focus locking of any kind, which loops in both directions, and which\n * prevents the lock from escaping the modal entirely.\n *\n * @method\n * @param {Event} event The event from the focus change\n */\nconst lockHandler = event => {\n if (ignoreFocusChanges) {\n // The focus change was made by an internal call to set focus.\n return;\n }\n\n // Find the current lock region.\n let lockRegion = getCurrentLockRegion();\n while (lockRegion) {\n if (document.contains(lockRegion)) {\n break;\n }\n\n // The lock region does not exist.\n // Perhaps it was removed without being untrapped.\n untrapFocus();\n lockRegion = getCurrentLockRegion();\n }\n if (!lockRegion) {\n return;\n }\n\n if (lockRegion.contains(event.target)) {\n lastFocus = event.target;\n } else {\n focusFirstDescendant();\n if (lastFocus == document.activeElement) {\n focusLastDescendant();\n }\n lastFocus = document.activeElement;\n }\n};\n\n/**\n * Focus the first descendant of the current lock region.\n *\n * @method\n * @returns {Bool} Whether a node was focused\n */\nconst focusFirstDescendant = () => {\n const lockRegion = getCurrentLockRegion();\n\n // Grab all elements in the lock region and attempt to focus each element until one is focused.\n // We can capture most of this in the query selector, but some cases may still reject focus.\n // For example, a disabled text area cannot be focused, and it becomes difficult to provide a decent query selector\n // to capture this.\n // The use of Array.some just ensures that we stop as soon as we have a successful focus.\n const focusableElements = Array.from(lockRegion.querySelectorAll(Selectors.elements.focusable));\n\n // The lock region itself may be focusable. This is particularly true on Moodle's older dialogues.\n // We must include it in the calculation of descendants to ensure that looping works correctly.\n focusableElements.unshift(lockRegion);\n return focusableElements.some(focusableElement => attemptFocus(focusableElement));\n};\n\n/**\n * Focus the last descendant of the current lock region.\n *\n * @method\n * @returns {Bool} Whether a node was focused\n */\nconst focusLastDescendant = () => {\n const lockRegion = getCurrentLockRegion();\n\n // Grab all elements in the lock region, reverse them, and attempt to focus each element until one is focused.\n // We can capture most of this in the query selector, but some cases may still reject focus.\n // For example, a disabled text area cannot be focused, and it becomes difficult to provide a decent query selector\n // to capture this.\n // The use of Array.some just ensures that we stop as soon as we have a successful focus.\n const focusableElements = Array.from(lockRegion.querySelectorAll(Selectors.elements.focusable)).reverse();\n\n // The lock region itself may be focusable. This is particularly true on Moodle's older dialogues.\n // We must include it in the calculation of descendants to ensure that looping works correctly.\n focusableElements.push(lockRegion);\n return focusableElements.some(focusableElement => attemptFocus(focusableElement));\n};\n\n/**\n * Check whether the supplied focusTarget is actually focusable.\n * There are cases where a normally focusable element can reject focus.\n *\n * Note: This example is a wholesale copy of the WCAG example.\n *\n * @method\n * @param {HTMLElement} focusTarget\n * @returns {Bool}\n */\nconst isFocusable = focusTarget => {\n if (focusTarget.tabIndex > 0 || (focusTarget.tabIndex === 0 && focusTarget.getAttribute('tabIndex') !== null)) {\n return true;\n }\n\n if (focusTarget.disabled) {\n return false;\n }\n\n switch (focusTarget.nodeName) {\n case 'A':\n return !!focusTarget.href && focusTarget.rel != 'ignore';\n case 'INPUT':\n return focusTarget.type != 'hidden' && focusTarget.type != 'file';\n case 'BUTTON':\n case 'SELECT':\n case 'TEXTAREA':\n return true;\n default:\n return false;\n }\n};\n\n/**\n * Attempt to focus the supplied focusTarget.\n *\n * Note: This example is a heavily inspired by the WCAG example.\n *\n * @method\n * @param {HTMLElement} focusTarget\n * @returns {Bool} Whether focus was successful o rnot.\n */\nconst attemptFocus = focusTarget => {\n if (!isFocusable(focusTarget)) {\n return false;\n }\n\n // The ignoreFocusChanges variable prevents the focus event handler from interfering and entering a fight with itself.\n ignoreFocusChanges = true;\n\n try {\n focusTarget.focus();\n } catch (e) {\n // Ignore failures. We will just try to focus the next element in the list.\n }\n\n ignoreFocusChanges = false;\n\n // If focus was successful the activeElement will be the one we focused.\n return (document.activeElement === focusTarget);\n};\n\n/**\n * Get the current lock region from the top of the stack.\n *\n * @method\n * @returns {HTMLElement}\n */\nconst getCurrentLockRegion = () => {\n return lockRegionStack[lockRegionStack.length - 1];\n};\n\n/**\n * Add a new lock region to the stack.\n *\n * @method\n * @param {HTMLElement} newLockRegion\n */\nconst addLockRegionToStack = newLockRegion => {\n if (newLockRegion === getCurrentLockRegion()) {\n return;\n }\n\n lockRegionStack.push(newLockRegion);\n const currentLockRegion = getCurrentLockRegion();\n\n // Append an empty div which can be focused just outside of the item locked.\n // This locks tab focus to within the tab region, and does not allow it to extend back into the window by\n // guaranteeing the existence of a tabable item after the lock region which can be focused but which will be caught\n // by the handler.\n const element = document.createElement('div');\n element.tabIndex = 0;\n element.style.position = 'fixed';\n element.style.top = 0;\n element.style.left = 0;\n\n const initialNode = element.cloneNode();\n currentLockRegion.parentNode.insertBefore(initialNode, currentLockRegion);\n initialFocusElementStack.push(initialNode);\n\n const finalNode = element.cloneNode();\n currentLockRegion.parentNode.insertBefore(finalNode, currentLockRegion.nextSibling);\n finalFocusElementStack.push(finalNode);\n};\n\n/**\n * Remove the top lock region from the stack.\n *\n * @method\n */\nconst removeLastLockRegionFromStack = () => {\n // Take the top element off the stack, and replce the current lockRegion value.\n lockRegionStack.pop();\n\n const finalNode = finalFocusElementStack.pop();\n if (finalNode) {\n // The final focus element may have been removed if it was part of a parent item.\n finalNode.remove();\n }\n\n const initialNode = initialFocusElementStack.pop();\n if (initialNode) {\n // The initial focus element may have been removed if it was part of a parent item.\n initialNode.remove();\n }\n};\n\n/**\n * Whether any region is left in the stack.\n *\n * @return {Bool}\n */\nconst hasTrappedRegionsInStack = () => {\n return !!lockRegionStack.length;\n};\n\n/**\n * Start trapping the focus and lock it to the specified newLockRegion.\n *\n * @method\n * @param {HTMLElement} newLockRegion The container to lock focus to\n */\nexport const trapFocus = newLockRegion => {\n // Update the lock region stack.\n // This allows us to support nesting.\n addLockRegionToStack(newLockRegion);\n\n if (!isLocked) {\n // Add the focus handler.\n document.addEventListener('focus', lockHandler, true);\n }\n\n // Attempt to focus on the first item in the lock region.\n if (!focusFirstDescendant()) {\n const currentLockRegion = getCurrentLockRegion();\n\n // No focusable descendants found in the region yet.\n // This can happen when the region is locked before content is generated.\n // Focus on the region itself for now.\n const originalRegionTabIndex = currentLockRegion.tabIndex;\n currentLockRegion.tabIndex = 0;\n attemptFocus(currentLockRegion);\n currentLockRegion.tabIndex = originalRegionTabIndex;\n }\n\n // Keep track of the last item focused.\n lastFocus = document.activeElement;\n\n isLocked = true;\n};\n\n/**\n * Stop trapping the focus.\n *\n * @method\n */\nexport const untrapFocus = () => {\n // Remove the top region from the stack.\n removeLastLockRegionFromStack();\n\n if (hasTrappedRegionsInStack()) {\n // The focus manager still has items in the stack.\n return;\n }\n\n document.removeEventListener('focus', lockHandler, true);\n\n lastFocus = null;\n ignoreFocusChanges = false;\n isLocked = false;\n};\n"],"names":["lockRegionStack","initialFocusElementStack","finalFocusElementStack","lastFocus","ignoreFocusChanges","isLocked","lockHandler","event","lockRegion","getCurrentLockRegion","document","contains","untrapFocus","target","focusFirstDescendant","activeElement","focusLastDescendant","focusableElements","Array","from","querySelectorAll","Selectors","elements","focusable","unshift","some","focusableElement","attemptFocus","reverse","push","focusTarget","tabIndex","getAttribute","disabled","nodeName","href","rel","type","isFocusable","focus","e","length","newLockRegion","currentLockRegion","element","createElement","style","position","top","left","initialNode","cloneNode","parentNode","insertBefore","finalNode","nextSibling","addLockRegionToStack","addEventListener","originalRegionTabIndex","pop","remove","removeLastLockRegionFromStack","removeEventListener"],"mappings":";;;;;;;;;;gLA2BMA,gBAAkB,GAClBC,yBAA2B,GAC3BC,uBAAyB,OAE3BC,UAAY,KACZC,oBAAqB,EACrBC,UAAW,QAkBTC,YAAcC,WACZH,8BAMAI,WAAaC,4BACVD,aACCE,SAASC,SAASH,aAMtBI,cACAJ,WAAaC,uBAEZD,aAIDA,WAAWG,SAASJ,MAAMM,QAC1BV,UAAYI,MAAMM,QAElBC,uBACIX,WAAaO,SAASK,eACtBC,sBAEJb,UAAYO,SAASK,iBAUvBD,qBAAuB,WACnBN,WAAaC,uBAObQ,kBAAoBC,MAAMC,KAAKX,WAAWY,iBAAiBC,mBAAUC,SAASC,mBAIpFN,kBAAkBO,QAAQhB,YACnBS,kBAAkBQ,MAAKC,kBAAoBC,aAAaD,qBAS7DV,oBAAsB,WAClBR,WAAaC,uBAObQ,kBAAoBC,MAAMC,KAAKX,WAAWY,iBAAiBC,mBAAUC,SAASC,YAAYK,iBAIhGX,kBAAkBY,KAAKrB,YAChBS,kBAAkBQ,MAAKC,kBAAoBC,aAAaD,qBA6C7DC,aAAeG,kBAhCDA,CAAAA,iBACZA,YAAYC,SAAW,GAA+B,IAAzBD,YAAYC,UAA2D,OAAzCD,YAAYE,aAAa,mBAC7E,KAGPF,YAAYG,gBACL,SAGHH,YAAYI,cACX,YACQJ,YAAYK,MAA2B,UAAnBL,YAAYM,QACxC,cAC0B,UAApBN,YAAYO,MAAwC,QAApBP,YAAYO,SAClD,aACA,aACA,kBACM,iBAEA,IAcVC,CAAYR,oBACN,EAIX1B,oBAAqB,MAGjB0B,YAAYS,QACd,MAAOC,WAITpC,oBAAqB,EAGbM,SAASK,gBAAkBe,aASjCrB,qBAAuB,IAClBT,gBAAgBA,gBAAgByC,OAAS,sBAyE3BC,mBAhEIA,CAAAA,mBACrBA,gBAAkBjC,8BAItBT,gBAAgB6B,KAAKa,qBACfC,kBAAoBlC,uBAMpBmC,QAAUlC,SAASmC,cAAc,OACvCD,QAAQb,SAAW,EACnBa,QAAQE,MAAMC,SAAW,QACzBH,QAAQE,MAAME,IAAM,EACpBJ,QAAQE,MAAMG,KAAO,QAEfC,YAAcN,QAAQO,YAC5BR,kBAAkBS,WAAWC,aAAaH,YAAaP,mBACvD1C,yBAAyB4B,KAAKqB,mBAExBI,UAAYV,QAAQO,YAC1BR,kBAAkBS,WAAWC,aAAaC,UAAWX,kBAAkBY,aACvErD,uBAAuB2B,KAAKyB,YA2C5BE,CAAqBd,eAEhBrC,UAEDK,SAAS+C,iBAAiB,QAASnD,aAAa,IAI/CQ,uBAAwB,OACnB6B,kBAAoBlC,uBAKpBiD,uBAAyBf,kBAAkBZ,SACjDY,kBAAkBZ,SAAW,EAC7BJ,aAAagB,mBACbA,kBAAkBZ,SAAW2B,uBAIjCvD,UAAYO,SAASK,cAErBV,UAAW,SAQFO,YAAc,KAlEW,MAElCZ,gBAAgB2D,YAEVL,UAAYpD,uBAAuByD,MACrCL,WAEAA,UAAUM,eAGRV,YAAcjD,yBAAyB0D,MACzCT,aAEAA,YAAYU,UAuDhBC,GA7CS7D,gBAAgByC,SAoDzB/B,SAASoD,oBAAoB,QAASxD,aAAa,GAEnDH,UAAY,KACZC,oBAAqB,EACrBC,UAAW"}
\ No newline at end of file
diff --git a/lib/amd/src/local/aria/focuslock.js b/lib/amd/src/local/aria/focuslock.js
index 2e628c8e7c0..7ba33e92ed3 100644
--- a/lib/amd/src/local/aria/focuslock.js
+++ b/lib/amd/src/local/aria/focuslock.js
@@ -180,7 +180,6 @@ const attemptFocus = focusTarget => {
focusTarget.focus();
} catch (e) {
// Ignore failures. We will just try to focus the next element in the list.
- // eslint-disable-line
}
ignoreFocusChanges = false;