Merge branch 'MDL-69301-master' of git://github.com/rezaies/moodle

This commit is contained in:
Adrian Greeve 2020-12-07 10:05:55 +08:00
commit b4e6552776
14 changed files with 271 additions and 249 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),resourceTabNav:b("region","resources-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)"}};a.default=c;return a.default});
define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)"}};a.default=c;return a.default});
//# sourceMappingURL=selectors.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -216,7 +216,6 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa
const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);
toggleFocusableChooserOption(firstChooserOption, true);
initTabsKeyboardNavigation(body);
initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);
return body;
@ -235,77 +234,6 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa
.catch();
};
/**
* Initialise the keyboard navigation controls for the tab list items.
*
* @method initTabsKeyboardNavigation
* @param {HTMLElement} body Our modal that we are working with
*/
const initTabsKeyboardNavigation = (body) => {
// Set up the tab handlers.
const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);
const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);
const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);
const activityTabNav = body.querySelector(selectors.regions.activityTabNav);
const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);
const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];
tabNavArray.forEach((element) => {
return element.addEventListener('keydown', (e) => {
// The first visible navigation tab link.
const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);
// The last navigation tab link. It would always be the default activities tab link.
const lastLink = e.target.parentElement.lastElementChild;
if (e.keyCode === arrowRight) {
const nextLink = e.target.nextElementSibling;
if (nextLink === null) {
e.target.tabIndex = -1;
firstLink.tabIndex = 0;
firstLink.focus();
} else if (nextLink.classList.contains('d-none')) {
e.target.tabIndex = -1;
lastLink.tabIndex = 0;
lastLink.focus();
} else {
e.target.tabIndex = -1;
nextLink.tabIndex = 0;
nextLink.focus();
}
}
if (e.keyCode === arrowLeft) {
const previousLink = e.target.previousElementSibling;
if (previousLink === null) {
e.target.tabIndex = -1;
lastLink.tabIndex = 0;
lastLink.focus();
} else if (previousLink.classList.contains('d-none')) {
e.target.tabIndex = -1;
firstLink.tabIndex = 0;
firstLink.focus();
} else {
e.target.tabIndex = -1;
previousLink.tabIndex = 0;
previousLink.focus();
}
}
if (e.keyCode === home) {
e.target.tabIndex = -1;
firstLink.tabIndex = 0;
firstLink.focus();
}
if (e.keyCode === end) {
e.target.tabIndex = -1;
lastLink.tabIndex = 0;
lastLink.focus();
}
if (e.keyCode === space) {
e.preventDefault();
e.target.click();
}
});
});
};
/**
* Initialise the keyboard navigation controls for the chooser options.
*

View File

@ -52,10 +52,8 @@ export default {
help: getDataSelector('region', 'help'),
modules: getDataSelector('region', 'modules'),
favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),
recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),
defaultTabNav: getDataSelector('region', 'default-tab-nav'),
activityTabNav: getDataSelector('region', 'activity-tab-nav'),
resourceTabNav: getDataSelector('region', 'resources-tab-nav'),
favouriteTab: getDataSelector('region', 'favourites'),
recommendedTab: getDataSelector('region', 'recommended'),
defaultTab: getDataSelector('region', 'default'),

View File

@ -40,7 +40,7 @@
<ul class="nav nav-tabs" role="tablist">
<!-- First the top most node and immediate children -->
<li class="nav-item">
<a class="nav-link active" href="#link{{node.key}}" data-toggle="tab" role="tab">{{node.text}}</a>
<a class="nav-link active" href="#link{{node.key}}" data-toggle="tab" role="tab" aria-selected="true">{{node.text}}</a>
</li>
<!-- Now the first level children with sub nodes -->
{{#node.children}}
@ -48,7 +48,7 @@
{{#display}}
{{^is_short_branch}}
<li class="nav-item">
<a class="nav-link" href="#link{{key}}" data-toggle="tab" role="tab">{{text}}</a>
<a class="nav-link" href="#link{{key}}" data-toggle="tab" role="tab" aria-selected="false" tabindex="-1">{{text}}</a>
</li>
{{/is_short_branch}}
{{/display}}

View File

@ -1,2 +1,2 @@
define ("theme_boost/aria",["jquery","core/pending"],function(a,b){return{init:function init(){var c=!1,d=function(){c=!0},e=function(){var a=c;c=!1;return a};a("[data-toggle=\"dropdown\"]").keydown(function(b){var c=b.which||b.keyCode,e;if(38==c){d()}if(27==c){e=a(b.target).attr("aria-expanded");b.preventDefault();if("false"==e){a(b.target).click()}}if(32==c||13==c){b.preventDefault();a(b.target).click()}});var f=function(c){var d=function(b){a(this).focus();b.resolve()}.bind(c);setTimeout(d,50,new b("core/aria:delayed-focus"))};a(".dropdown").on("shown.bs.dropdown",function(b){var c=a(b.target).find("[role=\"menu\"]"),d=!1,g=!1;if(c){d=a(c).find("[role=\"menuitem\"]")}if(d&&0<d.length){if(e()){g=d[d.length-1]}else{g=d[0]}}if(g){f(g)}});a(".dropdown [role=\"menu\"] [role=\"menuitem\"]").keypress(function(b){var c=String.fromCharCode(b.which||b.keyCode),d=a(b.target).closest("[role=\"menu\"]"),e=0,g=!1,h,j;if(!d){return}g=a(d).find("[role=\"menuitem\"]");if(!g){return}c=c.toLowerCase();for(e=0;e<g.length;e++){h=a(g[e]);j=h.text().trim().toLowerCase();if(0==j.indexOf(c)){f(h);break}}});a(".dropdown [role=\"menu\"] [role=\"menuitem\"]").keydown(function(b){var c=b.which||b.keyCode,d=!1,e=a(b.target).closest("[role=\"menu\"]"),g=0,h=!1;if(!e){return}h=a(e).find("[role=\"menuitem\"]");if(!h){return}if(40==c){for(g=0;g<h.length-1;g++){if(h[g]==b.target){d=h[g+1];break}}if(!d){d=h[0]}}else if(38==c){for(g=1;g<h.length;g++){if(h[g]==b.target){d=h[g-1];break}}if(!d){d=h[h.length-1]}}else if(36==c){d=h[0]}else if(35==c){d=h[h.length-1]}if(d){b.preventDefault();f(d)}});a(".dropdown").on("hidden.bs.dropdown",function(b){var c=a(b.target).find("[data-toggle=\"dropdown\"]");if(c){f(c)}});window.addEventListener("load",function(){var a=document.querySelectorAll("[data-aria-autofocus=\"true\"][role=\"alert\"]");Array.prototype.forEach.call(a,function(a){a.innerHTML+=" ";a.removeAttribute("data-aria-autofocus")})})}}});
define ("theme_boost/aria",["exports","jquery","core/pending"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}var e=function(){var a=!1,d=function(){a=!0},e=function(){var b=a;a=!1;return b};document.addEventListener("keydown",function(a){if(a.target.matches("[data-toggle=\"dropdown\"]")){var b=a.key;if("ArrowUp"==b){d()}if("Escape"==b){var c=a.target.getAttribute("aria-expanded");a.preventDefault();if("false"==c){a.target.click()}}if(" "==b||"Enter"==b){a.preventDefault();a.target.click()}}});var f=function(a){setTimeout(function delayedFocus(b){a.focus();b.resolve()},50,new c.default("core/aria:delayed-focus"))};(0,b.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<c.length){if(e()){d=c[c.length-1]}else{d=c[0]}}if(d){f(d)}});document.addEventListener("keypress",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var g=a.target.closest("[role=\"menu\"]");if(!g){return}var h=g.querySelectorAll("[role=\"menuitem\"]");if(!h){return}for(var b=a.key.toLowerCase(),c=0;c<h.length;c++){var d=h[c],e=d.text.trim().toLowerCase();if(0==e.indexOf(b)){f(d);break}}}});document.addEventListener("keydown",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var b=a.key,c=!1,d=a.target.closest("[role=\"menu\"]");if(!d){return}var e=d.querySelectorAll("[role=\"menuitem\"]");if(!e){return}if("ArrowDown"==b){for(var g=0;g<e.length-1;g++){if(e[g]==a.target){c=e[g+1];break}}if(!c){c=e[0]}}else if("ArrowUp"==b){for(var h=1;h<e.length;h++){if(e[h]==a.target){c=e[h-1];break}}if(!c){c=e[e.length-1]}}else if("Home"==b){c=e[0]}else if("End"==b){c=e[e.length-1]}if(c){a.preventDefault();f(c)}}});(0,b.default)(".dropdown").on("hidden.bs.dropdown",function(a){var b=a.target.querySelector("[data-toggle=\"dropdown\"]");if(b){f(b)}})},f=function(){window.addEventListener("load",function(){var a=document.querySelectorAll("[data-aria-autofocus=\"true\"][role=\"alert\"]");Array.prototype.forEach.call(a,function(a){a.innerHTML+=" ";a.removeAttribute("data-aria-autofocus")})})},g=function(a){for(var c=a.target.closest("[role=\"tablist\"]"),d="vertical"==c.getAttribute("aria-orientation"),e=window.right_to_left(),f=d?"ArrowDown":e?"ArrowLeft":"ArrowRight",g=d?"ArrowUp":e?"ArrowRight":"ArrowLeft",h=Array.prototype.filter.call(c.querySelectorAll("[role=\"tab\"]"),function(a){return"none"!==getComputedStyle(a).display}),j=0;j<h.length;j++){h[j].index=j}switch(a.key){case f:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index+1]){h[a.target.index+1].focus()}else{h[0].focus()}break;case g:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index-1]){h[a.target.index-1].focus()}else{h[h.length-1].focus()}break;case"Home":a.preventDefault();h[0].focus();break;case"End":a.preventDefault();h[h.length-1].focus();break;case"Enter":case" ":a.preventDefault();(0,b.default)(a.target).tab("show");h.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0;}},h=function(){document.addEventListener("keydown",function(a){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(a.key)){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){g(a)}}});document.addEventListener("click",function(a){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){var c=a.target.closest("[role=\"tablist\"]").querySelectorAll("[role=\"tab\"]");a.preventDefault();(0,b.default)(a.target).tab("show");c.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0}})};a.init=function init(){e();f();h()}});
//# sourceMappingURL=aria.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
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=h(b);c=h(c);d=h(d);e=h(e);f=h(f);g=h(g);function h(a){return a&&a.__esModule?a:{default:a}}var i=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")}},j=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")}})},k=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},l=new e.default("theme_boost/loader:init");(0,g.default)();i();j();k();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;c.default.init();l.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

View File

@ -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":"qSA0BA,OACA,OACA,OACA,OACA,OACA,O,sDAKMA,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,UAAKP,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 <http://www.gnu.org/licenses/>.\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 <damyon@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport 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"}
{"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 <http://www.gnu.org/licenses/>.\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 <damyon@moodle.com>\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"}

View File

@ -20,181 +20,272 @@
* @copyright 2018 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/pending'], function($, Pending) {
return {
init: function() {
// Drop downs from bootstrap don't support keyboard accessibility by default.
var focusEnd = false,
setFocusEnd = function() {
focusEnd = true;
},
getFocusEnd = function() {
var result = focusEnd;
focusEnd = false;
return result;
};
// Special handling for "up" keyboard control.
$('[data-toggle="dropdown"]').keydown(function(e) {
var trigger = e.which || e.keyCode,
expanded;
import $ from 'jquery';
import Pending from 'core/pending';
// Up key opens the menu at the end.
if (trigger == 38) {
// Focus the end of the menu, not the beginning.
setFocusEnd();
/**
* Drop downs from bootstrap don't support keyboard accessibility by default.
*/
const dropdownFix = () => {
let focusEnd = false;
const setFocusEnd = () => {
focusEnd = true;
};
const getFocusEnd = () => {
const result = focusEnd;
focusEnd = false;
return result;
};
// Special handling for "up" keyboard control.
document.addEventListener('keydown', e => {
if (e.target.matches('[data-toggle="dropdown"]')) {
const trigger = e.key;
// Up key opens the menu at the end.
if (trigger == 'ArrowUp') {
// Focus the end of the menu, not the beginning.
setFocusEnd();
}
// Escape key only closes the menu, it doesn't open it.
if (trigger == 'Escape') {
const expanded = e.target.getAttribute('aria-expanded');
e.preventDefault();
if (expanded == "false") {
e.target.click();
}
}
// Escape key only closes the menu, it doesn't open it.
if (trigger == 27) {
expanded = $(e.target).attr('aria-expanded');
e.preventDefault();
if (expanded == "false") {
$(e.target).click();
}
}
// Space key or Enter key opens the menu.
if (trigger == ' ' || trigger == 'Enter') {
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
e.target.click();
}
}
});
// Space key or Enter key opens the menu.
if (trigger == 32 || trigger == 13) {
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
$(e.target).click();
}
});
// Special handling for navigation keys when menu is open.
const shiftFocus = element => {
const delayedFocus = pendingPromise => {
element.focus();
pendingPromise.resolve();
};
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
};
// Special handling for navigation keys when menu is open.
var shiftFocus = function(element) {
var delayedFocus = function(pendingPromise) {
$(this).focus();
pendingPromise.resolve();
}.bind(element);
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
};
$('.dropdown').on('shown.bs.dropdown', e => {
// We need to focus on the first menuitem.
const menu = e.target.querySelector('[role="menu"]');
let menuItems = false;
let foundMenuItem = false;
$('.dropdown').on('shown.bs.dropdown', function(e) {
// We need to focus on the first menuitem.
var menu = $(e.target).find('[role="menu"]'),
menuItems = false,
foundMenuItem = false;
if (menu) {
menuItems = menu.querySelectorAll('[role="menuitem"]');
}
if (menuItems && menuItems.length > 0) {
if (getFocusEnd()) {
foundMenuItem = menuItems[menuItems.length - 1];
} else {
// The first menu entry, pretty reasonable.
foundMenuItem = menuItems[0];
}
}
if (foundMenuItem) {
shiftFocus(foundMenuItem);
}
});
// Search for menu items by finding the first item that has
// text starting with the typed character (case insensitive).
document.addEventListener('keypress', e => {
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
const menu = e.target.closest('[role="menu"]');
if (!menu) {
return;
}
const menuItems = menu.querySelectorAll('[role="menuitem"]');
if (!menuItems) {
return;
}
if (menu) {
menuItems = $(menu).find('[role="menuitem"]');
}
if (menuItems && menuItems.length > 0) {
if (getFocusEnd()) {
foundMenuItem = menuItems[menuItems.length - 1];
} else {
// The first menu entry, pretty reasonable.
foundMenuItem = menuItems[0];
}
}
if (foundMenuItem) {
shiftFocus(foundMenuItem);
}
});
// Search for menu items by finding the first item that has
// text starting with the typed character (case insensitive).
$('.dropdown [role="menu"] [role="menuitem"]').keypress(function(e) {
var trigger = String.fromCharCode(e.which || e.keyCode),
menu = $(e.target).closest('[role="menu"]'),
i = 0,
menuItems = false,
item,
itemText;
const trigger = e.key.toLowerCase();
if (!menu) {
return;
}
menuItems = $(menu).find('[role="menuitem"]');
if (!menuItems) {
return;
for (let i = 0; i < menuItems.length; i++) {
const item = menuItems[i];
const itemText = item.text.trim().toLowerCase();
if (itemText.indexOf(trigger) == 0) {
shiftFocus(item);
break;
}
}
}
});
trigger = trigger.toLowerCase();
for (i = 0; i < menuItems.length; i++) {
item = $(menuItems[i]);
itemText = item.text().trim().toLowerCase();
if (itemText.indexOf(trigger) == 0) {
shiftFocus(item);
// Keyboard navigation for arrow keys, home and end keys.
document.addEventListener('keydown', e => {
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
const trigger = e.key;
let next = false;
const menu = e.target.closest('[role="menu"]');
if (!menu) {
return;
}
const menuItems = menu.querySelectorAll('[role="menuitem"]');
if (!menuItems) {
return;
}
// Down key.
if (trigger == 'ArrowDown') {
for (let i = 0; i < menuItems.length - 1; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i + 1];
break;
}
}
});
// Keyboard navigation for arrow keys, home and end keys.
$('.dropdown [role="menu"] [role="menuitem"]').keydown(function(e) {
var trigger = e.which || e.keyCode,
next = false,
menu = $(e.target).closest('[role="menu"]'),
i = 0,
menuItems = false;
if (!menu) {
return;
}
menuItems = $(menu).find('[role="menuitem"]');
if (!menuItems) {
return;
}
// Down key.
if (trigger == 40) {
for (i = 0; i < menuItems.length - 1; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i + 1];
break;
}
}
if (!next) {
// Wrap to first item.
next = menuItems[0];
}
} else if (trigger == 38) {
// Up key.
for (i = 1; i < menuItems.length; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i - 1];
break;
}
}
if (!next) {
// Wrap to last item.
next = menuItems[menuItems.length - 1];
}
} else if (trigger == 36) {
// Home key.
if (!next) {
// Wrap to first item.
next = menuItems[0];
}
} else if (trigger == 35) {
// End key.
} else if (trigger == 'ArrowUp') {
// Up key.
for (let i = 1; i < menuItems.length; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i - 1];
break;
}
}
if (!next) {
// Wrap to last item.
next = menuItems[menuItems.length - 1];
}
// Variable next is set if we do want to act on the keypress.
if (next) {
e.preventDefault();
shiftFocus(next);
}
return;
});
$('.dropdown').on('hidden.bs.dropdown', function(e) {
// We need to focus on the menu trigger.
var trigger = $(e.target).find('[data-toggle="dropdown"]');
if (trigger) {
shiftFocus(trigger);
}
});
// After page load, focus on any element with special autofocus attribute.
window.addEventListener("load", () => {
const alerts = document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');
Array.prototype.forEach.call(alerts, autofocusElement => {
// According to the specification an role="alert" region is only read out on change to the content
// of that region.
autofocusElement.innerHTML += ' ';
autofocusElement.removeAttribute('data-aria-autofocus');
});
});
} else if (trigger == 'Home') {
// Home key.
next = menuItems[0];
} else if (trigger == 'End') {
// End key.
next = menuItems[menuItems.length - 1];
}
// Variable next is set if we do want to act on the keypress.
if (next) {
e.preventDefault();
shiftFocus(next);
}
return;
}
};
});
});
$('.dropdown').on('hidden.bs.dropdown', e => {
// We need to focus on the menu trigger.
const trigger = e.target.querySelector('[data-toggle="dropdown"]');
if (trigger) {
shiftFocus(trigger);
}
});
};
/**
* After page load, focus on any element with special autofocus attribute.
*/
const autoFocus = () => {
window.addEventListener("load", () => {
const alerts = document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');
Array.prototype.forEach.call(alerts, autofocusElement => {
// According to the specification an role="alert" region is only read out on change to the content
// of that region.
autofocusElement.innerHTML += ' ';
autofocusElement.removeAttribute('data-aria-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.key) {
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 ' ':
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', ' '].includes(e.key)) {
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();
};

View File

@ -25,7 +25,7 @@
*/
import $ from 'jquery';
import Aria from './aria';
import * as Aria from './aria';
import Bootstrap from './bootstrap/index';
import Pending from 'core/pending';
import Scroll from './scroll';
@ -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 {

View File

@ -39,7 +39,9 @@
<ul class="nav nav-tabs" role="tablist">
{{#tabs}}
<li class="nav-item">
<a href="#{{name}}" class="nav-link {{#active}}active{{/active}}" data-toggle="tab" role="tab">{{displayname}}</a>
<a href="#{{name}}" class="nav-link {{#active}}active{{/active}}" data-toggle="tab" role="tab"
{{#active}}aria-selected="true"{{/active}}
{{^active}}aria-selected="false" tabindex="-1"{{/active}}>{{displayname}}</a>
</li>
{{/tabs}}
</ul>