+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery'], function($) {
+ 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;
+
+ // Up key opens the menu at the end.
+ if (trigger == 38) {
+ // Focus the end of the menu, not the beginning.
+ setFocusEnd();
+ }
+
+ // 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 == 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() {
+ $(this).focus();
+ }.bind(element);
+ setTimeout(delayedFocus, 50);
+ };
+
+ $('.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.
+ $('.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;
+
+ if (!menu) {
+ return;
+ }
+ menuItems = $(menu).find('[role="menuitem"]');
+ if (!menuItems) {
+ return;
+ }
+
+ trigger = trigger.toLowerCase();
+ for (i = 0; i < menuItems.length; i++) {
+ item = $(menuItems[i]);
+ if ((item.text().trim().indexOf(trigger) == 0) ||
+ (item.text().trim().indexOf(trigger.toUpperCase()) == 0)) {
+ shiftFocus(item);
+ 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.
+ if (trigger == 40) {
+ for (i = 0; i < menuItems.length - 1; i++) {
+ if (menuItems[i] == e.target) {
+ next = menuItems[i + 1];
+ }
+ }
+ if (!next) {
+ // Wrap to first item.
+ trigger = 36;
+ }
+ }
+ // Up.
+ if (trigger == 38) {
+ for (i = 1; i < menuItems.length; i++) {
+ if (menuItems[i] == e.target) {
+ next = menuItems[i - 1];
+ }
+ }
+ if (!next) {
+ // Wrap to last item.
+ trigger = 35;
+ }
+ }
+ // Home.
+ if (trigger == 36) {
+ next = menuItems[0];
+ }
+ // End.
+ if (trigger == 35) {
+ next = menuItems[menuItems.length - 1];
+ }
+ 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);
+ }
+ });
+ }
+ };
+});
diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js
index bfd6d50fde5..8ab1706af09 100644
--- a/theme/boost/amd/src/loader.js
+++ b/theme/boost/amd/src/loader.js
@@ -28,7 +28,8 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
window.jQuery = jQuery;
window.Tether = Tether;
- require(['theme_boost/util',
+ require(['theme_boost/aria',
+ 'theme_boost/util',
'theme_boost/alert',
'theme_boost/button',
'theme_boost/carousel',
@@ -39,7 +40,7 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
'theme_boost/tab',
'theme_boost/tooltip',
'theme_boost/popover'],
- function() {
+ function(Aria) {
// We do twice because: https://github.com/twbs/bootstrap/issues/10547
jQuery('body').popover({
@@ -63,8 +64,11 @@ define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
selector: '[data-toggle="popover"]',
trigger: 'focus'
});
+
});
});
+
+ Aria.init();
});
diff --git a/theme/boost/templates/core/action_menu_trigger.mustache b/theme/boost/templates/core/action_menu_trigger.mustache
index bf68279054a..4df86f640a2 100644
--- a/theme/boost/templates/core/action_menu_trigger.mustache
+++ b/theme/boost/templates/core/action_menu_trigger.mustache
@@ -78,12 +78,12 @@
}
}}
-
+
{{#secondary}}
-
diff --git a/version.php b/version.php
index 8ed5ab4aa74..932665df171 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
-$version = 2017111305.07; // 20171113 = branching date YYYYMMDD - do not modify!
+$version = 2017111305.08; // 20171113 = branching date YYYYMMDD - do not modify!
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.