[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> heartbeat.js (source)

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


Generated : Thu Sep 10 08:20:30 2026 Cross-referenced by PHPXref