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