| 1 | | /** |
| 2 | | * Heartbeat API |
| 3 | | * |
| 4 | | * Heartbeat is a simple server polling API that sends XHR requests to |
| 5 | | * the server every 15 seconds and triggers events (or callbacks) upon |
| 6 | | * receiving data. Currently these 'ticks' handle transports for post locking, |
| 7 | | * login-expiration warnings, and related tasks while a user is logged in. |
| 8 | | * |
| 9 | | * Available filters in ajax-actions.php: |
| 10 | | * - heartbeat_received |
| 11 | | * - heartbeat_send |
| 12 | | * - heartbeat_tick |
| 13 | | * - heartbeat_nopriv_received |
| 14 | | * - heartbeat_nopriv_send |
| 15 | | * - heartbeat_nopriv_tick |
| 16 | | * @see wp_ajax_nopriv_heartbeat(), wp_ajax_heartbeat() |
| 17 | | * |
| 18 | | * @since 3.6.0 |
| 19 | | */ |
| | 1 | ( function( window, $, undefined ) { |
| 24 | | (function($){ |
| 25 | | var Heartbeat = function() { |
| 26 | | var self = this, |
| 27 | | running, |
| 28 | | beat, |
| 29 | | screenId = typeof pagenow != 'undefined' ? pagenow : '', |
| 30 | | url = typeof ajaxurl != 'undefined' ? ajaxurl : '', |
| 31 | | settings, |
| 32 | | tick = 0, |
| 33 | | queue = {}, |
| 34 | | interval, |
| 35 | | connecting, |
| 36 | | countdown = 0, |
| 37 | | errorcount = 0, |
| 38 | | tempInterval, |
| 39 | | hasFocus = true, |
| 40 | | isUserActive, |
| 41 | | userActiveEvents, |
| 42 | | winBlurTimeout, |
| 43 | | frameBlurTimeout = -1; |
| | 6 | /** |
| | 7 | * Heartbeat API |
| | 8 | * |
| | 9 | * Heartbeat is a simple server polling API that sends XHR requests to the server every 15 seconds and triggers events |
| | 10 | * (or callbacks) upon receiving data. Currently these 'ticks' handle transports for post locking, login-expiration |
| | 11 | * warnings, and related tasks while a user is logged in. |
| | 12 | * |
| | 13 | * todo: if this code is run multiple times in different iframes, there could be a problem with iframes becoming |
| | 14 | * inactive but activity occurring in the main window which is really weird... To fix this, we need to check if the |
| | 15 | * current window is the top window just like ads do. This could either be a major bug OR something that's intended |
| | 16 | * but either way should be confirmed ASAP. |
| | 17 | * |
| | 18 | * Available filters in ajax-actions.php: |
| | 19 | * heartbeat_received |
| | 20 | * heartbeat_send |
| | 21 | * heartbeat_tick |
| | 22 | * heartbeat_nopriv_received |
| | 23 | * heartbeat_nopriv_send |
| | 24 | * heartbeat_nopriv_tick |
| | 25 | * @see wp_ajax_nopriv_heartbeat(), wp_ajax_heartbeat() |
| | 26 | * |
| | 27 | * @since 3.6.0 |
| | 28 | * @type {*|{}} |
| | 29 | */ |
| | 30 | function Heartbeat() { |
| 51 | | // Add private vars |
| 52 | | url = settings.ajaxurl || url; |
| 53 | | delete settings.ajaxurl; |
| 54 | | delete settings.nonce; |
| | 48 | /** |
| | 49 | * Container for all of the settings that this module needs to track |
| | 50 | * |
| | 51 | * @type {{shouldAutoStart: boolean, hasActiveConnection: boolean, errorCountSinceConnected: number, ajaxURL: string, nonce: string, heartbeatInterval: number, temporaryHeartbeatInterval: number, screenId: string, hasFocus: boolean, userIsActive: boolean, lastUserActivityTimestamp: number, isConnecting: boolean, isRunning: boolean, userActivityCheckInterval: number, lastHeartbeatTick: number, countDown: number, ajaxRequestTimeout: number}} |
| | 52 | * @private |
| | 53 | */ |
| | 54 | var _Settings = { |
| | 55 | shouldAutoStart : true, |
| | 56 | hasActiveConnection : false, |
| | 57 | errorCountSinceConnected : 0, |
| | 58 | ajaxURL : "", |
| | 59 | nonce : "", |
| | 60 | heartbeatInterval : 15, |
| | 61 | temporaryHeartbeatInterval : 0, |
| | 62 | screenId : "", |
| | 63 | hasFocus : false, |
| | 64 | userIsActive : false, |
| | 65 | lastUserActivityTimestamp : _getUnixTimestamp(), |
| | 66 | isConnecting : false, |
| | 67 | isRunning : false, |
| | 68 | userActivityCheckInterval : 30000, |
| | 69 | lastHeartbeatTick : 0, |
| | 70 | countDown : 0, |
| | 71 | ajaxRequestTimeout : 30000 |
| | 72 | }; |
| 87 | | if ( src.indexOf( origin ) !== 0 ) |
| 88 | | return false; |
| | 134 | // setup the ajax URL |
| | 135 | _Settings.ajaxURL = window.ajaxurl || ""; |
| | 136 | _Settings.ajaxURL = tempSettings.ajaxurl || _Settings.ajaxURL; |
| | 137 | |
| | 138 | // setup the nonce |
| | 139 | _Settings.nonce = tempSettings.nonce || ""; |
| | 140 | |
| | 141 | // setup the heartbeat interval - force it to an integer. If the value of tempSettings.interval is incorrect |
| | 142 | // and cannot be parsed, we still default to 10 |
| | 143 | _Settings.heartbeatInterval = parseInt( tempSettings.interval, 10 ) || _Settings.heartbeatInterval; |
| | 144 | |
| | 145 | // keep the heartbeat interval within bounds |
| | 146 | if( _Settings.heartbeatInterval < 15 ) { |
| | 147 | _Settings.heartbeatInterval = 15; |
| 118 | | if ( errorcount > 2 ) |
| 119 | | trigger = true; |
| | 180 | /** |
| | 181 | * Handles enqueuing data to be sent along with the next heartbeat pulse. We require a key to be set. Note that |
| | 182 | * a value is not required in the event that a user might want to dequeue a key from being passed along. |
| | 183 | * |
| | 184 | * As the data is sent later, this function doesn't return the XHR response. |
| | 185 | * To see the response, use the custom jQuery event 'heartbeat-tick' on the document, example: |
| | 186 | * $(document).on( 'heartbeat-tick.myname', function( event, data, textStatus, jqXHR ) { |
| | 187 | * // code |
| | 188 | * }); |
| | 189 | * If the same `key` is used more than once, the data is not overwritten when the third argument is `true`. |
| | 190 | * Use wp.heartbeat.isQueued( 'key' ) to see if any data is already queued for that key. |
| | 191 | * |
| | 192 | * @param key Unique string for the data. The handle is used in PHP to receive the data. |
| | 193 | * @param value The data to send. |
| | 194 | * @param overwriteExistingData Whether to overwrite existing data in the queue. |
| | 195 | * @returns {boolean} Whether the data was queued or not. |
| | 196 | * @private |
| | 197 | */ |
| | 198 | function _enqueueData( key, value, overwriteExistingData ) { |
| | 199 | overwriteExistingData = ( typeof overwriteExistingData === "boolean" ) ? overwriteExistingData : false; |
| 135 | | function connect() { |
| 136 | | var send = {}, data, i, empty = true, |
| 137 | | nonce = typeof window.heartbeatSettings == 'object' ? window.heartbeatSettings.nonce : ''; |
| 138 | | tick = time(); |
| | 212 | /** |
| | 213 | * Check if data with a particular handle is queued. |
| | 214 | * |
| | 215 | * @param key The handle for the data |
| | 216 | * @returns {*} The data queued with that handle or null |
| | 217 | * @private |
| | 218 | */ |
| | 219 | function _dataIsQueued( key ) { |
| | 220 | return _QueuedAjaxData[ key ]; |
| | 221 | } |
| 140 | | data = $.extend( {}, queue ); |
| 141 | | // Clear the data queue, anything added after this point will be send on the next tick |
| 142 | | queue = {}; |
| | 223 | /** |
| | 224 | * If a value is specified, this function will set the internal value of the `shouldAutoStart` setting. Always |
| | 225 | * returns the value of `shouldAutoStart` |
| | 226 | * |
| | 227 | * @param value |
| | 228 | * @returns boolean |
| | 229 | * @private |
| | 230 | */ |
| | 231 | function _shouldAutoStart( value ) { |
| | 232 | if( typeof value === "boolean" ) { |
| | 233 | return _Settings.shouldAutoStart = value; |
| | 234 | } |
| 190 | | // Change the interval from PHP |
| 191 | | if ( response.heartbeat_interval ) { |
| 192 | | new_interval = response.heartbeat_interval; |
| 193 | | delete response.heartbeat_interval; |
| | 297 | // if the error was aborted, don't do anything |
| | 298 | if( error === "abort" ) { |
| | 299 | return; |
| | 300 | } |
| | 301 | else if( error === "timeout" ) { |
| | 302 | trigger = true; |
| | 303 | } |
| | 304 | else if( error === "unknown" || error === "parsererror" || error === "error" || error === "empty" ) { |
| | 305 | _Settings.errorCountSinceConnected++; |
| | 306 | if( _Settings.errorCountSinceConnected > 2 ) { |
| | 307 | trigger = true; |
| 196 | | self.tick( response, textStatus, jqXHR ); |
| | 311 | // take action if we need to trigger things |
| | 312 | if( trigger === true && _hasActiveConnection() === true ) { |
| | 313 | _hasActiveConnection( false ); |
| | 314 | _Cache.$document.trigger( "heartbeat-connection-lost", [ error ] ); |
| | 315 | } |
| | 316 | else if( _hasActiveConnection() === false ) { |
| | 317 | _Settings.errorCountSinceConnected = 0; |
| | 318 | _hasActiveConnection( true ); |
| | 319 | _Cache.$document.trigger( "heartbeat-connection-restored" ); |
| | 320 | } |
| | 321 | } |
| 198 | | // do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast' |
| 199 | | if ( new_interval ) |
| 200 | | self.interval.call( self, new_interval ); |
| 201 | | }).always( function() { |
| 202 | | connecting = false; |
| 203 | | next(); |
| 204 | | }).fail( function( jqXHR, textStatus, error ) { |
| 205 | | errorstate( textStatus || 'unknown' ); |
| 206 | | self.error( jqXHR, textStatus, error ); |
| 207 | | }); |
| 208 | | }; |
| | 323 | /** |
| | 324 | * Handles connecting to the server-side heartbeat API and sending any queued data along. |
| | 325 | * |
| | 326 | * @private |
| | 327 | */ |
| | 328 | function _connect() { |
| | 329 | // get the data we need to send |
| | 330 | var dataToSend = $.extend( {}, _QueuedAjaxData ); |
| | 331 | _clearAjaxDataQueue(); |
| 216 | | if ( ! hasFocus ) { |
| 217 | | t = 120000; // 2 min |
| 218 | | } else if ( countdown > 0 && tempInterval ) { |
| 219 | | t = tempInterval; |
| 220 | | countdown--; |
| | 344 | // build the ajax data we need to send now |
| | 345 | var ajaxData = _buildAjaxData( dataToSend ); |
| | 346 | |
| | 347 | // keep track of when we're connecting for this ajax call |
| | 348 | _Settings.isConnecting = true; |
| | 349 | |
| | 350 | // initiate a new ajax request |
| | 351 | _Cache.ajaxRequest = $.ajax( { |
| | 352 | url : _Settings.ajaxURL, |
| | 353 | type : "post", |
| | 354 | timeout : _Settings.ajaxRequestTimeout, |
| | 355 | data : ajaxData, |
| | 356 | dataType : "json" |
| | 357 | } ); |
| | 358 | |
| | 359 | // setup our promises |
| | 360 | _Cache.ajaxRequest.done( _onAjaxDone ); |
| | 361 | _Cache.ajaxRequest.always( _onAjaxAlwaysPromise ); |
| | 362 | _Cache.ajaxRequest.fail( _onAjaxFailed ); |
| | 363 | } |
| | 364 | |
| | 365 | /** |
| | 366 | * Handles taking action when the heartbeat pulse AJAX call is finished, whether fail or succeed |
| | 367 | * |
| | 368 | * @param response |
| | 369 | * @param textStatus |
| | 370 | * @param jqXHR |
| | 371 | * @private |
| | 372 | */ |
| | 373 | function _onAjaxDone( response, textStatus, jqXHR ) { |
| | 374 | var cachedHeartbeatInterval = null; |
| | 375 | |
| | 376 | // make sure we got a response |
| | 377 | if( ! response ) { |
| | 378 | _triggerError( "empty" ); |
| | 379 | return; |
| | 392 | |
| | 393 | // check to see if a new interval needs to be sent |
| | 394 | // todo: this could use a strict check |
| | 395 | if( response.heartbeat_interval ) { |
| | 396 | cachedHeartbeatInterval = response.heartbeat_interval; |
| | 397 | |
| | 398 | // todo: what's the purpose of deleting this? |
| | 399 | delete response.heartbeat_interval; |
| | 400 | } |
| | 401 | |
| | 402 | // emit an event with the data we need |
| | 403 | _Cache.$document.trigger( "heartbeat-tick", [ response, textStatus, jqXHR ] ); |
| | 404 | |
| | 405 | // check to see if we set a value |
| | 406 | // do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast' |
| | 407 | if( cachedHeartbeatInterval !== null ) { |
| | 408 | _interval( cachedHeartbeatInterval ); |
| | 409 | } |
| 238 | | function blurred() { |
| 239 | | window.clearTimeout(winBlurTimeout); |
| 240 | | window.clearTimeout(frameBlurTimeout); |
| 241 | | winBlurTimeout = frameBlurTimeout = 0; |
| | 412 | /** |
| | 413 | * Handles taking action when the heartbeat pulse is completed and has failed |
| | 414 | * |
| | 415 | * @param jqXHR |
| | 416 | * @param textStatus |
| | 417 | * @param error |
| | 418 | * @private |
| | 419 | */ |
| | 420 | function _onAjaxFailed( jqXHR, textStatus, error ) { |
| | 421 | _triggerError( textStatus || "unknown" ); |
| | 422 | _Cache.$document.trigger( "heartbeat-error", [ jqXHR, textStatus, error ] ); |
| | 423 | } |
| 246 | | function focused() { |
| 247 | | window.clearTimeout(winBlurTimeout); |
| 248 | | window.clearTimeout(frameBlurTimeout); |
| 249 | | winBlurTimeout = frameBlurTimeout = 0; |
| | 435 | /** |
| | 436 | * Builds the data object literal that we'll use for initiating an AJAX request for each heartbeat pulse |
| | 437 | * |
| | 438 | * @param queuedData |
| | 439 | * @returns {{}} |
| | 440 | * @private |
| | 441 | */ |
| | 442 | function _buildAjaxData( queuedData ) { |
| | 443 | return { |
| | 444 | data : queuedData, |
| | 445 | interval : ( _Settings.heartbeatInterval / 1000 ), |
| | 446 | _nonce : _Settings.nonce, |
| | 447 | action : "heartbeat", |
| | 448 | screen_id : _Settings.screenId, |
| | 449 | has_focus : _Settings.hasFocus |
| | 450 | }; |
| | 451 | } |
| 263 | | function setFrameEvents() { |
| 264 | | $('iframe').each( function( i, frame ){ |
| 265 | | if ( ! isLocalFrame( frame ) ) |
| | 489 | /** |
| | 490 | * Handles performing anything we expect to happen when the window is blurred |
| | 491 | * |
| | 492 | * @param event |
| | 493 | * @private |
| | 494 | */ |
| | 495 | function _onWindowBlur( event ) { |
| | 496 | // bind our iframe events |
| | 497 | _bindIframeEvents(); |
| | 498 | |
| | 499 | // reset our window blur timer |
| | 500 | _clearWindowBlurTimer(); |
| | 501 | |
| | 502 | // todo: not really sure why this was 500 in the first place, seems hacky? (there wasn't any commenting as to why) |
| | 503 | _Cache.windowBlurTimer = setTimeout( _userHasBecomeInactive, 500 ); |
| | 504 | } |
| | 505 | |
| | 506 | /** |
| | 507 | * Handles performing anything we expect to happen when the window is focused |
| | 508 | * |
| | 509 | * @param event |
| | 510 | * @private |
| | 511 | */ |
| | 512 | function _onWindowFocus( event ) { |
| | 513 | _unbindIframeEvents(); |
| | 514 | _userHasBecomeActive(); |
| | 515 | } |
| | 516 | |
| | 517 | /** |
| | 518 | * Handles binding any iframe events that are needed for this API to work effectively |
| | 519 | * |
| | 520 | * @private |
| | 521 | */ |
| | 522 | function _bindIframeEvents() { |
| | 523 | $( document.querySelectorAll( "iframe" ) ).each( function( i, iframe ) { |
| | 524 | if( _isLocalFrame( iframe ) === false ) { |
| 273 | | $( frame.contentWindow ).on( 'focus.wp-heartbeat-focus', function(e) { |
| 274 | | focused(); |
| 275 | | }).on('blur.wp-heartbeat-focus', function(e) { |
| 276 | | setFrameEvents(); |
| 277 | | frameBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); |
| 278 | | }); |
| 279 | | }); |
| | 536 | // cache a reference to the jquery object for this iframe window object |
| | 537 | var $iframeWindow = $( iframe.contentWindow ); |
| | 538 | |
| | 539 | // now setup a focus event for this iframe |
| | 540 | $iframeWindow.on( "focus.wp-heartbeat-focus", _userHasBecomeActive ); |
| | 541 | $iframeWindow.on( "blur.wp-heartbeat-focus", _onIframeBlur ); |
| | 542 | |
| | 543 | } ); |
| 282 | | $(window).on( 'blur.wp-heartbeat-focus', function(e) { |
| 283 | | setFrameEvents(); |
| 284 | | winBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); |
| 285 | | }).on( 'focus.wp-heartbeat-focus', function() { |
| 286 | | $('iframe').each( function( i, frame ) { |
| 287 | | if ( !isLocalFrame( frame ) ) |
| | 546 | /** |
| | 547 | * Callback for when an iframe becomes blurred on the page |
| | 548 | * |
| | 549 | * @param event |
| | 550 | * @private |
| | 551 | */ |
| | 552 | function _onIframeBlur( event ) { |
| | 553 | _bindIframeEvents(); |
| | 554 | |
| | 555 | //clear the existing frame blur timer |
| | 556 | _clearFrameBlurTimer(); |
| | 557 | |
| | 558 | // todo: not really sure why this was 500 in the first place, seems hacky? (there wasn't any commenting as to why) |
| | 559 | _Cache.frameBlurTimer = setTimeout( _userHasBecomeInactive, 500 ); |
| | 560 | } |
| | 561 | |
| | 562 | /** |
| | 563 | * Unbinds any previously bound heartbeat focus events from all iframes on the page (if they belong to us) |
| | 564 | * |
| | 565 | * @private |
| | 566 | */ |
| | 567 | function _unbindIframeEvents() { |
| | 568 | $( document.querySelectorAll( "iframe" ) ).each( function( i, iframe ) { |
| | 569 | if( _isLocalFrame( iframe ) === false ) { |
| 297 | | function userIsActive() { |
| 298 | | userActiveEvents = false; |
| 299 | | $(document).off( '.wp-heartbeat-active' ); |
| 300 | | $('iframe').each( function( i, frame ) { |
| 301 | | if ( ! isLocalFrame( frame ) ) |
| | 588 | // make sure we're running before doing anything |
| | 589 | if( _Settings.isRunning === false ) { |
| | 590 | return; |
| | 591 | } |
| | 592 | |
| | 593 | // normalize the timeLimit |
| | 594 | if( _hasFocus() === false ) { |
| | 595 | // set the time limit to 2 minutes because we don't have focus |
| | 596 | timeLimit = 120000; |
| | 597 | } |
| | 598 | else if( _Settings.countDown > 0 && _Settings.temporaryHeartbeatInterval > 0 ) { |
| | 599 | timeLimit = _Settings.temporaryHeartbeatInterval; |
| | 600 | } |
| | 601 | |
| | 602 | // clear the existing heartbeat timer |
| | 603 | _clearHeartbeatTimer(); |
| | 604 | |
| | 605 | // check to see if we need to connect now or later and then set things up to do just that |
| | 606 | if( timeSinceLastTick < timeLimit ) { |
| | 607 | _Cache.heartbeatTimer = setTimeout( _connect, timeLimit - timeSinceLastTick ); |
| | 608 | } |
| | 609 | else { |
| | 610 | _connect(); |
| | 611 | } |
| | 612 | } |
| | 613 | |
| | 614 | /** |
| | 615 | * Handles unbinding any events that were previously bound through `_bindUserActivityEvents()` |
| | 616 | * |
| | 617 | * @private |
| | 618 | */ |
| | 619 | function _unbindUserActivityEvents() { |
| | 620 | // remove the mouseover event for the document |
| | 621 | _Cache.$document.off( ".wp-heartbeat-active" ); |
| | 622 | |
| | 623 | // loop through all iframes on the page and if they are local iframes, remove the previously attached events |
| | 624 | $( document.querySelectorAll( "iframe" ) ).each( function( i, iframe ) { |
| | 625 | |
| | 626 | // make sure this frame is one of ours before trying to access it's contentWindow |
| | 627 | if( _isLocalFrame( iframe ) === false ) { |
| 310 | | // Set 'hasFocus = true' if user is active and the window is in the background. |
| 311 | | // Set 'hasFocus = false' if the user has been inactive (no mouse or keyboard activity) for 5 min. even when the window has focus. |
| 312 | | function checkUserActive() { |
| 313 | | var lastActive = isUserActive ? time() - isUserActive : 0; |
| | 637 | /** |
| | 638 | * Handles binding any events necessary to mark the user as active again |
| | 639 | * |
| | 640 | * @private |
| | 641 | */ |
| | 642 | function _bindUserActivityEvents() { |
| | 643 | // when the user moves their mouse, mark the the user as becoming active again |
| | 644 | _Cache.$document.on( "mouseover.wp-heartbeat-active keyup.wp-heartbeat-active", _userHasBecomeActive ); |
| 345 | | this.hasFocus = function() { |
| 346 | | return hasFocus; |
| | 696 | /** |
| | 697 | * Stops the heartbeat pulse if isn't already running |
| | 698 | * todo: should this be clearing timers as well? ( _clearWindowBlurTimer(), _clearFrameBlurTimer() and _clearHeartbeatTimer() ) |
| | 699 | * |
| | 700 | * @returns {boolean} |
| | 701 | * @private |
| | 702 | */ |
| | 703 | function _stop() { |
| | 704 | if( _Cache.ajaxRequest !== null && _Cache.ajaxRequest.readyState !== 4 ) { |
| | 705 | _Cache.ajaxRequest.abort(); |
| | 706 | _Cache.ajaxRequest = null; |
| | 707 | } |
| | 708 | |
| | 709 | // fire an error |
| | 710 | // todo: this wasn't passing anything previously. I feel like it should be passing 'abort' |
| | 711 | _triggerError(); |
| | 712 | |
| | 713 | // update our internal value for whether or not we are currently running heartbeat |
| | 714 | _Settings.isRunning = false; |
| | 715 | |
| | 716 | return true; |
| 355 | | * @param string speed Interval speed: 'fast' (5sec), 'standard' (15sec) default, 'slow' (60sec) |
| 356 | | * @param string ticks Used with speed = 'fast', how many ticks before the speed reverts back |
| 357 | | * @return int Current interval in seconds |
| | 726 | * @param speed Interval speed: 'fast' (5sec), 'standard' (15sec) default, 'slow' (60sec) |
| | 727 | * @param ticks Used with speed = 'fast', how many ticks before the speed reverts back |
| | 728 | * @returns {number} interval in seconds |
| | 729 | * @private |
| 398 | | if ( ! hasFocus ) |
| | 751 | // convert speed from a string if necessary |
| | 752 | if( speed === "fast" ) { |
| | 753 | seconds = 5; |
| | 754 | _Settings.countDown = ticks; |
| | 755 | } |
| | 756 | else if( speed === "slow" ) { |
| | 757 | seconds = 60; |
| | 758 | _Settings.countDown = 0; |
| | 759 | } |
| | 760 | // long-polling is currently experimental |
| | 761 | else if( speed === "long-polling" ) { |
| | 762 | return _Settings.heartbeatInterval = 0; |
| | 763 | } |
| | 764 | |
| | 765 | // determine whether or not we should reset based on whether the new interval value is lower than the current |
| | 766 | // one or not |
| | 767 | reset = seconds * 1000 < _Settings.heartbeatInterval; |
| | 768 | |
| | 769 | if( _Settings.countDown > 0 ) { |
| | 770 | _Settings.temporaryHeartbeatInterval = seconds * 1000; |
| | 771 | } |
| | 772 | else { |
| | 773 | _Settings.heartbeatInterval = seconds * 1000; |
| | 774 | _Settings.temporaryHeartbeatInterval = 0; |
| | 775 | } |
| | 776 | |
| | 777 | // check if we need to reset or not |
| | 778 | if( reset === true ) { |
| | 779 | _nextTick(); |
| | 780 | } |
| | 781 | |
| | 782 | // check to see if we have focus or not |
| | 783 | if( _hasFocus() === false ) { |
| 419 | | // Reset the error state |
| 420 | | errorstate(); |
| 421 | | running = false; |
| 422 | | return true; |
| | 815 | /** |
| | 816 | * Handles the actions to take when the heartbeat window becomes focused. |
| | 817 | * todo: possibly add a $document.trigger() here to notify other applications? |
| | 818 | * |
| | 819 | * @private |
| | 820 | */ |
| | 821 | function _userHasBecomeActive() { |
| | 822 | _clearWindowBlurTimer(); |
| | 823 | _clearFrameBlurTimer(); |
| | 824 | _unbindUserActivityEvents(); |
| | 825 | _updateUsersLastActivityTime(); |
| | 826 | |
| | 827 | if( _hasFocus() === true ) { |
| | 828 | return; |
| | 829 | } |
| | 830 | |
| | 831 | // set the the focus to true and clear the heartbeat pulse |
| | 832 | _Settings.hasFocus = true; |
| | 833 | _clearHeartbeatTimer(); |
| | 834 | |
| | 835 | if( _Settings.isConnecting === false ) { |
| | 836 | _nextTick(); |
| | 837 | } |
| 428 | | * As the data is sent later, this function doesn't return the XHR response. |
| 429 | | * To see the response, use the custom jQuery event 'heartbeat-tick' on the document, example: |
| 430 | | * $(document).on( 'heartbeat-tick.myname', function( event, data, textStatus, jqXHR ) { |
| 431 | | * // code |
| 432 | | * }); |
| 433 | | * If the same 'handle' is used more than once, the data is not overwritten when the third argument is 'true'. |
| 434 | | * Use wp.heartbeat.isQueued('handle') to see if any data is already queued for that handle. |
| | 844 | * @private |
| | 845 | */ |
| | 846 | function _updateUsersLastActivityTime() { |
| | 847 | _Settings.lastUserActivityTimestamp = _getUnixTimestamp(); |
| | 848 | } |
| | 849 | |
| | 850 | /** |
| | 851 | * Clears the window blur timer if it exists |
| | 885 | |
| | 886 | /** |
| | 887 | * Call our object initializer to make sure things get setup properly before this object is used |
| | 888 | */ |
| | 889 | _initialize(); |
| | 890 | |
| | 891 | /** |
| | 892 | * Explicitly expose any methods we want to be available to the public scope |
| | 893 | */ |
| | 894 | return { |
| | 895 | shouldAutoStart : _shouldAutoStart, |
| | 896 | hasActiveConnection : _hasActiveConnection, |
| | 897 | isLocalFrame : _isLocalFrame, |
| | 898 | enqueueData : _enqueueData, |
| | 899 | dataIsQueued : _dataIsQueued, |
| | 900 | start : _start, |
| | 901 | stop : _stop, |
| | 902 | interval : _interval, |
| | 903 | hasFocus : _hasFocus |
| | 904 | }; |
| 463 | | $.extend( Heartbeat.prototype, { |
| 464 | | tick: function( data, textStatus, jqXHR ) { |
| 465 | | $(document).trigger( 'heartbeat-tick', [data, textStatus, jqXHR] ); |
| 466 | | }, |
| 467 | | error: function( jqXHR, textStatus, error ) { |
| 468 | | $(document).trigger( 'heartbeat-error', [jqXHR, textStatus, error] ); |
| 469 | | } |
| 470 | | }); |
| | 907 | // ensure our global `wp` object exists |
| | 908 | window.wp = window.wp || {}; |