Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.visibleTimeout / visibleTimeout.js
blob4f6cf8a253eaccf838fd66910b2eb1b9c90fb9d3
1 let doc, HIDDEN, VISIBILITY_CHANGE,
2         nextId = 1;
3 const clearHandles = Object.create( null );
5 function init( overrideDoc ) {
6         doc = overrideDoc || document;
8         if ( doc.hidden !== undefined ) {
9                 HIDDEN = 'hidden';
10                 VISIBILITY_CHANGE = 'visibilitychange';
11         } else if ( doc.mozHidden !== undefined ) {
12                 HIDDEN = 'mozHidden';
13                 VISIBILITY_CHANGE = 'mozvisibilitychange';
14         } else if ( doc.webkitHidden !== undefined ) {
15                 HIDDEN = 'webkitHidden';
16                 VISIBILITY_CHANGE = 'webkitvisibilitychange';
17         }
20 init();
22 /**
23  * A library similar to similar to setTimeout and clearTimeout,
24  * that pauses the time when page visibility is hidden.
25  *
26  * @exports mediawiki.visibleTimeout
27  * @singleton
28  */
29 module.exports = {
30         /**
31          * Generally similar to setTimeout, but pauses the time when page visibility is hidden.
32          *
33          * The provided function is invoked after the page has been cumulatively visible for the
34          * specified number of milliseconds.
35          *
36          * @param {Function} fn Callback
37          * @param {number} delay Time left, in milliseconds.
38          * @return {number} A positive integer value which identifies the timer. This
39          *  value can be passed to clear() to cancel the timeout.
40          */
41         set: function ( fn, delay ) {
42                 let nativeId = null,
43                         lastStartedAt = mw.now();
44                 const visibleId = nextId++;
46                 function clearHandle() {
47                         if ( nativeId !== null ) {
48                                 clearTimeout( nativeId );
49                                 nativeId = null;
50                         }
51                         delete clearHandles[ visibleId ];
52                         if ( VISIBILITY_CHANGE ) {
53                                 // Circular reference is intentional, chain starts after last definition.
54                                 doc.removeEventListener( VISIBILITY_CHANGE, visibilityCheck, false );
55                         }
56                 }
58                 function onComplete() {
59                         clearHandle();
60                         fn();
61                 }
63                 function visibilityCheck() {
64                         const now = mw.now();
66                         if ( HIDDEN && doc[ HIDDEN ] ) {
67                                 if ( nativeId !== null ) {
68                                         // Calculate how long we were visible, and update the time left.
69                                         delay = Math.max( 0, delay - Math.max( 0, now - lastStartedAt ) );
70                                         if ( delay === 0 ) {
71                                                 onComplete();
72                                         } else {
73                                                 // Unschedule the native timeout, will restart when visible again.
74                                                 clearTimeout( nativeId );
75                                                 nativeId = null;
76                                         }
77                                 }
78                         } else {
79                                 // If we're visible, or if HIDDEN is not supported, then start
80                                 // (or resume) the timeout, which runs the user callback after one
81                                 // delay, unless the page becomes hidden first.
82                                 if ( nativeId === null ) {
83                                         lastStartedAt = now;
84                                         nativeId = setTimeout( onComplete, delay );
85                                 }
86                         }
87                 }
89                 clearHandles[ visibleId ] = clearHandle;
90                 if ( VISIBILITY_CHANGE ) {
91                         doc.addEventListener( VISIBILITY_CHANGE, visibilityCheck, false );
92                 }
93                 visibilityCheck();
95                 return visibleId;
96         },
98         /**
99          * Cancel a visible timeout previously established by calling set.
100          *
101          * Passing an invalid ID silently does nothing.
102          *
103          * @param {number} visibleId The identifier of the visible timeout you
104          *  want to cancel. This ID was returned by the corresponding call to set().
105          */
106         clear: function ( visibleId ) {
107                 if ( visibleId in clearHandles ) {
108                         clearHandles[ visibleId ]();
109                 }
110         }
113 if ( window.QUnit ) {
114         module.exports.init = init;