Index: wp-includes/js/heartbeat.js
===================================================================
--- wp-includes/js/heartbeat.js	(revision 25535)
+++ wp-includes/js/heartbeat.js	(working copy)
@@ -29,7 +29,7 @@
 	var Heartbeat = function() {
 		var self = this,
 			running,
-			beat,
+			beatTimeout,
 			screenId = typeof pagenow != 'undefined' ? pagenow : '',
 			url = typeof ajaxurl != 'undefined' ? ajaxurl : '',
 			settings,
@@ -146,6 +146,13 @@
 		function connect() {
 			var send = {}, data, i, empty = true,
 			nonce = typeof window.heartbeatSettings == 'object' ? window.heartbeatSettings.nonce : '';
+
+			// If the connection to the server is slower than the interval,
+			// heartbeat connects as soon as the previous connection's response is received.
+			if ( connecting ) {
+				return;
+			}
+
 			tick = time();
 
 			data = $.extend( {}, queue );
@@ -164,7 +171,6 @@
 			// If nothing to send (nothing is expecting a response),
 			// schedule the next tick and bail
 			if ( empty && ! self.hasConnectionError() ) {
-				connecting = false;
 				next();
 				return;
 			}
@@ -183,6 +189,9 @@
 				timeout: 30000, // throw an error if not completed after 30 sec.
 				data: send,
 				dataType: 'json'
+			}).always( function() {
+				connecting = false;
+				next();
 			}).done( function( response, textStatus, jqXHR ) {
 				var new_interval;
 
@@ -209,9 +218,6 @@
 				// do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast'
 				if ( new_interval )
 					self.interval.call( self, new_interval );
-			}).always( function() {
-				connecting = false;
-				next();
 			}).fail( function( jqXHR, textStatus, error ) {
 				errorstate( textStatus || 'unknown' );
 				self.error( jqXHR, textStatus, error );
@@ -231,10 +237,10 @@
 				countdown--;
 			}
 
-			window.clearTimeout(beat);
+			window.clearTimeout( beatTimeout );
 
 			if ( delta < t ) {
-				beat = window.setTimeout(
+				beatTimeout = window.setTimeout(
 					function(){
 						if ( running )
 							connect();
@@ -265,10 +271,7 @@
 				return;
 
 			hasFocus = true;
-			window.clearTimeout(beat);
-
-			if ( ! connecting )
-				next();
+			next();
 		}
 
 		function setFrameEvents() {
@@ -290,21 +293,6 @@
 			});
 		}
 
-		$(window).on( 'blur.wp-heartbeat-focus', function(e) {
-			setFrameEvents();
-			winBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 );
-		}).on( 'focus.wp-heartbeat-focus', function() {
-			$('iframe').each( function( i, frame ) {
-				if ( !isLocalFrame( frame ) )
-					return;
-
-				$.removeData( frame, 'wp-heartbeat-focus' );
-				$( frame.contentWindow ).off( '.wp-heartbeat-focus' );
-			});
-
-			focused();
-		});
-
 		function userIsActive() {
 			userActiveEvents = false;
 			$(document).off( '.wp-heartbeat-active' );
@@ -341,6 +329,24 @@
 			}
 		}
 
+		$(window).on( 'blur.wp-heartbeat-focus', function(e) {
+			setFrameEvents();
+			winBlurTimeout = window.setTimeout( function(){ blurred(); }, 500 );
+		}).on( 'focus.wp-heartbeat-focus', function() {
+			$('iframe').each( function( i, frame ) {
+				if ( !isLocalFrame( frame ) )
+					return;
+
+				$.removeData( frame, 'wp-heartbeat-focus' );
+				$( frame.contentWindow ).off( '.wp-heartbeat-focus' );
+			});
+
+			focused();
+		}).on( 'unload.wp-heartbeat', function() {
+			// Don't connect any more
+			running = false;
+		});
+
 		// Check for user activity every 30 seconds.
 		window.setInterval( function(){ checkUserActive(); }, 30000 );
 		$(document).ready( function() {
@@ -355,6 +361,16 @@
 		};
 
 		/**
+		 * Connect asap regardless of 'hasFocus'. Will not open two concurrent connections.
+		 * If a connection is in progress, will connect again immediately after the current connection completes.
+		 *
+		 */		 		 		
+		this.connectNow = function() {
+			tick = 0;
+			next();
+		};
+
+		/**
 		 * Get/Set the interval
 		 *
 		 * When setting to 'fast', the interval is 5 sec. for the next 30 ticks (for 2 min and 30 sec).
@@ -365,7 +381,8 @@
 		 * @return int Current interval in seconds
 		 */
 		this.interval = function( speed, ticks ) {
-			var reset, seconds;
+			var seconds, oldInerval = interval;
+
 			ticks = parseInt( ticks, 10 ) || 30;
 			ticks = ticks < 1 || ticks > 30 ? 30 : ticks;
 
@@ -389,9 +406,6 @@
 						countdown = 0;
 				}
 
-				// Reset when the new interval value is lower than the current one
-				reset = seconds * 1000 < interval;
-
 				if ( countdown > 0 ) {
 					tempInterval = seconds * 1000;
 				} else {
@@ -399,12 +413,15 @@
 					tempInterval = 0;
 				}
 
-				if ( reset )
+				// Change the next connection time if the new interval is different
+				if ( seconds * 1000 != oldInerval ) {
+					tick = tick - ( oldInerval - seconds * 1000 );
 					next();
+				}
 			}
 
 			if ( ! hasFocus )
-				return 120;
+				return 100;
 
 			return tempInterval ? tempInterval / 1000 : interval / 1000;
 		};
