Merge branch 'MOODLE_39_STABLE' into install_39_STABLE
This commit is contained in:
@@ -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" in the "Teacher 1" "table_row"
|
||||
And I should see "Teacher < \"editing\"" in the "Teaching Assistant" "table_row"
|
||||
And I should see "Studier & 'learner'" in the "Student One" "table_row"
|
||||
And I navigate to "Users > 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
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -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)",searchicon:".input-group-append .search-icon",clearsearch:".input-group-append .clear"}};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)",searchicon:".input-group-append .search-icon",clearsearch:".input-group-append .clear"}};a.default=c;return a.default});
|
||||
//# sourceMappingURL=selectors.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -36,7 +36,7 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_cohort/roleid',
|
||||
get_string('defaultrole', 'role'), '', $student->id, $options));
|
||||
get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
|
||||
|
||||
$options = array(
|
||||
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
|
||||
|
||||
@@ -86,7 +86,11 @@ if ($ADMIN->fulltree) {
|
||||
$options = get_default_enrol_roles(context_system::instance());
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_database/defaultrole', get_string('defaultrole', 'enrol_database'), get_string('defaultrole_desc', 'enrol_database'), $student->id, $options));
|
||||
$settings->add(new admin_setting_configselect('enrol_database/defaultrole',
|
||||
get_string('defaultrole', 'enrol_database'),
|
||||
get_string('defaultrole_desc', 'enrol_database'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configcheckbox('enrol_database/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0));
|
||||
|
||||
@@ -63,7 +63,7 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_manual/roleid',
|
||||
get_string('defaultrole', 'role'), '', $student->id, $options));
|
||||
get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
|
||||
}
|
||||
|
||||
$options = array(2 => get_string('coursestart'), 3 => get_string('today'), 4 => get_string('now', 'enrol_manual'));
|
||||
|
||||
@@ -36,6 +36,6 @@ if ($ADMIN->fulltree) {
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect_with_advanced('enrol_mnet/roleid',
|
||||
get_string('defaultrole', 'role'), '',
|
||||
array('value'=>$student->id, 'adv'=>true), $options));
|
||||
array('value' => $student->id ?? null, 'adv' => true), $options));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,10 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_paypal/roleid',
|
||||
get_string('defaultrole', 'enrol_paypal'), get_string('defaultrole_desc', 'enrol_paypal'), $student->id, $options));
|
||||
get_string('defaultrole', 'enrol_paypal'),
|
||||
get_string('defaultrole_desc', 'enrol_paypal'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configduration('enrol_paypal/enrolperiod',
|
||||
|
||||
@@ -79,7 +79,10 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_self/roleid',
|
||||
get_string('defaultrole', 'enrol_self'), get_string('defaultrole_desc', 'enrol_self'), $student->id, $options));
|
||||
get_string('defaultrole', 'enrol_self'),
|
||||
get_string('defaultrole_desc', 'enrol_self'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configduration('enrol_self/enrolperiod',
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
});
|
||||
|
||||
+6
-5
@@ -2592,11 +2592,12 @@ function xmldb_main_upgrade($oldversion) {
|
||||
if ($oldversion < 2020061502.09) {
|
||||
// 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, 2020061502.09);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"}]}
|
||||
}}
|
||||
<div class="popup_content" style="text-align:center;">
|
||||
{{message}} <hr/>
|
||||
<div class="role_buttons">
|
||||
{{#roles}}
|
||||
<input type="button" value="{{rolename}}" class="btn btn-secondary mb-1" data-role-id="{{roleid}}" {{disabled}}/>
|
||||
<button type="button" class="btn btn-secondary mb-1" data-role-id="{{roleid}}" {{disabled}}>{{{rolename}}}</button>
|
||||
{{/roles}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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/"}
|
||||
}}
|
||||
<span style="display:inline-block;" class="{{spanclass}}"> {{rolename}}
|
||||
<span style="display:inline-block;" class="{{spanclass}}"> {{{rolename}}}
|
||||
<a href="{{adminurl}}roles/permissions.php" class="{{linkclass}}" data-role-id="{{roleid}}" data-action="{{action}}">
|
||||
{{#icon}}
|
||||
{{#pix}}{{icon}}, core, {{iconalt}}{{/pix}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -789,8 +789,8 @@ class manager {
|
||||
}
|
||||
}
|
||||
|
||||
// Add all supported block contexts, in a single query for performance.
|
||||
if (!empty($areasbylevel[CONTEXT_BLOCK])) {
|
||||
// Add all supported block contexts for course contexts that user can access, in a single query for performance.
|
||||
if (!empty($areasbylevel[CONTEXT_BLOCK]) && !empty($coursecontextids)) {
|
||||
// Get list of all block types we care about.
|
||||
$blocklist = [];
|
||||
foreach ($areasbylevel[CONTEXT_BLOCK] as $areaid => $searchclass) {
|
||||
|
||||
@@ -791,7 +791,6 @@ class search_manager_testcase extends advanced_testcase {
|
||||
$this->assertEquals($contexts['block_html-content'], $limitedcontexts['block_html-content']);
|
||||
|
||||
// Get block context ids for the blocks that appear.
|
||||
global $DB;
|
||||
$blockcontextids = $DB->get_fieldset_sql('
|
||||
SELECT x.id
|
||||
FROM {block_instances} bi
|
||||
@@ -811,6 +810,43 @@ class search_manager_testcase extends advanced_testcase {
|
||||
$this->assertCount(1, $contexts['block_html-content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests retrieval of users search areas when limiting to a course the user is not enrolled in
|
||||
*/
|
||||
public function test_search_users_accesses_limit_non_enrolled_course() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
$search = testable_core_search::instance();
|
||||
$search->add_core_search_areas();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
// Limit courses to search to only those the user is enrolled in.
|
||||
set_config('searchallavailablecourses', 0);
|
||||
|
||||
$usercontexts = $search->get_areas_user_accesses([$course->id])->usercontexts;
|
||||
$this->assertNotEmpty($usercontexts);
|
||||
$this->assertArrayNotHasKey('core_course-course', $usercontexts);
|
||||
|
||||
// This config ensures the search will also include courses the user can view.
|
||||
set_config('searchallavailablecourses', 1);
|
||||
|
||||
// Allow "Authenticated user" role to view the course without being enrolled in it.
|
||||
$userrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
|
||||
role_change_permission($userrole->id, $context, 'moodle/course:view', CAP_ALLOW);
|
||||
|
||||
$usercontexts = $search->get_areas_user_accesses([$course->id])->usercontexts;
|
||||
$this->assertNotEmpty($usercontexts);
|
||||
$this->assertArrayHasKey('core_course-course', $usercontexts);
|
||||
$this->assertEquals($context->id, reset($usercontexts['core_course-course']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_areas_user_accesses with regard to the 'all available courses' config option.
|
||||
*
|
||||
|
||||
Vendored
+1
-1
@@ -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","core/key_codes","jquery","core/pending"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;c=e(c);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}var f=function(){var a=!1,e=function(){a=!0},f=function(){var b=a;a=!1;return b};document.addEventListener("keydown",function(a){if(a.target.matches("[data-toggle=\"dropdown\"]")){var c=a.which;if(c==b.arrowUp){e()}if(c==b.space||c==b.enter){a.preventDefault();a.target.click()}}});var g=function(a){setTimeout(function delayedFocus(b){a.focus();b.resolve()},50,new d.default("core/aria:delayed-focus"))};(0,c.default)(".dropdown").on("shown.bs.dropdown",function(a){var b=a.target.querySelector("[role=\"menu\"]"),c=!1,d=!1;if(b){c=b.querySelectorAll("[role=\"menuitem\"]")}if(c&&0<c.length){if(f()){d=c[c.length-1]}else{d=c[0]}}if(d){g(d)}});document.addEventListener("keypress",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var b=String.fromCharCode(a.which).toLowerCase(),c=a.target.closest("[role=\"menu\"]");if(!c){return}var f=c.querySelectorAll("[role=\"menuitem\"]");if(!f){return}for(var h=0;h<f.length;h++){var d=f[h],e=d.text.trim().toLowerCase();if(0==e.indexOf(b)){g(d);break}}}});document.addEventListener("keydown",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var c=a.which,d=!1,e=a.target.closest("[role=\"menu\"]");if(!e){return}var f=e.querySelectorAll("[role=\"menuitem\"]");if(!f){return}if(c==b.arrowDown){for(var h=0;h<f.length-1;h++){if(f[h]==a.target){d=f[h+1];break}}if(!d){d=f[0]}}else if(c==b.arrowUp){for(var i=1;i<f.length;i++){if(f[i]==a.target){d=f[i-1];break}}if(!d){d=f[f.length-1]}}else if(c==b.home){d=f[0]}else if(c==b.end){d=f[f.length-1]}if(d){a.preventDefault();g(d)}}});(0,c.default)(".dropdown").on("hidden.bs.dropdown",function(a){var b=a.target.querySelector("[data-toggle=\"dropdown\"]");if(b){g(b)}})},g=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")})})},h=function(a){for(var d=a.target.closest("[role=\"tablist\"]"),e="vertical"==d.getAttribute("aria-orientation"),f=window.right_to_left(),g=e?b.arrowDown:f?b.arrowLeft:b.arrowRight,h=e?b.arrowUp:f?b.arrowRight:b.arrowLeft,j=Array.prototype.filter.call(d.querySelectorAll("[role=\"tab\"]"),function(a){return"none"!==getComputedStyle(a).display}),k=0;k<j.length;k++){j[k].index=k}switch(a.keyCode){case g:a.preventDefault();if(a.target.index!==void 0&&j[a.target.index+1]){j[a.target.index+1].focus()}else{j[0].focus()}break;case h:a.preventDefault();if(a.target.index!==void 0&&j[a.target.index-1]){j[a.target.index-1].focus()}else{j[j.length-1].focus()}break;case b.home:a.preventDefault();j[0].focus();break;case b.end:a.preventDefault();j[j.length-1].focus();break;case b.enter:case b.space:a.preventDefault();(0,c.default)(a.target).tab("show");j.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0;}},i=function(){document.addEventListener("keydown",function(a){if([b.arrowUp,b.arrowDown,b.arrowLeft,b.arrowRight,b.home,b.end,b.enter,b.space].includes(a.keyCode)){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){h(a)}}});document.addEventListener("click",function(a){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){var b=a.target.closest("[role=\"tablist\"]").querySelectorAll("[role=\"tab\"]");a.preventDefault();(0,c.default)(a.target).tab("show");b.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0}})};a.init=function init(){f();g();i()}});
|
||||
//# sourceMappingURL=aria.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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
|
||||
|
||||
@@ -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 <[email protected]>\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 <[email protected]>\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"}
|
||||
+247
-164
@@ -20,181 +20,264 @@
|
||||
* @copyright 2018 Damyon Wiese <[email protected]>
|
||||
* @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, arrowUp, arrowDown, arrowLeft, arrowRight, 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();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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'));
|
||||
};
|
||||
|
||||
$('.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;
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
const menuItems = menu.querySelectorAll('[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
// 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"]');
|
||||
|
||||
// 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.
|
||||
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', 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).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;
|
||||
}
|
||||
menuItems = $(menu).find('[role="menuitem"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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.keyCode) {
|
||||
case arrowNext:
|
||||
e.preventDefault();
|
||||
if (e.target.index !== undefined && tabs[e.target.index + 1]) {
|
||||
tabs[e.target.index + 1].focus();
|
||||
} else {
|
||||
tabs[0].focus();
|
||||
}
|
||||
break;
|
||||
case arrowPrevious:
|
||||
e.preventDefault();
|
||||
if (e.target.index !== undefined && tabs[e.target.index - 1]) {
|
||||
tabs[e.target.index - 1].focus();
|
||||
} else {
|
||||
tabs[tabs.length - 1].focus();
|
||||
}
|
||||
break;
|
||||
case home:
|
||||
e.preventDefault();
|
||||
tabs[0].focus();
|
||||
break;
|
||||
case end:
|
||||
e.preventDefault();
|
||||
tabs[tabs.length - 1].focus();
|
||||
break;
|
||||
case enter:
|
||||
case space:
|
||||
e.preventDefault();
|
||||
$(e.target).tab('show');
|
||||
tabs.forEach(tab => {
|
||||
tab.tabIndex = -1;
|
||||
});
|
||||
e.target.tabIndex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.
|
||||
*/
|
||||
const tabElementFix = () => {
|
||||
document.addEventListener('keydown', e => {
|
||||
if ([arrowUp, arrowDown, arrowLeft, arrowRight, home, end, enter, space].includes(e.keyCode)) {
|
||||
if (e.target.matches('[role="tablist"] [role="tab"]')) {
|
||||
updateTabFocus(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', e => {
|
||||
if (e.target.matches('[role="tablist"] [role="tab"]')) {
|
||||
const tabs = e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');
|
||||
e.preventDefault();
|
||||
$(e.target).tab('show');
|
||||
tabs.forEach(tab => {
|
||||
tab.tabIndex = -1;
|
||||
});
|
||||
e.target.tabIndex = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const init = () => {
|
||||
dropdownFix();
|
||||
autoFocus();
|
||||
tabElementFix();
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
+2
-2
@@ -29,9 +29,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2020061503.04; // 20200615 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2020061503.05; // 20200615 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '3.9.3+ (Build: 20201204)'; // Human-friendly version name
|
||||
$release = '3.9.3+ (Build: 20201211)'; // Human-friendly version name
|
||||
$branch = '39'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user