VulpesOS/gaia/apps/ftu/js/ui.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

650 lines
20 KiB
JavaScript
Executable file

/* global utils, tzSelect, FinishScreen,
Basket, ConfirmDialog, ScreenLayout,
DataMobile, SimManager, SdManager,
Tutorial, TimeManager, WifiManager,
WifiUI, WifiHelper, FxAccountsIACHelper, SettingsListener */
/* exported UIManager */
'use strict';
var UIManager = {
// As in other Gaia apps, we store all the dom selectors in one
// place and then camelCase them and attach to the main object,
// eg. instead of calling document.getElementById('splash-screen')
// we can access this.splashScreen in our code.
domSelectors: [
'container',
'splash-screen',
'activation-screen',
'finish-screen',
'update-screen',
'nav-bar',
'main-title',
// Unlock SIM Screen
'unlock-sim-screen',
'unlock-sim-header',
'unlock-sim-action',
// PIN Screen
'pincode-screen',
'pin-label',
'pin-retries-left',
'pin-input',
'back-sim-button',
'pin-error',
'skip-pin-button',
'unlock-sim-button',
// PUK Screen
'pukcode-screen',
'puk-label',
'puk-retries-left',
'puk-input',
'puk-info',
'puk-error',
'newpin-input',
'newpin-error',
'confirm-newpin-input',
'confirm-newpin-error',
// XCK Screen
'xckcode-screen',
'xck-label',
'xck-retries-left',
'xck-input',
'xck-error',
// SIM info
'sim-info-screen',
'sim-info-back',
'sim-info-forward',
'sim-info-1',
'sim-info-2',
'sim-number-1',
'sim-number-2',
'sim-carrier-1',
'sim-carrier-2',
// Import contacts
'sim-import',
'sim-import-button',
'no-sim',
'sd-import-button',
'no-memorycard',
// Fxa Intro
'fxa-create-account',
'fxa-intro',
// Wifi
'networks',
'wifi-refresh-button',
'wifi-join-button',
'join-hidden-button',
// Hidden Wifi
'hidden-wifi-authentication',
'hidden-wifi-ssid',
'hidden-wifi-security',
'hidden-wifi-password',
'hidden-wifi-password-box',
'hidden-wifi-identity',
'hidden-wifi-identity-box',
'hidden-wifi-show-password',
//Date & Time
'date-configuration',
'time-configuration',
'date-configuration-label',
'time-configuration-label',
'time-form',
// 3G
'data-connection-switch',
// Geolocation
'geolocation-switch',
// Tutorial
'lets-go-button',
'update-lets-go-button',
'skip-tutorial-button',
'update-skip-tutorial-button',
// Privacy Settings
'metrics-basic',
'metrics-enhanced',
'metrics-none',
'offline-error-dialog',
// Browser privacy newsletter subscription
'newsletter-form',
'newsletter-input',
'newsletter-success-screen',
'offline-newsletter-error-dialog',
'invalid-email-error-dialog'
],
dataConnectionChangedByUsr: false,
timeZoneNeedsConfirmation: true,
DARK_THEME: '#242d33',
LIGHT_THEME: '#eeeeee',
init: function ui_init() {
// Initialization of the DOM selectors
this.domSelectors.forEach(function createElementRef(name) {
this[toCamelCase(name)] = document.getElementById(name);
}.bind(this));
// Setup settings observers
this._settingsObserveHandler = {
'geolocation.enabled': {
// the "checked" attribute in the DOM is currently the source of truth
// for default value, when the setting is not initially defined
defaultValue: this.geolocationSwitch.checked,
callback: function(value) {
var isEnabled = !!value;
if (this.geolocationSwitch.checked !== isEnabled) {
this.geolocationSwitch.checked = isEnabled;
}
}.bind(this)
}
};
for (var name in this._settingsObserveHandler) {
SettingsListener.observe(
name,
this._settingsObserveHandler[name].defaultValue,
this._settingsObserveHandler[name].callback
);
}
var currentDate = new Date();
this.timeConfigurationLabel.textContent = currentDate.toLocaleString(
navigator.languages,
{
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
);
this.dateConfigurationLabel.textContent = currentDate.toLocaleString(
navigator.languages,
{
year: 'numeric',
month: 'numeric',
day: 'numeric'
}
);
// Add events to DOM
this.simImportButton.addEventListener('click', this);
this.sdImportButton.addEventListener('click', this);
this.skipPinButton.addEventListener('click', this);
this.backSimButton.addEventListener('click', this);
this.unlockSimButton.addEventListener('click', this);
this.unlockSimAction.addEventListener('action', this);
this.simInfoBack.addEventListener('click', this);
this.simInfoForward.addEventListener('click', this);
this.dataConnectionSwitch.addEventListener('change', this);
this.wifiRefreshButton.addEventListener('click', this);
this.wifiJoinButton.addEventListener('click', this);
this.networks.addEventListener('click', this);
this.joinHiddenButton.addEventListener('click', this);
this.hiddenWifiSecurity.addEventListener('change', this);
this.wifiJoinButton.disabled = true;
var checkHiddenWifiJoin = function() {
this.wifiJoinButton.disabled = this.hiddenWifiSsid.value === '' ||
!WifiHelper.isValidInput(this.hiddenWifiSecurity.value,
this.hiddenWifiPassword.value,
this.hiddenWifiIdentity.value
);
}.bind(this);
this.hiddenWifiSsid.addEventListener('keyup', checkHiddenWifiJoin);
this.hiddenWifiIdentity.addEventListener('keyup', checkHiddenWifiJoin);
this.hiddenWifiPassword.addEventListener('keyup', checkHiddenWifiJoin);
this.hiddenWifiSecurity.addEventListener('change', checkHiddenWifiJoin);
this.hiddenWifiShowPassword.onchange = function togglePasswordVisibility() {
UIManager.hiddenWifiPassword.type = this.checked ? 'text' : 'password';
};
this.timeConfiguration.addEventListener('input', this);
this.dateConfiguration.addEventListener('input', this);
this.geolocationSwitch.addEventListener('change', this);
this.fxaCreateAccount.addEventListener('click', this);
// Prevent form submit in case something tries to send it
this.timeForm.addEventListener('submit', function(event) {
event.preventDefault();
});
// Input scroll workaround
this.newsletterInput.addEventListener('focus', function() {
window.addEventListener('resize', function resize() {
window.removeEventListener('resize', resize);
// Need to wait till resize is done
setTimeout(function() {
var page = document.getElementById('browser_privacy');
UIManager.scrollToElement(page, UIManager.newsletterInput);
}, 30);
});
});
this.offlineNewsletterErrorDialog
.querySelector('button')
.addEventListener('click',
function offlineDialogClick() {
this.offlineNewsletterErrorDialog.classList.remove('visible');
}.bind(this));
this.invalidEmailErrorDialog
.querySelector('button')
.addEventListener('click',
function invalidEmailDialogClick() {
this.invalidEmailErrorDialog.classList.remove('visible');
}.bind(this));
var skipTutorialAction = function() {
// Stop Wifi Manager
WifiManager.finish();
// For tiny devices
if (ScreenLayout.getCurrentLayout() === 'tiny') {
window.close();
} else {
// for large devices
FinishScreen.init();
}
};
var startTutorialAction = function(evt) {
// Stop Wifi Manager
WifiManager.finish();
// Play the tutorial steps as soon as config is done loading
Tutorial.start(function onTutorialLoaded() {
UIManager.activationScreen.classList.remove('show');
UIManager.updateScreen.classList.remove('show');
UIManager.finishScreen.classList.remove('show');
});
};
this.skipTutorialButton.addEventListener('click', skipTutorialAction);
this.updateSkipTutorialButton.addEventListener('click', skipTutorialAction);
this.letsGoButton.addEventListener('click', startTutorialAction);
this.updateLetsGoButton.addEventListener('click', startTutorialAction);
// Enable saving metrics level choice to settings.
this.metricsBasic.addEventListener('change', this);
this.metricsEnhanced.addEventListener('change', this);
this.metricsNone.addEventListener('change', this);
var button = this.offlineErrorDialog.querySelector('button');
button.addEventListener('click',
this.onOfflineDialogButtonClick.bind(this));
// Handle activation screen visibility.
['confirmdialogshowing',
'loadingoverlayshowing',
'tutorialinitialized'].forEach(function(event) {
window.addEventListener(event,
this.hideActivationScreenFromScreenReader.bind(this));
}, this);
['confirmdialoghiding',
'loadingoverlayhiding'].forEach(function(event) {
window.addEventListener(event,
this.showActivationScreenToScreenReader.bind(this));
}, this);
this.checkInitialFxAStatus();
},
scrollToElement: function ui_scrollToElement(container, element) {
container.scrollTop = element.offsetTop;
},
sendNewsletter: function ui_sendNewsletter(callback) {
var self = this;
var emailValue = self.newsletterInput.value;
if (emailValue === '') {
return callback(true);
} else {
utils.overlay.show('email-loading', 'spinner');
if (self.newsletterInput.checkValidity()) {
if (window.navigator.onLine) {
Basket.send(emailValue, function emailSent(err, data) {
if (err) {
if (err.code && err.code === Basket.errors.INVALID_EMAIL) {
ConfirmDialog.show('invalid-email-dialog-title',
'invalid-email-dialog-text',
{
title: 'cancel',
callback: function ok() {
ConfirmDialog.hide();
}
});
utils.overlay.hide();
return callback(false);
} else {
Basket.store(emailValue);
}
}
utils.overlay.hide();
return callback(true);
});
} else {
Basket.store(emailValue, function stored(errorStoring) {
utils.overlay.hide();
return callback(!errorStoring);
});
}
} else {
utils.overlay.hide();
ConfirmDialog.show('invalid-email-dialog-title',
'invalid-email-dialog-text',
{
title: 'cancel',
callback: function ok() {
ConfirmDialog.hide();
}
});
return callback(false);
}
}
},
initTZ: function ui_initTZ() {
// Initialize the timezone selector, see /shared/js/tz_select.js
var self = this;
var tzRegion = document.getElementById('tz-region');
var tzCity = document.getElementById('tz-city');
return new Promise(function(resolve) {
var onchange = self.setTimeZone.bind(self);
var onload = function() {
self.setTimeZone.apply(self, arguments);
resolve();
};
tzSelect(tzRegion, tzCity, onchange, onload);
});
},
handleEvent: function ui_handleEvent(event) {
switch (event.target.id) {
// SIM
case 'skip-pin-button':
SimManager.skip();
break;
case 'back-sim-button':
case 'sim-info-back':
SimManager.back();
break;
case 'unlock-sim-button':
SimManager.unlock();
break;
case 'unlock-sim-action':
SimManager.simUnlockBack();
break;
case 'sim-info-forward':
SimManager.finish();
break;
case 'sim-import-button':
// Needed to give the browser the opportunity to properly refresh the UI
// Particularly the button toggling cycle (from inactive to active)
window.setTimeout(SimManager.importContacts, 0);
break;
case 'sd-import-button':
// Needed to give the browser the opportunity to properly refresh the UI
// Particularly the button toggling cycle (from inactive to active)
window.setTimeout(SdManager.importContacts, 0);
break;
// 3G
case 'data-connection-switch':
this.dataConnectionChangedByUsr = true;
var status = event.target.checked;
DataMobile.toggle(status);
break;
// WIFI
case 'wifi-refresh-button':
WifiManager.scan(WifiUI.renderNetworks);
break;
case 'wifi-join-button':
if (window.location.hash === '#hidden-wifi-authentication') {
WifiUI.joinHiddenNetwork();
} else {
WifiUI.joinNetwork();
}
break;
case 'join-hidden-button':
WifiUI.addHiddenNetwork();
break;
case 'hidden-wifi-security':
// Assuming that [0] is None, we prefer '' for collision on translations
var securityType = event.target.selectedIndex ? event.target.value : '';
WifiUI.handleHiddenWifiSecurity(securityType);
break;
// Date & Time
case 'time-configuration':
this.setTime();
break;
case 'date-configuration':
this.setDate();
break;
// Geolocation
case 'geolocation-switch':
this.updateSetting(event.target.name, event.target.checked);
break;
// Privacy
case 'metrics-enhanced':
this.updateSetting('hud.hide', (event.target.value === 'Enhanced'));
this.updateSetting('devtools.overlay',
(event.target.value === 'Enhanced'));
/* falls through */
case 'metrics-basic':
case 'metrics-none':
this.updateSetting('metrics.selectedMetrics.level', event.target.value);
break;
case 'fxa-create-account':
this.createFirefoxAccount();
break;
default:
// wifi selection
if (event.target.parentNode.id === 'networks-list') {
WifiUI.chooseNetwork(event);
}
break;
}
},
updateSetting: function ui_updateSetting(name, value) {
if (!name) {
return;
}
var cset = {}; cset[name] = value;
return SettingsListener.getSettingsLock().set(cset);
},
setForwardButtonLabel: function ui_setForwardButtonLabel(label) {
var nextButton = document.getElementById('forward');
nextButton.setAttribute('data-l10n-id', label);
},
checkInitialFxAStatus: function ui_checkInitialFxAStatus() {
// It is possible that we enter the FTU after the user aborted a FTU
// session where she logged into her FxA. In that case, we show the
// information of her account if possible. If there is any reason why we
// can't get the account information, we try to log her out.
// It is quite unlikely that logging out fails, but in that case, we simply
// hide the FxA panel to avoid potential errors such as the one reported on
// bug 1113551. In any case, the user should be able to manage her account
// from the Settings app afterwards.
this.skipFxA = true;
FxAccountsIACHelper.getAccount((account) => {
this.skipFxA = false;
this.onFxALogin(account);
}, () => {
FxAccountsIACHelper.logout(() => {
this.skipFxA = false;
});
});
},
createFirefoxAccount: function ui_createFirefoxAccount() {
FxAccountsIACHelper.openFlow(UIManager.onFxAFlowDone,
UIManager.onFxAError);
},
onFxAFlowDone: function ui_onFxAFlowDone() {
FxAccountsIACHelper.getAccount((account) => {
if (!account) {
return;
}
UIManager.onFxALogin(account);
UIManager.setForwardButtonLabel('navbar-next');
}, UIManager.onFxAError);
},
onFxALogin: function ui_onFxALogin(account) {
if (!account) {
return;
}
// Update the email
UIManager.newsletterInput.value = account.email;
// Update the string
UIManager.fxaIntro.innerHTML = '';
document.l10n.setAttributes(
UIManager.fxaIntro,
account.verified ? 'fxa-signed-in' : 'fxa-email-sent',
{
email: account.email
}
);
// Disable the button
UIManager.fxaCreateAccount.disabled = true;
},
onFxAError: function ui_onFxAError(response) {
console.error('Create FxA Error: ' + JSON.stringify(response));
// Clean fields
UIManager.newsletterInput.value = '';
// Reset the field
document.l10n.setAttributes(
UIManager.fxaIntro,
'fxa-upsell'
);
// Enable the button
UIManager.fxaCreateAccount.disabled = false;
// Change the forward button label
UIManager.setForwardButtonLabel('skip');
},
displayOfflineDialog: function ui_displayOfflineDialog(href, title) {
var dialog = this.offlineErrorDialog;
document.l10n.setAttributes(dialog.querySelector('small'),
'offline-dialog-text', { url: href });
dialog.classList.add('visible');
this.hideActivationScreenFromScreenReader();
},
onOfflineDialogButtonClick: function ui_onOfflineDialogButtonClick(e) {
this.offlineErrorDialog.classList.remove('visible');
this.showActivationScreenToScreenReader();
},
hideActivationScreenFromScreenReader:
function ui_hideActivationScreenFromScreenReader() {
this.activationScreen.setAttribute('aria-hidden', true);
},
showActivationScreenToScreenReader:
function ui_showActivationScreenToScreenReader() {
this.activationScreen.setAttribute('aria-hidden', false);
},
setDate: function ui_sd() {
if (!!this.lock) {
return;
}
// Current time
var now = new Date();
// Format: 2012-09-01
var currentDate = this.dateConfiguration.value;
var currentTime = now.toLocaleFormat('%H:%M');
var timeToSet = new Date(currentDate + 'T' + currentTime);
TimeManager.set(timeToSet);
this.dateConfigurationLabel.textContent = timeToSet.toLocaleString(
navigator.languages,
{
year: 'numeric',
month: 'numeric',
day: 'numeric'
}
);
},
setTime: function ui_st() {
if (!!this.lock) {
return;
}
var timeLabel = document.getElementById('time-configuration-label');
// Current time
var now = new Date();
// Format: 2012-09-01
var currentTime = document.getElementById('time-configuration').value;
if (currentTime.indexOf(':') === 1) { // Format: 8:05 --> 08:05
currentTime = '0' + currentTime;
}
var currentDate = now.toLocaleFormat('%Y-%m-%d');
var timeToSet = new Date(currentDate + 'T' + currentTime);
// Set date through API
TimeManager.set(timeToSet);
// Set DATE properly
timeLabel.textContent = timeToSet.toLocaleString(
navigator.languages,
{
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
);
},
setTimeZone: function ui_stz(timezone, needsConfirmation) {
this.timeZoneNeedsConfirmation = !!needsConfirmation;
var utcOffset = timezone.utcOffset;
document.getElementById('time_zone_overlay').className =
'UTC' + utcOffset.replace(/[+:]/g, '');
var timezoneTitle = document.getElementById('time-zone-title');
document.l10n.setAttributes(timezoneTitle, 'timezoneTitle', {
utcOffset: utcOffset,
region: timezone.region,
city: timezone.city
});
document.getElementById('tz-region-label').textContent = timezone.region;
document.getElementById('tz-city-label').textContent = timezone.city;
var now = new Date();
var timeLabel = document.getElementById('time-configuration-label');
timeLabel.textContent = now.toLocaleString(
navigator.languages,
{
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
);
},
updateDataConnectionStatus: function ui_udcs(status) {
this.dataConnectionSwitch.checked = status;
},
changeStatusBarColor: function ui_csbc(color) {
var themeMeta = document.head.querySelector('meta[name="theme-color"]');
themeMeta.setAttribute('content', color);
}
};
function toCamelCase(str) {
return str.replace(/\-(.)/g, function replacer(str, p1) {
return p1.toUpperCase();
});
}