MDL-41870 atto: keyboard navigation for toolbar (arrows)

This commit is contained in:
Damyon Wiese 2013-09-20 14:16:47 +08:00
parent 36973d707e
commit 6f9cf841c9
5 changed files with 248 additions and 33 deletions

View File

@ -159,19 +159,29 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_menu : function(elementid, plugin, icon, groupname, entries) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
currentfocus,
button;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname + '_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'tabindex="-1" ' +
'data-menu="' + plugin + '_' + elementid + '" >' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// Save the name of the plugin.
M.editor_atto.widgets[plugin] = plugin;
@ -228,20 +238,30 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_button : function(elementid, plugin, icon, groupname, handler) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
button,
currentfocus;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname +'_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'tabindex="-1" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// We only need to attach this once.
if (!M.editor_atto.buttonhandlers[plugin]) {
Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_button');
@ -294,7 +314,7 @@ M.editor_atto = M.editor_atto || {
'spellcheck="true" ' +
'class="editor_atto"/>');
var cssfont = '';
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar"/>');
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar" role="toolbar"/>');
// Bleh - why are we sent a url and not the css to apply directly?
var css = Y.io(params.content_css, { sync: true });
@ -323,10 +343,61 @@ M.editor_atto = M.editor_atto || {
textarea.set('value', atto.getHTML());
});
// Listen for Arrow left and Arrow right keys.
Y.one(Y.config.doc.body).delegate('key',
this.keyboard_navigation,
'down:37,39',
'#' + params.elementid + '_toolbar',
this,
params.elementid);
// Save the file picker options for later.
M.editor_atto.filepickeroptions[params.elementid] = params.filepickeroptions;
},
/**
* Implement arrow key navigation for the buttons in the toolbar.
* @param Event e - the keyboard event.
* @param string elementid - the id of the textarea we created this editor from.
*/
keyboard_navigation : function(e, elementid) {
var buttons,
current,
currentid,
currentindex;
e.preventDefault();
buttons = Y.all('#' + elementid + '_toolbar button');
currentid = Y.one('#' + elementid + '_toolbar').getAttribute('aria-activedescendant');
if (!currentid) {
return;
}
current = Y.one('#' + currentid);
current.setAttribute('tabindex', '-1');
currentindex = buttons.indexOf(current);
if (e.keyCode === 37) {
// Left
currentindex--;
if (currentindex < 0) {
currentindex = buttons.size()-1;
}
} else {
// Right
currentindex++;
if (currentindex >= buttons.size()) {
currentindex = 0;
}
}
current = buttons.item(currentindex);
current.setAttribute('tabindex', '0');
current.focus();
Y.one('#' + elementid + '_toolbar').setAttribute('aria-activedescendant', current.generateID());
},
/**
* Show the filepicker.
* @param string elementid for this editor instance.
@ -432,4 +503,4 @@ M.editor_atto = M.editor_atto || {
};
}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "moodle-core-notification"]});
}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event-key", "moodle-core-notification"]});

File diff suppressed because one or more lines are too long

View File

@ -159,19 +159,29 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_menu : function(elementid, plugin, icon, groupname, entries) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
currentfocus,
button;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname + '_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'tabindex="-1" ' +
'data-menu="' + plugin + '_' + elementid + '" >' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// Save the name of the plugin.
M.editor_atto.widgets[plugin] = plugin;
@ -228,20 +238,30 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_button : function(elementid, plugin, icon, groupname, handler) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
button,
currentfocus;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname +'_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'tabindex="-1" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// We only need to attach this once.
if (!M.editor_atto.buttonhandlers[plugin]) {
Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_button');
@ -294,7 +314,7 @@ M.editor_atto = M.editor_atto || {
'spellcheck="true" ' +
'class="editor_atto"/>');
var cssfont = '';
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar"/>');
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar" role="toolbar"/>');
// Bleh - why are we sent a url and not the css to apply directly?
var css = Y.io(params.content_css, { sync: true });
@ -323,10 +343,61 @@ M.editor_atto = M.editor_atto || {
textarea.set('value', atto.getHTML());
});
// Listen for Arrow left and Arrow right keys.
Y.one(Y.config.doc.body).delegate('key',
this.keyboard_navigation,
'down:37,39',
'#' + params.elementid + '_toolbar',
this,
params.elementid);
// Save the file picker options for later.
M.editor_atto.filepickeroptions[params.elementid] = params.filepickeroptions;
},
/**
* Implement arrow key navigation for the buttons in the toolbar.
* @param Event e - the keyboard event.
* @param string elementid - the id of the textarea we created this editor from.
*/
keyboard_navigation : function(e, elementid) {
var buttons,
current,
currentid,
currentindex;
e.preventDefault();
buttons = Y.all('#' + elementid + '_toolbar button');
currentid = Y.one('#' + elementid + '_toolbar').getAttribute('aria-activedescendant');
if (!currentid) {
return;
}
current = Y.one('#' + currentid);
current.setAttribute('tabindex', '-1');
currentindex = buttons.indexOf(current);
if (e.keyCode === 37) {
// Left
currentindex--;
if (currentindex < 0) {
currentindex = buttons.size()-1;
}
} else {
// Right
currentindex++;
if (currentindex >= buttons.size()) {
currentindex = 0;
}
}
current = buttons.item(currentindex);
current.setAttribute('tabindex', '0');
current.focus();
Y.one('#' + elementid + '_toolbar').setAttribute('aria-activedescendant', current.generateID());
},
/**
* Show the filepicker.
* @param string elementid for this editor instance.
@ -432,4 +503,4 @@ M.editor_atto = M.editor_atto || {
};
}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "moodle-core-notification"]});
}, '@VERSION@', {"requires": ["node", "io", "overlay", "escape", "event-key", "moodle-core-notification"]});

View File

@ -157,19 +157,29 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_menu : function(elementid, plugin, icon, groupname, entries) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
currentfocus,
button;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname + '_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
button = Y.Node.create('<button class="atto_' + plugin + '_button atto_hasmenu" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'tabindex="-1" ' +
'data-menu="' + plugin + '_' + elementid + '" >' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// Save the name of the plugin.
M.editor_atto.widgets[plugin] = plugin;
@ -226,20 +236,30 @@ M.editor_atto = M.editor_atto || {
*/
add_toolbar_button : function(elementid, plugin, icon, groupname, handler) {
var toolbar = Y.one('#' + elementid + '_toolbar'),
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group');
group = Y.one('#' + elementid + '_toolbar .atto_group.' + groupname + '_group'),
button,
currentfocus;
if (!group) {
group = Y.Node.create('<div class="atto_group ' + groupname +'_group"></div>');
toolbar.append(group);
}
var button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
button = Y.Node.create('<button class="atto_' + plugin + '_button" ' +
'data-editor="' + Y.Escape.html(elementid) + '" ' +
'data-plugin="' + Y.Escape.html(plugin) + '" ' +
'tabindex="-1" ' +
'data-handler="' + Y.Escape.html(plugin) + '">' +
icon +
'</button>');
group.append(button);
currentfocus = toolbar.getAttribute('aria-activedescendant');
if (!currentfocus) {
button.setAttribute('tabindex', '0');
toolbar.setAttribute('aria-activedescendant', button.generateID());
}
// We only need to attach this once.
if (!M.editor_atto.buttonhandlers[plugin]) {
Y.one('body').delegate('click', M.editor_atto.buttonclicked_handler, '.atto_' + plugin + '_button');
@ -292,7 +312,7 @@ M.editor_atto = M.editor_atto || {
'spellcheck="true" ' +
'class="editor_atto"/>');
var cssfont = '';
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar"/>');
var toolbar = Y.Node.create('<div class="editor_atto_toolbar" id="' + params.elementid + '_toolbar" role="toolbar"/>');
// Bleh - why are we sent a url and not the css to apply directly?
var css = Y.io(params.content_css, { sync: true });
@ -321,10 +341,61 @@ M.editor_atto = M.editor_atto || {
textarea.set('value', atto.getHTML());
});
// Listen for Arrow left and Arrow right keys.
Y.one(Y.config.doc.body).delegate('key',
this.keyboard_navigation,
'down:37,39',
'#' + params.elementid + '_toolbar',
this,
params.elementid);
// Save the file picker options for later.
M.editor_atto.filepickeroptions[params.elementid] = params.filepickeroptions;
},
/**
* Implement arrow key navigation for the buttons in the toolbar.
* @param Event e - the keyboard event.
* @param string elementid - the id of the textarea we created this editor from.
*/
keyboard_navigation : function(e, elementid) {
var buttons,
current,
currentid,
currentindex;
e.preventDefault();
buttons = Y.all('#' + elementid + '_toolbar button');
currentid = Y.one('#' + elementid + '_toolbar').getAttribute('aria-activedescendant');
if (!currentid) {
return;
}
current = Y.one('#' + currentid);
current.setAttribute('tabindex', '-1');
currentindex = buttons.indexOf(current);
if (e.keyCode === 37) {
// Left
currentindex--;
if (currentindex < 0) {
currentindex = buttons.size()-1;
}
} else {
// Right
currentindex++;
if (currentindex >= buttons.size()) {
currentindex = 0;
}
}
current = buttons.item(currentindex);
current.setAttribute('tabindex', '0');
current.focus();
Y.one('#' + elementid + '_toolbar').setAttribute('aria-activedescendant', current.generateID());
},
/**
* Show the filepicker.
* @param string elementid for this editor instance.

View File

@ -5,6 +5,7 @@
"io",
"overlay",
"escape",
"event-key",
"moodle-core-notification"
]
}