diff --git a/lib/templates/settings_link_page.mustache b/lib/templates/settings_link_page.mustache
index 29917537fdc..07f07059992 100644
--- a/lib/templates/settings_link_page.mustache
+++ b/lib/templates/settings_link_page.mustache
@@ -40,7 +40,7 @@
-
- {{node.text}}
+ {{node.text}}
{{#node.children}}
@@ -48,7 +48,7 @@
{{#display}}
{{^is_short_branch}}
-
- {{text}}
+ {{text}}
{{/is_short_branch}}
{{/display}}
diff --git a/theme/boost/amd/build/aria.min.js b/theme/boost/amd/build/aria.min.js
index e266186de4e..2265ffa34e3 100644
--- a/theme/boost/amd/build/aria.min.js
+++ b/theme/boost/amd/build/aria.min.js
@@ -1,2 +1,2 @@
-define ("theme_boost/aria",["exports","core/key_codes","jquery","core/pending"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;c=e(c);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}var f=function(){var a=!1,e=function(){a=!0},f=function(){var b=a;a=!1;return b};document.addEventListener("keydown",function(a){if(a.target.matches("[data-toggle=\"dropdown\"]")){var c=a.which;if(c==b.arrowUp){e()}if(c==b.escape){var d=a.target.getAttribute("aria-expanded");a.preventDefault();if("false"==d){a.target.click()}}if(c==b.space||c==b.enter){a.preventDefault();a.target.click()}}});var g=function(a){setTimeout(function delayedFocus(b){a.focus();b.resolve()},50,new d.default("core/aria:delayed-focus"))};(0,c.default)(".dropdown").on("shown.bs.dropdown",function(a){var b=a.target.querySelector("[role=\"menu\"]"),c=!1,d=!1;if(b){c=b.querySelectorAll("[role=\"menuitem\"]")}if(c&&0.\n\n/**\n * Enhancements to Bootstrap components for accessibility.\n *\n * @module theme_boost/aria\n * @copyright 2018 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {end, escape, arrowUp, arrowDown, home, enter, space} from 'core/key_codes';\nimport $ from 'jquery';\nimport Pending from 'core/pending';\n\n/**\n * Drop downs from bootstrap don't support keyboard accessibility by default.\n */\nconst dropdownFix = () => {\n let focusEnd = false;\n const setFocusEnd = () => {\n focusEnd = true;\n };\n const getFocusEnd = () => {\n const result = focusEnd;\n focusEnd = false;\n return result;\n };\n\n // Special handling for \"up\" keyboard control.\n document.addEventListener('keydown', e => {\n if (e.target.matches('[data-toggle=\"dropdown\"]')) {\n const trigger = e.which;\n\n // Up key opens the menu at the end.\n if (trigger == arrowUp) {\n // Focus the end of the menu, not the beginning.\n setFocusEnd();\n }\n\n // Escape key only closes the menu, it doesn't open it.\n if (trigger == escape) {\n const expanded = e.target.getAttribute('aria-expanded');\n e.preventDefault();\n if (expanded == \"false\") {\n e.target.click();\n }\n }\n\n // Space key or Enter key opens the menu.\n if (trigger == space || trigger == enter) {\n // Cancel random scroll.\n e.preventDefault();\n // Open the menu instead.\n e.target.click();\n }\n }\n });\n\n // Special handling for navigation keys when menu is open.\n const shiftFocus = element => {\n const delayedFocus = pendingPromise => {\n element.focus();\n pendingPromise.resolve();\n };\n setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));\n };\n\n $('.dropdown').on('shown.bs.dropdown', e => {\n // We need to focus on the first menuitem.\n const menu = e.target.querySelector('[role=\"menu\"]');\n let menuItems = false;\n let foundMenuItem = false;\n\n if (menu) {\n menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n }\n if (menuItems && menuItems.length > 0) {\n if (getFocusEnd()) {\n foundMenuItem = menuItems[menuItems.length - 1];\n } else {\n // The first menu entry, pretty reasonable.\n foundMenuItem = menuItems[0];\n }\n }\n if (foundMenuItem) {\n shiftFocus(foundMenuItem);\n }\n });\n // Search for menu items by finding the first item that has\n // text starting with the typed character (case insensitive).\n document.addEventListener('keypress', e => {\n if (e.target.matches('.dropdown [role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = String.fromCharCode(e.which).toLowerCase();\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n\n for (let i = 0; i < menuItems.length; i++) {\n const item = menuItems[i];\n const itemText = item.text.trim().toLowerCase();\n if (itemText.indexOf(trigger) == 0) {\n shiftFocus(item);\n break;\n }\n }\n }\n });\n\n // Keyboard navigation for arrow keys, home and end keys.\n document.addEventListener('keydown', e => {\n if (e.target.matches('.dropdown [role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = e.which;\n let next = false;\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n // Down key.\n if (trigger == arrowDown) {\n for (let i = 0; i < menuItems.length - 1; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i + 1];\n break;\n }\n }\n if (!next) {\n // Wrap to first item.\n next = menuItems[0];\n }\n\n } else if (trigger == arrowUp) {\n // Up key.\n for (let i = 1; i < menuItems.length; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i - 1];\n break;\n }\n }\n if (!next) {\n // Wrap to last item.\n next = menuItems[menuItems.length - 1];\n }\n\n } else if (trigger == home) {\n // Home key.\n next = menuItems[0];\n\n } else if (trigger == end) {\n // End key.\n next = menuItems[menuItems.length - 1];\n }\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n shiftFocus(next);\n }\n return;\n }\n });\n\n $('.dropdown').on('hidden.bs.dropdown', e => {\n // We need to focus on the menu trigger.\n const trigger = e.target.querySelector('[data-toggle=\"dropdown\"]');\n if (trigger) {\n shiftFocus(trigger);\n }\n });\n};\n\n/**\n * After page load, focus on any element with special autofocus attribute.\n */\nconst autoFocus = () => {\n window.addEventListener(\"load\", () => {\n const alerts = document.querySelectorAll('[data-aria-autofocus=\"true\"][role=\"alert\"]');\n Array.prototype.forEach.call(alerts, autofocusElement => {\n // According to the specification an role=\"alert\" region is only read out on change to the content\n // of that region.\n autofocusElement.innerHTML += ' ';\n autofocusElement.removeAttribute('data-aria-autofocus');\n });\n });\n};\n\nexport const init = () => {\n dropdownFix();\n autoFocus();\n};\n"],"file":"aria.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/aria.js"],"names":["dropdownFix","focusEnd","setFocusEnd","getFocusEnd","result","document","addEventListener","e","target","matches","trigger","which","arrowUp","escape","expanded","getAttribute","preventDefault","click","space","enter","shiftFocus","element","setTimeout","delayedFocus","pendingPromise","focus","resolve","Pending","on","menu","querySelector","menuItems","foundMenuItem","querySelectorAll","length","String","fromCharCode","toLowerCase","closest","i","item","itemText","text","trim","indexOf","next","arrowDown","home","end","autoFocus","window","alerts","Array","prototype","forEach","call","autofocusElement","innerHTML","removeAttribute","updateTabFocus","tabList","vertical","rtl","right_to_left","arrowNext","arrowLeft","arrowRight","arrowPrevious","tabs","filter","tab","getComputedStyle","display","index","keyCode","tabIndex","tabElementFix","includes","init"],"mappings":"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","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 * Enhancements to Bootstrap components for accessibility.\n *\n * @module theme_boost/aria\n * @copyright 2018 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {end, escape, arrowUp, arrowDown, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport $ from 'jquery';\nimport Pending from 'core/pending';\n\n/**\n * Drop downs from bootstrap don't support keyboard accessibility by default.\n */\nconst dropdownFix = () => {\n let focusEnd = false;\n const setFocusEnd = () => {\n focusEnd = true;\n };\n const getFocusEnd = () => {\n const result = focusEnd;\n focusEnd = false;\n return result;\n };\n\n // Special handling for \"up\" keyboard control.\n document.addEventListener('keydown', e => {\n if (e.target.matches('[data-toggle=\"dropdown\"]')) {\n const trigger = e.which;\n\n // Up key opens the menu at the end.\n if (trigger == arrowUp) {\n // Focus the end of the menu, not the beginning.\n setFocusEnd();\n }\n\n // Escape key only closes the menu, it doesn't open it.\n if (trigger == escape) {\n const expanded = e.target.getAttribute('aria-expanded');\n e.preventDefault();\n if (expanded == \"false\") {\n e.target.click();\n }\n }\n\n // Space key or Enter key opens the menu.\n if (trigger == space || trigger == enter) {\n // Cancel random scroll.\n e.preventDefault();\n // Open the menu instead.\n e.target.click();\n }\n }\n });\n\n // Special handling for navigation keys when menu is open.\n const shiftFocus = element => {\n const delayedFocus = pendingPromise => {\n element.focus();\n pendingPromise.resolve();\n };\n setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));\n };\n\n $('.dropdown').on('shown.bs.dropdown', e => {\n // We need to focus on the first menuitem.\n const menu = e.target.querySelector('[role=\"menu\"]');\n let menuItems = false;\n let foundMenuItem = false;\n\n if (menu) {\n menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n }\n if (menuItems && menuItems.length > 0) {\n if (getFocusEnd()) {\n foundMenuItem = menuItems[menuItems.length - 1];\n } else {\n // The first menu entry, pretty reasonable.\n foundMenuItem = menuItems[0];\n }\n }\n if (foundMenuItem) {\n shiftFocus(foundMenuItem);\n }\n });\n // Search for menu items by finding the first item that has\n // text starting with the typed character (case insensitive).\n document.addEventListener('keypress', e => {\n if (e.target.matches('.dropdown [role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = String.fromCharCode(e.which).toLowerCase();\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n\n for (let i = 0; i < menuItems.length; i++) {\n const item = menuItems[i];\n const itemText = item.text.trim().toLowerCase();\n if (itemText.indexOf(trigger) == 0) {\n shiftFocus(item);\n break;\n }\n }\n }\n });\n\n // Keyboard navigation for arrow keys, home and end keys.\n document.addEventListener('keydown', e => {\n if (e.target.matches('.dropdown [role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = e.which;\n let next = false;\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n // Down key.\n if (trigger == arrowDown) {\n for (let i = 0; i < menuItems.length - 1; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i + 1];\n break;\n }\n }\n if (!next) {\n // Wrap to first item.\n next = menuItems[0];\n }\n\n } else if (trigger == arrowUp) {\n // Up key.\n for (let i = 1; i < menuItems.length; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i - 1];\n break;\n }\n }\n if (!next) {\n // Wrap to last item.\n next = menuItems[menuItems.length - 1];\n }\n\n } else if (trigger == home) {\n // Home key.\n next = menuItems[0];\n\n } else if (trigger == end) {\n // End key.\n next = menuItems[menuItems.length - 1];\n }\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n shiftFocus(next);\n }\n return;\n }\n });\n\n $('.dropdown').on('hidden.bs.dropdown', e => {\n // We need to focus on the menu trigger.\n const trigger = e.target.querySelector('[data-toggle=\"dropdown\"]');\n if (trigger) {\n shiftFocus(trigger);\n }\n });\n};\n\n/**\n * After page load, focus on any element with special autofocus attribute.\n */\nconst autoFocus = () => {\n window.addEventListener(\"load\", () => {\n const alerts = document.querySelectorAll('[data-aria-autofocus=\"true\"][role=\"alert\"]');\n Array.prototype.forEach.call(alerts, autofocusElement => {\n // According to the specification an role=\"alert\" region is only read out on change to the content\n // of that region.\n autofocusElement.innerHTML += ' ';\n autofocusElement.removeAttribute('data-aria-autofocus');\n });\n });\n};\n\n/**\n * Changes the focus to the correct tab based on the key that is pressed.\n * @param {KeyboardEvent} e\n */\nconst updateTabFocus = e => {\n const tabList = e.target.closest('[role=\"tablist\"]');\n const vertical = tabList.getAttribute('aria-orientation') == 'vertical';\n const rtl = window.right_to_left();\n const arrowNext = vertical ? arrowDown : (rtl ? arrowLeft : arrowRight);\n const arrowPrevious = vertical ? arrowUp : (rtl ? arrowRight : arrowLeft);\n const tabs = Array.prototype.filter.call(\n tabList.querySelectorAll('[role=\"tab\"]'),\n tab => getComputedStyle(tab).display !== 'none'); // We only work with the visible tabs.\n\n for (let i = 0; i < tabs.length; i++) {\n tabs[i].index = i;\n }\n\n switch (e.keyCode) {\n case arrowNext:\n e.preventDefault();\n if (e.target.index !== undefined && tabs[e.target.index + 1]) {\n tabs[e.target.index + 1].focus();\n } else {\n tabs[0].focus();\n }\n break;\n case arrowPrevious:\n e.preventDefault();\n if (e.target.index !== undefined && tabs[e.target.index - 1]) {\n tabs[e.target.index - 1].focus();\n } else {\n tabs[tabs.length - 1].focus();\n }\n break;\n case home:\n e.preventDefault();\n tabs[0].focus();\n break;\n case end:\n e.preventDefault();\n tabs[tabs.length - 1].focus();\n break;\n case enter:\n case space:\n e.preventDefault();\n $(e.target).tab('show');\n tabs.forEach(tab => {\n tab.tabIndex = -1;\n });\n e.target.tabIndex = 0;\n }\n};\n\n/**\n * Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.\n */\nconst tabElementFix = () => {\n document.addEventListener('keydown', e => {\n if ([arrowUp, arrowDown, arrowLeft, arrowRight, home, end, enter, space].includes(e.keyCode)) {\n if (e.target.matches('[role=\"tablist\"] [role=\"tab\"]')) {\n updateTabFocus(e);\n }\n }\n });\n\n document.addEventListener('click', e => {\n if (e.target.matches('[role=\"tablist\"] [role=\"tab\"]')) {\n const tabs = e.target.closest('[role=\"tablist\"]').querySelectorAll('[role=\"tab\"]');\n e.preventDefault();\n $(e.target).tab('show');\n tabs.forEach(tab => {\n tab.tabIndex = -1;\n });\n e.target.tabIndex = 0;\n }\n });\n};\n\nexport const init = () => {\n dropdownFix();\n autoFocus();\n tabElementFix();\n};\n"],"file":"aria.min.js"}
\ No newline at end of file
diff --git a/theme/boost/amd/build/loader.min.js b/theme/boost/amd/build/loader.min.js
index 188f865c930..4f9b80475c2 100644
--- a/theme/boost/amd/build/loader.min.js
+++ b/theme/boost/amd/build/loader.min.js
@@ -1,2 +1,2 @@
-function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_boost/loader",["exports","jquery","./aria","./bootstrap/index","core/pending","./scroll","./pending"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Bootstrap",{enumerable:!0,get:function get(){return d.default}});b=j(b);c=i(c);d=j(d);e=j(e);f=j(f);g=j(g);function h(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;h=function(){return a};return a}function i(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=h();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function j(a){return a&&a.__esModule?a:{default:a}}var k=function(){(0,b.default)("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(a){var c=(0,b.default)(a.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var a=window.location.hash;if(a){(0,b.default)(".nav-link[href=\""+a+"\"]").tab("show")}},l=function(){(0,b.default)("body").popover({container:"body",selector:"[data-toggle=\"popover\"]",trigger:"focus"});document.addEventListener("keydown",function(a){if("Escape"===a.key&&a.target.closest("[data-toggle=\"popover\"]")){(0,b.default)(a.target).popover("hide")}})},m=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},n=new e.default("theme_boost/loader:init");(0,g.default)();k();l();m();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;c.init();n.resolve()});
+function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_boost/loader",["exports","jquery","./aria","./bootstrap/index","core/pending","./scroll","./pending"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Bootstrap",{enumerable:!0,get:function get(){return d.default}});b=j(b);c=i(c);d=j(d);e=j(e);f=j(f);g=j(g);function h(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;h=function(){return a};return a}function i(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=h();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function j(a){return a&&a.__esModule?a:{default:a}}var k=function(){(0,b.default)("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(a){var c=(0,b.default)(a.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var a=window.location.hash;if(a){var c=document.querySelector(".nav-link[href=\""+a+"\"]");if(c){c.click()}}},l=function(){(0,b.default)("body").popover({container:"body",selector:"[data-toggle=\"popover\"]",trigger:"focus"});document.addEventListener("keydown",function(a){if("Escape"===a.key&&a.target.closest("[data-toggle=\"popover\"]")){(0,b.default)(a.target).popover("hide")}})},m=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},n=new e.default("theme_boost/loader:init");(0,g.default)();c.init();k();l();m();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;n.resolve()});
//# sourceMappingURL=loader.min.js.map
diff --git a/theme/boost/amd/build/loader.min.js.map b/theme/boost/amd/build/loader.min.js.map
index 63d1fc7f7c7..f9e4a1b19d0 100644
--- a/theme/boost/amd/build/loader.min.js.map
+++ b/theme/boost/amd/build/loader.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/loader.js"],"names":["rememberTabs","on","e","hash","target","attr","history","replaceState","location","window","tab","enablePopovers","popover","container","selector","trigger","document","addEventListener","key","closest","enableTooltips","tooltip","pendingPromise","Pending","Scroll","init","$","fn","dropdown","Constructor","Default","flip","Aria","resolve"],"mappings":"wkBA0BA,OACA,OACA,OACA,OACA,OACA,O,4lBAKMA,CAAAA,CAAY,CAAG,UAAM,CACvB,cAAE,wBAAF,EAA0BC,EAA1B,CAA6B,cAA7B,CAA6C,SAASC,CAAT,CAAY,CACrD,GAAIC,CAAAA,CAAI,CAAG,cAAED,CAAC,CAACE,MAAJ,EAAYC,IAAZ,CAAiB,MAAjB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EAQA,GAAIA,CAAAA,CAAI,CAAGM,MAAM,CAACD,QAAP,CAAgBL,IAA3B,CACA,GAAIA,CAAJ,CAAU,CACP,cAAE,oBAAqBA,CAArB,CAA4B,KAA9B,EAAoCO,GAApC,CAAwC,MAAxC,CACF,CACJ,C,CAMKC,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdC,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAGdC,OAAO,CAAE,OAHK,CAAlB,EAMAC,QAAQ,CAACC,gBAAT,CAA0B,SAA1B,CAAqC,SAAAf,CAAC,CAAI,CACtC,GAAc,QAAV,GAAAA,CAAC,CAACgB,GAAF,EAAsBhB,CAAC,CAACE,MAAF,CAASe,OAAT,CAAiB,2BAAjB,CAA1B,CAAuE,CACnE,cAAEjB,CAAC,CAACE,MAAJ,EAAYQ,OAAZ,CAAoB,MAApB,CACH,CACJ,CAJD,CAKH,C,CAMKQ,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdR,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAAlB,CAIH,C,CAEKQ,CAAc,CAAG,GAAIC,UAAJ,CAAY,yBAAZ,C,CAGvB,gBAGAvB,CAAY,GAGZW,CAAc,GAGdS,CAAc,GAGb,GAAII,UAAJ,EAAD,CAAeC,IAAf,GAGAC,UAAEC,EAAF,CAAKC,QAAL,CAAcC,WAAd,CAA0BC,OAA1B,CAAkCC,IAAlC,IAGAC,CAAI,CAACP,IAAL,GAEAH,CAAc,CAACW,OAAf,E","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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport * as Aria from './aria';\nimport Bootstrap from './bootstrap/index';\nimport Pending from 'core/pending';\nimport Scroll from './scroll';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n var hash = window.location.hash;\n if (hash) {\n $('.nav-link[href=\"' + hash + '\"]').tab('show');\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus',\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Add scroll handling.\n(new Scroll()).init();\n\n// Disables flipping the dropdowns up and getting hidden behind the navbar.\n$.fn.dropdown.Constructor.Default.flip = false;\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"file":"loader.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/loader.js"],"names":["rememberTabs","on","e","hash","target","attr","history","replaceState","location","window","tab","document","querySelector","click","enablePopovers","popover","container","selector","trigger","addEventListener","key","closest","enableTooltips","tooltip","pendingPromise","Pending","Aria","init","Scroll","$","fn","dropdown","Constructor","Default","flip","resolve"],"mappings":"wkBA0BA,OACA,OACA,OACA,OACA,OACA,O,4lBAKMA,CAAAA,CAAY,CAAG,UAAM,CACvB,cAAE,wBAAF,EAA0BC,EAA1B,CAA6B,cAA7B,CAA6C,SAASC,CAAT,CAAY,CACrD,GAAIC,CAAAA,CAAI,CAAG,cAAED,CAAC,CAACE,MAAJ,EAAYC,IAAZ,CAAiB,MAAjB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EAQA,GAAMA,CAAAA,CAAI,CAAGM,MAAM,CAACD,QAAP,CAAgBL,IAA7B,CACA,GAAIA,CAAJ,CAAU,CACN,GAAMO,CAAAA,CAAG,CAAGC,QAAQ,CAACC,aAAT,CAAuB,oBAAqBT,CAArB,CAA4B,KAAnD,CAAZ,CACA,GAAIO,CAAJ,CAAS,CACLA,CAAG,CAACG,KAAJ,EACH,CACJ,CACJ,C,CAMKC,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdC,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAGdC,OAAO,CAAE,OAHK,CAAlB,EAMAP,QAAQ,CAACQ,gBAAT,CAA0B,SAA1B,CAAqC,SAAAjB,CAAC,CAAI,CACtC,GAAc,QAAV,GAAAA,CAAC,CAACkB,GAAF,EAAsBlB,CAAC,CAACE,MAAF,CAASiB,OAAT,CAAiB,2BAAjB,CAA1B,CAAuE,CACnE,cAAEnB,CAAC,CAACE,MAAJ,EAAYW,OAAZ,CAAoB,MAApB,CACH,CACJ,CAJD,CAKH,C,CAMKO,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdP,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAAlB,CAIH,C,CAEKO,CAAc,CAAG,GAAIC,UAAJ,CAAY,yBAAZ,C,CAGvB,gBAGAC,CAAI,CAACC,IAAL,GAGA3B,CAAY,GAGZc,CAAc,GAGdQ,CAAc,GAGb,GAAIM,UAAJ,EAAD,CAAeD,IAAf,GAGAE,UAAEC,EAAF,CAAKC,QAAL,CAAcC,WAAd,CAA0BC,OAA1B,CAAkCC,IAAlC,IAEAV,CAAc,CAACW,OAAf,E","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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport * as Aria from './aria';\nimport Bootstrap from './bootstrap/index';\nimport Pending from 'core/pending';\nimport Scroll from './scroll';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('.nav-link[href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus',\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Add scroll handling.\n(new Scroll()).init();\n\n// Disables flipping the dropdowns up and getting hidden behind the navbar.\n$.fn.dropdown.Constructor.Default.flip = false;\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"file":"loader.min.js"}
\ No newline at end of file
diff --git a/theme/boost/amd/src/aria.js b/theme/boost/amd/src/aria.js
index 812aad76680..de4049ebb86 100644
--- a/theme/boost/amd/src/aria.js
+++ b/theme/boost/amd/src/aria.js
@@ -21,7 +21,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-import {end, escape, arrowUp, arrowDown, home, enter, space} from 'core/key_codes';
+import {end, escape, arrowUp, arrowDown, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';
import $ from 'jquery';
import Pending from 'core/pending';
@@ -206,7 +206,87 @@ const autoFocus = () => {
});
};
+/**
+ * Changes the focus to the correct tab based on the key that is pressed.
+ * @param {KeyboardEvent} e
+ */
+const updateTabFocus = e => {
+ const tabList = e.target.closest('[role="tablist"]');
+ const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
+ const rtl = window.right_to_left();
+ const arrowNext = vertical ? arrowDown : (rtl ? arrowLeft : arrowRight);
+ const arrowPrevious = vertical ? arrowUp : (rtl ? arrowRight : arrowLeft);
+ const tabs = Array.prototype.filter.call(
+ tabList.querySelectorAll('[role="tab"]'),
+ tab => getComputedStyle(tab).display !== 'none'); // We only work with the visible tabs.
+
+ for (let i = 0; i < tabs.length; i++) {
+ tabs[i].index = i;
+ }
+
+ switch (e.keyCode) {
+ case arrowNext:
+ e.preventDefault();
+ if (e.target.index !== undefined && tabs[e.target.index + 1]) {
+ tabs[e.target.index + 1].focus();
+ } else {
+ tabs[0].focus();
+ }
+ break;
+ case arrowPrevious:
+ e.preventDefault();
+ if (e.target.index !== undefined && tabs[e.target.index - 1]) {
+ tabs[e.target.index - 1].focus();
+ } else {
+ tabs[tabs.length - 1].focus();
+ }
+ break;
+ case home:
+ e.preventDefault();
+ tabs[0].focus();
+ break;
+ case end:
+ e.preventDefault();
+ tabs[tabs.length - 1].focus();
+ break;
+ case enter:
+ case space:
+ e.preventDefault();
+ $(e.target).tab('show');
+ tabs.forEach(tab => {
+ tab.tabIndex = -1;
+ });
+ e.target.tabIndex = 0;
+ }
+};
+
+/**
+ * Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.
+ */
+const tabElementFix = () => {
+ document.addEventListener('keydown', e => {
+ if ([arrowUp, arrowDown, arrowLeft, arrowRight, home, end, enter, space].includes(e.keyCode)) {
+ if (e.target.matches('[role="tablist"] [role="tab"]')) {
+ updateTabFocus(e);
+ }
+ }
+ });
+
+ document.addEventListener('click', e => {
+ if (e.target.matches('[role="tablist"] [role="tab"]')) {
+ const tabs = e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');
+ e.preventDefault();
+ $(e.target).tab('show');
+ tabs.forEach(tab => {
+ tab.tabIndex = -1;
+ });
+ e.target.tabIndex = 0;
+ }
+ });
+};
+
export const init = () => {
dropdownFix();
autoFocus();
+ tabElementFix();
};
diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js
index c5b8ea582b7..f92a379731c 100644
--- a/theme/boost/amd/src/loader.js
+++ b/theme/boost/amd/src/loader.js
@@ -43,9 +43,12 @@ const rememberTabs = () => {
location.hash = hash;
}
});
- var hash = window.location.hash;
+ const hash = window.location.hash;
if (hash) {
- $('.nav-link[href="' + hash + '"]').tab('show');
+ const tab = document.querySelector('.nav-link[href="' + hash + '"]');
+ if (tab) {
+ tab.click();
+ }
}
};
@@ -83,6 +86,9 @@ const pendingPromise = new Pending('theme_boost/loader:init');
// Add pending promise event listeners to relevant Bootstrap custom events.
setupBootstrapPendingChecks();
+// Setup Aria helpers for Bootstrap features.
+Aria.init();
+
// Remember the last visited tabs.
rememberTabs();
@@ -98,9 +104,6 @@ enableTooltips();
// Disables flipping the dropdowns up and getting hidden behind the navbar.
$.fn.dropdown.Constructor.Default.flip = false;
-// Setup Aria helpers for Bootstrap features.
-Aria.init();
-
pendingPromise.resolve();
export {
diff --git a/theme/boost/templates/admin_setting_tabs.mustache b/theme/boost/templates/admin_setting_tabs.mustache
index ec25e9fd81c..162314a4ad9 100644
--- a/theme/boost/templates/admin_setting_tabs.mustache
+++ b/theme/boost/templates/admin_setting_tabs.mustache
@@ -39,7 +39,9 @@