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( '
{{#if nonedit}}
{{/if}}{{#if allowStyling}}
{{get_string "appearance" component}}{{#if allowBorders}}
{{#each availableColours}}
{{/each}}
{{/if}}{{#if allowBackgroundColour}}
{{#each availableColours}}
{{/each}}
{{/if}}{{#if allowWidth}}
{{/if}}
{{/if}}

{{#if edit}}{{/if}}{{#if nonedit}}{{/if}}
' ),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+">");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(""),d.insert(u,0)),u.setHTML(t.get("value")),u.setStyle("caption-side",o.get("value")),u.getAttribute("style")||u.removeAttribute("style"),"rows"!==a.get("value")&&"both"!==a.get("value")||d.all("tr").each(function(e){var t=e.all("th, td"),o=t.shift();"TD"===o.get("tagName")?this._changeNodeType(o,"th").setAttribute("scope","row"):o.setAttribute("scope","row"),t.each(function(e){"TH"===e.get("tagName")&&this._changeNodeType(e,"td").removeAttribute("scope")},this)},this),"columns"!==a.get("value")&&"both"!==a.get("value")||((g=d.all("tr")).shift().all("td, th").each(function(e){"TD"===e.get("tagName")?this._changeNodeType(e,"th").setAttribute("scope","col"):e.setAttribute("scope","col")},this),g.each(function(e){var t=e.all("th, td");"both"===a.get("value")&&t.shift(),t.each(function(e){"TH"===e.get("tagName")&&this._changeNodeType(e,"td").removeAttribute("scope")},this)},this)),this.markUpdated()},_setTable:function(e){var t,o,a,n,r,l,i,s,d,c,u,g,h,b,m,_,v,p;if(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.BORDERS),n=e.currentTarget.ancestor(w.FORM).one(w.BORDERSIZE),l=e.currentTarget.ancestor(w.FORM).one(w.SELECTEDBORDERCOLOUR),r=e.currentTarget.ancestor(w.FORM).one(w.BORDERSTYLE),u=e.currentTarget.ancestor(w.FORM).one(w.SELECTEDBACKGROUNDCOLOUR),i=e.currentTarget.ancestor(w.FORM).one(w.ROWS),s=e.currentTarget.ancestor(w.FORM).one(w.COLUMNS),d=e.currentTarget.ancestor(w.FORM).one(w.HEADERS),g=e.currentTarget.ancestor(w.FORM).one(w.WIDTH),this.get("host").setSelection(this._currentSelection),c="
"+(m="\n")+''+m,v="",o.get("value")&&(v=' style="caption-side: '+o.get("value")+'"'),c+=""+O.Escape.html(t.get("value"))+""+m,h=0,"columns"===d.get("value")||"both"===d.get("value")){for(h=1,c+=""+m+""+m,b=0;b'+m;c+=""+m+""+m}for(c+=""+m;h"+m,b=0;b"+m:c+=''+m;c+=""+m}c+=""+m,c+="
"+m+"
",this.get("host").insertContentAtFocusPoint(c),p=O.one("#"+_),this._setAppearance(p,{width:g,borders:a,borderColour:l,borderSize:n,borderStyle:r,backgroundColour:u}),p.removeAttribute("id"),this.markUpdated()},_findColumnCells:function(){var r=this._getColumnIndex(this._lastTarget),e=this._lastTarget.ancestor("table").all("tr"),l=new O.NodeList,i=new O.NodeList,s=new O.NodeList;return e.each(function(e){var t=e.all("td, th"),o=t.item(r),a=t.item(r-1),n=t.item(r+1);l.push(o),a&&i.push(a),n&&s.push(n)}),{current:l,prev:i,next:s}},_hideInvalidEntries:function(e){var t,o,a,n,r=this._lastTarget.ancestor("table"),l=this._lastTarget.ancestor("tr"),i=r.all("tr"),s=i.indexOf(l),d=i.item(s-1),c=d?d.one("td"):null;l&&c?e.one('[data-change="moverowup"]').show():e.one('[data-change="moverowup"]').hide(),t=i.item(s+1),o=!!l&&l.one("td"),l&&t&&o?e.one('[data-change="moverowdown"]').show():e.one('[data-change="moverowdown"]').hide(),0<(a=this._findColumnCells()).prev.filter("td").size()?e.one('[data-change="movecolumnleft"]').show():e.one('[data-change="movecolumnleft"]').hide(),n=0 "),1)},_removeCaption:function(){var e=this._lastTarget.ancestor("table").one("caption");e&&e.remove(!0)},_moveColumnRight:function(){var e,t,o,a=this._findColumnCells();if(0");e.replace(t),e=t}e.setHTML(" ")}),o.ancestor("thead")?(o=e,a.insert(t,o)):o.insert(t,"after"),this.markUpdated())},_addColumnAfter:function(){var e=this._findColumnCells(),o=!0,t=e.next;e.next.size()<=0&&(o=!1,t=e.current),O.each(t,function(e){var t=e.cloneNode();t.setHTML(" "),o?e.get("parentNode").insert(t,e):(e.get("parentNode").insert(t,e),e.swap(t))},this),this.markUpdated()}},{ATTRS:{allowBorders:{value:!0},borderStyles:{value:["none","solid","dashed","dotted"]},allowBackgroundColour:{value:!0},allowWidth:{value:!0},allowStyling:{readOnly:!0,getter:function(){return this.get("allowBorders")||this.get("allowBackgroundColour")||this.get("allowWidth")}},availableColors:{value:["#FFFFFF","#EF4540","#FFCF35","#98CA3E","#7D9FD3","#333333"],readOnly:!0}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-editor_atto-menu","event","event-valuechange"]}); \ No newline at end of file +'[data-change="deleterow"]').hide()},_showTableMenu:function(e,t){var o,a;e.preventDefault(),this._contextMenu||(this._menuOptions=[{text:M.util.get_string("addcolumnafter",C),data:{change:"addcolumnafter"}},{text:M.util.get_string("addrowafter",C),data:{change:"addrowafter"}},{text:M.util.get_string("moverowup",C),data:{change:"moverowup"}},{text:M.util.get_string("moverowdown",C),data:{change:"moverowdown"}},{text:M.util.get_string("movecolumnleft",C),data:{change:"movecolumnleft"}},{text:M.util.get_string("movecolumnright",C),data:{change:"movecolumnright"}},{text:M.util.get_string("deleterow",C),data:{change:"deleterow"}},{text:M.util.get_string("deletecolumn",C),data:{change:"deletecolumn"}},{text:M.util.get_string("edittable",C),data:{change:"edittable"}}],this._contextMenu=new O.M.editor_atto.Menu({items:this._menuOptions,buttonId:t}),(o=this._contextMenu.get("boundingBox")).delegate("click",this._handleTableChange,"a",this)),o=this._contextMenu.get("boundingBox"),this._lastTarget=e.tableCell.ancestor(".editor_atto_content td, .editor_atto_content th",!0),this._hideInvalidEntries(o),O.Array.each(this.get("host").openMenus,function(e){e.set("focusAfterHide",null)}),a=this.buttons[this.name],this.get("host")._setTabFocus(a),this._contextMenu.show(),this._contextMenu.align(this.buttons.table,[O.WidgetPositionAlign.TL,O.WidgetPositionAlign.BL]),this._contextMenu.set("focusAfterHide",a),o.one("a")&&o.one("a").focus(),this.get("host").openMenus=[this._contextMenu]},_handleTableChange:function(e){switch(e.preventDefault(),this._contextMenu.set("focusAfterHide",this.get("host").editor),this._contextMenu.hide(e),e.target.getData("change")){case"addcolumnafter":this._addColumnAfter();break;case"addrowafter":this._addRowAfter();break;case"deleterow":this._deleteRow();break;case"deletecolumn":this._deleteColumn();break;case"edittable":this._editTable();break;case"moverowdown":this._moveRowDown();break;case"moverowup":this._moveRowUp();break;case"movecolumnleft":this._moveColumnLeft();break;case"movecolumnright":this._moveColumnRight()}},_getRowIndex:function(e){var t=e.ancestor("table"),o=e.ancestor("tr");if(t&&o)return t.all("tr").indexOf(o)},_getColumnIndex:function(e){var t=e.ancestor("tr");if(t)return t.all("td, th").indexOf(e)},_deleteRow:function(){var e=this._lastTarget.ancestor("tr");e&&e.one("td")&&e.remove(!0),this.markUpdated()},_moveRowUp:function(){var e=this._lastTarget.ancestor("tr"),t=e.previous("tr");e&&t&&(e.swap(t),this.markUpdated())},_moveColumnLeft:function(){var e,t,o,a=this._findColumnCells();if(0 "),1)},_removeCaption:function(){var e=this._lastTarget.ancestor("table").one("caption");e&&e.remove(!0)},_moveColumnRight:function(){var e,t,o,a=this._findColumnCells();if(0");e.replace(t),e=t}e.setHTML(" ")}),o.ancestor("thead")?(o=e,a.insert(t,o)):o.insert(t,"after"),this.markUpdated())},_addColumnAfter:function(){var e=this._findColumnCells(),o=!0,t=e.next;e.next.size()<=0&&(o=!1,t=e.current),O.each(t,function(e){var t=e.cloneNode();t.setHTML(" "),o?e.get("parentNode").insert(t,e):(e.get("parentNode").insert(t,e),e.swap(t))},this),this.markUpdated()}},{ATTRS:{allowBorders:{value:!0},borderStyles:{value:["none","solid","dashed","dotted"]},allowBackgroundColour:{value:!0},allowWidth:{value:!0},allowStyling:{readOnly:!0,getter:function(){return this.get("allowBorders")||this.get("allowBackgroundColour")||this.get("allowWidth")}},availableColors:{value:["#FFFFFF","#EF4540","#FFCF35","#98CA3E","#7D9FD3","#333333"],readOnly:!0}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","moodle-editor_atto-menu","event","event-valuechange"]}); \ No newline at end of file diff --git a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js index 606e57feb43..b433456296e 100644 --- a/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js +++ b/lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.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/src/button/js/button.js b/lib/editor/atto/plugins/table/yui/src/button/js/button.js index 3b2d4d19a80..8f94a3b93a5 100644 --- a/lib/editor/atto/plugins/table/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/table/yui/src/button/js/button.js @@ -291,11 +291,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); @@ -303,6 +312,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. * @@ -341,10 +374,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); }, @@ -813,9 +855,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; @@ -871,7 +914,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/yui/build/moodle-editor_atto-menu/moodle-editor_atto-menu-debug.js b/lib/editor/atto/yui/build/moodle-editor_atto-menu/moodle-editor_atto-menu-debug.js index e0d9cdbbf23..9bc53896819 100644 --- a/lib/editor/atto/yui/build/moodle-editor_atto-menu/moodle-editor_atto-menu-debug.js +++ b/lib/editor/atto/yui/build/moodle-editor_atto-menu/moodle-editor_atto-menu-debug.js @@ -29,9 +29,9 @@ var LOGNAME = 'moodle-editor_atto-menu'; var MENUDIALOGUE = '' + '
' + - '
'),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 = '' + '
' + - '