// Used when the interval is reset
originalInterval: 0,
+ // Used to limit the number of AJAX requests.
+ minimalInterval: 0,
+
// Used together with tempInterval
countdown: 0,
// Flags whether events tracking user activity were set
userActivityEvents: false,
- // References to various timeouts
- beatTimer: 0,
- winBlurTimer: 0,
- frameBlurTimer: 0
+ checkFocusTimer: 0,
+ beatTimer: 0
};
/**
* @return void
*/
function initialize() {
+ var options, hidden, visibilityState, visibilitychange;
+
if ( typeof window.pagenow === 'string' ) {
settings.screenId = window.pagenow;
}
// Pull in options passed from PHP
if ( typeof window.heartbeatSettings === 'object' ) {
- var options = window.heartbeatSettings;
+ options = window.heartbeatSettings;
// The XHR URL can be passed as option when window.ajaxurl is not set
if ( ! settings.url && options.ajaxurl ) {
settings.url = options.ajaxurl;
}
- // The interval can be from 15 to 60 sec. and can be set temporarily to 5 sec.
+ // The interval can be from 15 to 120 sec. and can be set temporarily to 5 sec.
+ // It can be set in the initial options or changed later from JS and/or from PHP.
if ( options.interval ) {
settings.mainInterval = options.interval;
if ( settings.mainInterval < 15 ) {
settings.mainInterval = 15;
- } else if ( settings.mainInterval > 60 ) {
- settings.mainInterval = 60;
+ } else if ( settings.mainInterval > 120 ) {
+ settings.mainInterval = 120;
}
}
- // 'screenId' can be added from settings on the front-end where the JS global 'pagenow' is not set
+ // Used to limit the number of AJAX requests. Overrides all other intervals if they are shorter.
+ // Needed for some hosts that cannot handle frequent requests and the user may exceed the allocated server CPU time, etc.
+ // The minimal interval can be up to 600 sec. however setting it to longer than 120 sec. will limit or disable
+ // some of the functionality (like post locks).
+ // Once set at initialization, minimalInterval cannot be changed/overriden.
+ if ( options.minimalInterval ) {
+ options.minimalInterval = parseInt( options.minimalInterval, 10 );
+ settings.minimalInterval = options.minimalInterval > 0 && options.minimalInterval <= 600 ? options.minimalInterval * 1000 : 0;
+ }
+
+ if ( settings.minimalInterval && settings.mainInterval < settings.minimalInterval ) {
+ settings.mainInterval = settings.minimalInterval;
+ }
+
+ // 'screenId' can be added from settings on the front end where the JS global 'pagenow' is not set
if ( ! settings.screenId ) {
settings.screenId = options.screenId || 'front';
}
settings.mainInterval = settings.mainInterval * 1000;
settings.originalInterval = settings.mainInterval;
- // Set focus/blur events on the window
- $(window).on( 'blur.wp-heartbeat-focus', function() {
- setFrameFocusEvents();
- // We don't know why the 'blur' was fired. Either the user clicked in an iframe or outside the browser.
- // Running blurred() after some timeout lets us cancel it if the user clicked in an iframe.
- settings.winBlurTimer = window.setTimeout( function(){ blurred(); }, 500 );
- }).on( 'focus.wp-heartbeat-focus', function() {
- removeFrameFocusEvents();
- focused();
- }).on( 'unload.wp-heartbeat', function() {
+ // Switch the interval to 120 sec. by using the Page Visibility API.
+ // If the browser doesn't support it (Safari < 7, Android < 4.4, IE < 10), the interval
+ // will be increased to 120 sec. after 5 min. of mouse and keyboard inactivity.
+ if ( typeof document.hidden !== 'undefined' ) {
+ hidden = 'hidden';
+ visibilitychange = 'visibilitychange';
+ visibilityState = 'visibilityState';
+ } else if ( typeof document.msHidden !== 'undefined' ) { // IE10
+ hidden = 'msHidden';
+ visibilitychange = 'msvisibilitychange';
+ visibilityState = 'msVisibilityState';
+ } else if ( typeof document.webkitHidden !== 'undefined' ) { // Android
+ hidden = 'webkitHidden';
+ visibilitychange = 'webkitvisibilitychange';
+ visibilityState = 'webkitVisibilityState';
+ }
+
+ if ( hidden ) {
+ if ( document[hidden] ) {
+ settings.hasFocus = false;
+ }
+
+ $document.on( visibilitychange + '.wp-heartbeat', function() {
+ if ( document[visibilityState] === 'hidden' ) {
+ blurred();
+ window.clearInterval( settings.checkFocusTimer );
+ } else {
+ focused();
+ if ( document.hasFocus ) {
+ settings.checkFocusTimer = window.setInterval( checkFocus, 10000 );
+ }
+ }
+ });
+ }
+
+ // Use document.hasFocus() if available.
+ if ( document.hasFocus ) {
+ settings.checkFocusTimer = window.setInterval( checkFocus, 10000 );
+ }
+
+ $(window).on( 'unload.wp-heartbeat', function() {
// Don't connect any more
settings.suspend = true;
});
// Check for user activity every 30 seconds.
- window.setInterval( function(){ checkUserActivity(); }, 30000 );
+ window.setInterval( checkUserActivity, 30000 );
// Start one tick after DOM ready
$document.ready( function() {
return false;
}
+ /**
+ * Check if the document's focus has changed
+ *
+ * @access private
+ *
+ * @return void
+ */
+ function checkFocus() {
+ if ( settings.hasFocus && ! document.hasFocus() ) {
+ blurred();
+ } else if ( ! settings.hasFocus && document.hasFocus() ) {
+ focused();
+ }
+ }
+
/**
* Set error state and fire an event on XHR errors or timeout
*
if ( response.nonces_expired ) {
$document.trigger( 'heartbeat-nonces-expired' );
- return;
}
// Change the interval from PHP
}
}
+ if ( settings.minimalInterval && interval < settings.minimalInterval ) {
+ interval = settings.minimalInterval;
+ }
+
window.clearTimeout( settings.beatTimer );
if ( delta < interval ) {
settings.beatTimer = window.setTimeout(
function() {
- connect();
+ connect();
},
interval - delta
);
}
/**
- * Set the internal state when the browser window looses focus
+ * Set the internal state when the browser window becomes hidden or loses focus
*
* @access private
*
* @return void
*/
function blurred() {
- clearFocusTimers();
settings.hasFocus = false;
}
/**
- * Set the internal state when the browser window is focused
+ * Set the internal state when the browser window becomes visible or is in focus
*
* @access private
*
* @return void
*/
function focused() {
- clearFocusTimers();
settings.userActivity = time();
// Resume if suspended
}
}
- /**
- * Add focus/blur events to all local iframes
- *
- * Used to detect when focus is moved from the main window to an iframe
- *
- * @access private
- *
- * @return void
- */
- function setFrameFocusEvents() {
- $('iframe').each( function( i, frame ) {
- if ( ! isLocalFrame( frame ) ) {
- return;
- }
-
- if ( $.data( frame, 'wp-heartbeat-focus' ) ) {
- return;
- }
-
- $.data( frame, 'wp-heartbeat-focus', 1 );
-
- $( frame.contentWindow ).on( 'focus.wp-heartbeat-focus', function() {
- focused();
- }).on('blur.wp-heartbeat-focus', function() {
- setFrameFocusEvents();
- // We don't know why 'blur' was fired. Either the user clicked in the main window or outside the browser.
- // Running blurred() after some timeout lets us cancel it if the user clicked in the main window.
- settings.frameBlurTimer = window.setTimeout( function(){ blurred(); }, 500 );
- });
- });
- }
-
- /**
- * Remove the focus/blur events to all local iframes
- *
- * @access private
- *
- * @return void
- */
- function removeFrameFocusEvents() {
- $('iframe').each( function( i, frame ) {
- if ( ! isLocalFrame( frame ) ) {
- return;
- }
-
- $.removeData( frame, 'wp-heartbeat-focus' );
- $( frame.contentWindow ).off( '.wp-heartbeat-focus' );
- });
- }
-
- /**
- * Clear the reset timers for focus/blur events on the window and iframes
- *
- * @access private
- *
- * @return void
- */
- function clearFocusTimers() {
- window.clearTimeout( settings.winBlurTimer );
- window.clearTimeout( settings.frameBlurTimer );
- }
-
/**
* Runs when the user becomes active after a period of inactivity
*
$document.off( '.wp-heartbeat-active' );
$('iframe').each( function( i, frame ) {
- if ( ! isLocalFrame( frame ) ) {
- return;
+ if ( isLocalFrame( frame ) ) {
+ $( frame.contentWindow ).off( '.wp-heartbeat-active' );
}
-
- $( frame.contentWindow ).off( '.wp-heartbeat-active' );
});
focused();
function checkUserActivity() {
var lastActive = settings.userActivity ? time() - settings.userActivity : 0;
+ // Throttle down when no mouse or keyboard activity for 5 min.
if ( lastActive > 300000 && settings.hasFocus ) {
- // Throttle down when no mouse or keyboard activity for 5 min
blurred();
}
- if ( settings.suspendEnabled && lastActive > 1200000 ) {
- // Suspend after 20 min. of inactivity
+ // Suspend after 10 min. of inactivity when suspending is enabled.
+ // Always suspend after 60 min. of inactivity. This will release the post lock, etc.
+ if ( ( settings.suspendEnabled && lastActive > 600000 ) || lastActive > 3600000 ) {
settings.suspend = true;
}
if ( ! settings.userActivityEvents ) {
- $document.on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); } );
+ $document.on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active touchend.wp-heartbeat-active', function() {
+ userIsActive();
+ });
$('iframe').each( function( i, frame ) {
- if ( ! isLocalFrame( frame ) ) {
- return;
+ if ( isLocalFrame( frame ) ) {
+ $( frame.contentWindow ).on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active touchend.wp-heartbeat-active', function() {
+ userIsActive();
+ });
}
-
- $( frame.contentWindow ).on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); } );
});
settings.userActivityEvents = true;
* In this case the number of 'ticks' can be passed as second argument.
* If the window doesn't have focus, the interval slows down to 2 min.
*
- * @param mixed speed Interval: 'fast' or 5, 15, 30, 60
+ * @param mixed speed Interval: 'fast' or 5, 15, 30, 60, 120
* @param string ticks Used with speed = 'fast' or 5, how many ticks before the interval reverts back
* @return int Current interval in seconds
*/
case 60:
newInterval = 60000;
break;
+ case 120:
+ newInterval = 120000;
+ break;
case 'long-polling':
// Allow long polling, (experimental)
settings.mainInterval = 0;
newInterval = settings.originalInterval;
}
+ if ( settings.minimalInterval && newInterval < settings.minimalInterval ) {
+ newInterval = settings.minimalInterval;
+ }
+
if ( 5000 === newInterval ) {
ticks = parseInt( ticks, 10 ) || 30;
ticks = ticks < 1 || ticks > 30 ? 30 : ticks;