Restore Gaia settings and media workflows, extend native network adapters, and align the APK payload. Generate French/English changelogs and website entries from the simplified JSON source. Validation: shared JS and Tundra unit tests; desktop workflows and persistence; signed APK payload parity; Sargo RAM and installed startup with boot readback. Android runtime qualification is recorded separately.
300 lines
8.7 KiB
JavaScript
Executable file
300 lines
8.7 KiB
JavaScript
Executable file
/**
|
|
* DialogManager is a singleton that will help DialogService to load panels
|
|
* and control any transitions on panels.
|
|
*
|
|
* API:
|
|
*
|
|
* DialogManager.open(dialog, options);
|
|
* DialogManager.close(dialog, options);
|
|
*
|
|
* @module DialogManager
|
|
*/
|
|
define(function(require) {
|
|
'use strict';
|
|
|
|
var PanelCache = require('modules/panel_cache');
|
|
var LazyLoader = require('shared/lazy_loader');
|
|
|
|
var DialogManager = function() {
|
|
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
|
|
|
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
|
};
|
|
|
|
DialogManager.prototype = {
|
|
/**
|
|
* load panel based on passed in panelId
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {String} panelId
|
|
* @return {Promise}
|
|
*/
|
|
_loadPanel: function dm__loadPanel(panelId) {
|
|
var promise = new Promise(function(resolve, reject) {
|
|
var panelElement = document.getElementById(panelId);
|
|
if (panelElement.dataset.rendered) { // already initialized
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
panelElement.dataset.rendered = true;
|
|
|
|
// XXX remove SubPanel loader once sub panel are modulized
|
|
if (panelElement.dataset.requireSubPanels) {
|
|
// load the panel and its sub-panels (dependencies)
|
|
// (load the main panel last because it contains the scripts)
|
|
var selector = 'section[id^="' + panelElement.id + '-"]';
|
|
var subPanels = document.querySelectorAll(selector);
|
|
for (var i = 0, il = subPanels.length; i < il; i++) {
|
|
LazyLoader.load([subPanels[i]]);
|
|
}
|
|
LazyLoader.load([panelElement], resolve);
|
|
} else {
|
|
LazyLoader.load([panelElement], resolve);
|
|
}
|
|
});
|
|
return promise;
|
|
},
|
|
|
|
/**
|
|
* promised version of PanelCache.get()
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {String} panelId
|
|
* @return {Promise}
|
|
*/
|
|
_getPanel: function dm__getPanel(panelId) {
|
|
var promise = new Promise(function(resolve) {
|
|
PanelCache.get(panelId, function(panel) {
|
|
resolve(panel);
|
|
});
|
|
});
|
|
return promise;
|
|
},
|
|
|
|
/**
|
|
* this is used to control visibility of overlayDOM
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {Boolean} show
|
|
*/
|
|
_showOverlay: function dm__showOverlay(show) {
|
|
this._overlayDOM.hidden = !show;
|
|
},
|
|
|
|
/**
|
|
* It is used to control the timing of transitions so that we can make sure
|
|
* whether animation is done or not.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {String} method
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
_transit: function dm__transit(method, dialog, options) {
|
|
var promise = new Promise(function(resolve) {
|
|
var panel = dialog.panel;
|
|
var completed = false;
|
|
var fallback;
|
|
function finish(event) {
|
|
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
|
if (completed) return;
|
|
completed = true;
|
|
clearTimeout(fallback);
|
|
panel.removeEventListener('transitionend', finish);
|
|
panel.removeEventListener('transitioncancel', finish);
|
|
if (method === 'close') panel.hidden = true;
|
|
resolve();
|
|
}
|
|
if (method === 'open') panel.hidden = false;
|
|
setTimeout(function() {
|
|
panel.addEventListener('transitionend', finish);
|
|
panel.addEventListener('transitioncancel', finish);
|
|
panel.classList.toggle('current', method === 'open');
|
|
var style = window.getComputedStyle(panel);
|
|
function milliseconds(value) {
|
|
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
|
}
|
|
var durations = style.transitionDuration.split(',').map(milliseconds);
|
|
var delays = style.transitionDelay.split(',').map(milliseconds);
|
|
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
|
return value + delays[index % delays.length];
|
|
}));
|
|
// Reduced motion, display changes or interrupted transitions may
|
|
// omit the visibility event. Always release the dialog queue.
|
|
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
|
}, 150);
|
|
});
|
|
return promise;
|
|
},
|
|
|
|
/**
|
|
* Do necessary works to open panel like loading panel, doing transition
|
|
* and call related functions.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
_open: function dm__open(dialog, options) {
|
|
var self = this;
|
|
var foundPanel;
|
|
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
// 1: load panel
|
|
return self._loadPanel(dialog.panel.id);
|
|
})
|
|
.then(function() {
|
|
// 2: Get that panel
|
|
return self._getPanel(dialog.panel.id);
|
|
})
|
|
.then(function(panel) {
|
|
// 3: call beforeShow
|
|
foundPanel = panel;
|
|
return foundPanel.beforeShow(dialog.panel, options);
|
|
})
|
|
.then(function() {
|
|
// 4. UI stuffs + transition
|
|
dialog.init();
|
|
dialog.initUI();
|
|
dialog.bindEvents();
|
|
|
|
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
|
self._showOverlay(true);
|
|
}
|
|
|
|
return self._transit('open', dialog, options);
|
|
})
|
|
.then(function() {
|
|
// 5. show that panel as a dialog
|
|
return foundPanel.show(dialog.panel, options);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Do necessary works to close panel like loading panel, doing transition
|
|
* and call related functions.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
_close: function dm__close(dialog, options) {
|
|
var self = this;
|
|
var foundPanel;
|
|
var cachedResult;
|
|
|
|
return Promise.resolve()
|
|
.then(function() {
|
|
// 1: Get that panel
|
|
return self._getPanel(dialog.panel.id);
|
|
})
|
|
.then(function(panel) {
|
|
// 2: Let's validate to see whether we can close this dialog or not.
|
|
foundPanel = panel;
|
|
|
|
var promise;
|
|
// custom dialog - onSubmit
|
|
if (foundPanel.onSubmit && options._type === 'submit') {
|
|
promise = foundPanel.onSubmit();
|
|
// custom dialog - onCancel
|
|
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
|
promise = foundPanel.onCancel();
|
|
// if no onSubmit & onCancel, pass directly
|
|
} else {
|
|
promise = Promise.resolve();
|
|
}
|
|
|
|
return promise;
|
|
})
|
|
.then(function(result) {
|
|
cachedResult = result;
|
|
|
|
// 3: call beforeHide
|
|
return foundPanel.beforeHide();
|
|
})
|
|
.then(function() {
|
|
// 4. transition
|
|
return self._transit('close', dialog, options);
|
|
})
|
|
.then(function() {
|
|
// 5. call hide
|
|
return foundPanel.hide();
|
|
})
|
|
.then(function() {
|
|
// 6. Get result and cleanup dialog
|
|
var result;
|
|
|
|
// for prompt dialog, we have to get its own result from input text.
|
|
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
|
result = dialog.getResult();
|
|
} else if (cachedResult) {
|
|
result = cachedResult;
|
|
}
|
|
|
|
dialog.cleanup();
|
|
|
|
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
|
self._showOverlay(false);
|
|
}
|
|
|
|
return result;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* It is a bridge to call open or close function.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access private
|
|
* @param {String} method
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
_navigate: function dm__navigate(method, dialog, options) {
|
|
method = (method === 'open') ? '_open' : '_close';
|
|
return this[method](dialog, options);
|
|
},
|
|
|
|
/**
|
|
* DialogService would use this exposed API to open dialog.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access public
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
open: function dm_open(dialog, options) {
|
|
return this._navigate('open', dialog, options);
|
|
},
|
|
|
|
/**
|
|
* DialogService would use this exposed API to close dialog.
|
|
*
|
|
* @memberOf DialogManager
|
|
* @access public
|
|
* @param {BaseDialog} dialog
|
|
* @param {Object} options
|
|
* @return {Promise}
|
|
*/
|
|
close: function dm_close(dialog, type, options) {
|
|
options._type = type;
|
|
return this._navigate('close', dialog, options);
|
|
}
|
|
};
|
|
|
|
var dialogManager = new DialogManager();
|
|
return dialogManager;
|
|
});
|