Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
255 lines
7.7 KiB
JavaScript
Executable file
255 lines
7.7 KiB
JavaScript
Executable file
/* 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;
|
|
}());
|