VulpesOS/gaia/shared/js/toaster.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

154 lines
4.8 KiB
JavaScript
Executable file

/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
/**
* XXX: Toaster create a HTML structure like this:
* <section role="status" hidden>
* <p></p>
* </section>
* and insert it to the bottom of <body> (or any other element you specified.)
* This HTML structure depends on styles declared in shared/style/status.css
*/
var Toaster = {
_containerElement: null,
_messageElement: null,
_defaultLatency: 3000,
_maxLatency: 5000,
_toastQueue: [],
_parentElement: null,
_toastVisibleClass: 'toast-visible',
_isBacklogged: function t_isBacklogged() {
return this._toastQueue.length > 0;
},
_isBusy: function t_isBusy() {
return this._containerElement.classList.contains(this._toastVisibleClass);
},
_showContainerElement: function t_showContainerElement() {
if (this._containerElement) {
this._containerElement.classList.add(this._toastVisibleClass);
this._containerElement.hidden = false;
}
},
_hideContainerElement: function t_hideContainerElement(useTransition) {
if (this._containerElement) {
this._containerElement.classList.remove(this._toastVisibleClass);
if (!useTransition) {
this._containerElement.hidden = true;
if (this._messageElement) {
this._messageElement.textContent = '';
this._messageElement.removeAttribute('data-l10n-id');
}
}
}
},
_onTransitionEnd: function t_onTransitionEnd(e) {
if (!Toaster._containerElement.classList.contains(
Toaster._toastVisibleClass)) {
Toaster._hideContainerElement();
}
},
_produceToast: function t_producetToast(messageId, messageArgs,
latency, useTransition) {
this._toastQueue.push({
messageL10nId: messageId,
messageL10nArgs: messageArgs,
latency: Math.min(latency || this._defaultLatency, this._maxLatency),
useTransition: useTransition
});
// if toaster is busy, don't bother to call it to consume toast
if (!this._isBusy()) {
this._consumeToast();
}
},
_consumeToast: function t_consumeToast() {
var self = this;
var toast = null;
if (self._isBacklogged()) {
toast = self._toastQueue.shift();
self._messageElement.textContent = '';
document.l10n.setAttributes(
self._messageElement, toast.messageL10nId, toast.messageL10nArgs);
self._showContainerElement();
setTimeout(function() {
self._hideContainerElement(toast.useTransition);
self._consumeToast();
}, toast.latency);
}
},
_destroy: function t_destroy() {
this._toastQueue = [];
if (this._containerElement) {
this._containerElement.removeEventListener(
'transitionend', Toaster._onTransitionEnd);
}
if (this._parentElement && this._containerElement) {
this._parentElement.removeChild(this._containerElement);
}
this._messageElement = null;
this._containerElement = null;
},
get containerElement() {
return this._containerElement;
},
get messageElement() {
return this._messageElement;
},
initialize: function t_initialize(parentElement) {
var existedToastContainer =
document.querySelector('section[role="status"]');
// Remove existing element of toast
// This case only exists in unit test
if (existedToastContainer) {
existedToastContainer.parentNode.removeChild(existedToastContainer);
}
this._destroy();
this._containerElement = document.createElement('section');
this._containerElement.setAttribute('role', 'status');
this._hideContainerElement();
this._messageElement = document.createElement('p');
this._containerElement.appendChild(this._messageElement);
this._parentElement = parentElement || document.body;
this._parentElement.appendChild(this._containerElement);
this._containerElement.addEventListener(
'transitionend', Toaster._onTransitionEnd);
},
isInitialized: function t_isInitialized() {
return (this._containerElement &&
this._messageElement &&
this._toastQueue);
},
// XXX: if you set useTransition to true, you must ensure that
// transition style does exist on the element.
// Argument 'options' are plain object containing properties below:
// messageL10nId: message l10n id
// messageL10nArgs: message l10n arguments (optional)
// latency: latency to show toast, in milliseconds
// useTransition: true/false (optional)
showToast: function t_showToast(options) {
// make sure toaster is initialized
if (!this.isInitialized()) {
this.initialize();
}
this._produceToast(
options.messageL10nId,
options.messageL10nArgs,
options.latency,
options.useTransition);
}
};