<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[23683] trunk: Autosave to the browser's sessionStorage, compare this autosave to the post content on page load and let the user restore it when the data is not the same.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg > ul, #logmsg > ol { margin-left: 0; margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/23683">23683</a></dd>
<dt>Author</dt> <dd>azaozz</dd>
<dt>Date</dt> <dd>2013-03-13 10:08:16 +0000 (Wed, 13 Mar 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Autosave to the browser's sessionStorage, compare this autosave to the post content on page load and let the user restore it when the data is not the same. First run, see <a href="http://core.trac.wordpress.org/ticket/23220">#23220</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpadmineditformadvancedphp">trunk/wp-admin/edit-form-advanced.php</a></li>
<li><a href="#trunkwpadminincludesajaxactionsphp">trunk/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunkwpadminincludesmiscphp">trunk/wp-admin/includes/misc.php</a></li>
<li><a href="#trunkwpincludesjsadminbarjs">trunk/wp-includes/js/admin-bar.js</a></li>
<li><a href="#trunkwpincludesjsautosavejs">trunk/wp-includes/js/autosave.js</a></li>
<li><a href="#trunkwpincludesscriptloaderphp">trunk/wp-includes/script-loader.php</a></li>
<li><a href="#trunkwploginphp">trunk/wp-login.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpadmineditformadvancedphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/edit-form-advanced.php (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/edit-form-advanced.php        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-admin/edit-form-advanced.php        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -301,7 +301,7 @@
</span><span class="cx">         echo ' &lt;a href=&quot;' . esc_url( $post_new_file ) . '&quot; class=&quot;add-new-h2&quot;&gt;' . esc_html( $post_type_object-&gt;labels-&gt;add_new ) . '&lt;/a&gt;';
</span><span class="cx"> ?&gt;&lt;/h2&gt;
</span><span class="cx"> &lt;?php if ( $notice ) : ?&gt;
</span><del>-&lt;div id=&quot;notice&quot; class=&quot;error&quot;&gt;&lt;p&gt;&lt;?php echo $notice ?&gt;&lt;/p&gt;&lt;/div&gt;
</del><ins>+&lt;div id=&quot;notice&quot; class=&quot;error&quot;&gt;&lt;p id=&quot;has-newer-autosave&quot;&gt;&lt;?php echo $notice ?&gt;&lt;/p&gt;&lt;/div&gt;
</ins><span class="cx"> &lt;?php endif; ?&gt;
</span><span class="cx"> &lt;?php if ( $message ) : ?&gt;
</span><span class="cx"> &lt;div id=&quot;message&quot; class=&quot;updated&quot;&gt;&lt;p&gt;&lt;?php echo $message; ?&gt;&lt;/p&gt;&lt;/div&gt;
</span></span></pre></div>
<a id="trunkwpadminincludesajaxactionsphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/includes/ajax-actions.php (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/includes/ajax-actions.php        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-admin/includes/ajax-actions.php        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -1050,9 +1050,9 @@
</span><span class="cx"> 
</span><span class="cx">         $id = $revision_id = 0;
</span><span class="cx"> 
</span><del>-        $post_ID = (int) $_POST['post_ID'];
-        $_POST['ID'] = $post_ID;
-        $post = get_post($post_ID);
</del><ins>+        $post_id = (int) $_POST['post_id'];
+        $_POST['ID'] = $_POST['post_ID'] = $post_id;
+        $post = get_post($post_id);
</ins><span class="cx">         if ( 'auto-draft' == $post-&gt;post_status )
</span><span class="cx">                 $_POST['post_status'] = 'draft';
</span><span class="cx"> 
</span><span class="lines">@@ -1068,10 +1068,10 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if ( 'page' == $post-&gt;post_type ) {
</span><del>-                if ( !current_user_can('edit_page', $post_ID) )
</del><ins>+                if ( !current_user_can('edit_page', $post-&gt;ID) )
</ins><span class="cx">                         wp_die( __( 'You are not allowed to edit this page.' ) );
</span><span class="cx">         } else {
</span><del>-                if ( !current_user_can('edit_post', $post_ID) )
</del><ins>+                if ( !current_user_can('edit_post', $post-&gt;ID) )
</ins><span class="cx">                         wp_die( __( 'You are not allowed to edit this post.' ) );
</span><span class="cx">         }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkwpadminincludesmiscphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/includes/misc.php (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/includes/misc.php        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-admin/includes/misc.php        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -632,3 +632,29 @@
</span><span class="cx">         return $response;
</span><span class="cx"> }
</span><span class="cx"> add_filter( 'heartbeat_received', 'wp_refresh_post_lock', 10, 3 );
</span><ins>+
+/**
+ * Output the HTML for restoring the post data from DOM storage
+ *
+ * @since 3.6
+ * @access private
+ */
+function _local_storage_notice() {
+        $screen = get_current_screen();
+        if ( ! $screen || 'post' != $screen-&gt;id )
+                return;
+
+        ?&gt;
+        &lt;div id=&quot;local-storage-notice&quot; class=&quot;hidden&quot;&gt;
+        &lt;p class=&quot;local-restore&quot;&gt;
+                &lt;?php _e('The backup of this post in your browser is different from the version below.'); ?&gt;
+                &lt;a class=&quot;restore-backup&quot; href=&quot;#&quot;&gt;&lt;?php _e('Restore the backup.'); ?&gt;&lt;/a&gt;
+        &lt;/p&gt;
+        &lt;p class=&quot;undo-restore hidden&quot;&gt;
+                &lt;?php _e('Post restored successfully.'); ?&gt;
+                &lt;a class=&quot;undo-restore-backup&quot; href=&quot;#&quot;&gt;&lt;?php _e('Undo.'); ?&gt;&lt;/a&gt;
+        &lt;/p&gt;
+        &lt;/div&gt;
+        &lt;?php
+}
+add_action( 'admin_footer', '_local_storage_notice' );
</ins></span></pre></div>
<a id="trunkwpincludesjsadminbarjs"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/js/admin-bar.js (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/admin-bar.js        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-includes/js/admin-bar.js        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -132,6 +132,18 @@
</span><span class="cx">                                 }, 100);
</span><span class="cx">                         }
</span><span class="cx">                 });
</span><ins>+
+                // Empty sessionStorage on logging out
+                if ( 'sessionStorage' in window ) {
+                        $('#wp-admin-bar-logout a').click( function() {
+                                try {
+                                        for ( var key in sessionStorage ) {
+                                                if ( key.indexOf('wp-autosave-') != -1 )
+                                                        sessionStorage.removeItem(key);
+                                        }
+                                } catch(e) {}
+                        });
+                }
</ins><span class="cx">         });
</span><span class="cx"> } else {
</span><span class="cx">         (function(d, w) {
</span><span class="lines">@@ -310,6 +322,17 @@
</span><span class="cx">                                 addEvent(aB, 'click', function(e) {
</span><span class="cx">                                         scrollToTop( e.target || e.srcElement );
</span><span class="cx">                                 });
</span><ins>+
+                                addEvent( document.getElementById('wp-admin-bar-logout'), 'click', function() {
+                                        if ( 'sessionStorage' in window ) {
+                                                try {
+                                                        for ( var key in sessionStorage ) {
+                                                                if ( key.indexOf('wp-autosave-') != -1 )
+                                                                        sessionStorage.removeItem(key);
+                                                        }
+                                                } catch(e) {}
+                                        }
+                                });
</ins><span class="cx">                         }
</span><span class="cx"> 
</span><span class="cx">                         if ( w.location.hash )
</span></span></pre></div>
<a id="trunkwpincludesjsautosavejs"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/js/autosave.js (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/autosave.js        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-includes/js/autosave.js        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -187,6 +187,7 @@
</span><span class="cx"> function autosave_saved_new(response) {
</span><span class="cx">         blockSave = false;
</span><span class="cx">         var res = autosave_parse_response(response), postID;
</span><ins>+
</ins><span class="cx">         if ( res &amp;&amp; res.responses.length &amp;&amp; !res.errors ) {
</span><span class="cx">                 // An ID is sent only for real auto-saves, not for autosave=0 &quot;keepalive&quot; saves
</span><span class="cx">                 postID = parseInt( res.responses[0].id, 10 );
</span><span class="lines">@@ -257,77 +258,21 @@
</span><span class="cx"> 
</span><span class="cx">         autosave_disable_buttons();
</span><span class="cx"> 
</span><del>-        post_data = {
-                action: &quot;autosave&quot;,
-                post_ID:  jQuery(&quot;#post_ID&quot;).val() || 0,
-                autosavenonce: jQuery('#autosavenonce').val(),
-                post_type: jQuery('#post_type').val() || &quot;&quot;,
-                autosave: 1
-        };
</del><ins>+        post_data = wp.autosave.getPostData();
</ins><span class="cx"> 
</span><del>-        jQuery('.tags-input').each( function() {
-                post_data[this.name] = this.value;
-        } );
-
</del><span class="cx">         // We always send the ajax request in order to keep the post lock fresh.
</span><span class="cx">         // This (bool) tells whether or not to write the post to the DB during the ajax request.
</span><del>-        doAutoSave = true;
</del><ins>+        doAutoSave = post_data.autosave;
</ins><span class="cx"> 
</span><span class="cx">         // No autosave while thickbox is open (media buttons)
</span><span class="cx">         if ( jQuery(&quot;#TB_window&quot;).css('display') == 'block' )
</span><span class="cx">                 doAutoSave = false;
</span><span class="cx"> 
</span><del>-        /* Gotta do this up here so we can check the length when tinymce is in use */
-        if ( rich &amp;&amp; doAutoSave ) {
-                ed = tinymce.activeEditor;
-                // Don't run while the tinymce spellcheck is on. It resets all found words.
-                if ( ed.plugins.spellchecker &amp;&amp; ed.plugins.spellchecker.active ) {
-                        doAutoSave = false;
-                } else {
-                        if ( 'mce_fullscreen' == ed.id || 'wp_mce_fullscreen' == ed.id )
-                                tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
-                        tinymce.triggerSave();
-                }
-        }
-
-        if ( fullscreen &amp;&amp; fullscreen.settings.visible ) {
-                post_data[&quot;post_title&quot;] = jQuery('#wp-fullscreen-title').val() || '';
-                post_data[&quot;content&quot;] = jQuery(&quot;#wp_mce_fullscreen&quot;).val() || '';
-        } else {
-                post_data[&quot;post_title&quot;] = jQuery(&quot;#title&quot;).val() || '';
-                post_data[&quot;content&quot;] = jQuery(&quot;#content&quot;).val() || '';
-        }
-
-        if ( jQuery('#post_name').val() )
-                post_data[&quot;post_name&quot;] = jQuery('#post_name').val();
-
</del><span class="cx">         // Nothing to save or no change.
</span><span class="cx">         if ( ( post_data[&quot;post_title&quot;].length == 0 &amp;&amp; post_data[&quot;content&quot;].length == 0 ) || post_data[&quot;post_title&quot;] + post_data[&quot;content&quot;] == autosaveLast ) {
</span><span class="cx">                 doAutoSave = false;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        origStatus = jQuery('#original_post_status').val();
-
-        goodcats = ([]);
-        jQuery(&quot;[name='post_category[]']:checked&quot;).each( function(i) {
-                goodcats.push(this.value);
-        } );
-        post_data[&quot;catslist&quot;] = goodcats.join(&quot;,&quot;);
-
-        if ( jQuery(&quot;#comment_status&quot;).prop(&quot;checked&quot;) )
-                post_data[&quot;comment_status&quot;] = 'open';
-        if ( jQuery(&quot;#ping_status&quot;).prop(&quot;checked&quot;) )
-                post_data[&quot;ping_status&quot;] = 'open';
-        if ( jQuery(&quot;#excerpt&quot;).size() )
-                post_data[&quot;excerpt&quot;] = jQuery(&quot;#excerpt&quot;).val();
-        if ( jQuery(&quot;#post_author&quot;).size() )
-                post_data[&quot;post_author&quot;] = jQuery(&quot;#post_author&quot;).val();
-        if ( jQuery(&quot;#parent_id&quot;).val() )
-                post_data[&quot;parent_id&quot;] = jQuery(&quot;#parent_id&quot;).val();
-        post_data[&quot;user_ID&quot;] = jQuery(&quot;#user-id&quot;).val();
-        if ( jQuery('#auto_draft').val() == '1' )
-                post_data[&quot;auto_draft&quot;] = '1';
-
</del><span class="cx">         if ( doAutoSave ) {
</span><span class="cx">                 autosaveLast = post_data[&quot;post_title&quot;] + post_data[&quot;content&quot;];
</span><span class="cx">                 jQuery(document).triggerHandler('wpcountwords', [ post_data[&quot;content&quot;] ]);
</span><span class="lines">@@ -350,3 +295,382 @@
</span><span class="cx">                 success: successCallback
</span><span class="cx">         });
</span><span class="cx"> }
</span><ins>+
+// Autosave in localStorage
+// set as simple object/mixin for now
+window.wp = window.wp || {};
+wp.autosave = wp.autosave || {};
+
+(function($){
+// Returns the data for saving in both localStorage and autosaves to the server
+wp.autosave.getPostData = function() {
+        var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [],
+                data = {
+                        action: 'autosave',
+                        autosave: true,
+                        post_id: $('#post_ID').val() || 0,
+                        autosavenonce: $('#autosavenonce').val() || '',
+                        post_type: $('#post_type').val() || '',
+                        post_author: $('#post_author').val() || '',
+                        excerpt: $('#excerpt').val() || ''
+                };
+
+        if ( ed &amp;&amp; !ed.isHidden() ) {
+                // Don't run while the tinymce spellcheck is on. It resets all found words.
+                if ( ed.plugins.spellchecker &amp;&amp; ed.plugins.spellchecker.active ) {
+                        data.autosave = false;
+                        return data;
+                } else {
+                        if ( 'mce_fullscreen' == ed.id )
+                                tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
+
+                        tinymce.triggerSave();
+                }
+        }
+
+        if ( typeof fullscreen != 'undefined' &amp;&amp; fullscreen.settings.visible ) {
+                data['post_title'] = $('#wp-fullscreen-title').val() || '';
+                data['content'] = $('#wp_mce_fullscreen').val() || '';
+        } else {
+                data['post_title'] = $('#title').val() || '';
+                data['content'] = $('#content').val() || '';
+        }
+
+        /*
+        // We haven't been saving tags with autosave since 2.8... Start again?
+        $('.the-tags').each( function() {
+                data[this.name] = this.value;
+        });
+        */
+
+        $('input[id^=&quot;in-category-&quot;]:checked').each( function() {
+                cats.push(this.value);
+        });
+        data['catslist'] = cats.join(',');
+
+        if ( post_name = $('#post_name').val() )
+                data['post_name'] = post_name;
+
+        if ( parent_id = $('#parent_id').val() )
+                data['parent_id'] = parent_id;
+
+        if ( $('#comment_status').prop('checked') )
+                data['comment_status'] = 'open';
+
+        if ( $('#ping_status').prop('checked') )
+                data['ping_status'] = 'open';
+
+        if ( $('#auto_draft').val() == '1' )
+                data['auto_draft'] = '1';
+
+        return data;
+}
+
+wp.autosave.local = {
+
+        lastsaveddata: '',
+        blog_id: 0,
+        ajaxurl: window.ajaxurl || 'wp-admin/admin-ajax.php',
+        hasStorage: false,
+
+        // Check if the browser supports sessionStorage and it's not disabled
+        checkStorage: function() {
+                var test = Math.random(), result = false;
+
+                try {
+                        sessionStorage.setItem('wp-test', test);
+                        result = sessionStorage.getItem('wp-test') == test;
+                        sessionStorage.removeItem('wp-test');
+                } catch(e) {}
+
+                this.hasStorage = result;
+                return result;
+    },
+
+        /**
+         * Initialize the local storage
+         *
+         * @return mixed False if no sessionStorage in the browser or an Object containing all post_data for this blog
+         */
+        getStorage: function() {
+                var stored_obj = false;
+                // Separate local storage containers for each blog_id
+                if ( this.hasStorage &amp;&amp; this.blog_id ) {
+                        stored_obj = sessionStorage.getItem( 'wp-autosave-' + this.blog_id );
+
+                        if ( stored_obj )
+                                stored_obj = JSON.parse( stored_obj );
+                        else
+                                stored_obj = {};
+                }
+
+                return stored_obj;
+        },
+
+        /**
+         * Set the storage for this blog
+         *
+         * Confirms that the data was saved successfully.
+         *
+         * @return bool
+         */
+        setStorage: function( stored_obj ) {
+                var key;
+
+                if ( this.hasStorage &amp;&amp; this.blog_id ) {
+                        key = 'wp-autosave-' + this.blog_id;
+                        sessionStorage.setItem( key, JSON.stringify( stored_obj ) );
+                        return sessionStorage.getItem( key ) !== null;
+                }
+
+                return false;
+        },
+
+        /**
+         * Get the saved post data for the current post
+         *
+         * @return mixed False if no storage or no data or the post_data as an Object
+         */
+        getData: function() {
+                var stored = this.getStorage(), post_id = $('#post_ID').val();
+
+                if ( !stored || !post_id )
+                        return false;
+
+                return stored[ 'post_' + post_id ] || false;
+        },
+
+        /**
+         * Set (save) post data in the storage
+         *
+         * @return bool
+         */
+        setData: function( stored_data ) {
+                var stored = this.getStorage(), post_id = $('#post_ID').val();
+
+                if ( !stored || !post_id )
+                        return false;
+
+                stored[ 'post_' + post_id ] = stored_data;
+
+                return this.setStorage(stored);
+        },
+
+        /**
+         * Save post data for the current post
+         *
+         * Runs on a 15 sec. schedule, saves when there are differences in the post title or content.
+         * When the optional data is provided, updates the last saved post data.
+         *
+         * $param data optional Object The post data for saving, minimum 'post_title' and 'content'
+         * @return bool
+         */
+        save: function( data ) {
+                var result = false;
+
+                if ( ! data ) {
+                        post_data = wp.autosave.getPostData();
+                } else {
+                        post_data = this.getData() || {};
+                        $.extend( post_data, data );
+                }
+
+                // If the content and title are empty or did not change since the last save, don't save again
+                if ( post_data.post_title + ': ' + post_data.content == this.lastsaveddata )
+                        return false;
+
+                // Cannot get the post data at the moment
+                if ( !post_data.autosave )
+                        return false;
+
+                post_data['save_time'] = (new Date()).getTime();
+                post_data['status'] = $('#post_status').val() || '';
+                result = this.setData( post_data );
+
+                if ( result )
+                        this.lastsaveddata = post_data.post_title + ': ' + post_data.content;
+
+                return result;
+        },
+
+        // Initialize and run checkPost() on loading the script (before TinyMCE init)
+        init: function( settings ) {
+                var self = this;
+
+                // Run only on the Add/Edit Post screens and in browsers that have sessionStorage
+                if ( 'post' != window.pagenow || ! this.checkStorage() )
+                        return;
+                // editor.js has to be loaded before autosave.js
+                if ( typeof switchEditors == 'undefined' )
+                        return;
+
+                if ( settings )
+                        $.extend( this, settings );
+
+                if ( !this.blog_id )
+                        this.blog_id = typeof window.autosaveL10n != 'undefined' ? window.autosaveL10n.blog_id : 0;
+
+                this.checkPost();
+                $(document).ready( self.run );
+        },
+
+        // Run on DOM ready
+        run: function() {
+                var self = this, post_data;
+
+                // Set the comparison string
+                if ( !this.lastsaveddata ) {
+                        post_data = wp.autosave.getPostData();
+
+                        if ( post_data.content &amp;&amp; $('#wp-content-wrap').hasClass('tmce-active') )
+                                this.lastsaveddata = post_data.post_title + ': ' + switchEditors.pre_wpautop( post_data.content );
+                        else
+                                this.lastsaveddata = post_data.post_title + ': ' + post_data.content;
+                }
+
+                // Set the schedule
+                this.schedule = $.schedule({
+                        time: 15 * 1000,
+                        func: function() { wp.autosave.local.save(); },
+                        repeat: true,
+                        protect: true
+                });
+
+                $('form#post').on('submit.autosave-local', function() {
+                        var editor = typeof tinymce != 'undefined' &amp;&amp; tinymce.get('content');
+
+                        if ( editor &amp;&amp; ! editor.isHidden() ) {
+                                // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea.
+                                editor.onSubmit.add( function() {
+                                        wp.autosave.local.save({
+                                                post_title: $('#title').val() || '',
+                                                content: $('#content').val() || '',
+                                                excerpt: $('#excerpt').val() || ''
+                                        });
+                                });
+                        } else {
+                                self.save({
+                                        post_title: $('#title').val() || '',
+                                        content: $('#content').val() || '',
+                                        excerpt: $('#excerpt').val() || ''
+                                });
+                        }
+                });
+        },
+
+        // Strip whitespace and compare two strings
+        compare: function( str1, str2, strip_tags ) {
+                function remove( string, strip_tags ) {
+                        string = string.toString();
+
+                        if ( strip_tags )
+                                string = string.replace(/&lt;[^&lt;&gt;]+&gt;/g, '');
+
+                        return string.replace(/[\x20\t\r\n\f]+/g, '');
+                }
+
+                return ( remove( str1 || '', strip_tags ) == remove( str2 || '', strip_tags ) );
+        },
+
+        /**
+         * Check if the saved data for the current post (if any) is different than the loaded post data on the screen
+         *
+         * Shows a standard message letting the user restore the post data if different.
+         *
+         * @return void
+         */
+        checkPost: function() {
+                var self = this, post_data = this.getData(), content, check_data, strip_tags = false, notice;
+
+                if ( ! post_data )
+                        return;
+
+                // There is a newer autosave. Don't show two &quot;restore&quot; notices at the same time.
+                if ( $('#has-newer-autosave').length )
+                        return;
+
+                content = $('#content').val();
+                check_data = $.extend( {}, post_data );
+
+                if ( $('#wp-content-wrap').hasClass('tmce-active') )
+                        content = switchEditors.pre_wpautop( content );
+
+                // The post has just been published, only compare text
+                if ( $('#post_status').val() == 'publish' &amp;&amp; check_data.status != 'publish' )
+                        strip_tags = true;
+
+                if ( this.compare( content, check_data.content, strip_tags ) &amp;&amp; this.compare( $('#title').val(), check_data.post_title, strip_tags ) &amp;&amp; this.compare( $('#excerpt').val(), check_data.excerpt, strip_tags ) )
+                        return;
+
+                // We have three choices here:
+                // - Do an autosave and then show the standard notice &quot;There is an autosave newer than...&quot;.
+                // - Offer to load/restore the backed up post data.
+                // - Restore the post_data without asking, then show a notice with an Undo link/button.
+                // Doing an autosave will take few seconds and may take up to 30 and fail if network connectivity is bad
+                // Restoring the post will leave the user with the proper content, but it won't be saved to the server until the next autosave.
+
+                this.restore_post_data = post_data;
+                this.undo_post_data = wp.autosave.getPostData();
+
+                /*
+                if ( $('#post_status').val() == 'publish' ) {
+                        // Different message when a post is published?
+                        // Comparing the current and saved post data may fail (false positive) when the post is published
+                        // as in some cases there are changes to post_content on publishing and updating before saving to the DB.
+                }
+                */
+
+                notice = $('#local-storage-notice');
+                $('form#post').before( notice.addClass('updated').show() );
+
+                notice.on( 'click', function(e) {
+                        var target = $( e.target );
+
+                        if ( target.hasClass('restore-backup') ) {
+                                self.restorePost( self.restore_post_data );
+                                target.parent().hide();
+                                $(this).find('p.undo-restore').show();
+                        } else if ( target.hasClass('undo-restore-backup') ) {
+                                self.restorePost( self.undo_post_data );
+                                target.parent().hide();
+                                $(this).find('p.local-restore').show();
+                        }
+
+                        e.preventDefault();
+                });
+        },
+
+        // Restore the current title, content and excerpt from post_data.
+        restorePost: function( post_data ) {
+                var editor;
+
+                if ( post_data ) {
+                        // Set the last saved data
+                        this.lastsaveddata = post_data.post_title + ': ' + post_data.content;
+
+                        if ( $('#title').val() != post_data.post_title )
+                                $('#title').focus().val( post_data.post_title || '' );
+
+                        $('#excerpt').val( post_data.excerpt || '' );
+                        editor = typeof tinymce != 'undefined' &amp;&amp; tinymce.get('content');
+
+                        if ( editor &amp;&amp; ! editor.isHidden() ) {
+                                // Make sure there's an undo level in the editor
+                                editor.undoManager.add();
+                                editor.setContent( post_data.content ? switchEditors.wpautop( post_data.content ) : '' );
+                        } else {
+                                // Make sure the Text editor is selected
+                                $('#content-html').click();
+                                $('#content').val( post_data.content );
+                        }
+
+                        return true;
+                }
+
+                return false;
+        }
+}
+
+wp.autosave.local.init();
+
+}(jQuery));
</ins></span></pre></div>
<a id="trunkwpincludesscriptloaderphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/script-loader.php (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/script-loader.php        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-includes/script-loader.php        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -106,7 +106,7 @@
</span><span class="cx">                 'dismiss' =&gt; __('Dismiss'),
</span><span class="cx">         ) );
</span><span class="cx"> 
</span><del>-        $scripts-&gt;add( 'autosave', &quot;/wp-includes/js/autosave$suffix.js&quot;, array('schedule', 'wp-ajax-response'), false, 1 );
</del><ins>+        $scripts-&gt;add( 'autosave', &quot;/wp-includes/js/autosave$suffix.js&quot;, array('schedule', 'wp-ajax-response', 'editor'), false, 1 );
</ins><span class="cx"> 
</span><span class="cx">         $scripts-&gt;add( 'heartbeat', &quot;/wp-includes/js/heartbeat$suffix.js&quot;, array('jquery'), false, 1 );
</span><span class="cx">         did_action( 'init' ) &amp;&amp; $scripts-&gt;localize( 'heartbeat', 'heartbeatSettings',
</span><span class="lines">@@ -585,7 +585,8 @@
</span><span class="cx">         wp_localize_script( 'autosave', 'autosaveL10n', array(
</span><span class="cx">                 'autosaveInterval' =&gt; AUTOSAVE_INTERVAL,
</span><span class="cx">                 'savingText' =&gt; __('Saving Draft&amp;#8230;'),
</span><del>-                'saveAlert' =&gt; __('The changes you made will be lost if you navigate away from this page.')
</del><ins>+                'saveAlert' =&gt; __('The changes you made will be lost if you navigate away from this page.'),
+                'blog_id' =&gt; get_current_blog_id(),
</ins><span class="cx">         ) );
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkwploginphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-login.php (23682 => 23683)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-login.php        2013-03-13 00:58:39 UTC (rev 23682)
+++ trunk/wp-login.php        2013-03-13 10:08:16 UTC (rev 23683)
</span><span class="lines">@@ -68,6 +68,15 @@
</span><span class="cx">                 &lt;meta name=&quot;viewport&quot; content=&quot;width=320; initial-scale=0.9; maximum-scale=1.0; user-scalable=0;&quot; /&gt;&lt;?php
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        // Remove all stored post data on logging out.
+        // This could be added by add_action('login_head'...) like wp_shake_js()
+        // but maybe better if it's not removable by plugins
+        if ( 'loggedout' == $wp_error-&gt;get_error_code() ) {
+                ?&gt;
+                &lt;script&gt;if(&quot;sessionStorage&quot; in window){try{for(var key in sessionStorage){if(key.indexOf(&quot;wp-autosave-&quot;)!=-1){sessionStorage.removeItem(key)}}}catch(e){}};&lt;/script&gt;
+                &lt;?php
+        }
+
</ins><span class="cx">         do_action( 'login_enqueue_scripts' );
</span><span class="cx">         do_action( 'login_head' );
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>