UNPKG

twilio-video

Version:

Twilio Video JavaScript Library

127 lines (113 loc) 3.23 kB
'use strict'; /** * The {@link DocumentVisibilityMonitor} monitors the visibility state of the DOM * and executes the attached listeners in phase order when the DOM is visible. */ class DocumentVisibilityMonitor { /** * Constructor. * @param {number} [nPhases=1] - the number of phases */ constructor(nPhases = 1) { Object.defineProperties(this, { _listeners: { value: [] }, _onVisibilityChange: { value: () => { this._emitVisible(document.visibilityState === 'visible'); } } }); for (let i = 0; i < nPhases; i++) { this._listeners.push([]); } } /** * clears the state. */ clear() { const nPhases = this._listeners.length; for (let i = 0; i < nPhases; i++) { this._listeners[i] = []; } } _listenerCount() { return this._listeners.reduce((count, phaseListeners) => count + phaseListeners.length, 0); } /** * Call all the listeners. Makes sure that all listeners for a given phase * are executed before calling the listeners of the next phase. * @private */ _emitVisible(isVisible) { let promise = Promise.resolve(); for (let phase = 1; phase <= this._listeners.length; phase++) { promise = promise.then(() => this._emitVisiblePhase(phase, isVisible)); } return promise; } /** * Call all the listeners for a given phase. * @private */ _emitVisiblePhase(phase, isVisible) { const phaseListeners = this._listeners[phase - 1]; return Promise.all(phaseListeners.map(listener => { const ret = listener(isVisible); return ret instanceof Promise ? ret : Promise.resolve(ret); })); } /** * Start listening to the DOM visibility state change. * @private */ _start() { document.addEventListener('visibilitychange', this._onVisibilityChange); } /** * Stop listening to the DOM visibility state change. * @private */ _stop() { document.removeEventListener('visibilitychange', this._onVisibilityChange); } /** * Listen for the DOM visibility changes at the given phase. * @param {number} phase * @param {function} listener * @returns {this} */ onVisibilityChange(phase, listener) { if (typeof phase !== 'number' || phase <= 0 || phase > this._listeners.length) { throw new Error('invalid phase: ', phase); } const phaseListeners = this._listeners[phase - 1]; phaseListeners.push(listener); if (this._listenerCount() === 1) { this._start(); } return this; } /** * Stop listening for the DOM visibility change at the given phase. * @param {number} phase * @param {function} listener * @returns {this} */ offVisibilityChange(phase, listener) { if (typeof phase !== 'number' || phase <= 0 || phase > this._listeners.length) { throw new Error('invalid phase: ', phase); } const phaseListeners = this._listeners[phase - 1]; const index = phaseListeners.indexOf(listener); if (index !== -1) { phaseListeners.splice(index, 1); if (this._listenerCount() === 0) { this._stop(); } } return this; } } module.exports = new DocumentVisibilityMonitor(2);