Ticket #25073: 25073-4.patch
File 25073-4.patch, 27.7 KB (added by , 11 years ago) |
---|
-
src/wp-admin/includes/post.php
1185 1185 $time = $lock[0]; 1186 1186 $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true ); 1187 1187 1188 $time_window = apply_filters( 'wp_check_post_lock_window', 1 20 );1188 $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); 1189 1189 1190 1190 if ( $time && $time > time() - $time_window && $user != get_current_user_id() ) 1191 1191 return $user; -
src/wp-admin/post-new.php
48 48 49 49 wp_enqueue_script( 'autosave' ); 50 50 51 if ( is_multisite() ) {51 if ( is_multisite() || count( get_users( array( 'fields' => 'ID', 'number' => 2 ) ) ) > 1 ) 52 52 add_action( 'admin_footer', '_admin_notice_post_locked' ); 53 } else {54 $check_users = get_users( array( 'fields' => 'ID', 'number' => 2 ) );55 53 56 if ( count( $check_users ) > 1 )57 add_action( 'admin_footer', '_admin_notice_post_locked' );58 59 unset( $check_users );60 }61 62 54 // Show post form. 63 55 $post = get_default_post_to_edit( $post_type, true ); 64 56 $post_ID = $post->ID; -
src/wp-admin/post.php
176 176 wp_enqueue_script('autosave'); 177 177 } 178 178 179 if ( is_multisite() ) {179 if ( is_multisite() || count( get_users( array( 'fields' => 'ID', 'number' => 2 ) ) ) > 1 ) 180 180 add_action( 'admin_footer', '_admin_notice_post_locked' ); 181 } else {182 $check_users = get_users( array( 'fields' => 'ID', 'number' => 2 ) );183 181 184 if ( count( $check_users ) > 1 )185 add_action( 'admin_footer', '_admin_notice_post_locked' );186 187 unset( $check_users );188 }189 190 182 $title = $post_type_object->labels->edit_item; 191 183 $post = get_post($post_id, OBJECT, 'edit'); 192 184 -
src/wp-includes/general-template.php
2362 2362 * @return array $settings 2363 2363 */ 2364 2364 function wp_heartbeat_settings( $settings ) { 2365 global $pagenow; 2366 2365 2367 if ( ! is_admin() ) 2366 2368 $settings['ajaxurl'] = admin_url( 'admin-ajax.php', 'relative' ); 2369 elseif ( in_array( $pagenow, array( 'post.php', 'post-new.php', 'edit.php' ), true ) ) 2370 $settings['interval'] = 15; 2367 2371 2368 2372 if ( is_user_logged_in() ) 2369 2373 $settings['nonce'] = wp_create_nonce( 'heartbeat-nonce' ); -
src/wp-includes/js/heartbeat.js
6 6 * to use it, please follow the development closely. 7 7 * 8 8 * Heartbeat is a simple server polling API that sends XHR requests to 9 * the server every 15 seconds and triggers events (or callbacks) upon9 * the server every 15 - 60 seconds and triggers events (or callbacks) upon 10 10 * receiving data. Currently these 'ticks' handle transports for post locking, 11 11 * login-expiration warnings, and related tasks while a user is logged in. 12 12 * 13 * Available filters in ajax-actions.php:13 * Available PHP filters (in ajax-actions.php): 14 14 * - heartbeat_received 15 15 * - heartbeat_send 16 16 * - heartbeat_tick … … 19 19 * - heartbeat_nopriv_tick 20 20 * @see wp_ajax_nopriv_heartbeat(), wp_ajax_heartbeat() 21 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 * 22 30 * @since 3.6.0 23 31 */ 24 32 25 // Ensure the global `wp` object exists. 26 window.wp = window.wp || {}; 27 28 (function($){ 33 ( function( $, window, undefined ) { 29 34 var Heartbeat = function() { 30 var self = this, 31 running, 32 beat, 33 screenId = typeof pagenow != 'undefined' ? pagenow : '', 34 url = typeof ajaxurl != 'undefined' ? ajaxurl : '', 35 settings, 36 tick = 0, 37 queue = {}, 38 interval, 39 connecting, 40 countdown = 0, 41 errorcount = 0, 42 tempInterval, 43 hasFocus = true, 44 isUserActive, 45 userActiveEvents, 46 winBlurTimeout, 47 frameBlurTimeout = -1, 48 hasConnectionError = null; 35 var $document = $(document), 36 settings = { 37 isRunning: true, 38 screenId: '', 39 url: '', 40 lastTick: 0, 41 queue: {}, 42 mainInterval: 60, // Default connect interval (in seconds) 43 tempInterval: 0, 44 originalInterval: 0, 45 countdown: 0, 46 connecting: false, 47 errorcount: 0, 48 hasConnected: false, 49 hasFocus: true, 50 userActivity: 0, 51 userActiveEvents: false, 52 connectionError: false, 53 beatTimer: 0, 54 winBlurTimer: 0, 55 frameBlurTimer: 0 56 }; 49 57 50 58 /** 51 * Returns a boolean that's indicative of whether or not there is a connection error59 * Set local vars and events, then start 52 60 * 53 * @returns boolean 61 * @access private 62 * 63 * @return void 54 64 */ 55 this.hasConnectionError = function() { 56 return !! hasConnectionError; 57 }; 65 function initialize() { 66 if ( typeof window.pagenow != 'undefined' ) { 67 settings.screenId = window.pagenow; 68 } 58 69 59 if ( typeof( window.heartbeatSettings ) == 'object' ) { 60 settings = $.extend( {}, window.heartbeatSettings ); 70 if ( typeof window.ajaxurl != 'undefined' ) { 71 settings.url = window.ajaxurl; 72 } 61 73 62 // Add private vars 63 url = settings.ajaxurl || url; 64 delete settings.ajaxurl; 65 delete settings.nonce; 74 // Pull in options passed from PHP 75 if ( typeof window.heartbeatSettings === 'object' ) { 76 var options = window.heartbeatSettings; 66 77 67 interval = settings.interval || 15; // default interval 68 delete settings.interval; 69 // The interval can be from 15 to 60 sec. and can be set temporarily to 5 sec. 70 if ( interval < 15 ) 71 interval = 15; 72 else if ( interval > 60 ) 73 interval = 60; 78 // The XHR URL can be passed as option when window.ajaxurl is not set 79 if ( ! settings.url && options.ajaxurl ) { 80 settings.url = options.ajaxurl; 81 } 74 82 75 interval = interval * 1000; 83 // The interval can be from 15 to 60 sec. and can be set temporarily to 5 sec. 84 if ( options.interval ) { 85 settings.mainInterval = options.interval; 76 86 77 // 'screenId' can be added from settings on the front-end where the JS global 'pagenow' is not set 78 screenId = screenId || settings.screenId || 'front'; 79 delete settings.screenId; 87 if ( settings.mainInterval < 15 ) { 88 settings.mainInterval = 15; 89 } else if ( settings.mainInterval > 60 ) { 90 settings.mainInterval = 60; 91 } 92 } 80 93 81 // Add or overwrite public vars 82 $.extend( this, settings ); 94 // 'screenId' can be added from settings on the front-end where the JS global 'pagenow' is not set 95 if ( ! settings.screenId ) { 96 settings.screenId = options.screenId || 'front'; 97 } 98 } 99 100 // Convert to milliseconds 101 settings.mainInterval = settings.mainInterval * 1000; 102 settings.originalInterval = settings.mainInterval; 103 104 // Set focus/blur events on the window 105 $(window).on( 'blur.wp-heartbeat-focus', function() { 106 setFrameFocusEvents(); 107 // We don't know why the 'blur' was fired. Either the user clicked in an iframe or outside the browser. 108 // Running blurred() after some timeout lets us cancel it if the user clicked in an iframe. 109 settings.winBlurTimer = window.setTimeout( function(){ blurred(); }, 500 ); 110 }).on( 'focus.wp-heartbeat-focus', function() { 111 removeFrameFocusEvents(); 112 focused(); 113 }).on( 'unload.wp-heartbeat', function() { 114 // Don't connect any more 115 settings.isRunning = false; 116 }); 117 118 // Check for user activity every 30 seconds. 119 window.setInterval( function(){ checkUserActive(); }, 30000 ); 120 121 // Start one tick after DOM ready 122 $document.ready( function() { 123 settings.lastTick = time(); 124 next(); 125 }); 83 126 } 84 127 85 function time(s) { 86 if ( s ) 87 return parseInt( (new Date()).getTime() / 1000 ); 88 128 /** 129 * Return the current time according to the browser 130 * 131 * @access private 132 * 133 * @return int 134 */ 135 function time() { 89 136 return (new Date()).getTime(); 90 137 } 91 138 139 /** 140 * Check if the iframe is from the same origin 141 * 142 * @access private 143 * 144 * @return bool 145 */ 92 146 function isLocalFrame( frame ) { 93 147 var origin, src = frame.src; 94 148 … … 95 149 if ( src && /^https?:\/\//.test( src ) ) { 96 150 origin = window.location.origin ? window.location.origin : window.location.protocol + '//' + window.location.host; 97 151 98 if ( src.indexOf( origin ) !== 0 ) 152 if ( src.indexOf( origin ) !== 0 ) { 99 153 return false; 154 } 100 155 } 101 156 102 157 try { 103 if ( frame.contentWindow.document ) 158 if ( frame.contentWindow.document ) { 104 159 return true; 160 } 105 161 } catch(e) {} 106 162 107 163 return false; 108 164 } 109 165 110 // Set error state and fire an event on XHR errors or timeout 111 function errorstate( error, status ) { 166 /** 167 * Set error state and fire an event on XHR errors or timeout 168 * 169 * @access private 170 * 171 * @param string error The error type passed from the XHR 172 * @param int status The HTTP status code passed from jqXHR (200, 404, 500, etc.) 173 * @return void 174 */ 175 function setErrorState( error, status ) { 112 176 var trigger; 113 177 114 178 if ( error ) { … … 120 184 // no response for 30 sec. 121 185 trigger = true; 122 186 break; 187 case 'error': 188 if ( 503 == status && settings.hasConnected ) { 189 trigger = true; 190 break; 191 } 192 // Pass through other error statuses 123 193 case 'parsererror': 124 case 'error':125 194 case 'empty': 126 195 case 'unknown': 127 errorcount++;196 settings.errorcount++; 128 197 129 if ( errorcount > 2 )198 if ( settings.errorcount > 2 && settings.hasConnected ) { 130 199 trigger = true; 200 } 131 201 132 202 break; 133 203 } 134 204 135 if ( 503 == status && false === hasConnectionError ) { 136 trigger = true; 137 } 138 139 if ( trigger && ! self.hasConnectionError() ) { 140 hasConnectionError = true; 205 if ( trigger && ! hasConnectionError() ) { 206 settings.connectionError = true; 141 207 $(document).trigger( 'heartbeat-connection-lost', [error, status] ); 142 208 } 143 } else if ( self.hasConnectionError() ) {144 errorcount = 0;145 hasConnectionError = false;146 $(document).trigger( 'heartbeat-connection-restored' );147 } else if ( null === hasConnectionError ) {148 hasConnectionError = false;149 209 } 150 210 } 151 211 152 function connect() { 153 var send = {}, data, i, empty = true, 154 nonce = typeof window.heartbeatSettings == 'object' ? window.heartbeatSettings.nonce : ''; 155 tick = time(); 212 /** 213 * Clear the error state and fire an event 214 * 215 * @access private 216 * 217 * @return void 218 */ 219 function clearErrorState() { 220 // Has connected successfully 221 settings.hasConnected = true; 156 222 157 data = $.extend( {}, queue ); 158 // Clear the data queue, anything added after this point will be send on the next tick 159 queue = {}; 223 if ( hasConnectionError() ) { 224 settings.errorcount = 0; 225 settings.connectionError = false; 226 $document.trigger( 'heartbeat-connection-restored' ); 227 } 228 } 160 229 161 $(document).trigger( 'heartbeat-send', [data] ); 230 /** 231 * Gather the data and connect to the server 232 * 233 * @access private 234 * 235 * @return void 236 */ 237 function connect() { 238 var ajaxData, heartbeatData; 162 239 163 for ( i in data ) { 164 if ( data.hasOwnProperty( i ) ) { 165 empty = false; 166 break; 167 } 168 } 169 170 // If nothing to send (nothing is expecting a response), 171 // schedule the next tick and bail 172 if ( empty && ! self.hasConnectionError() ) { 173 connecting = false; 174 next(); 240 // If the connection to the server is slower than the interval, 241 // heartbeat connects as soon as the previous connection's response is received. 242 if ( settings.connecting ) { 175 243 return; 176 244 } 177 245 178 send.data = data; 179 send.interval = interval / 1000; 180 send._nonce = nonce; 181 send.action = 'heartbeat'; 182 send.screen_id = screenId; 183 send.has_focus = hasFocus; 246 settings.lastTick = time(); 184 247 185 connecting = true; 186 self.xhr = $.ajax({ 187 url: url, 248 heartbeatData = $.extend( {}, settings.queue ); 249 // Clear the data queue, anything added after this point will be send on the next tick 250 settings.queue = {}; 251 252 $document.trigger( 'heartbeat-send', [ heartbeatData ] ); 253 254 ajaxData = { 255 data: heartbeatData, 256 interval: settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000, 257 _nonce: typeof window.heartbeatSettings === 'object' ? window.heartbeatSettings.nonce : '', 258 action: 'heartbeat', 259 screen_id: settings.screenId, 260 has_focus: settings.hasFocus 261 }; 262 263 settings.connecting = true; 264 settings.xhr = $.ajax({ 265 url: settings.url, 188 266 type: 'post', 189 267 timeout: 30000, // throw an error if not completed after 30 sec. 190 data: send,268 data: ajaxData, 191 269 dataType: 'json' 270 }).always( function() { 271 settings.connecting = false; 272 next(); 192 273 }).done( function( response, textStatus, jqXHR ) { 193 274 var new_interval; 194 275 195 if ( ! response ) 196 return errorstate( 'empty' ); 276 if ( ! response ) { 277 setErrorState( 'empty' ); 278 return; 279 } 197 280 198 // Clear error state 199 if ( self.hasConnectionError() ) 200 errorstate(); 281 clearErrorState(); 201 282 202 283 if ( response.nonces_expired ) { 203 $ (document).trigger( 'heartbeat-nonces-expired' );284 $document.trigger( 'heartbeat-nonces-expired' ); 204 285 return; 205 286 } 206 287 … … 210 291 delete response.heartbeat_interval; 211 292 } 212 293 213 self.tick( response, textStatus, jqXHR);294 $document.trigger( 'heartbeat-tick', [response, textStatus, jqXHR] ); 214 295 215 // do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast' 216 if ( new_interval ) 217 self.interval.call( self, new_interval ); 218 }).always( function() { 219 connecting = false; 220 next(); 296 // Do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast' 297 if ( new_interval ) { 298 setInterval( new_interval ); 299 } 221 300 }).fail( function( jqXHR, textStatus, error ) { 222 errorstate( textStatus || 'unknown', jqXHR.status );223 self.error( jqXHR, textStatus, error);301 setErrorState( textStatus || 'unknown', jqXHR.status ); 302 $document.trigger( 'heartbeat-error', [jqXHR, textStatus, error] ); 224 303 }); 225 304 } 226 305 306 /** 307 * Schedule the next connection 308 * 309 * Fires immediately if the connection time is longer than the interval. 310 * 311 * @access private 312 * 313 * @return void 314 */ 227 315 function next() { 228 var delta = time() - tick, t = interval;316 var delta = time() - settings.lastTick, interval = settings.mainInterval; 229 317 230 if ( ! running )318 if ( ! settings.isRunning ) { 231 319 return; 320 } 232 321 233 if ( ! hasFocus ) { 234 t = 100000; // 100 sec. Post locks expire after 120 sec. 235 } else if ( countdown > 0 && tempInterval ) { 236 t = tempInterval; 237 countdown--; 322 if ( ! settings.hasFocus ) { 323 interval = 120000; // 120 sec. Post locks expire after 150 sec. 324 } else if ( settings.countdown > 0 && settings.tempInterval ) { 325 interval = settings.tempInterval; 326 settings.countdown--; 327 328 if ( settings.countdown < 1 ) { 329 settings.tempInterval = 0; 330 } 238 331 } 239 332 240 window.clearTimeout( beat);333 window.clearTimeout( settings.beatTimer ); 241 334 242 if ( delta < t) {243 beat= window.setTimeout(335 if ( delta < interval ) { 336 settings.beatTimer = window.setTimeout( 244 337 function(){ 245 if ( running )338 if ( settings.isRunning ) { 246 339 connect(); 340 } 247 341 }, 248 t- delta342 interval - delta 249 343 ); 250 344 } else { 251 345 connect(); … … 252 346 } 253 347 } 254 348 349 /** 350 * Set the internal state when the browser window looses focus 351 * 352 * @access private 353 * 354 * @return void 355 */ 255 356 function blurred() { 256 window.clearTimeout(winBlurTimeout); 257 window.clearTimeout(frameBlurTimeout); 258 winBlurTimeout = frameBlurTimeout = 0; 259 260 hasFocus = false; 357 clearFocusTimers(); 358 settings.hasFocus = false; 261 359 } 262 360 361 /** 362 * Set the internal state when the browser window is focused 363 * 364 * @access private 365 * 366 * @return void 367 */ 263 368 function focused() { 264 window.clearTimeout(winBlurTimeout); 265 window.clearTimeout(frameBlurTimeout); 266 winBlurTimeout = frameBlurTimeout = 0; 369 clearFocusTimers(); 370 settings.userActivity = time(); 267 371 268 isUserActive = time(); 269 270 if ( hasFocus ) 271 return; 272 273 hasFocus = true; 274 window.clearTimeout(beat); 275 276 if ( ! connecting ) 372 if ( ! settings.hasFocus ) { 373 settings.hasFocus = true; 277 374 next(); 375 } 278 376 } 279 377 280 function setFrameEvents() { 281 $('iframe').each( function( i, frame ){ 282 if ( ! isLocalFrame( frame ) ) 378 /** 379 * Add focus/blur events to all local iframes 380 * 381 * Used to detect when focus is moved from the main window to an iframe 382 * 383 * @access private 384 * 385 * @return void 386 */ 387 function setFrameFocusEvents() { 388 $('iframe').each( function( i, frame ) { 389 if ( ! isLocalFrame( frame ) ) { 283 390 return; 391 } 284 392 285 if ( $.data( frame, 'wp-heartbeat-focus' ) ) 393 if ( $.data( frame, 'wp-heartbeat-focus' ) ) { 286 394 return; 395 } 287 396 288 397 $.data( frame, 'wp-heartbeat-focus', 1 ); 289 398 290 $( frame.contentWindow ).on( 'focus.wp-heartbeat-focus', function( e) {399 $( frame.contentWindow ).on( 'focus.wp-heartbeat-focus', function() { 291 400 focused(); 292 }).on('blur.wp-heartbeat-focus', function(e) { 293 setFrameEvents(); 294 frameBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); 401 }).on('blur.wp-heartbeat-focus', function() { 402 setFrameFocusEvents(); 403 // We don't know why the 'blur' was fired. Either the user clicked in the main window or outside the browser. 404 // Running blurred() after some timeout lets us cancel it if the user clicked in the main window. 405 settings.frameBlurTimer = window.setTimeout( function(){ blurred(); }, 500 ); 295 406 }); 296 407 }); 297 408 } 298 299 $(window).on( 'blur.wp-heartbeat-focus', function(e) { 300 setFrameEvents(); 301 winBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); 302 }).on( 'focus.wp-heartbeat-focus', function() { 409 410 /** 411 * Remove the focus/blur events to all local iframes 412 * 413 * @access private 414 * 415 * @return void 416 */ 417 function removeFrameFocusEvents() { 303 418 $('iframe').each( function( i, frame ) { 304 if ( ! isLocalFrame( frame ) )419 if ( ! isLocalFrame( frame ) ) { 305 420 return; 421 } 306 422 307 423 $.removeData( frame, 'wp-heartbeat-focus' ); 308 424 $( frame.contentWindow ).off( '.wp-heartbeat-focus' ); 309 425 }); 426 } 310 427 311 focused(); 312 }); 428 /** 429 * Clear the reset timers for focus/blur events on the window and iframes 430 * 431 * @access private 432 * 433 * @return void 434 */ 435 function clearFocusTimers() { 436 window.clearTimeout( settings.winBlurTimer ); 437 window.clearTimeout( settings.frameBlurTimer ); 438 } 313 439 440 /** 441 * Runs when the user becomes active after a period of inactivity 442 * 443 * @access private 444 * 445 * @return void 446 */ 314 447 function userIsActive() { 315 userActiveEvents = false; 316 $(document).off( '.wp-heartbeat-active' ); 448 settings.isRunning = true; 449 settings.userActiveEvents = false; 450 $document.off( '.wp-heartbeat-active' ); 451 317 452 $('iframe').each( function( i, frame ) { 318 if ( ! isLocalFrame( frame ) ) 453 if ( ! isLocalFrame( frame ) ) { 319 454 return; 455 } 320 456 321 457 $( frame.contentWindow ).off( '.wp-heartbeat-active' ); 322 458 }); … … 324 460 focused(); 325 461 } 326 462 327 // Set 'hasFocus = true' if user is active and the window is in the background. 328 // Set 'hasFocus = false' if the user has been inactive (no mouse or keyboard activity) for 5 min. even when the window has focus. 463 /** 464 * Check for user activity 465 * 466 * Runs every 30 sec. 467 * Sets 'hasFocus = true' if user is active and the window is in the background. 468 * Set 'hasFocus = false' if the user has been inactive (no mouse or keyboard activity) 469 * for 5 min. even when the window has focus. 470 * 471 * @access private 472 * 473 * @return void 474 */ 329 475 function checkUserActive() { 330 var lastActive = isUserActive ? time() - isUserActive: 0;476 var lastActive = settings.userActivity ? time() - settings.userActivity : 0; 331 477 332 // Throttle down when no mouse or keyboard activity for 5 min 333 if ( lastActive > 300000 && hasFocus ) 334 blurred(); 478 if ( lastActive > 300000 && settings.hasFocus ) { 479 // Throttle down when no mouse or keyboard activity for 5 min 480 blurred(); 481 } 335 482 336 if ( ! userActiveEvents ) { 337 $(document).on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); } ); 483 if ( lastActive > 900000 ) { 484 // The user has been inactive for 15 min. Stop heartbeat. 485 // (This will also release the post lock, etc.) 486 // When the user becomes active again, heartbeat will connect straight away. 487 settings.isRunning = false; 488 } 338 489 490 if ( ! settings.userActiveEvents ) { 491 $document.on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); } ); 492 339 493 $('iframe').each( function( i, frame ) { 340 if ( ! isLocalFrame( frame ) ) 494 if ( ! isLocalFrame( frame ) ) { 341 495 return; 496 } 342 497 343 498 $( frame.contentWindow ).on( 'mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); } ); 344 499 }); 345 500 346 userActiveEvents = true;501 settings.userActiveEvents = true; 347 502 } 348 503 } 349 504 350 // Check for user activity every 30 seconds. 351 window.setInterval( function(){ checkUserActive(); }, 30000 ); 352 $(document).ready( function() { 353 // Start one tick (15 sec) after DOM ready 354 running = true; 355 tick = time(); 356 next(); 357 }); 505 // Public methods 358 506 359 this.hasFocus = function() { 360 return hasFocus; 361 }; 507 /** 508 * Whether the window (or any local iframe in it) has focus, or the user is active 509 * 510 * @return bool 511 */ 512 function hasFocus() { 513 return settings.hasFocus; 514 } 362 515 363 516 /** 517 * Whether there is a connection error 518 * 519 * @return bool 520 */ 521 function hasConnectionError() { 522 return settings.connectionError; 523 } 524 525 /** 526 * Connect asap regardless of 'hasFocus' 527 * 528 * Will not open two concurrent connections. If a connection is in progress, 529 * will connect again immediately after the current connection completes. 530 * 531 * @return void 532 */ 533 function connectNow() { 534 settings.lastTick = 0; 535 next(); 536 } 537 538 /** 364 539 * Get/Set the interval 365 540 * 366 * When setting to 'fast', the interval is 5 sec. for the next 30 ticks (for 2 min and 30 sec). 541 * When setting to 'fast' or 5, by default interval is 5 sec. for the next 30 ticks (for 2 min and 30 sec). 542 * In this case the number of 'ticks' can be passed as second argument. 367 543 * If the window doesn't have focus, the interval slows down to 2 min. 368 544 * 369 * @param string speed Interval speed: 'fast' (5sec), 'standard' (15sec) default, 'slow' (60sec)370 * @param string ticks Used with speed = 'fast' , how many ticks before the speedreverts back545 * @param mixed speed Interval: 'fast' or 5, 15, 30, 60 546 * @param string ticks Used with speed = 'fast' or 5, how many ticks before the interval reverts back 371 547 * @return int Current interval in seconds 372 548 */ 373 this.interval = function( speed, ticks ) { 374 var reset, seconds; 375 ticks = parseInt( ticks, 10 ) || 30; 376 ticks = ticks < 1 || ticks > 30 ? 30 : ticks; 549 function setInterval( speed, ticks ) { 550 var interval, oldInerval = settings.tempInterval ? settings.tempInterval : settings.mainInterval; 377 551 378 552 if ( speed ) { 379 553 switch ( speed ) { 380 554 case 'fast': 381 seconds = 5;382 countdown = ticks;555 case 5: 556 interval = 5000; 383 557 break; 384 case 'slow': 385 seconds = 60; 386 countdown = 0; 558 case 15: 559 interval = 15000; 387 560 break; 561 case 30: 562 interval = 30000; 563 break; 564 case 60: 565 interval = 60000; 566 break; 388 567 case 'long-polling': 389 568 // Allow long polling, (experimental) 390 interval = 0;569 settings.mainInterval = 0; 391 570 return 0; 392 571 break; 393 572 default: 394 seconds = 15; 395 countdown = 0; 573 interval = settings.originalInterval; 396 574 } 397 575 398 // Reset when the new interval value is lower than the current one 399 reset = seconds * 1000 < interval; 576 if ( 5000 === interval ) { 577 ticks = parseInt( ticks, 10 ) || 30; 578 ticks = ticks < 1 || ticks > 30 ? 30 : ticks; 400 579 401 if ( countdown > 0 ) {402 tempInterval = seconds * 1000;580 settings.countdown = ticks; 581 settings.tempInterval = interval; 403 582 } else { 404 interval = seconds * 1000; 405 tempInterval = 0; 583 settings.countdown = 0; 584 settings.tempInterval = 0; 585 settings.mainInterval = interval; 406 586 } 407 587 408 if ( reset ) 588 // Change the next connection time if new interval has been set. 589 // Will connect immediately if the time since the last connection 590 // is greater than the new interval. 591 if ( interval !== oldInerval ) { 409 592 next(); 593 } 410 594 } 411 595 412 if ( ! hasFocus )413 return 120;596 return settings.tempInterval ? settings.tempInterval / 1000 : settings.mainInterval / 1000; 597 } 414 598 415 return tempInterval ? tempInterval / 1000 : interval / 1000;416 };417 418 599 /** 419 600 * Enqueue data to send with the next XHR 420 601 * … … 431 612 * $param bool dont_overwrite Whether to overwrite existing data in the queue. 432 613 * $return bool Whether the data was queued or not. 433 614 */ 434 this.enqueue = function( handle, data, dont_overwrite ) {615 function enqueue( handle, data, dont_overwrite ) { 435 616 if ( handle ) { 436 if ( dont_overwrite && this.isQueued( handle ) ) 617 if ( dont_overwrite && this.isQueued( handle ) ) { 437 618 return false; 619 } 438 620 439 queue[handle] = data;621 settings.queue[handle] = data; 440 622 return true; 441 623 } 442 624 return false; 443 } ;625 } 444 626 445 627 /** 446 628 * Check if data with a particular handle is queued … … 448 630 * $param string handle The handle for the data 449 631 * $return bool Whether some data is queued with this handle 450 632 */ 451 this.isQueued = function( handle ) { 452 if ( handle ) 453 return queue.hasOwnProperty( handle ); 454 }; 633 function isQueued( handle ) { 634 if ( handle ) { 635 return settings.queue.hasOwnProperty( handle ); 636 } 637 } 455 638 456 639 /** 457 640 * Remove data with a particular handle from the queue … … 459 642 * $param string handle The handle for the data 460 643 * $return void 461 644 */ 462 this.dequeue = function( handle ) { 463 if ( handle ) 464 delete queue[handle]; 465 }; 645 function dequeue( handle ) { 646 if ( handle ) { 647 delete settings.queue[handle]; 648 } 649 } 466 650 467 651 /** 468 652 * Get data that was enqueued with a particular handle … … 470 654 * $param string handle The handle for the data 471 655 * $return mixed The data or undefined 472 656 */ 473 this.getQueuedItem = function( handle ) {474 if ( handle ) 657 function getQueuedItem( handle ) { 658 if ( handle ) { 475 659 return this.isQueued( handle ) ? queue[handle] : undefined; 660 } 661 } 662 663 initialize(); 664 665 // Expose public methods 666 return { 667 hasFocus: hasFocus, 668 connectNow: connectNow, 669 setInterval: setInterval, 670 hasConnectionError: hasConnectionError, 671 enqueue: enqueue, 672 dequeue: dequeue, 673 isQueued: isQueued, 674 getQueuedItem: getQueuedItem 476 675 }; 477 676 }; 478 677 479 $.extend( Heartbeat.prototype, { 480 tick: function( data, textStatus, jqXHR ) { 481 $(document).trigger( 'heartbeat-tick', [data, textStatus, jqXHR] ); 482 }, 483 error: function( jqXHR, textStatus, error ) { 484 $(document).trigger( 'heartbeat-error', [jqXHR, textStatus, error] ); 485 } 486 }); 678 // Ensure the global `wp` object exists. 679 window.wp = window.wp || {}; 680 window.wp.heartbeat = new Heartbeat(); 487 681 488 wp.heartbeat = new Heartbeat(); 489 490 }(jQuery)); 682 }( jQuery, window ));