Commit 0e850aff by Valera Rozuvan

Completed main functionality. Ready for code review.

Now when controls are uatohidden and focus is on one of them, we
switch the focus to the mian Video element.

Code documented a bit.

When controls are autohidden, we make sure that speeds and volume
controls drop downs are closed.
parent 28888bda
/*
* 025_focus_grabber.js
*
* Purpose: Provide a way to focus on autohidden Video controls.
*
*
* Because in HTML player mode we have a feature of autohiding controls on
* mouse inactivity, sometimes focus is lost from the currently selected
* control. What's more, when all controls are autohidden, we can't get to any
* of them because by default browser does not place hidden elements on the
* focus chain.
*
* To get around this minor annoyance, this module will manage 2 placeholder
* elements that will be invisible to the user's eye, but visible to the
* browser. This will allow for a sneaky stealing of focus and placing it where
* we need (on hidden controls).
*
* This code has been moved to a separate module because it provides a concrete
* block of functionality that can be turned on (off).
*/
/*
* "If you want to climb a mountain, begin at the top."
*
* ~ Zen saying
*/
(function (requirejs, require, define) {
// FocusGrabber module.
......@@ -19,14 +46,15 @@ function () {
function _makeFunctionsPublic(state) {
state.focusGrabber.enableFocusGrabber = _.bind(enableFocusGrabber, state);
state.focusGrabber.disableFocusGrabber = _.bind(disableFocusGrabber, state);
state.focusGrabber.onFocus = _.bind(onFocus, state);
state.focusGrabber.onBlur = _.bind(onBlur, state);
}
function _renderElements(state) {
state.focusGrabber.elFirst = state.el.find('.focus_grabber.first');
state.focusGrabber.elLast = state.el.find('.focus_grabber.last');
// From the start, the Focus Grabber must be disabled so that
// tabbing (switching focus) does not land the user on one of the
// placeholder elements (elFirst, elLast).
state.focusGrabber.disableFocusGrabber();
}
......@@ -34,8 +62,12 @@ function () {
state.focusGrabber.elFirst.on('focus', state.focusGrabber.onFocus);
state.focusGrabber.elLast.on('focus', state.focusGrabber.onFocus);
state.focusGrabber.elFirst.on('blur', state.focusGrabber.onBlur);
state.focusGrabber.elLast.on('blur', state.focusGrabber.onBlur);
// When the video container element receives programmatic focus, then
// on un-focus ('blur' event) we should trigger a 'mousemove' event so
// as to reveal autohidden controls.
state.el.on('blur', function () {
state.el.trigger('mousemove');
});
}
......@@ -44,6 +76,14 @@ function () {
function enableFocusGrabber() {
var tabIndex;
// When the Focus Grabber is being enabled, there are two different
// scenarios:
//
// 1.) Currently focused element was inside the video player.
// 2.) Currently focused element was somewhere else on the page.
//
// In the first case we must make sure that the video player doesn't
// loose focus, even though the cfontrols are uatohidden.
if ($(document.activeElement).parents().hasClass('video')) {
tabIndex = -1;
} else {
......@@ -53,48 +93,36 @@ function () {
this.focusGrabber.elFirst.attr('tabindex', tabIndex);
this.focusGrabber.elLast.attr('tabindex', tabIndex);
$(document.activeElement).blur();
// Don't loose focus. We are inside video player on some control, but
// because we can't remain focused on a hidden element, we will shift
// focus to the main video element.
//
// Once the main element will receive the un-focus ('blur') event, a
// 'mousemove' event will be triggered, and the video controls will
// receive focus once again.
if (tabIndex === -1) {
this.focusGrabber.elFirst.trigger(
'focus',
{
simpleFocus: true
}
);
this.el.focus();
this.focusGrabber.elFirst.attr('tabindex', 0);
this.focusGrabber.elLast.attr('tabindex', 0);
}
}
function disableFocusGrabber() {
// Only programmatic focusing on these elements will be available.
// We don't want the user to focus on them (for example with the 'Tab'
// key).
this.focusGrabber.elFirst.attr('tabindex', -1);
this.focusGrabber.elLast.attr('tabindex', -1);
}
function onFocus(event, params) {
if (params && params.simpleFocus) {
this.focusGrabber.elFirst.attr('tabindex', 0);
this.focusGrabber.elLast.attr('tabindex', 0);
return;
}
// Once the Focus Grabber placeholder elements will gain focus, we will
// trigger 'mousemove' event so that the autohidden controls will
// become visible.
this.el.trigger('mousemove');
this.el.trigger('focus');
$('html, body').animate({
scrollTop: this.el.offset().top
}, 200);
this.focusGrabber.disableFocusGrabber();
}
function onBlur(event) {
this.el.trigger('mousemove');
this.el.trigger('focus');
$('html, body').animate({
scrollTop: this.el.offset().top
}, 200);
}
});
}(RequireJS.requirejs, RequireJS.require, RequireJS.define));
......@@ -129,6 +129,13 @@ function () {
this.videoControl.el.fadeOut(this.videoControl.fadeOutTimeout, function () {
_this.controlState = 'invisible';
// If the focus was on the video control or the volume control,
// then we must make sure to close these dialogs. Otherwise, after
// next autofocus, these dialogs will be open, but the focus will
// not be on them.
_this.videoVolumeControl.el.removeClass('open');
_this.videoSpeedControl.el.removeClass('open');
_this.focusGrabber.enableFocusGrabber();
});
}
......
......@@ -25,6 +25,8 @@
data-autoplay="${autoplay}"
data-yt-test-timeout="${yt_test_timeout}"
data-yt-test-url="${yt_test_url}"
tabindex="-1"
>
<div class="focus_grabber first"></div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment