Merge "Group messages in WANObjectCache by key"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.visibleTimeout.js
1 ( function ( mw, document ) {
2 var hidden, visibilityChange,
3 nextVisibleTimeoutId = 0,
4 activeTimeouts = {},
5 init = function ( overrideDoc ) {
6 if ( overrideDoc !== undefined ) {
7 document = overrideDoc;
8 }
9
10 if ( document.hidden !== undefined ) {
11 hidden = 'hidden';
12 visibilityChange = 'visibilitychange';
13 } else if ( document.mozHidden !== undefined ) {
14 hidden = 'mozHidden';
15 visibilityChange = 'mozvisibilitychange';
16 } else if ( document.msHidden !== undefined ) {
17 hidden = 'msHidden';
18 visibilityChange = 'msvisibilitychange';
19 } else if ( document.webkitHidden !== undefined ) {
20 hidden = 'webkitHidden';
21 visibilityChange = 'webkitvisibilitychange';
22 }
23 };
24
25 init();
26
27 /**
28 * @class mw.visibleTimeout
29 * @singleton
30 */
31 module.exports = {
32 /**
33 * Generally similar to setTimeout, but turns itself on/off on page
34 * visibility changes. The passed function fires after the page has been
35 * cumulatively visible for the specified number of ms.
36 *
37 * @param {Function} fn The action to execute after visible timeout has expired.
38 * @param {number} delay The number of ms the page should be visible before
39 * calling fn.
40 * @return {number} A positive integer value which identifies the timer. This
41 * value can be passed to clearVisibleTimeout() to cancel the timeout.
42 */
43 set: function ( fn, delay ) {
44 var handleVisibilityChange,
45 timeoutId = null,
46 visibleTimeoutId = nextVisibleTimeoutId++,
47 lastStartedAt = mw.now(),
48 clearVisibleTimeout = function () {
49 if ( timeoutId !== null ) {
50 clearTimeout( timeoutId );
51 timeoutId = null;
52 }
53 delete activeTimeouts[ visibleTimeoutId ];
54 if ( hidden !== undefined ) {
55 document.removeEventListener( visibilityChange, handleVisibilityChange, false );
56 }
57 },
58 onComplete = function () {
59 clearVisibleTimeout();
60 fn();
61 };
62
63 handleVisibilityChange = function () {
64 var now = mw.now();
65
66 if ( document[ hidden ] ) {
67 // pause timeout if running
68 if ( timeoutId !== null ) {
69 delay = Math.max( 0, delay - Math.max( 0, now - lastStartedAt ) );
70 if ( delay === 0 ) {
71 onComplete();
72 } else {
73 clearTimeout( timeoutId );
74 timeoutId = null;
75 }
76 }
77 } else {
78 // resume timeout if not running
79 if ( timeoutId === null ) {
80 lastStartedAt = now;
81 timeoutId = setTimeout( onComplete, delay );
82 }
83 }
84 };
85
86 activeTimeouts[ visibleTimeoutId ] = clearVisibleTimeout;
87 if ( hidden !== undefined ) {
88 document.addEventListener( visibilityChange, handleVisibilityChange, false );
89 }
90 handleVisibilityChange();
91
92 return visibleTimeoutId;
93 },
94
95 /**
96 * Cancel a visible timeout previously established by calling set.
97 * Passing an invalid ID silently does nothing.
98 *
99 * @param {number} visibleTimeoutId The identifier of the visible
100 * timeout you want to cancel. This ID was returned by the
101 * corresponding call to set().
102 */
103 clear: function ( visibleTimeoutId ) {
104 if ( activeTimeouts.hasOwnProperty( visibleTimeoutId ) ) {
105 activeTimeouts[ visibleTimeoutId ]();
106 }
107 }
108 };
109
110 if ( window.QUnit ) {
111 module.exports.setDocument = init;
112 }
113
114 }( mediaWiki, document ) );