%PDF- %PDF-
Direktori : /home/vacivi36/ava/lib/editor/atto/yui/src/editor/js/ |
Current File : /home/vacivi36/ava/lib/editor/atto/yui/src/editor/js/toolbar-keyboardnav.js |
// 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 <http://www.gnu.org/licenses/>. /** * @module moodle-editor_atto-editor * @submodule toolbarnav */ /** * Toolbar Navigation functions for the Atto editor. * * See {{#crossLink "M.editor_atto.Editor"}}{{/crossLink}} for details. * * @namespace M.editor_atto * @class EditorToolbarNav */ function EditorToolbarNav() {} EditorToolbarNav.ATTRS = { }; EditorToolbarNav.prototype = { /** * The current focal point for tabbing. * * @property _tabFocus * @type Node * @default null * @private */ _tabFocus: null, /** * Set up the watchers for toolbar navigation. * * @method setupToolbarNavigation * @chainable */ setupToolbarNavigation: function() { // Listen for Arrow left and Arrow right keys. this._wrapper.delegate('key', this.toolbarKeyboardNavigation, 'down:37,39', '.' + CSS.TOOLBAR, this); this._wrapper.delegate('focus', function(e) { this._setTabFocus(e.currentTarget); }, '.' + CSS.TOOLBAR + ' button', this); return this; }, /** * Implement arrow key navigation for the buttons in the toolbar. * * @method toolbarKeyboardNavigation * @param {EventFacade} e - the keyboard event. */ toolbarKeyboardNavigation: function(e) { // Prevent the default browser behaviour. e.preventDefault(); // On cursor moves we loops through the buttons. var buttons = this.toolbar.all('button'), direction = 1, button, current = e.target.ancestor('button', true), innerButtons = e.target.all('button'); // If we are not on a button and the element we are on contains some buttons, then move between the inner buttons. if (!current && innerButtons) { buttons = innerButtons; } if (e.keyCode === 37) { // Moving left so reverse the direction. direction = -1; } button = this._findFirstFocusable(buttons, current, direction); if (button) { button.focus(); this._setTabFocus(button); } else { Y.log("Unable to find a button to focus on", 'debug', LOGNAME); } }, /** * Find the first focusable button. * * @param {NodeList} buttons A list of nodes. * @param {Node} startAt The node in the list to start the search from. * @param {Number} direction The direction in which to search (1 or -1). * @return {Node | Undefined} The Node or undefined. * @method _findFirstFocusable * @private */ _findFirstFocusable: function(buttons, startAt, direction) { var checkCount = 0, candidate, button, index; // Determine which button to start the search from. index = buttons.indexOf(startAt); if (index < -1) { Y.log("Unable to find the button in the list of buttons", 'debug', LOGNAME); index = 0; } // Try to find the next. while (checkCount < buttons.size()) { index += direction; if (index < 0) { index = buttons.size() - 1; } else if (index >= buttons.size()) { // Handle wrapping. index = 0; } candidate = buttons.item(index); // Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item. checkCount++; // Loop while: // * we haven't checked every button; // * the button is hidden or disabled; // * the button is inside a hidden wrapper element. if (candidate.hasAttribute('hidden') || candidate.hasAttribute('disabled') || candidate.ancestor('[hidden]')) { continue; } button = candidate; break; } return button; }, /** * Check the tab focus. * * When we disable or hide a button, we should call this method to ensure that the * focus is not currently set on an inaccessible button, otherwise tabbing to the toolbar * would be impossible. * * @method checkTabFocus * @chainable */ checkTabFocus: function() { if (this._tabFocus) { if (this._tabFocus.hasAttribute('disabled') || this._tabFocus.hasAttribute('hidden') || this._tabFocus.ancestor('.atto_group').hasAttribute('hidden')) { // Find first available button. var button = this._findFirstFocusable(this.toolbar.all('button'), this._tabFocus, -1); if (button) { if (this._tabFocus.compareTo(document.activeElement)) { // We should also move the focus, because the inaccessible button also has the focus. button.focus(); } this._setTabFocus(button); } } } return this; }, /** * Sets tab focus for the toolbar to the specified Node. * * @method _setTabFocus * @param {Node} button The node that focus should now be set to * @chainable * @private */ _setTabFocus: function(button) { if (this._tabFocus) { // Unset the previous entry. this._tabFocus.setAttribute('tabindex', '-1'); } // Set up the new entry. this._tabFocus = button; this._tabFocus.setAttribute('tabindex', 0); // And update the activedescendant to point at the currently selected button. this.toolbar.setAttribute('aria-activedescendant', this._tabFocus.generateID()); return this; } }; Y.Base.mix(Y.M.editor_atto.Editor, [EditorToolbarNav]);