From 050159dc9faa76f577d2b5b381ee14abb2cb756c Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Thu, 3 Apr 2014 15:53:06 +0800 Subject: [PATCH] MDL-44131 atto_equation: Equation library groups are aria toolbars --- .../moodle-atto_equation-button-debug.js | 106 ++++++++++++++++-- .../moodle-atto_equation-button-min.js | 3 +- .../moodle-atto_equation-button.js | 105 +++++++++++++++-- .../equation/yui/src/button/js/button.js | 106 ++++++++++++++++-- 4 files changed, 292 insertions(+), 28 deletions(-) diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js index 00abb9c9ce2..9e412cd2727 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-debug.js @@ -34,15 +34,18 @@ YUI.add('moodle-atto_equation-button', function (Y, NAME) { */ var COMPONENTNAME = 'atto_equation', + LOGNAME = 'atto_equation', CSS = { EQUATION_TEXT: 'atto_equation_equation', EQUATION_PREVIEW: 'atto_equation_preview', SUBMIT: 'atto_equation_submit', LIBRARY: 'atto_equation_library', - LIBRARY_GROUP_PREFIX: 'atto_equation_library' + LIBRARY_GROUPS: 'atto_equation_groups', + LIBRARY_GROUP_PREFIX: 'atto_equation_group' }, SELECTORS = { - LIBRARY_GROUP_PREFIX: '.' + CSS.LIBRARY_GROUP_PREFIX, + LIBRARY: '.' + CSS.LIBRARY, + LIBRARY_GROUP: '.' + CSS.LIBRARY_GROUPS + ' > div > div', EQUATION_TEXT: '.' + CSS.EQUATION_TEXT, EQUATION_PREVIEW: '.' + CSS.EQUATION_PREVIEW, SUBMIT: '.' + CSS.SUBMIT, @@ -65,15 +68,19 @@ var COMPONENTNAME = 'atto_equation', '
' + '' + - '
' + + '
' + '{{#each library}}' + - '
' + - '{{#split "\n" elements}}' + - '' + - '{{/split}}' + + '
' + + '
' + + '{{#split "\n" elements}}' + + '' + + '{{/split}}' + + '
' + '
' + '{{/each}}' + '
' + @@ -111,7 +118,19 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. */ _content: null, + /** + * A reference to the tab focus set on each group. + * + * The keys are the IDs of the group, the value is the Node on which the focus is set. + * + * @property _groupFocus + * @type Object + * @private + */ + _groupFocus: null, + initializer: function() { + this._groupFocus = {}; if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ @@ -152,7 +171,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. var content = this._getDialogueContent(); dialogue.set('bodyContent', content); - var library = content.one(SELECTORS.LIBRARY_GROUP_PREFIX); + var library = content.one(SELECTORS.LIBRARY); var tabview = new Y.TabView({ srcNode: library @@ -321,6 +340,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS })); + // Sets the default focus. + this._content.all(SELECTORS.LIBRARY_GROUP).each(function(group) { + // The first button gets the focus. + this._setGroupTabFocus(group, group.one('button')); + // Sometimes the filter adds an anchor in the button, no tabindex on that. + group.all('button a').setAttribute('tabindex', '-1'); + }, this); + + // Keyboard navigation in groups. + this._content.delegate('key', this._groupNavigation, 'down:37,39', SELECTORS.LIBRARY_BUTTON, this); + this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this); this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this); this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this); @@ -330,6 +360,61 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. return this._content; }, + /** + * Callback handling the keyboard navigation in the groups of the library. + * + * @param {EventFacade} e The event. + * @method _groupNavigation + * @private + */ + _groupNavigation: function(e) { + e.preventDefault(); + + var current = e.currentTarget, + parent = current.get('parentNode'), // This must be the
containing all the buttons of the group. + buttons = parent.all('button'), + direction = e.keyCode !== 37 ? 1 : -1, + index = buttons.indexOf(current), + nextButton; + + if (index < 0) { + Y.log('Unable to find the current button in the list of buttons', 'debug', LOGNAME); + index = 0; + } + + index += direction; + if (index < 0) { + index = buttons.size() - 1; + } else if (index >= buttons.size()) { + index = 0; + } + nextButton = buttons.item(index); + + this._setGroupTabFocus(parent, nextButton); + nextButton.focus(); + }, + + /** + * Sets tab focus for the group. + * + * @method _setGroupTabFocus + * @param {Node} button The node that focus should now be set to. + * @private + */ + _setGroupTabFocus: function(parent, button) { + var parentId = parent.generateID(); + + // Unset the previous entry. + if (typeof this._groupFocus[parentId] !== 'undefined') { + this._groupFocus[parentId].setAttribute('tabindex', '-1'); + } + + // Set on the new entry. + this._groupFocus[parentId] = button; + button.setAttribute('tabindex', 0); + parent.setAttribute('aria-activedescendant', button.generateID()); + }, + /** * Reponse to button presses in the TeX library panels. * @@ -343,6 +428,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. e.preventDefault(); + // Set the group focus on the button. + this._setGroupTabFocus(e.currentTarget.get('parentNode'), e.currentTarget); + input = e.currentTarget.ancestor('.atto_form').one('textarea'); value = input.get('value'); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js index c043250ead3..a15a84eb374 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button-min.js @@ -1 +1,2 @@ -YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUP_PREFIX:"atto_equation_library"},i={LIBRARY_GROUP_PREFIX:"."+r.LIBRARY_GROUP_PREFIX,EQUATION_TEXT:"."+r.EQUATION_TEXT,EQUATION_PREVIEW:"."+r.EQUATION_PREVIEW,SUBMIT:"."+r.SUBMIT,LIBRARY_BUTTON:"."+r.LIBRARY+" button"},s={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,initializer:function(){this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var s=r.one(i.LIBRARY_GROUP_PREFIX),o=new e.TabView({srcNode:s});o.render(),t.show();var u=this._resolveEquation();u&&r.one(i.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},_setEquation:function(t){var n,r,i,s,o,u,a=this.get("host");t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(".atto_form").one("textarea"),u=n.get("value"),u!==""&&(a.setSelection(this._currentSelection),u="$$ "+u.trim()+" $$",r=e.one(a.getSelectionParentNode()),i=r.get("text"),s=/\$\$[\S\s]*\$\$/,o=s.exec(i),o&&o.length?(o=o.pop(),i=i.replace(o,"$$"+u+"$$"),r.set("text",i)):a.insertContentAtFocusPoint(u),this.markUpdated())},_updatePreview:function(t){var n=this._content.one(i.EQUATION_TEXT),r=n.get("value"),s,o,u=n.get("selectionStart"),a="",f="\\square ",l;t&&t.preventDefault(),u||(u=0);while(r.charAt(u)==="\\"&&u>0)u-=1;l=/[\w\{\}]/;while(l.test(r.charAt(u))&&u0)i=r.shift(),s+=n.fn(i);return s}),o=t({elementid:this.get("host").get("elementid"),component:n,library:i,CSS:r});var u=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",a={sesskey:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:o};return preview=e.io(u,{sync:!0,data:a,method:"POST"}),preview.status===200&&(o=preview.responseText),o}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","io","event-valuechange","tabview"]}); +YUI.add("moodle-atto_equation-button",function(e,t){var n="atto_equation",r="atto_equation",i={EQUATION_TEXT:"atto_equation_equation",EQUATION_PREVIEW:"atto_equation_preview",SUBMIT:"atto_equation_submit",LIBRARY:"atto_equation_library",LIBRARY_GROUPS:"atto_equation_groups",LIBRARY_GROUP_PREFIX:"atto_equation_group"},s={LIBRARY:"."+i.LIBRARY,LIBRARY_GROUP:"."+i.LIBRARY_GROUPS+" > div > div",EQUATION_TEXT:"."+i.EQUATION_TEXT,EQUATION_PREVIEW:"."+i.EQUATION_PREVIEW,SUBMIT:"."+i.SUBMIT,LIBRARY_BUTTON:"."+i.LIBRARY+" button"},o={FORM:'
{{{library}}}

',LIBRARY:'
{{#each library}}
{{#split "\n" elements}}{{/split}}
{{/each}}
'};e.namespace("M.atto_equation").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_lastCursorPos:0,_content:null,_groupFocus:null,initializer:function(){this._groupFocus={},this.get("texfilteractive")&&(this.addButton({icon:"e/math",callback:this._displayDialogue}),this.get("host").on("atto:selectionchanged",function(){this._resolveEquation()?this.highlightButtons():this.unHighlightButtons()},this))},_displayDialogue:function(){this._currentSelection=this.get("host").getSelection();if(this._currentSelection===!1)return;var t=this.getDialogue({headerContent:M.util.get_string("pluginname",n),focusAfterHide:!0,width:600}),r=this._getDialogueContent();t.set("bodyContent",r);var i=r.one(s.LIBRARY),o=new e.TabView({srcNode:i});o.render(),t.show();var u=this._resolveEquation();u&&r.one(s.EQUATION_TEXT).set("text",u),this._updatePreview(!1)},_resolveEquation:function(){var t=this.get("host").getSelectionParentNode(),n,r;return t?(n=e.one(t).get("text"),pattern=/\$\$[\S\s]*\$\$/,r=pattern.exec(n),r&&r.length?(r=r.pop(),r=r.substring(2,r.length-2),r):!1):!1},_setEquation:function(t){var n,r,i,s,o,u,a=this.get("host");t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),n=t.currentTarget.ancestor(".atto_form").one("textarea"),u=n.get("value"),u!==""&&(a.setSelection(this._currentSelection),u="$$ "+u.trim()+" $$",r=e.one(a.getSelectionParentNode()),i=r.get("text"),s=/\$\$[\S\s]*\$\$/,o=s.exec(i),o&&o.length?(o=o.pop(),i=i.replace(o,"$$"+u+"$$"),r.set("text",i)):a.insertContentAtFocusPoint(u),this.markUpdated())},_updatePreview:function(t){var n=this._content.one(s.EQUATION_TEXT),r=n.get("value"),i,o,u=n.get("selectionStart"),a="",f="\\square ",l;t&&t.preventDefault(),u||(u=0);while(r.charAt(u)==="\\"&&u>0)u-=1;l=/[\w\{\}]/;while(l.test(r.charAt(u))&&u=r.size()&&(s=0),o=r.item(s),this._setGroupTabFocus(n,o),o.focus()},_setGroupTabFocus:function(e,t){var n=e.generateID();typeof this._groupFocus[n]!="undefined"&&this._groupFocus[n].setAttribute("tabindex","-1"),this._groupFocus[n]=t,t.setAttribute("tabindex",0),e.setAttribute("aria-activedescendant",t.generateID())},_selectLibraryItem:function(e){var t=e.currentTarget.getAttribute("data-tex");e.preventDefault(),this._setGroupTabFocus(e.currentTarget.get("parentNode"),e.currentTarget),input=e.currentTarget.ancestor(".atto_form").one("textarea"),value=input.get("value"),value=value.substring(0,this._lastCursorPos)+t+value.substring(this._lastCursorPos,value.length),input.set("value",value),input.focus();var n=this._lastCursorPos+t.length,r=input.getDOMNode();if(typeof r.selectionStart=="number")r.selectionStart=r.selectionEnd=n;else if(typeof r.createTextRange!="undefined"){var i=r.createTextRange();i.moveToPoint(n),i.select()}this._updatePreview(!1)},_getLibraryContent:function(){var t=e.Handlebars.compile(o.LIBRARY),r=this.get("library"),s="";e.Handlebars.registerHelper("split",function(e,t,n){var r,i,s;if(typeof e=="undefined"||typeof t=="undefined")return"" +;s="",r=t.trim().split(e);while(r.length>0)i=r.shift(),s+=n.fn(i);return s}),s=t({elementid:this.get("host").get("elementid"),component:n,library:r,CSS:i});var u=M.cfg.wwwroot+"/lib/editor/atto/plugins/equation/ajax.php",a={sesskey:M.cfg.sesskey,contextid:this.get("contextid"),action:"filtertext",text:s};return preview=e.io(u,{sync:!0,data:a,method:"POST"}),preview.status===200&&(s=preview.responseText),s}},{ATTRS:{texfilteractive:{value:!1},contextid:{value:null},library:{value:{}},texdocsurl:{value:null}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin","io","event-valuechange","tabview"]}); diff --git a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js index 76df89c2ec8..19a1daeaa49 100644 --- a/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js +++ b/lib/editor/atto/plugins/equation/yui/build/moodle-atto_equation-button/moodle-atto_equation-button.js @@ -34,15 +34,18 @@ YUI.add('moodle-atto_equation-button', function (Y, NAME) { */ var COMPONENTNAME = 'atto_equation', + LOGNAME = 'atto_equation', CSS = { EQUATION_TEXT: 'atto_equation_equation', EQUATION_PREVIEW: 'atto_equation_preview', SUBMIT: 'atto_equation_submit', LIBRARY: 'atto_equation_library', - LIBRARY_GROUP_PREFIX: 'atto_equation_library' + LIBRARY_GROUPS: 'atto_equation_groups', + LIBRARY_GROUP_PREFIX: 'atto_equation_group' }, SELECTORS = { - LIBRARY_GROUP_PREFIX: '.' + CSS.LIBRARY_GROUP_PREFIX, + LIBRARY: '.' + CSS.LIBRARY, + LIBRARY_GROUP: '.' + CSS.LIBRARY_GROUPS + ' > div > div', EQUATION_TEXT: '.' + CSS.EQUATION_TEXT, EQUATION_PREVIEW: '.' + CSS.EQUATION_PREVIEW, SUBMIT: '.' + CSS.SUBMIT, @@ -65,15 +68,19 @@ var COMPONENTNAME = 'atto_equation', '
' + '' + - '
' + + '
' + '{{#each library}}' + - '
' + - '{{#split "\n" elements}}' + - '' + - '{{/split}}' + + '
' + + '
' + + '{{#split "\n" elements}}' + + '' + + '{{/split}}' + + '
' + '
' + '{{/each}}' + '
' + @@ -111,7 +118,19 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. */ _content: null, + /** + * A reference to the tab focus set on each group. + * + * The keys are the IDs of the group, the value is the Node on which the focus is set. + * + * @property _groupFocus + * @type Object + * @private + */ + _groupFocus: null, + initializer: function() { + this._groupFocus = {}; if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ @@ -152,7 +171,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. var content = this._getDialogueContent(); dialogue.set('bodyContent', content); - var library = content.one(SELECTORS.LIBRARY_GROUP_PREFIX); + var library = content.one(SELECTORS.LIBRARY); var tabview = new Y.TabView({ srcNode: library @@ -321,6 +340,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS })); + // Sets the default focus. + this._content.all(SELECTORS.LIBRARY_GROUP).each(function(group) { + // The first button gets the focus. + this._setGroupTabFocus(group, group.one('button')); + // Sometimes the filter adds an anchor in the button, no tabindex on that. + group.all('button a').setAttribute('tabindex', '-1'); + }, this); + + // Keyboard navigation in groups. + this._content.delegate('key', this._groupNavigation, 'down:37,39', SELECTORS.LIBRARY_BUTTON, this); + this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this); this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this); this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this); @@ -330,6 +360,60 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. return this._content; }, + /** + * Callback handling the keyboard navigation in the groups of the library. + * + * @param {EventFacade} e The event. + * @method _groupNavigation + * @private + */ + _groupNavigation: function(e) { + e.preventDefault(); + + var current = e.currentTarget, + parent = current.get('parentNode'), // This must be the
containing all the buttons of the group. + buttons = parent.all('button'), + direction = e.keyCode !== 37 ? 1 : -1, + index = buttons.indexOf(current), + nextButton; + + if (index < 0) { + index = 0; + } + + index += direction; + if (index < 0) { + index = buttons.size() - 1; + } else if (index >= buttons.size()) { + index = 0; + } + nextButton = buttons.item(index); + + this._setGroupTabFocus(parent, nextButton); + nextButton.focus(); + }, + + /** + * Sets tab focus for the group. + * + * @method _setGroupTabFocus + * @param {Node} button The node that focus should now be set to. + * @private + */ + _setGroupTabFocus: function(parent, button) { + var parentId = parent.generateID(); + + // Unset the previous entry. + if (typeof this._groupFocus[parentId] !== 'undefined') { + this._groupFocus[parentId].setAttribute('tabindex', '-1'); + } + + // Set on the new entry. + this._groupFocus[parentId] = button; + button.setAttribute('tabindex', 0); + parent.setAttribute('aria-activedescendant', button.generateID()); + }, + /** * Reponse to button presses in the TeX library panels. * @@ -343,6 +427,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. e.preventDefault(); + // Set the group focus on the button. + this._setGroupTabFocus(e.currentTarget.get('parentNode'), e.currentTarget); + input = e.currentTarget.ancestor('.atto_form').one('textarea'); value = input.get('value'); diff --git a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js index a5af93df7ef..e84acfc54ab 100644 --- a/lib/editor/atto/plugins/equation/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/equation/yui/src/button/js/button.js @@ -32,15 +32,18 @@ */ var COMPONENTNAME = 'atto_equation', + LOGNAME = 'atto_equation', CSS = { EQUATION_TEXT: 'atto_equation_equation', EQUATION_PREVIEW: 'atto_equation_preview', SUBMIT: 'atto_equation_submit', LIBRARY: 'atto_equation_library', - LIBRARY_GROUP_PREFIX: 'atto_equation_library' + LIBRARY_GROUPS: 'atto_equation_groups', + LIBRARY_GROUP_PREFIX: 'atto_equation_group' }, SELECTORS = { - LIBRARY_GROUP_PREFIX: '.' + CSS.LIBRARY_GROUP_PREFIX, + LIBRARY: '.' + CSS.LIBRARY, + LIBRARY_GROUP: '.' + CSS.LIBRARY_GROUPS + ' > div > div', EQUATION_TEXT: '.' + CSS.EQUATION_TEXT, EQUATION_PREVIEW: '.' + CSS.EQUATION_PREVIEW, SUBMIT: '.' + CSS.SUBMIT, @@ -63,15 +66,19 @@ var COMPONENTNAME = 'atto_equation', '
' + '' + - '
' + + '
' + '{{#each library}}' + - '
' + - '{{#split "\n" elements}}' + - '' + - '{{/split}}' + + '
' + + '
' + + '{{#split "\n" elements}}' + + '' + + '{{/split}}' + + '
' + '
' + '{{/each}}' + '
' + @@ -109,7 +116,19 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. */ _content: null, + /** + * A reference to the tab focus set on each group. + * + * The keys are the IDs of the group, the value is the Node on which the focus is set. + * + * @property _groupFocus + * @type Object + * @private + */ + _groupFocus: null, + initializer: function() { + this._groupFocus = {}; if (this.get('texfilteractive')) { // Add the button to the toolbar. this.addButton({ @@ -150,7 +169,7 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. var content = this._getDialogueContent(); dialogue.set('bodyContent', content); - var library = content.one(SELECTORS.LIBRARY_GROUP_PREFIX); + var library = content.one(SELECTORS.LIBRARY); var tabview = new Y.TabView({ srcNode: library @@ -319,6 +338,17 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. CSS: CSS })); + // Sets the default focus. + this._content.all(SELECTORS.LIBRARY_GROUP).each(function(group) { + // The first button gets the focus. + this._setGroupTabFocus(group, group.one('button')); + // Sometimes the filter adds an anchor in the button, no tabindex on that. + group.all('button a').setAttribute('tabindex', '-1'); + }, this); + + // Keyboard navigation in groups. + this._content.delegate('key', this._groupNavigation, 'down:37,39', SELECTORS.LIBRARY_BUTTON, this); + this._content.one(SELECTORS.SUBMIT).on('click', this._setEquation, this); this._content.one(SELECTORS.EQUATION_TEXT).on('valuechange', this._updatePreview, this); this._content.one(SELECTORS.EQUATION_TEXT).on('mouseup', this._updatePreview, this); @@ -328,6 +358,61 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. return this._content; }, + /** + * Callback handling the keyboard navigation in the groups of the library. + * + * @param {EventFacade} e The event. + * @method _groupNavigation + * @private + */ + _groupNavigation: function(e) { + e.preventDefault(); + + var current = e.currentTarget, + parent = current.get('parentNode'), // This must be the
containing all the buttons of the group. + buttons = parent.all('button'), + direction = e.keyCode !== 37 ? 1 : -1, + index = buttons.indexOf(current), + nextButton; + + if (index < 0) { + Y.log('Unable to find the current button in the list of buttons', 'debug', LOGNAME); + index = 0; + } + + index += direction; + if (index < 0) { + index = buttons.size() - 1; + } else if (index >= buttons.size()) { + index = 0; + } + nextButton = buttons.item(index); + + this._setGroupTabFocus(parent, nextButton); + nextButton.focus(); + }, + + /** + * Sets tab focus for the group. + * + * @method _setGroupTabFocus + * @param {Node} button The node that focus should now be set to. + * @private + */ + _setGroupTabFocus: function(parent, button) { + var parentId = parent.generateID(); + + // Unset the previous entry. + if (typeof this._groupFocus[parentId] !== 'undefined') { + this._groupFocus[parentId].setAttribute('tabindex', '-1'); + } + + // Set on the new entry. + this._groupFocus[parentId] = button; + button.setAttribute('tabindex', 0); + parent.setAttribute('aria-activedescendant', button.generateID()); + }, + /** * Reponse to button presses in the TeX library panels. * @@ -341,6 +426,9 @@ Y.namespace('M.atto_equation').Button = Y.Base.create('button', Y.M.editor_atto. e.preventDefault(); + // Set the group focus on the button. + this._setGroupTabFocus(e.currentTarget.get('parentNode'), e.currentTarget); + input = e.currentTarget.ancestor('.atto_form').one('textarea'); value = input.get('value');