Changeset 23382
- Timestamp:
- 02/03/2013 07:03:27 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-admin/includes/ajax-actions.php
r23355 r23382 2075 2075 function wp_ajax_heartbeat() { 2076 2076 check_ajax_referer( 'heartbeat-nonce', '_nonce' ); 2077 $response = array( 'pagenow' => '' ); 2078 2079 if ( ! empty($_POST['pagenow']) ) 2080 $response['pagenow'] = sanitize_key($_POST['pagenow']); 2077 $response = array(); 2078 2079 // screenid is the same as $current_screen->id and the JS global 'pagenow' 2080 if ( ! empty($_POST['screenid']) ) 2081 $screen_id = sanitize_key($_POST['screenid']); 2082 else 2083 $screen_id = 'site'; 2081 2084 2082 2085 if ( ! empty($_POST['data']) ) { … … 2088 2091 // todo: separate filters: 'heartbeat_[action]' so we call different callbacks only when there is data for them, 2089 2092 // or all callbacks listen to one filter and run when there is something for them in $data? 2090 $response = apply_filters( 'heartbeat_received', $response, $data );2091 } 2092 2093 $response = apply_filters( 'heartbeat_send', $response );2093 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id ); 2094 } 2095 2096 $response = apply_filters( 'heartbeat_send', $response, $screen_id ); 2094 2097 2095 2098 // Allow the transport to be replaced with long-polling easily 2096 do_action( 'heartbeat_tick', $response ); 2097 2098 // always send the current time acording to the server 2099 $response['time'] = time(); 2099 do_action( 'heartbeat_tick', $response, $screen_id ); 2100 2101 // send the current time acording to the server 2102 $response['servertime'] = time(); 2103 2104 // Change the interval, format: array( speed, ticks ) 2105 if ( isset($response['heartbeat_interval']) ) 2106 $response['heartbeat_interval'] = (array) $response['heartbeat_interval']; 2100 2107 2101 2108 wp_send_json($response); -
trunk/wp-includes/js/heartbeat.js
r23355 r23382 10 10 var self = this, 11 11 running, 12 timeout,12 beat, 13 13 nonce, 14 screen = typeof pagenow != 'undefined' ? pagenow : '',14 screenid = typeof pagenow != 'undefined' ? pagenow : '', 15 15 settings, 16 16 tick = 0, 17 17 queue = {}, 18 18 interval, 19 lastconnect = 0; 19 lastconnect = 0, 20 connecting, 21 countdown, 22 tempInterval, 23 hasFocus = true, 24 isUserActive, 25 userActiveEvents, 26 winBlurTimeout, 27 frameBlurTimeout = -1; 20 28 21 29 this.url = typeof ajaxurl != 'undefined' ? ajaxurl : 'wp-admin/admin-ajax.php'; … … 30 38 delete settings.nonce; 31 39 32 interval = settings.interval || 15 000; // default interval40 interval = settings.interval || 15; // default interval 33 41 delete settings.interval; 34 42 // The interval can be from 5 to 60 sec. 43 if ( interval < 5 ) 44 interval = 5; 45 else if ( interval > 60 ) 46 interval = 60; 47 48 interval = interval * 1000; 49 35 50 // todo: needed? 36 // ' pagenow' can be added from settings if not already defined37 screen = screen || settings.pagenow;38 delete settings. pagenow;39 40 // Add public vars51 // 'screenid' can be added from settings on the front-end where the JS global 'pagenow' is not set 52 screenid = screenid || settings.screenid || 'site'; 53 delete settings.screenid; 54 55 // Add or overwrite public vars 41 56 $.extend( this, settings ); 42 57 } … … 49 64 } 50 65 51 // Set error state and fire an event if it persists for over 3 min 66 // Set error state and fire an event if errors persist for over 2 min when the window has focus 67 // or 6 min when the window is in the background 52 68 function errorstate() { 53 69 var since; 54 70 55 71 if ( lastconnect ) { 56 since = time() - lastconnect ;57 58 if ( since > 180000) {72 since = time() - lastconnect, duration = hasFocus ? 120000 : 360000; 73 74 if ( since > duration ) { 59 75 self.connectionLost = true; 60 76 $(document).trigger( 'heartbeat-connection-lost', parseInt(since / 1000) ); … … 71 87 72 88 data.data = $.extend( {}, queue ); 73 queue = {};74 89 75 90 data.interval = interval / 1000; 76 91 data._nonce = nonce; 77 92 data.action = 'heartbeat'; 78 data.pagenow = screen; 79 80 self.xhr = $.post( self.url, data, function(r){ 93 data.screenid = screenid; 94 data.has_focus = hasFocus; 95 96 connecting = true; 97 self.xhr = $.post( self.url, data, 'json' ) 98 .done( function( data, textStatus, jqXHR ) { 99 var interval; 100 101 // Clear the data queue 102 queue = {}; 103 104 // Clear error state 81 105 lastconnect = time(); 82 // Clear error state83 106 if ( self.connectionLost ) 84 107 errorstate(); 85 86 self.tick(r); 87 }, 'json' ).always( function(){ 108 109 // Change the interval from PHP 110 interval = data.heartbeat_interval; 111 delete data.heartbeat_interval; 112 113 self.tick( data, textStatus, jqXHR ); 114 115 // do this last, can trigger the next XHR 116 if ( interval ) 117 self.interval.apply( self, data.heartbeat_interval ); 118 }).always( function(){ 119 connecting = false; 88 120 next(); 89 }).fail( function( r){121 }).fail( function( jqXHR, textStatus, error ){ 90 122 errorstate(); 91 self.error( r);123 self.error( jqXHR, textStatus, error ); 92 124 }); 93 125 }; 94 126 95 127 function next() { 96 var delta = time() - tick ;128 var delta = time() - tick, t = interval; 97 129 98 130 if ( !running ) 99 131 return; 100 132 101 if ( delta < interval ) { 102 timeout = window.setTimeout( 133 if ( !hasFocus ) { 134 t = 120000; // 2 min 135 } else if ( countdown && tempInterval ) { 136 t = tempInterval; 137 countdown--; 138 } 139 140 window.clearTimeout(beat); 141 142 if ( delta < t ) { 143 beat = window.setTimeout( 103 144 function(){ 104 145 if ( running ) 105 146 connect(); 106 147 }, 107 interval- delta148 t - delta 108 149 ); 109 150 } else { 110 window.clearTimeout(timeout); // this has already expired?111 151 connect(); 112 152 } 113 }; 114 115 this.interval = function(seconds) { 116 if ( seconds ) { 117 // Limit 118 if ( 5 > seconds || seconds > 60 ) 119 return false; 120 121 interval = seconds * 1000; 122 } else if ( seconds === 0 ) { 123 // Allow long polling to be turned on 124 interval = 0; 125 } 126 return interval / 1000; 127 }; 128 129 this.start = function() { 130 // start only once 131 if ( running ) 132 return false; 133 134 running = true; 135 connect(); 136 137 return true; 138 }; 139 140 this.stop = function() { 141 if ( !running ) 142 return false; 143 144 if ( self.xhr ) 145 self.xhr.abort(); 146 147 running = false; 148 return true; 149 } 150 151 this.send = function(action, data) { 152 if ( action ) 153 queue[action] = data; 154 } 153 } 154 155 function blurred() { 156 window.clearTimeout(winBlurTimeout); 157 window.clearTimeout(frameBlurTimeout); 158 winBlurTimeout = frameBlurTimeout = 0; 159 160 hasFocus = false; 161 162 // temp debug 163 if ( self.debug ) 164 console.log('### blurred(), slow down...') 165 } 166 167 function focused() { 168 window.clearTimeout(winBlurTimeout); 169 window.clearTimeout(frameBlurTimeout); 170 winBlurTimeout = frameBlurTimeout = 0; 171 172 isUserActive = time(); 173 174 if ( hasFocus ) 175 return; 176 177 hasFocus = true; 178 window.clearTimeout(beat); 179 180 if ( !connecting ) 181 next(); 182 183 // temp debug 184 if ( self.debug ) 185 console.log('### focused(), speed up... ') 186 } 187 188 function setFrameEvents() { 189 $('iframe').each( function(i, frame){ 190 if ( $.data(frame, 'wp-heartbeat-focus') ) 191 return; 192 193 $.data(frame, 'wp-heartbeat-focus', 1); 194 195 $(frame.contentWindow).on('focus.wp-heartbeat-focus', function(e){ 196 focused(); 197 }).on('blur.wp-heartbeat-focus', function(e){ 198 setFrameEvents(); 199 frameBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); 200 }); 201 }); 202 } 203 204 $(window).on('blur.wp-heartbeat-focus', function(e){ 205 setFrameEvents(); 206 winBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 ); 207 }).on('focus.wp-heartbeat-focus', function(){ 208 $('iframe').each( function(i, frame){ 209 $.removeData(frame, 'wp-heartbeat-focus'); 210 $(frame.contentWindow).off('.wp-heartbeat-focus'); 211 }); 212 213 focused(); 214 }); 215 216 function userIsActive() { 217 userActiveEvents = false; 218 $(document).off('.wp-heartbeat-active'); 219 $('iframe').each( function(i, frame){ 220 $(frame.contentWindow).off('.wp-heartbeat-active'); 221 }); 222 223 focused(); 224 225 // temp debug 226 if ( self.debug ) 227 console.log( 'userIsActive()' ); 228 } 229 230 // Set 'hasFocus = true' if user is active and the window is in the background. 231 // Set 'hasFocus = false' if the user has been inactive (no mouse or keyboard activity) for 5 min. even when the window has focus. 232 function checkUserActive() { 233 var lastActive = isUserActive ? time() - isUserActive : 0; 234 235 // temp debug 236 if ( self.debug ) 237 console.log( 'checkUserActive(), lastActive = %s seconds ago', parseInt(lastActive / 1000) || 'null' ); 238 239 // Throttle down when no mouse or keyboard activity for 5 min 240 if ( lastActive > 300000 && hasFocus ) 241 blurred(); 242 243 if ( !userActiveEvents ) { 244 $(document).on('mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); }); 245 $('iframe').each( function(i, frame){ 246 $(frame.contentWindow).on('mouseover.wp-heartbeat-active keyup.wp-heartbeat-active', function(){ userIsActive(); }); 247 }); 248 userActiveEvents = true; 249 } 250 } 251 252 // Check for user activity every 30 seconds. 253 window.setInterval( function(){ checkUserActive(); }, 30000 ); 155 254 156 255 if ( this.autostart ) { … … 162 261 }); 163 262 } 164 263 264 this.winHasFocus = function() { 265 return hasFocus; 266 } 267 268 /** 269 * Get/Set the interval 270 * 271 * When setting the interval to 'fast', the number of ticks is specified wiht the second argument, default 30. 272 * If the window doesn't have focus, the interval is overridden to 2 min. In this case setting the 'ticks' 273 * will start counting after the window gets focus. 274 * 275 * @param string speed Interval speed: 'fast' (5sec), 'standard' (15sec) default, 'slow' (60sec) 276 * @param int ticks Number of ticks for the changed interval, optional when setting 'standard' or 'slow' 277 * @return int Current interval in seconds 278 */ 279 this.interval = function(speed, ticks) { 280 var reset, seconds; 281 282 if ( speed ) { 283 switch ( speed ) { 284 case 'fast': 285 seconds = 5; 286 countdown = parseInt(ticks) || 30; 287 break; 288 case 'slow': 289 seconds = 60; 290 countdown = parseInt(ticks) || 0; 291 break; 292 case 'long-polling': 293 // Allow long polling, (experimental) 294 interval = 0; 295 return 0; 296 break; 297 default: 298 seconds = 15; 299 countdown = 0; 300 } 301 302 // Reset when the new interval value is lower than the current one 303 reset = seconds * 1000 < interval; 304 305 if ( countdown ) { 306 tempInterval = seconds * 1000; 307 } else { 308 interval = seconds * 1000; 309 tempInterval = 0; 310 } 311 312 if ( reset ) 313 next(); 314 } 315 316 if ( !hasFocus ) 317 return 120; 318 319 return tempInterval ? tempInterval / 1000 : interval / 1000; 320 }; 321 322 // Start. Has no effect if heartbeat is already running 323 this.start = function() { 324 if ( running ) 325 return false; 326 327 running = true; 328 connect(); 329 return true; 330 }; 331 332 // Stop. If a XHR is in progress, abort it 333 this.stop = function() { 334 if ( self.xhr && self.xhr.readyState != 4 ) 335 self.xhr.abort(); 336 337 running = false; 338 return true; 339 } 340 341 /** 342 * Send data with the next XHR 343 * 344 * As the data is sent later, this function doesn't return the XHR response. 345 * To see the response, use the custom jQuery event 'heartbeat-tick' on the document, example: 346 * $(document).on('heartbeat-tick.myname', function(data, textStatus, jqXHR) { 347 * // code 348 * }); 349 * If the same 'handle' is used more than once, the data is overwritten when the third argument is 'true'. 350 * Use wp.heartbeat.isQueued('handle') to see if any data is already queued for that handle. 351 * 352 * $param string handle Unique handle for the data. The handle is used in PHP to receive the data 353 * $param mixed data The data to be sent 354 * $param bool overwrite Whether to overwrite existing data in the queue 355 * $return bool Whether the data was queued or not 356 */ 357 this.send = function(handle, data, overwrite) { 358 if ( handle ) { 359 if ( queue.hasOwnProperty(handle) && !overwrite ) 360 return false; 361 362 queue[handle] = data; 363 return true; 364 } 365 return false; 366 } 367 368 /** 369 * Check if data with a particular handle is queued 370 * 371 * $param string handle The handle for the data 372 * $return mixed The data queued with that handle or null 373 */ 374 this.isQueued = function(handle) { 375 return queue[handle]; 376 } 165 377 } 166 378 167 379 $.extend( Heartbeat.prototype, { 168 tick: function( r) {169 $(document).trigger( 'heartbeat-tick', r);380 tick: function(data, textStatus, jqXHR) { 381 $(document).trigger( 'heartbeat-tick', [data, textStatus, jqXHR] ); 170 382 }, 171 error: function( r) {172 $(document).trigger( 'heartbeat-error', r);383 error: function(jqXHR, textStatus, error) { 384 $(document).trigger( 'heartbeat-error', [jqXHR, textStatus, error] ); 173 385 } 174 386 });
Note: See TracChangeset
for help on using the changeset viewer.