| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 ));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Sep 10 08:20:30 2026 | Cross-referenced by PHPXref |