diff --git a/lib/editor/atto/amd/build/events.min.js b/lib/editor/atto/amd/build/events.min.js
new file mode 100644
index 00000000000..d19aedbd088
--- /dev/null
+++ b/lib/editor/atto/amd/build/events.min.js
@@ -0,0 +1,2 @@
+define ("editor_atto/events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.notifyButtonHighlightToggled=a.eventTypes=void 0;var b={attoButtonHighlightToggled:"editor_atto/attoButtonHighlightToggled"};a.eventTypes=b;a.notifyButtonHighlightToggled=function notifyButtonHighlightToggled(a,c,d){var e=new CustomEvent(b.attoButtonHighlightToggled,{bubbles:!0,cancelable:!1,composed:!1,detail:{buttonName:c,highlight:d}}),f=a||document;f.dispatchEvent(e);return e}});
+//# sourceMappingURL=events.min.js.map
diff --git a/lib/editor/atto/amd/build/events.min.js.map b/lib/editor/atto/amd/build/events.min.js.map
new file mode 100644
index 00000000000..60a8aca310e
--- /dev/null
+++ b/lib/editor/atto/amd/build/events.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/events.js"],"names":["eventTypes","attoButtonHighlightToggled","notifyButtonHighlightToggled","attoButton","buttonName","highlight","customEvent","CustomEvent","bubbles","cancelable","composed","detail","container","document","dispatchEvent"],"mappings":"sKA6BO,GAAMA,CAAAA,CAAU,CAAG,CAWtBC,0BAA0B,CAAE,wCAXN,CAAnB,C,8CAwBqC,QAA/BC,CAAAA,4BAA+B,CAACC,CAAD,CAAaC,CAAb,CAAyBC,CAAzB,CAAuC,IAMzEC,CAAAA,CAAW,CAAG,GAAIC,CAAAA,WAAJ,CAChBP,CAAU,CAACC,0BADK,CAEhB,CACIO,OAAO,GADX,CAEIC,UAAU,GAFd,CAGIC,QAAQ,GAHZ,CAIIC,MAAM,CAXC,CACXP,UAAU,CAAVA,CADW,CAEXC,SAAS,CAATA,CAFW,CAOX,CAFgB,CAN2D,CAgBzEO,CAAS,CAAGT,CAAU,EAAIU,QAhB+C,CAiB/ED,CAAS,CAACE,aAAV,CAAwBR,CAAxB,EAEA,MAAOA,CAAAA,CACV,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 * Javascript events for the `editor_atto` plugin.\n *\n * @module editor_atto/events\n * @copyright 2021 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.10.5\n */\n\n/**\n * Events for the `editor_atto` plugin.\n *\n * @constant\n * @property {String} attoButtonHighlightToggled See {@link event:attoButtonHighlightToggled}\n */\nexport const eventTypes = {\n /**\n * An event triggered when a toolbar button's highlight gets toggled.\n *\n * @event attoButtonHighlightToggled\n * @type {CustomEvent}\n * @property {HTMLElement} target The button which had its highlight toggled.\n * @property {object} detail\n * @property {String} detail.buttonName The name of the Atto button that has had its highlight toggled.\n * @property {Boolean} detail.highlight True when the button was highlighted. False, otherwise.\n */\n attoButtonHighlightToggled: 'editor_atto/attoButtonHighlightToggled',\n};\n\n/**\n * Trigger an event to indicate that a button's highlight was toggled.\n *\n * @method notifyButtonHighlightToggled\n * @returns {CustomEvent}\n * @fires attoButtonHighlightToggled\n * @param {HTMLElement} attoButton The button object.\n * @param {String} buttonName The button name.\n * @param {Boolean} highlight True when the button was highlighted. False, otherwise.\n */\nexport const notifyButtonHighlightToggled = (attoButton, buttonName, highlight) => {\n const detail = {\n buttonName,\n highlight,\n };\n\n const customEvent = new CustomEvent(\n eventTypes.attoButtonHighlightToggled,\n {\n bubbles: true,\n cancelable: false,\n composed: false,\n detail,\n }\n );\n\n const container = attoButton || document;\n container.dispatchEvent(customEvent);\n\n return customEvent;\n};\n"],"file":"events.min.js"}
\ No newline at end of file
diff --git a/lib/editor/atto/amd/src/events.js b/lib/editor/atto/amd/src/events.js
new file mode 100644
index 00000000000..41967b52607
--- /dev/null
+++ b/lib/editor/atto/amd/src/events.js
@@ -0,0 +1,73 @@
+// This file is part of Moodle - http://moodle.org/ //
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * Javascript events for the `editor_atto` plugin.
+ *
+ * @module editor_atto/events
+ * @copyright 2021 Jun Pataleta
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since 3.10.5
+ */
+
+/**
+ * Events for the `editor_atto` plugin.
+ *
+ * @constant
+ * @property {String} attoButtonHighlightToggled See {@link event:attoButtonHighlightToggled}
+ */
+export const eventTypes = {
+ /**
+ * An event triggered when a toolbar button's highlight gets toggled.
+ *
+ * @event attoButtonHighlightToggled
+ * @type {CustomEvent}
+ * @property {HTMLElement} target The button which had its highlight toggled.
+ * @property {object} detail
+ * @property {String} detail.buttonName The name of the Atto button that has had its highlight toggled.
+ * @property {Boolean} detail.highlight True when the button was highlighted. False, otherwise.
+ */
+ attoButtonHighlightToggled: 'editor_atto/attoButtonHighlightToggled',
+};
+
+/**
+ * Trigger an event to indicate that a button's highlight was toggled.
+ *
+ * @method notifyButtonHighlightToggled
+ * @returns {CustomEvent}
+ * @fires attoButtonHighlightToggled
+ * @param {HTMLElement} attoButton The button object.
+ * @param {String} buttonName The button name.
+ * @param {Boolean} highlight True when the button was highlighted. False, otherwise.
+ */
+export const notifyButtonHighlightToggled = (attoButton, buttonName, highlight) => {
+ const detail = {
+ buttonName,
+ highlight,
+ };
+
+ const customEvent = new CustomEvent(
+ eventTypes.attoButtonHighlightToggled,
+ {
+ bubbles: true,
+ cancelable: false,
+ composed: false,
+ detail,
+ }
+ );
+
+ attoButton.dispatchEvent(customEvent);
+
+ return customEvent;
+};
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
index 606e57feb43..b433456296e 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js
@@ -293,11 +293,20 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
_menuOptions: null,
initializer: function() {
- this.addButton({
+ var button = this.addButton({
icon: 'e/table',
callback: this._displayTableEditor,
tags: 'table'
});
+
+ // Listen for toggled highlighting.
+ require(['editor_atto/events'], function(attoEvents) {
+ var domButton = button.getDOMNode();
+ domButton.addEventListener(attoEvents.eventTypes.attoButtonHighlightToggled, function(e) {
+ this._setAriaAttributes(e.detail.buttonName, e.detail.highlight);
+ }.bind(this));
+ }.bind(this));
+
// Disable mozilla table controls.
if (Y.UA.gecko) {
document.execCommand("enableInlineTableEditing", false, false);
@@ -305,6 +314,30 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
}
},
+ /**
+ * Sets the appropriate ARIA attributes for the table button when it switches roles between a button and a menu button.
+ *
+ * @param {String} buttonName The button name.
+ * @param {Boolean} highlight True when the button was highlighted. False, otherwise.
+ * @private
+ */
+ _setAriaAttributes: function(buttonName, highlight) {
+ var menuButton = this.buttons[buttonName];
+ if (menuButton) {
+ if (highlight) {
+ // This button becomes a menu button. Add appropriate ARIA attributes.
+ var id = menuButton.getAttribute('id');
+ menuButton.setAttribute('aria-haspopup', true);
+ menuButton.setAttribute('aria-controls', id + '_menu');
+ menuButton.setAttribute('aria-expanded', true);
+ } else {
+ menuButton.removeAttribute('aria-haspopup');
+ menuButton.removeAttribute('aria-controls');
+ menuButton.removeAttribute('aria-expanded');
+ }
+ }
+ },
+
/**
* Display the table tool.
*
@@ -343,10 +376,19 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
*/
_displayTableEditor: function(e) {
var cell = this._getSuitableTableCell();
+ var menuButton = e.currentTarget.ancestor('button', true);
if (cell) {
+ var id = menuButton.getAttribute('id');
+
+ // Indicate that the menu is expanded.
+ menuButton.setAttribute('aria-expanded', true);
+
// Add the cell to the EventFacade to save duplication in when showing the menu.
e.tableCell = cell;
- return this._showTableMenu(e);
+ return this._showTableMenu(e, id);
+ } else {
+ // Dialog mode. Remove aria-expanded attribute.
+ menuButton.removeAttribute('aria-expanded');
}
return this._displayDialogue(e);
},
@@ -815,9 +857,10 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
*
* @method _showTableMenu
* @param {EventFacade} e
+ * @param {String} buttonId The ID of the menu button associated with this menu.
* @private
*/
- _showTableMenu: function(e) {
+ _showTableMenu: function(e, buttonId) {
e.preventDefault();
var boundingBox;
@@ -873,7 +916,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
];
this._contextMenu = new Y.M.editor_atto.Menu({
- items: this._menuOptions
+ items: this._menuOptions,
+ buttonId: buttonId
});
// Add event handlers for table control menus.
diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
index 4520cacef2e..ec098cdc782 100644
--- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
+++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js
@@ -1,5 +1,5 @@
-YUI.add("moodle-atto_table-button",function(O,e){var C="atto_table",S="none",R="1",T="480px",l={CAPTION:"caption",CAPTIONPOSITION:"captionposition",HEADERS:"headers",ROWS:"rows",COLUMNS:"columns",SUBMIT:"submit",FORM:"atto_form",BORDERS:"borders",BORDERSIZE:"bordersize",BORDERSIZEUNIT:"px",BORDERCOLOUR:"bordercolour",BORDERSTYLE:"borderstyle",BACKGROUNDCOLOUR:"backgroundcolour",WIDTH:"customwidth",WIDTHUNIT:"%",AVAILABLECOLORS:"availablecolors",COLOURROW:"colourrow"},w={CAPTION:"."+l.CAPTION,CAPTIONPOSITION:"."+l.CAPTIONPOSITION,HEADERS:"."+l.HEADERS,ROWS:"."+l.ROWS,COLUMNS:"."+l.COLUMNS,SUBMIT:"."+l.SUBMIT,BORDERS:"."+l.BORDERS,BORDERSIZE:"."+l.BORDERSIZE,BORDERCOLOURS:"."+l.BORDERCOLOUR+' input[name="borderColour"]',SELECTEDBORDERCOLOUR:"."+l.BORDERCOLOUR+' input[name="borderColour"]:checked',BORDERSTYLE:"."+l.BORDERSTYLE,BACKGROUNDCOLOURS:"."+l.BACKGROUNDCOLOUR+' input[name="backgroundColour"]',SELECTEDBACKGROUNDCOLOUR:"."+l.BACKGROUNDCOLOUR+' input[name="backgroundColour"]:checked',FORM:".atto_form",WIDTH:"."+l.WIDTH,AVAILABLECOLORS:"."+l.AVAILABLECOLORS};O.namespace("M.atto_table").Button=O.Base.create("button",O.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_contextMenu:null,_lastTarget:null,_menuOptions:null,initializer:function(){this.addButton({icon:"e/table",callback:this._displayTableEditor,tags:"table"}),O.UA.gecko&&(document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("enableObjectResizing",!1,!1))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),!1===this._currentSelection||this._currentSelection.collapsed||(this.getDialogue({headerContent:M.util.get_string("createtable",C),focusAfterHide:!0,focusOnShowSelector:w.CAPTION,width:T}).set("bodyContent",this._getDialogueContent(!1)).show(),this._updateAvailableSettings())},_displayTableEditor:function(e){var t=this._getSuitableTableCell();return t?(e.tableCell=t,this._showTableMenu(e)):this._displayDialogue(e)},_stopAtContentEditableFilter:function(e){return this.editor.contains(e)},_getDialogueContent:function(e){var t=O.Handlebars.compile(
+YUI.add("moodle-atto_table-button",function(O,e){var C="atto_table",S="none",R="1",T="480px",l={CAPTION:"caption",CAPTIONPOSITION:"captionposition",HEADERS:"headers",ROWS:"rows",COLUMNS:"columns",SUBMIT:"submit",FORM:"atto_form",BORDERS:"borders",BORDERSIZE:"bordersize",BORDERSIZEUNIT:"px",BORDERCOLOUR:"bordercolour",BORDERSTYLE:"borderstyle",BACKGROUNDCOLOUR:"backgroundcolour",WIDTH:"customwidth",WIDTHUNIT:"%",AVAILABLECOLORS:"availablecolors",COLOURROW:"colourrow"},w={CAPTION:"."+l.CAPTION,CAPTIONPOSITION:"."+l.CAPTIONPOSITION,HEADERS:"."+l.HEADERS,ROWS:"."+l.ROWS,COLUMNS:"."+l.COLUMNS,SUBMIT:"."+l.SUBMIT,BORDERS:"."+l.BORDERS,BORDERSIZE:"."+l.BORDERSIZE,BORDERCOLOURS:"."+l.BORDERCOLOUR+' input[name="borderColour"]',SELECTEDBORDERCOLOUR:"."+l.BORDERCOLOUR+' input[name="borderColour"]:checked',BORDERSTYLE:"."+l.BORDERSTYLE,BACKGROUNDCOLOURS:"."+l.BACKGROUNDCOLOUR+' input[name="backgroundColour"]',SELECTEDBACKGROUNDCOLOUR:"."+l.BACKGROUNDCOLOUR+' input[name="backgroundColour"]:checked',FORM:".atto_form",WIDTH:"."+l.WIDTH,AVAILABLECOLORS:"."+l.AVAILABLECOLORS};O.namespace("M.atto_table").Button=O.Base.create("button",O.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_contextMenu:null,_lastTarget:null,_menuOptions:null,initializer:function(){var t=this.addButton({icon:"e/table",callback:this._displayTableEditor,tags:"table"});require(["editor_atto/events"],function(e){t.getDOMNode().addEventListener(e.eventTypes.attoButtonHighlightToggled,function(e){this._setAriaAttributes(e.detail.buttonName,e.detail.highlight)}.bind(this))}.bind(this)),O.UA.gecko&&(document.execCommand("enableInlineTableEditing",!1,!1),document.execCommand("enableObjectResizing",!1,!1))},_setAriaAttributes:function(e,t){var o,a=this.buttons[e];a&&(t?(o=a.getAttribute("id"),a.setAttribute("aria-haspopup",!0),a.setAttribute("aria-controls",o+"_menu"),a.setAttribute("aria-expanded",!0)):(a.removeAttribute("aria-haspopup"),a.removeAttribute("aria-controls"),a.removeAttribute("aria-expanded")))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection(),!1===this._currentSelection||this._currentSelection.collapsed||(this.getDialogue({headerContent:M.util.get_string("createtable",C),focusAfterHide:!0,focusOnShowSelector:w.CAPTION,width:T}).set("bodyContent",this._getDialogueContent(!1)).show(),this._updateAvailableSettings())},_displayTableEditor:function(e){var t,o=this._getSuitableTableCell(),a=e.currentTarget.ancestor("button",!0);return o?(t=a.getAttribute("id"),a.setAttribute("aria-expanded",!0),e.tableCell=o,this._showTableMenu(e,t)):(a.removeAttribute("aria-expanded"),this._displayDialogue(e))},_stopAtContentEditableFilter:function(e){return this.editor.contains(e)},_getDialogueContent:function(e){var t=O.Handlebars.compile(
''
),o=this.get("allowBorders");return this._content=O.Node.create(t({CSS:l,elementid:this.get("host").get("elementid"),component:C,edit:e,nonedit:!e,allowStyling:this.get("allowStyling"),allowBorders:o,borderStyles:this.get("borderStyles"),allowBackgroundColour:this.get("allowBackgroundColour"),availableColours:this.get("availableColors"),allowWidth:this.get("allowWidth")})),e?this._content.one(".submit").on("click",this._updateTable,this):this._content.one(".submit").on("click",this._setTable,this),o&&this._content.one('[name="borders"]').on("change",this._updateAvailableSettings,this),this._content},_updateAvailableSettings:function(){var e=this._content,t=e.one('[name="borders"]'),o=e.one('[name="borderstyles"]'),a=e.one('[name="bordersize"]'),n=e.all('[name="borderColour"]'),r="removeAttribute";t&&("default"===t.get("value")&&(r="setAttribute"),o&&o[r]("disabled"),a&&a[r]("disabled"),n&&n[r]("disabled"))},_getSuitableTableCell:function(){var e,a=null,t=this.get("host"),n=O.bind(this._stopAtContentEditableFilter,this);return t.getSelectedNodes().some(function(e){var t,o;if(e.ancestor("td, th, caption",!0,n))return(t=(a=e).ancestor("caption",!0,n))&&(o=t.get("parentNode"))&&(a=o.one("td, th")),!0}),a&&(e=t.getSelectionFromNode(a),t.setSelection(e)),a},_changeNodeType:function(e,t){var o=O.Node.create("<"+t+">"+t+">");return o.setAttrs(e.getAttrs()),e.get("childNodes").each(function(e){o.append(e.remove())}),e.replace(o),o},_updateTable:function(e){var t,o,a,n,r,l,i,s,d,c,u,g;e.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),t=e.currentTarget.ancestor(w.FORM).one(w.CAPTION),o=e.currentTarget.ancestor(w.FORM).one(w.CAPTIONPOSITION),a=e.currentTarget.ancestor(w.FORM).one(w.HEADERS),n=e.currentTarget.ancestor(w.FORM).one(w.BORDERS),r=e.currentTarget.ancestor(w.FORM).one(w.BORDERSIZE),i=e.currentTarget.ancestor(w.FORM).one(w.SELECTEDBORDERCOLOUR),l=e.currentTarget.ancestor(w.FORM).one(w.BORDERSTYLE),s=e.currentTarget.ancestor(w.FORM).one(w.SELECTEDBACKGROUNDCOLOUR),c=e.currentTarget.ancestor(w.FORM).one(w.WIDTH),d=this._lastTarget.ancestor("table"),this._setAppearance(d,{width:c,borders:n,borderColour:i,borderSize:r,borderStyle:l,backgroundColour:s}),(u=d.one("caption"))||(u=O.Node.create("
' +
'' +
'{{{text}}}' +
'' +
@@ -67,12 +67,19 @@ Y.extend(Menu, M.core.dialogue, {
*/
_menuHandlers: null,
+ /**
+ * The menu button that controls this menu.
+ */
+ _menuButton: null,
+
initializer: function(config) {
- var headertext,
+ var headerText,
bb;
this._menuHandlers = [];
+ this._menuButton = document.getElementById(config.buttonId);
+
// Create the actual button.
var template = Y.Handlebars.compile(MENUDIALOGUE),
menu = Y.Node.create(template({
@@ -83,14 +90,24 @@ Y.extend(Menu, M.core.dialogue, {
bb = this.get('boundingBox');
bb.addClass('editor_atto_controlmenu');
bb.addClass('editor_atto_menu');
- bb.one('.moodle-dialogue-wrap')
- .removeClass('moodle-dialogue-wrap')
- .addClass('moodle-dialogue-content');
- headertext = Y.Node.create('')
+ // Get the dialogue container for this menu.
+ var content = bb.one('.moodle-dialogue-wrap');
+ content.removeClass('moodle-dialogue-wrap')
+ .addClass('moodle-dialogue-content');
+ // Remove the dialog role attribute.
+ content.removeAttribute('role');
+ // Remove aria-labelledby in the container. The aria-labelledby attribute is properly set in the menu's template.
+ content.removeAttribute('aria-labelledby');
+
+ // Render heading if necessary.
+ headerText = this.get('headerText').trim();
+ if (headerText) {
+ var heading = Y.Node.create('')
.addClass('accesshide')
- .setHTML(this.get('headerText'));
- this.get('bodyContent').prepend(headertext);
+ .setHTML(headerText);
+ this.get('bodyContent').prepend(heading);
+ }
// Hide the header and footer node entirely.
this.headerNode.hide();
@@ -152,6 +169,11 @@ Y.extend(Menu, M.core.dialogue, {
e.preventDefault();
}
+ // Remove menu button's aria-expanded attribute when this menu is hidden.
+ if (this._menuButton) {
+ this._menuButton.removeAttribute('aria-expanded');
+ }
+
return Menu.superclass.hide.call(this, arguments);
},
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
index 90dd933769f..d4b147acf61 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
@@ -179,9 +179,11 @@ Y.namespace('M.editor_atto').EditorPlugin = EditorPlugin;
var MENUTEMPLATE = '' +
'';
@@ -527,13 +529,18 @@ EditorPluginButtons.prototype = {
}
// Create the actual button.
+ var id = 'atto_' + pluginname + '_menubutton_' + Y.stamp(this);
var template = Y.Handlebars.compile(MENUTEMPLATE);
button = Y.Node.create(template({
buttonClass: buttonClass,
config: config,
- title: title
+ title: title,
+ id: id
}));
+ // Add this button id to the config. It will be used in the menu later.
+ config.buttonId = id;
+
window.require(['core/templates'], function(Templates) {
Templates.renderPix(config.icon, config.iconComponent, title).then(function(iconhtml) {
button.one(CSS.MENUICON).append(iconhtml);
@@ -585,7 +592,8 @@ EditorPluginButtons.prototype = {
return;
}
- if (e.currentTarget.ancestor('button', true).hasAttribute(DISABLED)) {
+ var menuButton = e.currentTarget.ancestor('button', true);
+ if (menuButton.hasAttribute(DISABLED)) {
// Exit early if the clicked button was disabled.
return;
}
@@ -627,6 +635,9 @@ EditorPluginButtons.prototype = {
// Display the menu.
menuDialogue.show();
+ // Indicate that the menu is expanded.
+ menuButton.setAttribute("aria-expanded", true);
+
// Position it next to the button which opened it.
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
@@ -1024,16 +1035,35 @@ EditorPluginButtons.prototype = {
if (button) {
if (this.buttons[button]) {
this.buttons[button][method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}
} else {
Y.Object.each(this.buttons, function(button) {
button[method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}, this);
}
return this;
},
+ /**
+ * Fires a custom event that notifies listeners that a button's highlight has been toggled.
+ *
+ * @param {String} buttonName The button name.
+ * @param {Boolean} highlight True when the button was highlighted. False, otherwise.
+ * @private
+ */
+ _buttonHighlightToggled: function(buttonName, highlight) {
+ var toggledButton = this.buttons[buttonName];
+ if (toggledButton) {
+ // Fire an event that the button highlight was toggled.
+ require(['editor_atto/events'], function(attoEvents) {
+ attoEvents.notifyButtonHighlightToggled(toggledButton.getDOMNode(), buttonName, highlight);
+ });
+ }
+ },
+
/**
* Get the default meta key to use with keyboard events.
*
diff --git a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
index 4b36d0a3e6e..cc1f5fc8c54 100644
--- a/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
+++ b/lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
@@ -1,2 +1,2 @@
-YUI.add("moodle-editor_atto-plugin",function(u,t){var n,i,r,a,o,l,h,c;function e(){e.superclass.constructor.apply(this,arguments)}function s(){}function d(){}n=".atto_group.",i="_group",u.extend(e,u.Base,{name:null,editor:null,toolbar:null,initializer:function(t){this.name=t.name,this.toolbar=t.toolbar,this.editor=t.editor,this.buttons={},this.buttonNames=[],this.buttonStates={},this.menus={},this._primaryKeyboardShortcut=[],this._buttonHandlers=[],this._menuHideHandlers=[],this._highlightQueue={}},markUpdated:function(){return this.get("host").saveSelection(),this.get("host").updateOriginal()}},{NAME:"editorPlugin",ATTRS:{host:{writeOnce:!0},group:{writeOnce:!0,getter:function(t){var e=this.toolbar.one(n+t+i);return e||(e=u.Node.create(''),this.toolbar.append(e)),e}}}}),u.namespace("M.editor_atto").EditorPlugin=e,r='',a="disabled",o="highlight",l=".editor_atto_content",h=".editor_atto_menu_icon",c=".editor_atto_menu_expand",s.ATTRS={},s.prototype={buttons:null,buttonNames:null,buttonStates:null,menus:null,DISABLED:0,ENABLED:1,_buttonHandlers:null,_menuHideHandlers:null,_primaryKeyboardShortcut:null,_highlightQueue:null,addButton:function(e){var n,i,o,t=this.get("group"),a=this.name,s="atto_"+a+"_button",r=this.get("host");return e.exec&&(s=s+"_"+e.exec),e.buttonName?s=s+"_"+e.buttonName:e.buttonName=e.exec||a,e.buttonClass=s,(e=this._normalizeIcon(e)).title||(e.title="pluginname"),i=M.util.get_string(e.title,"atto_"+a),(n=u.Node.create('')).setAttribute("title",i),window.require(["core/templates"],function(t){t.renderPix(e.icon,e.iconComponent,i).then(function(t){n.append(t)})}),t.append(n),this.toolbar.getAttribute("aria-activedescendant")||(n.setAttribute("tabindex","0"),this.toolbar.setAttribute("aria-activedescendant",n.generateID()),this.get("host")._tabFocus=n),e=this._normalizeCallback(e),this._buttonHandlers.push(this.toolbar.delegate("click",e.callback,"."+s,this)),e.keys&&("undefined"!=typeof e.keyDescription&&(this._primaryKeyboardShortcut[s]=e.keyDescription),this._addKeyboardListener(e.callback,e.keys,s),this._primaryKeyboardShortcut[s]&&n.setAttribute("title",M.util.get_string("plugin_title_shortcut","editor_atto",{title:i,shortcut:this._primaryKeyboardShortcut[s]}))),e.tags&&(o=!0,"boolean"==typeof e.tagMatchRequiresAll&&(o=e.tagMatchRequiresAll),this._buttonHandlers.push(r.on(["atto:selectionchanged","change"],function(t){"undefined"!=typeof this._highlightQueue[e.buttonName]&&this._highlightQueue[e.buttonName].cancel(),this._highlightQueue[e.buttonName]=u.soon(u.bind(function(t){r.selectionFilterMatches(e.tags,t.selectedNodes,o)?this.highlightButtons(e.buttonName):this.unHighlightButtons(e.buttonName)},this,t))},this))),this.buttonNames.push(e.buttonName),this.buttons[e.buttonName]=n,this.buttonStates[e.buttonName]=this.ENABLED,n},addBasicButton:function(t){return t.exec?(t.icon||(t.icon="e/"+t.exec),t.callback=function(){document.execCommand(t.exec,!1,null),this.markUpdated()},this.addButton(t)):null},addToolbarMenu:function(e){var n,i,t,o=this.get("group"),a=this.name,s="atto_"+a+"_button";return e.buttonName?s=s+"_"+e.buttonName:e.buttonName=a,e.buttonClass=s,(e=this._normalizeIcon(e)).title||(e.title="pluginname"),i=M.util.get_string(e.title,"atto_"+a),e.menuColor||(e.menuColor="transparent"),t=u.Handlebars.compile(r),n=u.Node.create(t({buttonClass:s,config:e,title:i})),window.require(["core/templates"],function(t){t.renderPix(e.icon,e.iconComponent,i).then(function(t){n.one(h).append(t)}),t.renderPix("t/expanded","core","").then(function(t){n.one(c).append(t)})}),o.append(n),this.toolbar.getAttribute("aria-activedescendant")||(n.setAttribute("tabindex","0"),this.toolbar.setAttribute("aria-activedescendant",n.generateID())),this._buttonHandlers.push(this.toolbar.delegate("click",this._showToolbarMenu,"."+s,this,e),this.toolbar.delegate("key",this._showToolbarMenuAndFocus,"40, 32, enter","."+s,this,e)),this.buttonNames.push(e.buttonName),this.buttons[e.buttonName]=n,this.buttonStates[e.buttonName]=this.ENABLED,n},_showToolbarMenu:function(t,e){var n,i;t.preventDefault(),this.isEnabled()&&(t.currentTarget.ancestor("button",!0).hasAttribute(a)||(this.menus[e.buttonClass]||(e.overlayWidth||(e.overlayWidth="14"),e.innerOverlayWidth||(e.innerOverlayWidth=parseInt(e.overlayWidth,10)-2+"em"),e.overlayWidth=parseInt(e.overlayWidth,10)+"em",this.menus[e.buttonClass]=new u.M.editor_atto.Menu(e),this.menus[e.buttonClass].get("contentBox").delegate("click",this._chooseMenuItem,".atto_menuentry a",this,e)),u.Array.each(this.get("host").openMenus,function(t){t.set("focusAfterHide",null)}),(i=this.buttons[e.buttonName]).focus(),this.get("host")._setTabFocus(i),(n=this.menus[e.buttonClass]).set("focusAfterHide",i),n.show(),n.align(this.buttons[e.buttonName],[u.WidgetPositionAlign.TL,u.WidgetPositionAlign.BL]),this.get("host").openMenus=[n]))},_showToolbarMenuAndFocus:function(t,e){this._showToolbarMenu(t,e),this.menus[e.buttonClass].get("boundingBox").one("a").focus()},_chooseMenuItem:function(t,e,n){var i=t.target.ancestor("a",!0).getData("index"),o=this._normalizeCallback(e.items[i],e.globalItemConfig);(n=this.menus[e.buttonClass]).set("preventHideMenu",!0),o.callback(t,o._callback,o.callbackArgs),n.set("preventHideMenu",!1),n.set("focusAfterHide",this.get("host").editor),n.hide(t)},_normalizeCallback:function(n,t){return n._callbackNormalized||(t=t||{},n.inlineFormat=n.inlineFormat||t.inlineFormat,n._inlineCallback=n.callback||t.callback,n._callback=n.callback||t.callback,n.inlineFormat&&"function"==typeof n._inlineCallback&&(n._callback=function(t,e){this.get("host").applyFormat(t,n._inlineCallback,this,e)}),n.callback=u.rbind(this._callbackWrapper,this,n._callback,n.callbackArgs),
-n._callbackNormalized=!0),n},_normalizeIcon:function(t){return t.iconurl||(t.iconComponent&&"moodle"!=t.iconComponent||(t.iconComponent="core"),t.iconurl=M.util.image_url(t.icon,t.iconComponent)),t},_callbackWrapper:function(t,e,n){var i,o;if(t.preventDefault(),this.isEnabled()&&(!(i=t.currentTarget.ancestor("button",!0))||!i.hasAttribute(a)))return YUI.Env.UA.android||this.get("host").isActive()||this.get("host").focus(),this.get("host").saveSelection(),i&&this.get("host")._setTabFocus(i),o=[t,n],this.get("host").restoreSelection(),e.apply(this,o)},_addKeyboardListener:function(n,t,e){var i,o,a,s="key",r=l;if(u.Lang.isArray(t))return u.Array.each(t,function(t){this._addKeyboardListener(n,t)},this),this;o="object"==typeof t?(t.eventtype&&(s=t.eventtype),t.container&&(r=t.container),i=t.keyCodes,n):(a=this._getDefaultMetaKey(),i=this._getKeyEvent()+t+"+"+a,"undefined"==typeof this._primaryKeyboardShortcut[e]&&(this._primaryKeyboardShortcut[e]=this._getDefaultMetaKeyDescription(t)),u.bind(function(t,e){this._eventUsesExactKeyModifiers(t,e)&&n.apply(this,[e])},this,[a])),this._buttonHandlers.push(this.editor.delegate(s,o,i,r,this))},_eventUsesExactKeyModifiers:function(t,e){var n,i=!0;return"key"===e.type&&(n=-1
'),this.toolbar.append(e)),e}}}}),u.namespace("M.editor_atto").EditorPlugin=e,l='',a="disabled",o="highlight",h=".editor_atto_content",c=".editor_atto_menu_icon",d=".editor_atto_menu_expand",s.ATTRS={},s.prototype={buttons:null,buttonNames:null,buttonStates:null,menus:null,DISABLED:0,ENABLED:1,_buttonHandlers:null,_menuHideHandlers:null,_primaryKeyboardShortcut:null,_highlightQueue:null,addButton:function(e){var n,i,o,t=this.get("group"),a=this.name,s="atto_"+a+"_button",r=this.get("host");return e.exec&&(s=s+"_"+e.exec),e.buttonName?s=s+"_"+e.buttonName:e.buttonName=e.exec||a,e.buttonClass=s,(e=this._normalizeIcon(e)).title||(e.title="pluginname"),i=M.util.get_string(e.title,"atto_"+a),(n=u.Node.create('')).setAttribute("title",i),window.require(["core/templates"],function(t){t.renderPix(e.icon,e.iconComponent,i).then(function(t){n.append(t)})}),t.append(n),this.toolbar.getAttribute("aria-activedescendant")||(n.setAttribute("tabindex","0"),this.toolbar.setAttribute("aria-activedescendant",n.generateID()),this.get("host")._tabFocus=n),e=this._normalizeCallback(e),this._buttonHandlers.push(this.toolbar.delegate("click",e.callback,"."+s,this)),e.keys&&("undefined"!=typeof e.keyDescription&&(this._primaryKeyboardShortcut[s]=e.keyDescription),this._addKeyboardListener(e.callback,e.keys,s),this._primaryKeyboardShortcut[s]&&n.setAttribute("title",M.util.get_string("plugin_title_shortcut","editor_atto",{title:i,shortcut:this._primaryKeyboardShortcut[s]}))),e.tags&&(o=!0,"boolean"==typeof e.tagMatchRequiresAll&&(o=e.tagMatchRequiresAll),this._buttonHandlers.push(r.on(["atto:selectionchanged","change"],function(t){"undefined"!=typeof this._highlightQueue[e.buttonName]&&this._highlightQueue[e.buttonName].cancel(),this._highlightQueue[e.buttonName]=u.soon(u.bind(function(t){r.selectionFilterMatches(e.tags,t.selectedNodes,o)?this.highlightButtons(e.buttonName):this.unHighlightButtons(e.buttonName)},this,t))},this))),this.buttonNames.push(e.buttonName),this.buttons[e.buttonName]=n,this.buttonStates[e.buttonName]=this.ENABLED,n},addBasicButton:function(t){return t.exec?(t.icon||(t.icon="e/"+t.exec),t.callback=function(){document.execCommand(t.exec,!1,null),this.markUpdated()},this.addButton(t)):null},addToolbarMenu:function(e){var n,i,t,o,a=this.get("group"),s=this.name,r="atto_"+s+"_button";return e.buttonName?r=r+"_"+e.buttonName:e.buttonName=s,e.buttonClass=r,(e=this._normalizeIcon(e)).title||(e.title="pluginname"),i=M.util.get_string(e.title,"atto_"+s),e.menuColor||(e.menuColor="transparent"),t="atto_"+s+"_menubutton_"+u.stamp(this),o=u.Handlebars.compile(l),n=u.Node.create(o({buttonClass:r,config:e,title:i,id:t})),e.buttonId=t,window.require(["core/templates"],function(t){t.renderPix(e.icon,e.iconComponent,i).then(function(t){n.one(c).append(t)}),t.renderPix("t/expanded","core","").then(function(t){n.one(d).append(t)})}),a.append(n),this.toolbar.getAttribute("aria-activedescendant")||(n.setAttribute("tabindex","0"),this.toolbar.setAttribute("aria-activedescendant",n.generateID())),this._buttonHandlers.push(this.toolbar.delegate("click",this._showToolbarMenu,"."+r,this,e),this.toolbar.delegate("key",this._showToolbarMenuAndFocus,"40, 32, enter","."+r,this,e)),this.buttonNames.push(e.buttonName),this.buttons[e.buttonName]=n,this.buttonStates[e.buttonName]=this.ENABLED,n},_showToolbarMenu:function(t,e){var n,i,o;t.preventDefault(),this.isEnabled()&&((n=t.currentTarget.ancestor("button",!0)).hasAttribute(a)||(this.menus[e.buttonClass]||(e.overlayWidth||(e.overlayWidth="14"),e.innerOverlayWidth||(e.innerOverlayWidth=parseInt(e.overlayWidth,10)-2+"em"),e.overlayWidth=parseInt(e.overlayWidth,10)+"em",this.menus[e.buttonClass]=new u.M.editor_atto.Menu(e),this.menus[e.buttonClass].get("contentBox").delegate("click",this._chooseMenuItem,".atto_menuentry a",this,e)),u.Array.each(this.get("host").openMenus,function(t){t.set("focusAfterHide",null)}),(o=this.buttons[e.buttonName]).focus(),this.get("host")._setTabFocus(o),(i=this.menus[e.buttonClass]).set("focusAfterHide",o),i.show(),n.setAttribute("aria-expanded",!0),i.align(this.buttons[e.buttonName],[u.WidgetPositionAlign.TL,u.WidgetPositionAlign.BL]),this.get("host").openMenus=[i]))},_showToolbarMenuAndFocus:function(t,e){this._showToolbarMenu(t,e),this.menus[e.buttonClass].get("boundingBox").one("a").focus()},_chooseMenuItem:function(t,e,n){var i=t.target.ancestor("a",!0).getData("index"),o=this._normalizeCallback(e.items[i],e.globalItemConfig);(n=this.menus[e.buttonClass]).set("preventHideMenu",!0),o.callback(t,o._callback,o.callbackArgs),n.set("preventHideMenu",!1),n.set("focusAfterHide",this.get("host").editor),n.hide(t)},_normalizeCallback:function(n,t){return n._callbackNormalized||(t=t||{},n.inlineFormat=n.inlineFormat||t.inlineFormat,n._inlineCallback=n.callback||t.callback,n._callback=n.callback||t.callback,n.inlineFormat&&"function"==typeof n._inlineCallback&&(n._callback=function(t,e){
+this.get("host").applyFormat(t,n._inlineCallback,this,e)}),n.callback=u.rbind(this._callbackWrapper,this,n._callback,n.callbackArgs),n._callbackNormalized=!0),n},_normalizeIcon:function(t){return t.iconurl||(t.iconComponent&&"moodle"!=t.iconComponent||(t.iconComponent="core"),t.iconurl=M.util.image_url(t.icon,t.iconComponent)),t},_callbackWrapper:function(t,e,n){var i,o;if(t.preventDefault(),this.isEnabled()&&(!(i=t.currentTarget.ancestor("button",!0))||!i.hasAttribute(a)))return YUI.Env.UA.android||this.get("host").isActive()||this.get("host").focus(),this.get("host").saveSelection(),i&&this.get("host")._setTabFocus(i),o=[t,n],this.get("host").restoreSelection(),e.apply(this,o)},_addKeyboardListener:function(n,t,e){var i,o,a,s="key",r=h;if(u.Lang.isArray(t))return u.Array.each(t,function(t){this._addKeyboardListener(n,t)},this),this;o="object"==typeof t?(t.eventtype&&(s=t.eventtype),t.container&&(r=t.container),i=t.keyCodes,n):(a=this._getDefaultMetaKey(),i=this._getKeyEvent()+t+"+"+a,"undefined"==typeof this._primaryKeyboardShortcut[e]&&(this._primaryKeyboardShortcut[e]=this._getDefaultMetaKeyDescription(t)),u.bind(function(t,e){this._eventUsesExactKeyModifiers(t,e)&&n.apply(this,[e])},this,[a])),this._buttonHandlers.push(this.editor.delegate(s,o,i,r,this))},_eventUsesExactKeyModifiers:function(t,e){var n,i=!0;return"key"===e.type&&(n=-1' +
+ 'title="{{title}}" ' +
+ 'aria-haspopup="true" ' +
+ 'aria-controls="{{id}}_menu">' +
'' +
'' +
'';
@@ -525,13 +527,18 @@ EditorPluginButtons.prototype = {
}
// Create the actual button.
+ var id = 'atto_' + pluginname + '_menubutton_' + Y.stamp(this);
var template = Y.Handlebars.compile(MENUTEMPLATE);
button = Y.Node.create(template({
buttonClass: buttonClass,
config: config,
- title: title
+ title: title,
+ id: id
}));
+ // Add this button id to the config. It will be used in the menu later.
+ config.buttonId = id;
+
window.require(['core/templates'], function(Templates) {
Templates.renderPix(config.icon, config.iconComponent, title).then(function(iconhtml) {
button.one(CSS.MENUICON).append(iconhtml);
@@ -583,7 +590,8 @@ EditorPluginButtons.prototype = {
return;
}
- if (e.currentTarget.ancestor('button', true).hasAttribute(DISABLED)) {
+ var menuButton = e.currentTarget.ancestor('button', true);
+ if (menuButton.hasAttribute(DISABLED)) {
// Exit early if the clicked button was disabled.
return;
}
@@ -625,6 +633,9 @@ EditorPluginButtons.prototype = {
// Display the menu.
menuDialogue.show();
+ // Indicate that the menu is expanded.
+ menuButton.setAttribute("aria-expanded", true);
+
// Position it next to the button which opened it.
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
@@ -1020,16 +1031,35 @@ EditorPluginButtons.prototype = {
if (button) {
if (this.buttons[button]) {
this.buttons[button][method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}
} else {
Y.Object.each(this.buttons, function(button) {
button[method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}, this);
}
return this;
},
+ /**
+ * Fires a custom event that notifies listeners that a button's highlight has been toggled.
+ *
+ * @param {String} buttonName The button name.
+ * @param {Boolean} highlight True when the button was highlighted. False, otherwise.
+ * @private
+ */
+ _buttonHighlightToggled: function(buttonName, highlight) {
+ var toggledButton = this.buttons[buttonName];
+ if (toggledButton) {
+ // Fire an event that the button highlight was toggled.
+ require(['editor_atto/events'], function(attoEvents) {
+ attoEvents.notifyButtonHighlightToggled(toggledButton.getDOMNode(), buttonName, highlight);
+ });
+ }
+ },
+
/**
* Get the default meta key to use with keyboard events.
*
diff --git a/lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js b/lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
index 78db77f075e..4b1665cbf7f 100644
--- a/lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
+++ b/lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
@@ -29,9 +29,11 @@
var MENUTEMPLATE = '' +
'';
@@ -377,13 +379,18 @@ EditorPluginButtons.prototype = {
}
// Create the actual button.
+ var id = 'atto_' + pluginname + '_menubutton_' + Y.stamp(this);
var template = Y.Handlebars.compile(MENUTEMPLATE);
button = Y.Node.create(template({
buttonClass: buttonClass,
config: config,
- title: title
+ title: title,
+ id: id
}));
+ // Add this button id to the config. It will be used in the menu later.
+ config.buttonId = id;
+
window.require(['core/templates'], function(Templates) {
Templates.renderPix(config.icon, config.iconComponent, title).then(function(iconhtml) {
button.one(CSS.MENUICON).append(iconhtml);
@@ -435,7 +442,8 @@ EditorPluginButtons.prototype = {
return;
}
- if (e.currentTarget.ancestor('button', true).hasAttribute(DISABLED)) {
+ var menuButton = e.currentTarget.ancestor('button', true);
+ if (menuButton.hasAttribute(DISABLED)) {
// Exit early if the clicked button was disabled.
return;
}
@@ -477,6 +485,9 @@ EditorPluginButtons.prototype = {
// Display the menu.
menuDialogue.show();
+ // Indicate that the menu is expanded.
+ menuButton.setAttribute("aria-expanded", true);
+
// Position it next to the button which opened it.
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
@@ -874,16 +885,35 @@ EditorPluginButtons.prototype = {
if (button) {
if (this.buttons[button]) {
this.buttons[button][method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}
} else {
Y.Object.each(this.buttons, function(button) {
button[method](HIGHLIGHT);
+ this._buttonHighlightToggled(button, highlight);
}, this);
}
return this;
},
+ /**
+ * Fires a custom event that notifies listeners that a button's highlight has been toggled.
+ *
+ * @param {String} buttonName The button name.
+ * @param {Boolean} highlight True when the button was highlighted. False, otherwise.
+ * @private
+ */
+ _buttonHighlightToggled: function(buttonName, highlight) {
+ var toggledButton = this.buttons[buttonName];
+ if (toggledButton) {
+ // Fire an event that the button highlight was toggled.
+ require(['editor_atto/events'], function(attoEvents) {
+ attoEvents.notifyButtonHighlightToggled(toggledButton.getDOMNode(), buttonName, highlight);
+ });
+ }
+ },
+
/**
* Get the default meta key to use with keyboard events.
*
diff --git a/lib/editor/atto/yui/src/editor/js/menu.js b/lib/editor/atto/yui/src/editor/js/menu.js
index bbe2e9d6f2f..352540f209b 100644
--- a/lib/editor/atto/yui/src/editor/js/menu.js
+++ b/lib/editor/atto/yui/src/editor/js/menu.js
@@ -27,9 +27,9 @@ var LOGNAME = 'moodle-editor_atto-menu';
var MENUDIALOGUE = '' +
'
' +
- '
' +
+ '
' +
'{{#each config.items}}' +
- '
' +
+ '
' +
'' +
'{{{text}}}' +
'' +
@@ -65,12 +65,19 @@ Y.extend(Menu, M.core.dialogue, {
*/
_menuHandlers: null,
+ /**
+ * The menu button that controls this menu.
+ */
+ _menuButton: null,
+
initializer: function(config) {
- var headertext,
+ var headerText,
bb;
this._menuHandlers = [];
+ this._menuButton = document.getElementById(config.buttonId);
+
// Create the actual button.
var template = Y.Handlebars.compile(MENUDIALOGUE),
menu = Y.Node.create(template({
@@ -81,14 +88,24 @@ Y.extend(Menu, M.core.dialogue, {
bb = this.get('boundingBox');
bb.addClass('editor_atto_controlmenu');
bb.addClass('editor_atto_menu');
- bb.one('.moodle-dialogue-wrap')
- .removeClass('moodle-dialogue-wrap')
- .addClass('moodle-dialogue-content');
- headertext = Y.Node.create('')
+ // Get the dialogue container for this menu.
+ var content = bb.one('.moodle-dialogue-wrap');
+ content.removeClass('moodle-dialogue-wrap')
+ .addClass('moodle-dialogue-content');
+ // Remove the dialog role attribute.
+ content.removeAttribute('role');
+ // Remove aria-labelledby in the container. The aria-labelledby attribute is properly set in the menu's template.
+ content.removeAttribute('aria-labelledby');
+
+ // Render heading if necessary.
+ headerText = this.get('headerText').trim();
+ if (headerText) {
+ var heading = Y.Node.create('')
.addClass('accesshide')
- .setHTML(this.get('headerText'));
- this.get('bodyContent').prepend(headertext);
+ .setHTML(headerText);
+ this.get('bodyContent').prepend(heading);
+ }
// Hide the header and footer node entirely.
this.headerNode.hide();
@@ -150,6 +167,11 @@ Y.extend(Menu, M.core.dialogue, {
e.preventDefault();
}
+ // Remove menu button's aria-expanded attribute when this menu is hidden.
+ if (this._menuButton) {
+ this._menuButton.removeAttribute('aria-expanded');
+ }
+
return Menu.superclass.hide.call(this, arguments);
},