Skip to content

Commit

Permalink
Refactored DomNavigationManager, and renamed some public members.
Browse files Browse the repository at this point in the history
  • Loading branch information
ymeine committed Dec 1, 2016
1 parent 3af0fe1 commit efce49a
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 87 deletions.
10 changes: 5 additions & 5 deletions src/aria/popups/Popup.js
Expand Up @@ -27,7 +27,7 @@ var ariaCoreTimer = require("../core/Timer");
var ariaCoreJsonValidator = require("../core/JsonValidator");
var ViewportPopupContainer = require("./container/Viewport");
var environment = require("../core/environment/Environment");
var DialogNavigationInterceptor = require('../utils/DomNavigationManager').DialogNavigationInterceptor;
var ModalNavigationInterceptor = require('../utils/DomNavigationManager').ModalNavigationInterceptor;

/**
* Popup instance
Expand Down Expand Up @@ -190,7 +190,7 @@ module.exports = Aria.classDefinition({
this._document = Aria.$window.document;

var self = this;
this._dialogNavigationInterceptor = DialogNavigationInterceptor(function() {
this._modalNavigationInterceptor = ModalNavigationInterceptor(function() {
return self.domElement;
});

Expand Down Expand Up @@ -800,12 +800,12 @@ module.exports = Aria.classDefinition({
}

if (modalMaskDomElement && waiAria && popupContainer.getContainerRef() === ariaUtilsDom.VIEWPORT) {
this._dialogNavigationInterceptor.ensureElements();
this._modalNavigationInterceptor.ensureElements();
}
},

getNavigationInformation : function(focusedElement) {
return this._dialogNavigationInterceptor.getNavigationInformation(focusedElement);
return this._modalNavigationInterceptor.getNavigationInformation(focusedElement);
},

/**
Expand Down Expand Up @@ -862,7 +862,7 @@ module.exports = Aria.classDefinition({
if (!event.cancelClose) {
this._hide();

this._dialogNavigationInterceptor.destroyElements();
this._modalNavigationInterceptor.destroyElements();

this.isOpen = false;
// Notify the popup manager this popup was closed
Expand Down
219 changes: 141 additions & 78 deletions src/aria/utils/DomNavigationManager.js
Expand Up @@ -524,10 +524,31 @@ prototype.getNavigationInformation = function(focusedElement) {
/**
* A composite interceptor, implementing the NavigationInterceptor interface and delegating to multiple interceptors underneath.
*
* <p><em>commonSpec</em> is an object which accepts the following properties: </p>
* </ul>
* <li><em>onfocus</em>: when the value is not void, will set <em>onfocus</em> on each interceptor with the given value</li>
* </ul>
*
* @param {Array} interceptors The collection of actual navigation interceptor instances
* @param {Object} commonSpec Optional object with properties that can be applied to each given interceptor. See description for more information.
*/
var CompositeInterceptor = createConstructor(function(interceptors) {
var CompositeInterceptor = createConstructor(function(interceptors, commonSpec) {
// -------------------------------------------------------------- properties

this.interceptors = interceptors;

// -------------------------------------------------------------- processing

if (commonSpec == null) {
commonSpec = {};
}

var onfocus = commonSpec.onfocus;
if (onfocus != null) {
ariaUtilsArray.forEach(interceptors, function(interceptor) {
interceptor.onfocus = onfocus;
});
}
});
var prototype = CompositeInterceptor.prototype;

Expand Down Expand Up @@ -577,7 +598,7 @@ prototype.ensureElements = function () {


////////////////////////////////////////////////////////////////////////////////
//
// Specific navigation interceptors
////////////////////////////////////////////////////////////////////////////////

/**
Expand All @@ -591,7 +612,7 @@ prototype.ensureElements = function () {
*
* @return {NavigationInterceptor} an instance of NavigationInterceptor
*/
function DialogNavigationInterceptor(spec) {
function ModalNavigationInterceptor(spec) {
// ---------------------------------------------- input arguments processing

if (!ariaUtilsType.isObject(spec)) {
Expand Down Expand Up @@ -621,7 +642,10 @@ function DialogNavigationInterceptor(spec) {

return NavigationInterceptor(finalSpec);
}
exports.DialogNavigationInterceptor = DialogNavigationInterceptor;
exports.ModalNavigationInterceptor = ModalNavigationInterceptor;
/* BACKWARD-COMPATIBILITY-BEGIN (GitHub #1735) */
exports.DialogNavigationInterceptor = ModalNavigationInterceptor;
/* BACKWARD-COMPATIBILITY-END (GitHub #1735) */

/**
* Creates a NavigationInterceptor for an element to be skipped.
Expand Down Expand Up @@ -673,7 +697,7 @@ exports.SkippedElementNavigationInterceptor = SkippedElementNavigationIntercepto
* Interceptor elements will be inserted at the very beginning and at the very end of the viewport ("document.body"), corresponding respectively to a navigation direction "forward" or "backward".
* </p>
*
* * @param {Object|Function} spec Part of the input spec for NavigationInterceptor, excluding "origin", "interceptors" and "getReferenceElement".
* * @param {Object} spec Part of the input spec for NavigationInterceptor, excluding "origin", "interceptors" and "getReferenceElement".
*
* @return {NavigationInterceptor} an instance of NavigationInterceptor
*/
Expand Down Expand Up @@ -713,68 +737,80 @@ function ViewportNavigationInterceptor(spec) {
}
exports.ViewportNavigationInterceptor = ViewportNavigationInterceptor;



////////////////////////////////////////////////////////////////////////////////
// Navigation handlers
////////////////////////////////////////////////////////////////////////////////

/**
* Creates a NavigationInterceptor for a given element.
* This is a helper function to handle focus events due to elements inserted by a NavigationInterceptor.
*
* <p>This is voluntarily not flexible and will do the following: </p>
* <ul>
* <li>find the DOM element that received the focus</li>
* <li>use it to query navigation information from the interceptor</li>
* <li>calls the given function if there is any information, otherwise does nothing</li>
* </ul>
*
* @param {DOMEvent} event The DOM event received on focus
* @param {NavigationInterceptor} interceptor The navigation interceptor responsible for the catch of this focus event
* @param {Function} react The function that will be called is there is navigation information (and therefore a potentially wanted reaction). This function receives: the navigation information, the DOM element target
*
* @protected
*/
function handleInterceptorFocus(event, interceptor, react) {
// ----------------------------------------------------------- destructuring

var target = event.target;
if (target == null) {
target = event.srcElement;
}

// -------------------------------------------------------------- processing

var navigationInformation = interceptor.getNavigationInformation(target);

if (navigationInformation != null) {
react(navigationInformation, target);
}
}
exports.handleInterceptorFocus = handleInterceptorFocus;

/**
* Handles navigation for a modal element.
*
* <p>
* Interceptor elements will be inserted around the given element.
* </p>
* <p>
* Behavior when intercepting focus depends on the given options:
* Behavior is to fully contain the navigation inside the modal element, both:
* </p>
* <ul>
* <li><em>loop</em>: when true, will make the focus cycle inside the given element - not compatible with <em>skip</em>. Otherwise, it will block at the edges</li>
* <li><em>skip</em>: when true, it will make the tab navigation skip the element rather than containing it inside. Otherwise, it will contain the focus inside the element.</li>
* <li>preventing from going out of the page</li>
* <li>going directly into the modal element when coming from outside of the page</li>
* </ul>
*
* @param {HtmlElement} element The reference DOM element
* @param {Object} options See full description for more information.
* @param {Boolean} loop When true, it will make the focus cycle inside the given element. Otherwise, it will block at the edges. Defaults to false.
*
* @return {NavigationInterceptor} an instance of NavigationInterceptor
*/
function ElementNavigationInterceptor(element, options) {
function ModalNavigationHandler(element, loop) {
// ---------------------------------------------- input arguments processing

if (options == null) {
options = {};
}

if (!ariaUtilsType.isObject(options)) {
options = {loop: options};
}

var loop = options.loop;
if (loop == null) {
loop = false;
}
loop = !!loop;

var skip = options.skip;
if (skip == null) {
skip = false;
}
skip = !!skip;

// -------------------------------------------------------------- processing

var ElementInterceptor;
var reactToNavigationInformation;

if (skip) {
ElementInterceptor = SkippedElementNavigationInterceptor;
// ----------------------------------------------------------- local globals

reactToNavigationInformation = function (navigationInformation) {
var direction = navigationInformation.direction;
var interceptor;

var reverse = direction === 'backward';
var element = elementInterceptor.interceptors[reverse ? 0 : 1].element;
// -------------------------------------------------------------- processing

ariaTemplatesNavigationManager.focusNext(element, reverse);
};
} else {
ElementInterceptor = DialogNavigationInterceptor;
// -------------------------------------------------------------------------

reactToNavigationInformation = function (navigationInformation) {
var onfocus = function(event) {
handleInterceptorFocus(event, interceptor, function (navigationInformation) {
var direction = navigationInformation.direction;
var origin = navigationInformation.origin;

Expand All @@ -783,57 +819,84 @@ function ElementNavigationInterceptor(element, options) {
reverse = !reverse;
}
ariaTemplatesNavigationManager.focusFirst(element, reverse);
};
}
});
};

onfocus = {
fn: onfocus
};

// -------------------------------------------------------------------------

var onfocus = {
fn: function(event) {
// --------------------------------------------------- destructuring
var elementInterceptor = ModalNavigationInterceptor({
getReferenceElement: function() {return element;}
});

var target = event.target;
if (target == null) {
target = event.srcElement;
}
var viewportInterceptor = ViewportNavigationInterceptor();

// ------------------------------------------------------ processing
interceptor = CompositeInterceptor([
elementInterceptor,
viewportInterceptor
], {
onfocus: onfocus
});

var navigationInformation = finalInterceptor.getNavigationInformation(target);
// ------------------------------------------------------------------ return

if (navigationInformation != null) {
reactToNavigationInformation(navigationInformation);
}
}
};
return interceptor;
}
exports.ModalNavigationHandler = ModalNavigationHandler;
/* BACKWARD-COMPATIBILITY-BEGIN (GitHub #1735) */
exports.ElementNavigationInterceptor = ModalNavigationHandler;
/* BACKWARD-COMPATIBILITY-END (GitHub #1735) */

/**
* Handles navigation for a skipped element.
*
* <p>
* Behavior is to skip navigation through the element.
* </p>
*
* @param {HtmlElement} element The reference DOM element
*
* @return {NavigationInterceptor} an instance of NavigationInterceptor
*/
function SkippedElementNavigationHandler(element) {
// ----------------------------------------------------------- local globals

var interceptor;

// -------------------------------------------------------------- processing

// -------------------------------------------------------------------------

var elementInterceptor = ElementInterceptor({
onfocus: onfocus,
getReferenceElement: function() {return element;}
});
var onfocus = function(event) {
handleInterceptorFocus(event, interceptor, function (navigationInformation) {
var direction = navigationInformation.direction;

var finalInterceptor;
var reverse = direction === 'backward';
var element = interceptor.interceptors[reverse ? 0 : 1].element;

if (skip) {
finalInterceptor = elementInterceptor;
} else {
var viewportInterceptor = ViewportNavigationInterceptor({
onfocus: onfocus
ariaTemplatesNavigationManager.focusNext(element, reverse);
});
};

finalInterceptor = CompositeInterceptor([
elementInterceptor,
viewportInterceptor
]);
}
onfocus = {
fn: onfocus
};

// -------------------------------------------------------------------------

interceptor = SkippedElementNavigationInterceptor({
onfocus: onfocus,
getReferenceElement: function() {return element;}
});

// ------------------------------------------------------------------ return

return finalInterceptor;
return interceptor;
}
exports.ElementNavigationInterceptor = ElementNavigationInterceptor;
exports.SkippedElementNavigationHandler = SkippedElementNavigationHandler;



Expand Down
4 changes: 1 addition & 3 deletions src/aria/utils/overlay/LoadingOverlay.js
Expand Up @@ -141,9 +141,7 @@ module.exports = Aria.classDefinition({

if (waiAria) {
this._showElementBack = ariaUtilsDomNavigationManager.hidingManager.hide(element);
this._navigationInterceptor = ariaUtilsDomNavigationManager.ElementNavigationInterceptor(element, {
skip: true
});
this._navigationInterceptor = ariaUtilsDomNavigationManager.SkippedElementNavigationHandler(element);
this._navigationInterceptor.ensureElements();
}

Expand Down
2 changes: 1 addition & 1 deletion src/aria/widgets/form/MultiSelect.js
Expand Up @@ -302,7 +302,7 @@ module.exports = Aria.classDefinition({
_afterDropdownOpen : function () {

var waiAria = this._cfg.waiAria;
this.navigationInterceptor = DomNavigationManager.ElementNavigationInterceptor(this._dropdownPopup.domElement, !waiAria);
this.navigationInterceptor = DomNavigationManager.ModalNavigationHandler(this._dropdownPopup.domElement, !waiAria);
this.navigationInterceptor.ensureElements();

this._setPopupOpenProperty(true);
Expand Down

0 comments on commit efce49a

Please sign in to comment.