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