MDL-44131 atto_equation: Equation library groups are aria toolbars

This commit is contained in:
Frederic Massart 2014-04-03 15:53:06 +08:00
parent 1a727e121e
commit 050159dc9f
4 changed files with 292 additions and 28 deletions

View File

@ -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',
'<div class="{{CSS.LIBRARY}}">' +
'<ul>' +
'{{#each library}}' +
'<li><a href="#{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">{{get_string groupname ../component}}</a></li>' +
'<li><a href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'{{get_string groupname ../component}}' +
'</a></li>' +
'{{/each}}' +
'</ul>' +
'<div>' +
'<div class="{{CSS.LIBRARY_GROUPS}}">' +
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'<div id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'<div role="toolbar">' +
'{{#split "\n" elements}}' +
'<button tabindex="-1" data-tex="{{this}}" aria-label="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'</div>' +
'</div>' +
'{{/each}}' +
'</div>' +
@ -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 <div> 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');

View File

@ -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',
'<div class="{{CSS.LIBRARY}}">' +
'<ul>' +
'{{#each library}}' +
'<li><a href="#{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">{{get_string groupname ../component}}</a></li>' +
'<li><a href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'{{get_string groupname ../component}}' +
'</a></li>' +
'{{/each}}' +
'</ul>' +
'<div>' +
'<div class="{{CSS.LIBRARY_GROUPS}}">' +
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'<div id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'<div role="toolbar">' +
'{{#split "\n" elements}}' +
'<button tabindex="-1" data-tex="{{this}}" aria-label="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'</div>' +
'</div>' +
'{{/each}}' +
'</div>' +
@ -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 <div> 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');

View File

@ -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',
'<div class="{{CSS.LIBRARY}}">' +
'<ul>' +
'{{#each library}}' +
'<li><a href="#{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">{{get_string groupname ../component}}</a></li>' +
'<li><a href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'{{get_string groupname ../component}}' +
'</a></li>' +
'{{/each}}' +
'</ul>' +
'<div>' +
'<div class="{{CSS.LIBRARY_GROUPS}}">' +
'{{#each library}}' +
'<div id="{{elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}{{@key}}">' +
'{{#split "\n" elements}}' +
'<button data-tex="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'<div id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'<div role="toolbar">' +
'{{#split "\n" elements}}' +
'<button tabindex="-1" data-tex="{{this}}" aria-label="{{this}}" title="{{this}}">$${{this}}$$</button>' +
'{{/split}}' +
'</div>' +
'</div>' +
'{{/each}}' +
'</div>' +
@ -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 <div> 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');