From e7bfbaf7b2d49d3f9dc2ceb7873cac73fce286fe Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 17 Dec 2019 10:44:32 +0800 Subject: [PATCH 01/13] MDL-70151 assign: Fix bulk change for behat Behat does not play well with confirmation dialogues and in some situations it does not wait for the page to reload before starting the next step. This change adds a pending_js call which is only resolved if the cancel button was pressed, otherwise it remains in-progress until the page reloads resetting the entire state of it. --- mod/assign/module.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mod/assign/module.js b/mod/assign/module.js index 111003ba61d..9b7ad6b3c7e 100644 --- a/mod/assign/module.js +++ b/mod/assign/module.js @@ -76,6 +76,7 @@ M.mod_assign.init_grading_table = function(Y) { var batchform = Y.one('form.gradingbatchoperationsform'); if (batchform) { batchform.on('submit', function(e) { + M.util.js_pending('mod_assign/module.js:batch:submit'); checkboxes = Y.all('td.c0 input'); var selectedusers = []; checkboxes.each(function(node) { @@ -102,8 +103,10 @@ M.mod_assign.init_grading_table = function(Y) { confirmmessage = M.util.get_string('batchoperationconfirm' + operation.get('value'), 'assign'); } if (!confirm(confirmmessage)) { + M.util.js_complete('mod_assign/module.js:batch:submit'); e.preventDefault(); } + // Note: Do not js_complete. The page being reloaded will empty it. } }); } From b5b562b7eeca0e1c552289e9c6d840ddee0b7a55 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 23 Nov 2020 20:45:46 +0000 Subject: [PATCH 02/13] MDL-70285 completion upgrade: fix performance of the MDL-69687 code --- lib/db/upgrade.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 8c462379092..25e5433498d 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2786,11 +2786,12 @@ function xmldb_main_upgrade($oldversion) { if ($oldversion < 2020101600.01) { // Delete orphaned course_modules_completion rows; these were not deleted properly // by remove_course_contents function. - $DB->delete_records_subquery('course_modules_completion', 'id', 'id', - "SELECT cmc.id - FROM {course_modules_completion} cmc - LEFT JOIN {course_modules} cm ON cm.id = cmc.coursemoduleid - WHERE cm.id IS NULL"); + $DB->delete_records_select('course_modules_completion', " + NOT EXISTS ( + SELECT 1 + FROM {course_modules} cm + WHERE cm.id = {course_modules_completion}.coursemoduleid + )"); upgrade_main_savepoint(true, 2020101600.01); } From cb82824f6a28ba99f8cfc8c226fe7f594f03b81b Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 24 Nov 2020 17:20:12 +0000 Subject: [PATCH 03/13] MDL-70320 permission overrides: role names were double-escaped --- .../tests/behat/edit_permissions.feature | 38 ++++++++++++++++--- lib/amd/build/permissionmanager.min.js | 2 +- lib/amd/build/permissionmanager.min.js.map | 2 +- lib/amd/src/permissionmanager.js | 2 +- .../permissionmanager_panelcontent.mustache | 7 ++-- lib/templates/permissionmanager_role.mustache | 5 ++- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/admin/tool/behat/tests/behat/edit_permissions.feature b/admin/tool/behat/tests/behat/edit_permissions.feature index e3ffc92aef1..52599d31dca 100644 --- a/admin/tool/behat/tests/behat/edit_permissions.feature +++ b/admin/tool/behat/tests/behat/edit_permissions.feature @@ -6,14 +6,18 @@ Feature: Edit capabilities Background: Given the following "users" exist: - | username | firstname | lastname | email | - | teacher1 | Teacher | 1 | teacher1@example.com | + | username | firstname | lastname | + | teacher1 | Teacher | 1 | + | tutor | Teaching | Assistant | + | student | Student | One | And the following "courses" exist: - | fullname | shortname | category | - | Course 1 | C1 | 0 | + | fullname | shortname | + | Course 1 | C1 | And the following "course enrolments" exist: - | user | course | role | - | teacher1 | C1 | editingteacher | + | user | course | role | + | teacher1 | C1 | editingteacher | + | tutor | C1 | teacher | + | student | C1 | student | Scenario: Default system capabilities modification Given I log in as "admin" @@ -60,3 +64,25 @@ Feature: Edit capabilities Then "mod/forum:deleteanypost" capability has "Prohibit" permission And "mod/forum:editanypost" capability has "Prevent" permission And "mod/forum:addquestion" capability has "Allow" permission + + @javascript + Scenario: Edit permissions escapes role names correctly + When I am on the "C1" "Course" page logged in as "admin" + And I navigate to "Edit settings" in current page administration + And I set the following fields to these values: + | Your word for 'Teacher' | Teacher >= editing | + | Your word for 'Non-editing teacher' | Teacher < "editing" | + | Your word for 'Student' | Studier & 'learner' | + And I press "Save and display" + And I navigate to course participants + Then I should see "Teacher >= editing (Teacher)" in the "Teacher 1" "table_row" + And I should see "Teacher < \"editing\" (Non-editing teacher)" in the "Teaching Assistant" "table_row" + And I should see "Studier & 'learner' (Student)" in the "Student One" "table_row" + And I navigate to "Permissions" in current page administration + And I should see "Teacher >= editing" in the "mod/forum:replypost" "table_row" + And I should see "Teacher < \"editing\"" in the "mod/forum:replypost" "table_row" + And I should see "Studier & 'learner'" in the "mod/forum:replypost" "table_row" + And I follow "Prohibit" + And "Teacher >= editing" "button" in the "Prohibit role" "dialogue" should be visible + And "Teacher < \"editing\"" "button" in the "Prohibit role" "dialogue" should be visible + And "Studier & 'learner'" "button" in the "Prohibit role" "dialogue" should be visible diff --git a/lib/amd/build/permissionmanager.min.js b/lib/amd/build/permissionmanager.min.js index f53bd414708..3e200156772 100644 --- a/lib/amd/build/permissionmanager.min.js +++ b/lib/amd/build/permissionmanager.min.js @@ -1,2 +1,2 @@ -define ("core/permissionmanager",["jquery","core/config","core/notification","core/templates","core/yui"],function(a,b,c,d,e){var f={ADDROLE:"a.allowlink, a.prohibitlink",REMOVEROLE:"a.preventlink, a.unprohibitlink",UNPROHIBIT:"a.unprohibitlink"},g=a.Event("rolesloaded"),h,j,k,l,m=null,n=function loadOverideableRoles(){var d={contextid:h,getroles:1,sesskey:b.sesskey};a.post(k+"roles/ajax.php",d,null,"json").done(function(b){try{l=b;n=function loadOverideableRoles(){a("body").trigger(g)};n()}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},o=function(b,e,f){var g={contextid:h,roleid:e,sesskey:M.cfg.sesskey,action:f,capability:b.data("name")};a.post(k+"roles/ajax.php",g,null,"json").done(function(f){var g=f;try{var h={rolename:l[e],roleid:e,adminurl:k,imageurl:M.util.image_url("t/delete","moodle")};switch(g){case"allow":h.spanclass="allowed";h.linkclass="preventlink";h.action="prevent";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prohibit":h.spanclass="forbidden";h.linkclass="unprohibitlink";h.action="unprohibit";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prevent":b.find("a[data-role-id=\""+e+"\"]").first().closest(".allowed").remove();return;case"unprohibit":b.find("a[data-role-id=\""+e+"\"]").first().closest(".forbidden").remove();return;default:return;}d.render("core/permissionmanager_role",h).done(function(c){if("allow"==g){a(c).insertBefore(b.find(".allowmore:first"))}else if("prohibit"==g){a(c).insertBefore(b.find(".prohibitmore:first"));var d=b.find(".allowedroles").first().find("a[data-role-id=\""+e+"\"]");if(d){d.first().closest(".allowed").remove()}}m.hide()}).fail(c.exception)}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},p=function(b){b.preventDefault();var g=a(b.currentTarget);a("body").one("rolesloaded",function(){e.use("moodle-core-notification-dialogue",function(){var b=g.data("action"),h=g.closest("tr.rolecap"),k={cap:h.data("humanname"),context:j},n=M.util.get_string("role"+b+"info","core_role",k);if(null===m){m=new M.core.dialogue({draggable:!0,modal:!0,closeButton:!0,width:"450px"})}m.set("headerContent",M.util.get_string("role"+b+"header","core_role"));var p,e,q=[];switch(b){case"allow":e=h.find(f.REMOVEROLE);break;case"prohibit":e=h.find(f.UNPROHIBIT);break;}for(p in l){var r="",s=e.filter("[data-role-id='"+p+"']").length;if(s){r="disabled"}var t={roleid:p,rolename:l[p],disabled:r};q.push(t)}d.render("core/permissionmanager_panelcontent",{message:n,roles:q}).done(function(c){m.set("bodyContent",c);m.show();a("div.role_buttons").on("click","input",function(c){var d=a(c.currentTarget).data("role-id");o(h,d,b)})}).fail(c.exception)})});n()},q=function(b){b.preventDefault();var d=a(b.currentTarget);a("body").one("rolesloaded",function(){var a=d.data("action"),b=d.data("role-id"),e=d.closest("tr.rolecap"),f={role:l[b],cap:e.data("humanname"),context:j};c.confirm(M.util.get_string("confirmunassigntitle","core_role"),M.util.get_string("confirmrole"+a,"core_role",f),M.util.get_string("confirmunassignyes","core_role"),M.util.get_string("confirmunassignno","core_role"),function(){o(e,b,a)})});n()};return{initialize:function initialize(b){h=b.contextid;j=b.contextname;k=b.adminurl;var c=a("body");c.on("click",f.ADDROLE,p);c.on("click",f.REMOVEROLE,q)}}}); +define ("core/permissionmanager",["jquery","core/config","core/notification","core/templates","core/yui"],function(a,b,c,d,e){var f={ADDROLE:"a.allowlink, a.prohibitlink",REMOVEROLE:"a.preventlink, a.unprohibitlink",UNPROHIBIT:"a.unprohibitlink"},g=a.Event("rolesloaded"),h,j,k,l,m=null,n=function loadOverideableRoles(){var d={contextid:h,getroles:1,sesskey:b.sesskey};a.post(k+"roles/ajax.php",d,null,"json").done(function(b){try{l=b;n=function loadOverideableRoles(){a("body").trigger(g)};n()}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},o=function(b,e,f){var g={contextid:h,roleid:e,sesskey:M.cfg.sesskey,action:f,capability:b.data("name")};a.post(k+"roles/ajax.php",g,null,"json").done(function(f){var g=f;try{var h={rolename:l[e],roleid:e,adminurl:k,imageurl:M.util.image_url("t/delete","moodle")};switch(g){case"allow":h.spanclass="allowed";h.linkclass="preventlink";h.action="prevent";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prohibit":h.spanclass="forbidden";h.linkclass="unprohibitlink";h.action="unprohibit";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prevent":b.find("a[data-role-id=\""+e+"\"]").first().closest(".allowed").remove();return;case"unprohibit":b.find("a[data-role-id=\""+e+"\"]").first().closest(".forbidden").remove();return;default:return;}d.render("core/permissionmanager_role",h).done(function(c){if("allow"==g){a(c).insertBefore(b.find(".allowmore:first"))}else if("prohibit"==g){a(c).insertBefore(b.find(".prohibitmore:first"));var d=b.find(".allowedroles").first().find("a[data-role-id=\""+e+"\"]");if(d){d.first().closest(".allowed").remove()}}m.hide()}).fail(c.exception)}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},p=function(b){b.preventDefault();var g=a(b.currentTarget);a("body").one("rolesloaded",function(){e.use("moodle-core-notification-dialogue",function(){var b=g.data("action"),h=g.closest("tr.rolecap"),k={cap:h.data("humanname"),context:j},n=M.util.get_string("role"+b+"info","core_role",k);if(null===m){m=new M.core.dialogue({draggable:!0,modal:!0,closeButton:!0,width:"450px"})}m.set("headerContent",M.util.get_string("role"+b+"header","core_role"));var p,e,q=[];switch(b){case"allow":e=h.find(f.REMOVEROLE);break;case"prohibit":e=h.find(f.UNPROHIBIT);break;}for(p in l){var r="",s=e.filter("[data-role-id='"+p+"']").length;if(s){r="disabled"}var t={roleid:p,rolename:l[p],disabled:r};q.push(t)}d.render("core/permissionmanager_panelcontent",{message:n,roles:q}).done(function(c){m.set("bodyContent",c);m.show();a("div.role_buttons").on("click","button",function(c){var d=a(c.currentTarget).data("role-id");o(h,d,b)})}).fail(c.exception)})});n()},q=function(b){b.preventDefault();var d=a(b.currentTarget);a("body").one("rolesloaded",function(){var a=d.data("action"),b=d.data("role-id"),e=d.closest("tr.rolecap"),f={role:l[b],cap:e.data("humanname"),context:j};c.confirm(M.util.get_string("confirmunassigntitle","core_role"),M.util.get_string("confirmrole"+a,"core_role",f),M.util.get_string("confirmunassignyes","core_role"),M.util.get_string("confirmunassignno","core_role"),function(){o(e,b,a)})});n()};return{initialize:function initialize(b){h=b.contextid;j=b.contextname;k=b.adminurl;var c=a("body");c.on("click",f.ADDROLE,p);c.on("click",f.REMOVEROLE,q)}}}); //# sourceMappingURL=permissionmanager.min.js.map diff --git a/lib/amd/build/permissionmanager.min.js.map b/lib/amd/build/permissionmanager.min.js.map index 8413c766c4f..63e284bc594 100644 --- a/lib/amd/build/permissionmanager.min.js.map +++ b/lib/amd/build/permissionmanager.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/permissionmanager.js"],"names":["define","$","config","notification","templates","Y","SELECTORS","ADDROLE","REMOVEROLE","UNPROHIBIT","rolesloadedevent","Event","contextid","contextname","adminurl","overideableroles","panel","loadOverideableRoles","params","getroles","sesskey","post","done","data","trigger","err","exception","fail","jqXHR","status","error","changePermissions","row","roleid","action","M","cfg","capability","templatedata","rolename","imageurl","util","image_url","spanclass","linkclass","icon","iconalt","get_string","find","first","closest","remove","render","content","insertBefore","allowedLink","hide","handleAddRole","e","preventDefault","link","currentTarget","one","use","confirmationDetails","cap","context","message","core","dialogue","draggable","modal","closeButton","width","set","i","existingrolelinks","roles","disabled","disable","filter","length","roledetails","push","show","on","handleRemoveRole","questionDetails","role","confirm","initialize","args","body"],"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 * @package core\n * @class permissionmanager\n * @copyright 2015 Martin Mastny \n * @since 3.0\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n /**\n * @module admin/permissionmanager\n */\ndefine(['jquery', 'core/config', 'core/notification', 'core/templates', 'core/yui'],\n function($, config, notification, templates, Y) {\n\n /**\n * Used CSS selectors\n * @access private\n */\n var SELECTORS = {\n ADDROLE: 'a.allowlink, a.prohibitlink',\n REMOVEROLE: 'a.preventlink, a.unprohibitlink',\n UNPROHIBIT: 'a.unprohibitlink'\n };\n var rolesloadedevent = $.Event('rolesloaded');\n var contextid;\n var contextname;\n var adminurl;\n var overideableroles;\n var panel = null;\n\n /**\n * Load all possible roles, which could be assigned from server\n *\n * @access private\n * @method loadOverideableRoles\n */\n var loadOverideableRoles = function() {\n var params = {\n contextid: contextid,\n getroles: 1,\n sesskey: config.sesskey\n };\n\n // Need to tell jQuery to expect JSON as the content type may not be correct (MDL-55041).\n $.post(adminurl + 'roles/ajax.php', params, null, 'json')\n .done(function(data) {\n try {\n overideableroles = data;\n loadOverideableRoles = function() {\n $('body').trigger(rolesloadedevent);\n };\n loadOverideableRoles();\n } catch (err) {\n notification.exception(err);\n }\n })\n .fail(function(jqXHR, status, error) {\n notification.exception(error);\n });\n };\n\n /**\n * Perform the UI changes after server change\n *\n * @access private\n * @method changePermissions\n * @param {JQuery} row\n * @param {int} roleid\n * @param {string} action\n */\n var changePermissions = function(row, roleid, action) {\n var params = {\n contextid: contextid,\n roleid: roleid,\n sesskey: M.cfg.sesskey,\n action: action,\n capability: row.data('name')\n };\n $.post(adminurl + 'roles/ajax.php', params, null, 'json')\n .done(function(data) {\n var action = data;\n try {\n var templatedata = {rolename: overideableroles[roleid],\n roleid: roleid,\n adminurl: adminurl,\n imageurl: M.util.image_url('t/delete', 'moodle')\n };\n switch (action) {\n case 'allow':\n templatedata.spanclass = 'allowed';\n templatedata.linkclass = 'preventlink';\n templatedata.action = 'prevent';\n templatedata.icon = 't/delete';\n templatedata.iconalt = M.util.get_string('deletexrole', 'core_role', overideableroles[roleid]);\n break;\n case 'prohibit':\n templatedata.spanclass = 'forbidden';\n templatedata.linkclass = 'unprohibitlink';\n templatedata.action = 'unprohibit';\n templatedata.icon = 't/delete';\n templatedata.iconalt = M.util.get_string('deletexrole', 'core_role', overideableroles[roleid]);\n break;\n case 'prevent':\n row.find('a[data-role-id=\"' + roleid + '\"]').first().closest('.allowed').remove();\n return;\n case 'unprohibit':\n row.find('a[data-role-id=\"' + roleid + '\"]').first().closest('.forbidden').remove();\n return;\n default:\n return;\n }\n templates.render('core/permissionmanager_role', templatedata)\n .done(function(content) {\n if (action == 'allow') {\n $(content).insertBefore(row.find('.allowmore:first'));\n } else if (action == 'prohibit') {\n $(content).insertBefore(row.find('.prohibitmore:first'));\n // Remove allowed link\n var allowedLink = row.find('.allowedroles').first().find('a[data-role-id=\"' + roleid + '\"]');\n if (allowedLink) {\n allowedLink.first().closest('.allowed').remove();\n }\n }\n panel.hide();\n })\n .fail(notification.exception);\n } catch (err) {\n notification.exception(err);\n }\n })\n .fail(function(jqXHR, status, error) {\n notification.exception(error);\n });\n };\n\n /**\n * Prompts user for selecting a role which is permitted\n *\n * @access private\n * @method handleAddRole\n * @param {event} e\n */\n var handleAddRole = function(e) {\n e.preventDefault();\n\n var link = $(e.currentTarget);\n\n // TODO: MDL-57778 Convert to core/modal.\n $('body').one('rolesloaded', function() {\n Y.use('moodle-core-notification-dialogue', function() {\n var action = link.data('action');\n var row = link.closest('tr.rolecap');\n var confirmationDetails = {\n cap: row.data('humanname'),\n context: contextname\n };\n var message = M.util.get_string('role' + action + 'info', 'core_role', confirmationDetails);\n if (panel === null) {\n panel = new M.core.dialogue({\n draggable: true,\n modal: true,\n closeButton: true,\n width: '450px'\n });\n }\n panel.set('headerContent', M.util.get_string('role' + action + 'header', 'core_role'));\n\n var i, existingrolelinks;\n\n var roles = [];\n switch (action) {\n case 'allow':\n existingrolelinks = row.find(SELECTORS.REMOVEROLE);\n break;\n case 'prohibit':\n existingrolelinks = row.find(SELECTORS.UNPROHIBIT);\n break;\n }\n for (i in overideableroles) {\n var disabled = '';\n var disable = existingrolelinks.filter(\"[data-role-id='\" + i + \"']\").length;\n if (disable) {\n disabled = 'disabled';\n }\n var roledetails = {roleid: i, rolename: overideableroles[i], disabled: disabled};\n roles.push(roledetails);\n }\n\n templates.render('core/permissionmanager_panelcontent', {message: message, roles: roles})\n .done(function(content) {\n panel.set('bodyContent', content);\n panel.show();\n $('div.role_buttons').on('click', 'input', function(e) {\n var roleid = $(e.currentTarget).data('role-id');\n changePermissions(row, roleid, action);\n });\n })\n .fail(notification.exception);\n\n });\n });\n loadOverideableRoles();\n };\n\n /**\n * Prompts user when removing permission\n *\n * @access private\n * @method handleRemoveRole\n * @param {event} e\n */\n var handleRemoveRole = function(e) {\n e.preventDefault();\n var link = $(e.currentTarget);\n $('body').one('rolesloaded', function() {\n var action = link.data('action');\n var roleid = link.data('role-id');\n var row = link.closest('tr.rolecap');\n var questionDetails = {\n role: overideableroles[roleid],\n cap: row.data('humanname'),\n context: contextname\n };\n\n notification.confirm(M.util.get_string('confirmunassigntitle', 'core_role'),\n M.util.get_string('confirmrole' + action, 'core_role', questionDetails),\n M.util.get_string('confirmunassignyes', 'core_role'),\n M.util.get_string('confirmunassignno', 'core_role'),\n function() {\n changePermissions(row, roleid, action);\n }\n );\n });\n loadOverideableRoles();\n };\n\n return /** @alias module:core/permissionmanager */ {\n /**\n * Initialize permissionmanager\n * @access public\n * @param {Object} args\n */\n initialize: function(args) {\n contextid = args.contextid;\n contextname = args.contextname;\n adminurl = args.adminurl;\n var body = $('body');\n body.on('click', SELECTORS.ADDROLE, handleAddRole);\n body.on('click', SELECTORS.REMOVEROLE, handleRemoveRole);\n }\n };\n});\n"],"file":"permissionmanager.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/permissionmanager.js"],"names":["define","$","config","notification","templates","Y","SELECTORS","ADDROLE","REMOVEROLE","UNPROHIBIT","rolesloadedevent","Event","contextid","contextname","adminurl","overideableroles","panel","loadOverideableRoles","params","getroles","sesskey","post","done","data","trigger","err","exception","fail","jqXHR","status","error","changePermissions","row","roleid","action","M","cfg","capability","templatedata","rolename","imageurl","util","image_url","spanclass","linkclass","icon","iconalt","get_string","find","first","closest","remove","render","content","insertBefore","allowedLink","hide","handleAddRole","e","preventDefault","link","currentTarget","one","use","confirmationDetails","cap","context","message","core","dialogue","draggable","modal","closeButton","width","set","i","existingrolelinks","roles","disabled","disable","filter","length","roledetails","push","show","on","handleRemoveRole","questionDetails","role","confirm","initialize","args","body"],"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 * @package core\n * @class permissionmanager\n * @copyright 2015 Martin Mastny \n * @since 3.0\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n /**\n * @module admin/permissionmanager\n */\ndefine(['jquery', 'core/config', 'core/notification', 'core/templates', 'core/yui'],\n function($, config, notification, templates, Y) {\n\n /**\n * Used CSS selectors\n * @access private\n */\n var SELECTORS = {\n ADDROLE: 'a.allowlink, a.prohibitlink',\n REMOVEROLE: 'a.preventlink, a.unprohibitlink',\n UNPROHIBIT: 'a.unprohibitlink'\n };\n var rolesloadedevent = $.Event('rolesloaded');\n var contextid;\n var contextname;\n var adminurl;\n var overideableroles;\n var panel = null;\n\n /**\n * Load all possible roles, which could be assigned from server\n *\n * @access private\n * @method loadOverideableRoles\n */\n var loadOverideableRoles = function() {\n var params = {\n contextid: contextid,\n getroles: 1,\n sesskey: config.sesskey\n };\n\n // Need to tell jQuery to expect JSON as the content type may not be correct (MDL-55041).\n $.post(adminurl + 'roles/ajax.php', params, null, 'json')\n .done(function(data) {\n try {\n overideableroles = data;\n loadOverideableRoles = function() {\n $('body').trigger(rolesloadedevent);\n };\n loadOverideableRoles();\n } catch (err) {\n notification.exception(err);\n }\n })\n .fail(function(jqXHR, status, error) {\n notification.exception(error);\n });\n };\n\n /**\n * Perform the UI changes after server change\n *\n * @access private\n * @method changePermissions\n * @param {JQuery} row\n * @param {int} roleid\n * @param {string} action\n */\n var changePermissions = function(row, roleid, action) {\n var params = {\n contextid: contextid,\n roleid: roleid,\n sesskey: M.cfg.sesskey,\n action: action,\n capability: row.data('name')\n };\n $.post(adminurl + 'roles/ajax.php', params, null, 'json')\n .done(function(data) {\n var action = data;\n try {\n var templatedata = {rolename: overideableroles[roleid],\n roleid: roleid,\n adminurl: adminurl,\n imageurl: M.util.image_url('t/delete', 'moodle')\n };\n switch (action) {\n case 'allow':\n templatedata.spanclass = 'allowed';\n templatedata.linkclass = 'preventlink';\n templatedata.action = 'prevent';\n templatedata.icon = 't/delete';\n templatedata.iconalt = M.util.get_string('deletexrole', 'core_role', overideableroles[roleid]);\n break;\n case 'prohibit':\n templatedata.spanclass = 'forbidden';\n templatedata.linkclass = 'unprohibitlink';\n templatedata.action = 'unprohibit';\n templatedata.icon = 't/delete';\n templatedata.iconalt = M.util.get_string('deletexrole', 'core_role', overideableroles[roleid]);\n break;\n case 'prevent':\n row.find('a[data-role-id=\"' + roleid + '\"]').first().closest('.allowed').remove();\n return;\n case 'unprohibit':\n row.find('a[data-role-id=\"' + roleid + '\"]').first().closest('.forbidden').remove();\n return;\n default:\n return;\n }\n templates.render('core/permissionmanager_role', templatedata)\n .done(function(content) {\n if (action == 'allow') {\n $(content).insertBefore(row.find('.allowmore:first'));\n } else if (action == 'prohibit') {\n $(content).insertBefore(row.find('.prohibitmore:first'));\n // Remove allowed link\n var allowedLink = row.find('.allowedroles').first().find('a[data-role-id=\"' + roleid + '\"]');\n if (allowedLink) {\n allowedLink.first().closest('.allowed').remove();\n }\n }\n panel.hide();\n })\n .fail(notification.exception);\n } catch (err) {\n notification.exception(err);\n }\n })\n .fail(function(jqXHR, status, error) {\n notification.exception(error);\n });\n };\n\n /**\n * Prompts user for selecting a role which is permitted\n *\n * @access private\n * @method handleAddRole\n * @param {event} e\n */\n var handleAddRole = function(e) {\n e.preventDefault();\n\n var link = $(e.currentTarget);\n\n // TODO: MDL-57778 Convert to core/modal.\n $('body').one('rolesloaded', function() {\n Y.use('moodle-core-notification-dialogue', function() {\n var action = link.data('action');\n var row = link.closest('tr.rolecap');\n var confirmationDetails = {\n cap: row.data('humanname'),\n context: contextname\n };\n var message = M.util.get_string('role' + action + 'info', 'core_role', confirmationDetails);\n if (panel === null) {\n panel = new M.core.dialogue({\n draggable: true,\n modal: true,\n closeButton: true,\n width: '450px'\n });\n }\n panel.set('headerContent', M.util.get_string('role' + action + 'header', 'core_role'));\n\n var i, existingrolelinks;\n\n var roles = [];\n switch (action) {\n case 'allow':\n existingrolelinks = row.find(SELECTORS.REMOVEROLE);\n break;\n case 'prohibit':\n existingrolelinks = row.find(SELECTORS.UNPROHIBIT);\n break;\n }\n for (i in overideableroles) {\n var disabled = '';\n var disable = existingrolelinks.filter(\"[data-role-id='\" + i + \"']\").length;\n if (disable) {\n disabled = 'disabled';\n }\n var roledetails = {roleid: i, rolename: overideableroles[i], disabled: disabled};\n roles.push(roledetails);\n }\n\n templates.render('core/permissionmanager_panelcontent', {message: message, roles: roles})\n .done(function(content) {\n panel.set('bodyContent', content);\n panel.show();\n $('div.role_buttons').on('click', 'button', function(e) {\n var roleid = $(e.currentTarget).data('role-id');\n changePermissions(row, roleid, action);\n });\n })\n .fail(notification.exception);\n\n });\n });\n loadOverideableRoles();\n };\n\n /**\n * Prompts user when removing permission\n *\n * @access private\n * @method handleRemoveRole\n * @param {event} e\n */\n var handleRemoveRole = function(e) {\n e.preventDefault();\n var link = $(e.currentTarget);\n $('body').one('rolesloaded', function() {\n var action = link.data('action');\n var roleid = link.data('role-id');\n var row = link.closest('tr.rolecap');\n var questionDetails = {\n role: overideableroles[roleid],\n cap: row.data('humanname'),\n context: contextname\n };\n\n notification.confirm(M.util.get_string('confirmunassigntitle', 'core_role'),\n M.util.get_string('confirmrole' + action, 'core_role', questionDetails),\n M.util.get_string('confirmunassignyes', 'core_role'),\n M.util.get_string('confirmunassignno', 'core_role'),\n function() {\n changePermissions(row, roleid, action);\n }\n );\n });\n loadOverideableRoles();\n };\n\n return /** @alias module:core/permissionmanager */ {\n /**\n * Initialize permissionmanager\n * @access public\n * @param {Object} args\n */\n initialize: function(args) {\n contextid = args.contextid;\n contextname = args.contextname;\n adminurl = args.adminurl;\n var body = $('body');\n body.on('click', SELECTORS.ADDROLE, handleAddRole);\n body.on('click', SELECTORS.REMOVEROLE, handleRemoveRole);\n }\n };\n});\n"],"file":"permissionmanager.min.js"} \ No newline at end of file diff --git a/lib/amd/src/permissionmanager.js b/lib/amd/src/permissionmanager.js index 07721043e58..3f350d30fce 100644 --- a/lib/amd/src/permissionmanager.js +++ b/lib/amd/src/permissionmanager.js @@ -204,7 +204,7 @@ define(['jquery', 'core/config', 'core/notification', 'core/templates', 'core/yu .done(function(content) { panel.set('bodyContent', content); panel.show(); - $('div.role_buttons').on('click', 'input', function(e) { + $('div.role_buttons').on('click', 'button', function(e) { var roleid = $(e.currentTarget).data('role-id'); changePermissions(row, roleid, action); }); diff --git a/lib/templates/permissionmanager_panelcontent.mustache b/lib/templates/permissionmanager_panelcontent.mustache index dc74d2497e4..da3825cd839 100644 --- a/lib/templates/permissionmanager_panelcontent.mustache +++ b/lib/templates/permissionmanager_panelcontent.mustache @@ -27,17 +27,18 @@ Context variables required for this template: * confirmation Confirmation text - * roles array of role details + * roles array of role details. Note: in this array, rolename must have been + prepared for output with format_string, or more likely one of the role API functions like role_fix_names. Example context (json): { "message": "Do you really want to remove Non-editing teacher from the list of allowed roles for capability View added and updated modules in recent activity block?", - "roles": [{"roleid": 1, "rolename": "manager", "disabled":"disabled"}]} + "roles": [{"roleid": 1, "rolename": "Manager", "disabled": "disabled"}]} }} diff --git a/lib/templates/permissionmanager_role.mustache b/lib/templates/permissionmanager_role.mustache index d2a1652dc38..b30865371b8 100644 --- a/lib/templates/permissionmanager_role.mustache +++ b/lib/templates/permissionmanager_role.mustache @@ -27,7 +27,8 @@ * action Context variables required for this template: - * rolename Name of the role rendered + * rolename Name of the role rendered - must have been prepared for output with format_string, + or more likely one of the role API functions like role_fix_names. * roleid Id of the role * action WEhich action is done on click * spanclass class attribute of span @@ -43,7 +44,7 @@ "linkclass": "preventlink", "adminurl" : "http://localhost/moodle/admin/"} }} - {{rolename}}  + {{{rolename}}}  {{#icon}} {{#pix}}{{icon}}, core, {{iconalt}}{{/pix}} From 2cc04e2b29708d455d9f3d165f6dc0f9bcbbea0e Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Wed, 16 Sep 2020 23:25:36 +1000 Subject: [PATCH 04/13] MDL-69301 theme_boost: convert aria.js to es6 module --- theme/boost/amd/build/aria.min.js | 2 +- theme/boost/amd/build/aria.min.js.map | 2 +- theme/boost/amd/build/loader.min.js | 2 +- theme/boost/amd/build/loader.min.js.map | 2 +- theme/boost/amd/src/aria.js | 328 ++++++++++++------------ theme/boost/amd/src/loader.js | 2 +- 6 files changed, 175 insertions(+), 163 deletions(-) diff --git a/theme/boost/amd/build/aria.min.js b/theme/boost/amd/build/aria.min.js index 447fd662e94..e266186de4e 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",["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.\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 */\ndefine(['jquery', 'core/pending'], function($, Pending) {\n return {\n init: function() {\n // Drop downs from bootstrap don't support keyboard accessibility by default.\n var focusEnd = false,\n setFocusEnd = function() {\n focusEnd = true;\n },\n getFocusEnd = function() {\n var result = focusEnd;\n focusEnd = false;\n return result;\n };\n\n // Special handling for \"up\" keyboard control.\n $('[data-toggle=\"dropdown\"]').keydown(function(e) {\n var trigger = e.which || e.keyCode,\n expanded;\n\n // Up key opens the menu at the end.\n if (trigger == 38) {\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 == 27) {\n expanded = $(e.target).attr('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 == 32 || trigger == 13) {\n // Cancel random scroll.\n e.preventDefault();\n // Open the menu instead.\n $(e.target).click();\n }\n });\n\n // Special handling for navigation keys when menu is open.\n var shiftFocus = function(element) {\n var delayedFocus = function(pendingPromise) {\n $(this).focus();\n pendingPromise.resolve();\n }.bind(element);\n setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));\n };\n\n $('.dropdown').on('shown.bs.dropdown', function(e) {\n // We need to focus on the first menuitem.\n var menu = $(e.target).find('[role=\"menu\"]'),\n menuItems = false,\n foundMenuItem = false;\n\n if (menu) {\n menuItems = $(menu).find('[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 $('.dropdown [role=\"menu\"] [role=\"menuitem\"]').keypress(function(e) {\n var trigger = String.fromCharCode(e.which || e.keyCode),\n menu = $(e.target).closest('[role=\"menu\"]'),\n i = 0,\n menuItems = false,\n item,\n itemText;\n\n if (!menu) {\n return;\n }\n menuItems = $(menu).find('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n\n trigger = trigger.toLowerCase();\n for (i = 0; i < menuItems.length; i++) {\n item = $(menuItems[i]);\n itemText = item.text().trim().toLowerCase();\n if (itemText.indexOf(trigger) == 0) {\n shiftFocus(item);\n break;\n }\n }\n });\n\n // Keyboard navigation for arrow keys, home and end keys.\n $('.dropdown [role=\"menu\"] [role=\"menuitem\"]').keydown(function(e) {\n var trigger = e.which || e.keyCode,\n next = false,\n menu = $(e.target).closest('[role=\"menu\"]'),\n i = 0,\n menuItems = false;\n if (!menu) {\n return;\n }\n menuItems = $(menu).find('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n // Down key.\n if (trigger == 40) {\n for (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 == 38) {\n // Up key.\n for (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 == 36) {\n // Home key.\n next = menuItems[0];\n\n } else if (trigger == 35) {\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 $('.dropdown').on('hidden.bs.dropdown', function(e) {\n // We need to focus on the menu trigger.\n var trigger = $(e.target).find('[data-toggle=\"dropdown\"]');\n if (trigger) {\n shiftFocus(trigger);\n }\n });\n\n // After page load, focus on any element with special autofocus attribute.\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"],"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","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, 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 diff --git a/theme/boost/amd/build/loader.min.js b/theme/boost/amd/build/loader.min.js index baacba5c056..188f865c930 100644 --- a/theme/boost/amd/build/loader.min.js +++ b/theme/boost/amd/build/loader.min.js @@ -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){(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()}); //# 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 bcde39cc2fc..63d1fc7f7c7 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":"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 .\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 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","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 diff --git a/theme/boost/amd/src/aria.js b/theme/boost/amd/src/aria.js index b2b96b0e143..812aad76680 100644 --- a/theme/boost/amd/src/aria.js +++ b/theme/boost/amd/src/aria.js @@ -20,181 +20,193 @@ * @copyright 2018 Damyon Wiese * @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 {end, escape, arrowUp, arrowDown, home, enter, space} from 'core/key_codes'; +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.which; + + // 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 == space || 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 trigger = String.fromCharCode(e.which).toLowerCase(); + const menu = e.target.closest('[role="menu"]'); - 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; + if (!menu) { + return; + } + const menuItems = menu.querySelectorAll('[role="menuitem"]'); + if (!menuItems) { + return; + } - 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.which; + 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'); + }); + }); +}; + +export const init = () => { + dropdownFix(); + autoFocus(); +}; diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js index a050c6f5187..c5b8ea582b7 100644 --- a/theme/boost/amd/src/loader.js +++ b/theme/boost/amd/src/loader.js @@ -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'; From 65a9b2679170a6d10fc5f5e20c5701a5064ced55 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Thu, 17 Sep 2020 01:12:54 +1000 Subject: [PATCH 05/13] MDL-69301 core: Enhanced WAI-ARIA support for tab elements --- lib/templates/settings_link_page.mustache | 4 +- theme/boost/amd/build/aria.min.js | 2 +- theme/boost/amd/build/aria.min.js.map | 2 +- theme/boost/amd/build/loader.min.js | 2 +- theme/boost/amd/build/loader.min.js.map | 2 +- theme/boost/amd/src/aria.js | 82 ++++++++++++++++++- theme/boost/amd/src/loader.js | 13 +-- .../templates/admin_setting_tabs.mustache | 4 +- 8 files changed, 98 insertions(+), 13 deletions(-) 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 @@