%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/ui/ |
Current File : /www/varak.net/wiki.varak.net/extensions/VisualEditor/lib/ve/src/ui/ve.ui.TabIndexScope.js |
/*! * VisualEditor TabIndexScope class. * * @copyright 2011-2016 VisualEditor Team and others; see http://ve.mit-license.org */ /** * TabIndex Scope container constructor * * @class * @constructor * * @param {Object} [config] Configuration options * @cfg {Array} [root] Initial root element to scope tabIndex within * @cfg {boolean} [skipAriaDisabled] Whether to skip elements that are just aria-disabled from the order */ ve.ui.TabIndexScope = function VeUiTabIndexScope( config ) { config = $.extend( { root: false, skipAriaDisabled: true, skipAriaHidden: true }, config ); this.skipAriaDisabled = config.skipAriaDisabled; this.skipAriaHidden = config.skipAriaHidden; this.onRootKeyDownBound = this.onRootKeyDown.bind( this ); if ( config.root ) { this.setTabRoot( config.root ); } }; /* Setup */ OO.initClass( ve.ui.TabIndexScope ); /* Methods */ /** * Set the current root element for tabbing * * @param {HTMLElement[]} $root root element to scope tabIndex within */ ve.ui.TabIndexScope.prototype.setTabRoot = function ( $root ) { if ( this.$root ) { this.$root.off( 'keydown', this.onRootKeyDownBound ); } this.$root = $( $root ).on( 'keydown', this.onRootKeyDownBound ); }; /** * Build a list of elements in the current root, in tab order * * This mimics browser behavior: fetch focusable elements, sort by [tabIndex, DOM order] * * @return {HTMLElement[]} list of elements in the order they should be tabbed through */ ve.ui.TabIndexScope.prototype.getElementsInRoot = function () { var self = this, elements = this.$root.find( '*' ).filter( function () { if ( this.tabIndex === -1 ) { // tabIndex -1 is focusable, but shouldn't appear to keyboard-navigation return false; } if ( self.skipAriaDisabled && this.getAttribute( 'aria-disabled' ) === 'true' ) { return false; } if ( self.skipAriaHidden && $( this ).closest( '[aria-hidden="true"]' ).length ) { return false; } return OO.ui.isFocusableElement( $( this ) ); } ).map( function ( index ) { return { element: this, index: index }; } ).get(); elements.sort( function ( a, b ) { if ( a.element.tabIndex < b.element.tabIndex ) { return -1; } if ( a.element.tabIndex > b.element.tabIndex ) { return 1; } return a.index - b.index; } ); return elements.map( function ( data ) { return data.element; } ); }; /** * Handle keydown events on elements * * @private * @param {jQuery.Event} e */ ve.ui.TabIndexScope.prototype.onRootKeyDown = function ( e ) { var elements, index; if ( e.which !== OO.ui.Keys.TAB ) { return; } elements = this.getElementsInRoot(); index = elements.indexOf( e.target ); if ( index === -1 ) { return; } index += e.shiftKey ? -1 : 1; if ( ( index < 0 || index >= elements.length ) ) { return; } e.preventDefault(); elements[ index ].focus(); }; /** * Teardown tabbable elements manager * */ ve.ui.TabIndexScope.prototype.teardown = function () { this.setRoot( [] ); };