Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
255 lines
7.7 KiB
JavaScript
255 lines
7.7 KiB
JavaScript
/* global AccessibilityHelper, LazyLoader */
|
|
/* exported ViewManager */
|
|
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
var ViewManager = (function() {
|
|
|
|
// The ViewManager is in charge of simply manage the different views of the
|
|
// applications. ViewManager.changeViewTo() valid values are listed above
|
|
// these lines.
|
|
function ViewManager(tabs) {
|
|
tabs = tabs || [];
|
|
|
|
this._tabs = {};
|
|
tabs.forEach(function _registerTab(tabItem) {
|
|
if (typeof tabItem !== 'object') {
|
|
tabItem = { id: tabItem };
|
|
}
|
|
this._tabs[tabItem.id] = tabItem.tab || 'left';
|
|
}, this);
|
|
|
|
this._currentView = null;
|
|
this._currentTab = null;
|
|
|
|
}
|
|
|
|
// Return true if the passed view is a tab
|
|
ViewManager.prototype._isTab = function _isTab(view) {
|
|
return this._tabs.hasOwnProperty(view);
|
|
};
|
|
|
|
// Make target enter screen's main area and call callback after, passing as
|
|
// arguments if the new view is a tab, the new view id and a third parameter
|
|
// depending on if the view was a tab or not:
|
|
// If it is a tab: it returns the current overlay view id or null
|
|
// If it is not a tab: it returns the previous ovrlay view or null
|
|
ViewManager.prototype.changeViewTo = function _changeViewTo(viewHash,
|
|
obstructed,
|
|
callback) {
|
|
var hashParts = viewHash.split('?');
|
|
var viewId = hashParts[0];
|
|
if (this.isCurrentView(viewId)) {
|
|
return;
|
|
}
|
|
|
|
// Note here how we set the same value with different semantincs.
|
|
// This is used at the end of the function and the names are the correct
|
|
// because, depending on if the view is a tab or not, semantics may change.
|
|
var previousViewId, currentViewId;
|
|
previousViewId = currentViewId = this._currentView ?
|
|
this._currentView.id : null;
|
|
|
|
var view = document.getElementById(viewId);
|
|
var params = parseQueryParams(hashParts[1]);
|
|
var self = this;
|
|
|
|
// lazy load HTML of the panel
|
|
this.loadPanel(view, function() {
|
|
// Tabs are treated in a different way than overlay views
|
|
var isTab = self._isTab(viewId);
|
|
if (isTab) {
|
|
|
|
// Disposing the current view
|
|
var disposingTab = null;
|
|
if (self._currentTab) {
|
|
disposingTab = document.getElementById(self._currentTab);
|
|
}
|
|
if (disposingTab) {
|
|
disposingTab.dataset.viewport = self._tabs[disposingTab.id];
|
|
document.getElementById(disposingTab.id + '-filter').classList.remove(
|
|
'.selected');
|
|
}
|
|
// Showing the new one
|
|
view.dataset.viewport = '';
|
|
document.getElementById(view.id + '-filter').classList.add('.selected');
|
|
AccessibilityHelper.setAriaSelected(
|
|
document.getElementById(view.id + '-control'),
|
|
document.querySelectorAll('[role="tab"]'));
|
|
|
|
self._currentTab = viewId;
|
|
|
|
// Overlay view
|
|
} else {
|
|
self.closeCurrentView();
|
|
previousViewId = self._currentView ? self._currentView.id : '';
|
|
self._currentView = {
|
|
id: viewId,
|
|
defaultViewport: view.dataset.viewport,
|
|
obstructed: obstructed
|
|
};
|
|
|
|
// With a combination of CSS, we actually animate and display the view
|
|
delete view.dataset.viewport;
|
|
if (obstructed) {
|
|
document.querySelector(obstructed).classList.add('behind');
|
|
}
|
|
}
|
|
|
|
if (typeof callback === 'function') {
|
|
callback(isTab, viewId, isTab ? currentViewId : previousViewId);
|
|
}
|
|
notifyViewChange(isTab, viewId, params);
|
|
});
|
|
};
|
|
|
|
function notifyViewChange(isTab, current, params) {
|
|
var type = isTab ? 'tabchanged' : 'viewchanged';
|
|
var event = new CustomEvent(type, {
|
|
detail: {
|
|
id: current,
|
|
params: params
|
|
}
|
|
});
|
|
window.dispatchEvent(event);
|
|
}
|
|
|
|
ViewManager.prototype.loadPanel = function _loadPanel(panel, callback) {
|
|
if (!panel || panel.hidden === false) {
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
return;
|
|
}
|
|
|
|
LazyLoader.load(panel, function() {
|
|
var resourceRemaining = 0;
|
|
|
|
function loadResource(node) {
|
|
node.onload = onResourceLoaded;
|
|
document.head.appendChild(node);
|
|
resourceRemaining++;
|
|
}
|
|
|
|
// activate all styles
|
|
Array.from(panel.querySelectorAll('link')).forEach((node) => {
|
|
if (!document.querySelector('head > link[href="' + node.href + '"]')) {
|
|
var styleLink = document.createElement('link');
|
|
styleLink.href = node.href;
|
|
styleLink.media = node.media;
|
|
styleLink.rel = node.rel;
|
|
styleLink.type = node.type;
|
|
|
|
loadResource(styleLink);
|
|
}
|
|
node.remove();
|
|
});
|
|
|
|
// activate all scripts
|
|
Array.from(panel.querySelectorAll('script')).forEach((node) => {
|
|
if (!document.querySelector('head > script[src="' + node.src + '"]')) {
|
|
var scriptLink = document.createElement('script');
|
|
scriptLink.src = node.src;
|
|
scriptLink.type = node.type;
|
|
|
|
loadResource(scriptLink);
|
|
}
|
|
node.remove();
|
|
});
|
|
|
|
// add listeners
|
|
var headers = Array.from(
|
|
panel.querySelectorAll('gaia-header[action="close"]')
|
|
);
|
|
headers.forEach((headerWithClose) => {
|
|
headerWithClose.addEventListener('action', function() {
|
|
window.parent.location.hash = '#';
|
|
});
|
|
});
|
|
|
|
panel.hidden = false;
|
|
|
|
checkCallback();
|
|
|
|
function checkCallback() {
|
|
if (resourceRemaining === 0 && typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
function onResourceLoaded() {
|
|
resourceRemaining--;
|
|
checkCallback();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Close the current view returning to the previous one
|
|
ViewManager.prototype.closeCurrentView = function _closeCurrentView() {
|
|
if (!this._currentView) {
|
|
return;
|
|
}
|
|
|
|
var view = document.getElementById(this._currentView.id);
|
|
|
|
// With a combination of CSS, Restoring the last viewport we actually
|
|
// animate and hide the current view
|
|
view.dataset.viewport = this._currentView.defaultViewport;
|
|
|
|
if (this._currentView.obstructed) {
|
|
var obstructed = document.querySelector(this._currentView.obstructed);
|
|
obstructed.classList.remove('behind');
|
|
}
|
|
this._currentView = null;
|
|
};
|
|
|
|
// Test if the current view is the one passed as parameter
|
|
ViewManager.prototype.isCurrentView = function _isCurrentView(view) {
|
|
return this._currentView && this._currentView.id === view;
|
|
};
|
|
|
|
// Return the current view id or null if not current view
|
|
ViewManager.prototype.getCurrentView = function _getCurrentView() {
|
|
return this._currentView ? this._currentView.id : null;
|
|
};
|
|
|
|
// Return true if the tab id passed is the current tab
|
|
ViewManager.prototype.isCurrentTab = function _isCurrentTab(tab) {
|
|
return this._currentTab && this._currentTab === tab;
|
|
};
|
|
|
|
// Return the current tab
|
|
ViewManager.prototype.getCurrentTab = function _getCurrentTab() {
|
|
return this._currentTab;
|
|
};
|
|
|
|
function parseQueryParams(queryParams) {
|
|
var params = {};
|
|
|
|
queryParams = (queryParams || '').split('&');
|
|
queryParams.forEach(function(param) {
|
|
if (!param) { return; }
|
|
|
|
var pair = param.split('=');
|
|
var key = pair[0];
|
|
var value = pair[1];
|
|
|
|
if (params[key]) {
|
|
if (!Array.isArray(params[key])) {
|
|
params[key] = [params[key]];
|
|
}
|
|
|
|
params[key].push(value);
|
|
return;
|
|
}
|
|
|
|
params[key] = value;
|
|
});
|
|
|
|
return params;
|
|
}
|
|
|
|
return ViewManager;
|
|
}());
|