Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
183 lines
4.7 KiB
JavaScript
Executable file
183 lines
4.7 KiB
JavaScript
Executable file
/* global SpatialNavigator */
|
|
'use strict';
|
|
|
|
(function(exports) {
|
|
function Section(id) {
|
|
this.id = id;
|
|
this._dom = document.getElementById(this.id);
|
|
|
|
var focusables = this._dom.querySelectorAll('.focusable');
|
|
Array.from(focusables).forEach(function(elem) {
|
|
elem.setAttribute('tabindex', -1);
|
|
});
|
|
|
|
this._spatialNav = new SpatialNavigator(
|
|
focusables,
|
|
{
|
|
ignoreHiddenElement: true,
|
|
rememberSource: true
|
|
}
|
|
);
|
|
this._spatialNav.on('focus', (elem) => {
|
|
elem.focus();
|
|
});
|
|
|
|
this._childSections = {};
|
|
this._activeChildSectionId = '';
|
|
this._defaultFocusElement = undefined;
|
|
this._focusableElementCount = focusables.length;
|
|
}
|
|
|
|
Section.prototype = {
|
|
uninit: function() {
|
|
if (this._uninit) {
|
|
this._uninit();
|
|
}
|
|
|
|
this._defaultFocusElement = undefined;
|
|
this._activeChildSectionId = '';
|
|
this._childSections = {};
|
|
this._parentSection = undefined;
|
|
this._spatialNav = undefined;
|
|
this._dom = undefined;
|
|
},
|
|
|
|
addChild: function(id, section) {
|
|
this._childSections[id] = section;
|
|
section._parentSection = this;
|
|
},
|
|
|
|
removeChild: function(id) {
|
|
this._childSections[id]._parentSection = undefined;
|
|
delete this._childSections[id];
|
|
},
|
|
|
|
focus: function(elem) {
|
|
if (typeof elem === 'string') {
|
|
elem = document.getElementById(elem);
|
|
}
|
|
return this._spatialNav.focus(elem);
|
|
},
|
|
|
|
unfocus: function() {
|
|
return this._spatialNav.unfocus();
|
|
},
|
|
|
|
handleMove: function(direction) {
|
|
var activeChildSection = this.getActiveChildSection();
|
|
if (activeChildSection) {
|
|
activeChildSection.handleMove(direction);
|
|
} else {
|
|
if (this._handleMove) {
|
|
var nextElement = this._handleMove(direction);
|
|
if (nextElement) {
|
|
if (this._spatialNav.focus(nextElement)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (!this._spatialNav.move(direction)) {
|
|
// If failed to move, re-focus the current one.
|
|
this._spatialNav.focus();
|
|
}
|
|
}
|
|
},
|
|
|
|
handleClick: function() {
|
|
var activeChildSection = this.getActiveChildSection();
|
|
if (activeChildSection) {
|
|
activeChildSection.handleClick();
|
|
} else if (this._handleClick) {
|
|
this._handleClick();
|
|
} else if (!this._focusableElementCount) {
|
|
// If there is no focusable element in this section, just close it.
|
|
this.backToParent();
|
|
}
|
|
},
|
|
|
|
handleBack: function() {
|
|
var activeChildSection = this.getActiveChildSection();
|
|
if (activeChildSection) {
|
|
if (!activeChildSection.handleBack()) {
|
|
// Hide the child section if it doesn't handle BACK key.
|
|
this.hideChildSection();
|
|
return true;
|
|
}
|
|
} else if (this._handleBack) {
|
|
return this._handleBack();
|
|
}
|
|
// Returning false can let BACK key to be bubbled to its parent.
|
|
return false;
|
|
},
|
|
|
|
getFocusedElement: function() {
|
|
return this._spatialNav.getFocusedElement();
|
|
},
|
|
|
|
getActiveChildSection: function() {
|
|
if (!this._activeChildSectionId) {
|
|
return undefined;
|
|
}
|
|
return this._childSections[this._activeChildSectionId];
|
|
},
|
|
|
|
backToParent: function() {
|
|
if (this._parentSection) {
|
|
this._parentSection.hideChildSection();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
show: function(previousId) {
|
|
document.activeElement.blur();
|
|
|
|
if (this._readyToShow) {
|
|
this._readyToShow(previousId);
|
|
}
|
|
|
|
this._dom.classList.add('visible');
|
|
this._dom.removeAttribute('aria-hidden');
|
|
|
|
this._spatialNav.focus(this._defaultFocusElement);
|
|
},
|
|
|
|
hide: function() {
|
|
this._dom.classList.remove('visible');
|
|
this._dom.setAttribute('aria-hidden', true);
|
|
return this;
|
|
},
|
|
|
|
showChildSection: function(id) {
|
|
var child = this._childSections[id];
|
|
if (child) {
|
|
this.hide();
|
|
this._childSections[id].show();
|
|
this._activeChildSectionId = id;
|
|
}
|
|
},
|
|
|
|
hideChildSection: function() {
|
|
var activeChildSection = this.getActiveChildSection();
|
|
if (activeChildSection) {
|
|
var previousId = this._activeChildSectionId;
|
|
this._activeChildSectionId = '';
|
|
activeChildSection.hide();
|
|
this.show(previousId);
|
|
}
|
|
},
|
|
|
|
setDefaultFocusElement: function(elem) {
|
|
if (typeof elem === 'string') {
|
|
elem = document.getElementById(elem);
|
|
}
|
|
if (elem) {
|
|
this._defaultFocusElement = elem;
|
|
} else {
|
|
this._defaultFocusElement = undefined;
|
|
}
|
|
}
|
|
};
|
|
|
|
exports.Section = Section;
|
|
}(window));
|