--- a/wp/wp-includes/js/autosave.js Tue Jun 09 11:14:17 2015 +0000
+++ b/wp/wp-includes/js/autosave.js Mon Oct 14 17:39:30 2019 +0200
@@ -4,26 +4,55 @@
return true;
};
+/**
+ * @summary Adds autosave to the window object on dom ready.
+ *
+ * @since 3.9.0
+ *
+ * @param {jQuery} $ jQuery object.
+ * @param {window} The window object.
+ *
+ */
( function( $, window ) {
+ /**
+ * @summary Auto saves the post.
+ *
+ * @since 3.9.0
+ *
+ * @returns {Object}
+ * {{
+ * getPostData: getPostData,
+ * getCompareString: getCompareString,
+ * disableButtons: disableButtons,
+ * enableButtons: enableButtons,
+ * local: ({hasStorage, getSavedPostData, save, suspend, resume}|*),
+ * server: ({tempBlockSave, triggerSave, postChanged, suspend, resume}|*)}
+ * }
+ * The object with all functions for autosave.
+ */
function autosave() {
var initialCompareString,
- lastTriggerSave = 0,
- $document = $(document);
+ lastTriggerSave = 0,
+ $document = $(document);
/**
- * Returns the data saved in both local and remote autosave
+ * @summary Returns the data saved in both local and remote autosave.
+ *
+ * @since 3.9.0
*
- * @return object Object containing the post data
+ * @param {string} type The type of autosave either local or remote.
+ *
+ * @returns {Object} Object containing the post data.
*/
function getPostData( type ) {
var post_name, parent_id, data,
time = ( new Date() ).getTime(),
cats = [],
- editor = typeof tinymce !== 'undefined' && tinymce.get('content');
+ editor = getEditor();
- // Don't run editor.save() more often than every 3 sec.
+ // Don't run editor.save() more often than every 3 seconds.
// It is resource intensive and might slow down typing in long posts on slow devices.
- if ( editor && ! editor.isHidden() && time - 3000 > lastTriggerSave ) {
+ if ( editor && editor.isDirty() && ! editor.isHidden() && time - 3000 > lastTriggerSave ) {
editor.save();
lastTriggerSave = time;
}
@@ -69,7 +98,17 @@
return data;
}
- // Concatenate title, content and excerpt. Used to track changes when auto-saving.
+ /**
+ * @summary Concatenates the title, content and excerpt.
+ *
+ * This is used to track changes when auto-saving.
+ *
+ * @since 3.9.0
+ *
+ * @param {Object} postData The object containing the post data.
+ *
+ * @returns {string} A concatenated string with title, content and excerpt.
+ */
function getCompareString( postData ) {
if ( typeof postData === 'object' ) {
return ( postData.post_title || '' ) + '::' + ( postData.content || '' ) + '::' + ( postData.excerpt || '' );
@@ -78,23 +117,71 @@
return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' );
}
+ /**
+ * @summary Disables save buttons.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function disableButtons() {
$document.trigger('autosave-disable-buttons');
+
// Re-enable 5 sec later. Just gives autosave a head start to avoid collisions.
setTimeout( enableButtons, 5000 );
}
+ /**
+ * @summary Enables save buttons.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function enableButtons() {
$document.trigger( 'autosave-enable-buttons' );
}
- // Autosave in localStorage
+ /**
+ * @summary Gets the content editor.
+ *
+ * @since 4.6.0
+ *
+ * @returns {boolean|*} Returns either false if the editor is undefined,
+ * or the instance of the content editor.
+ */
+ function getEditor() {
+ return typeof tinymce !== 'undefined' && tinymce.get('content');
+ }
+
+ /**
+ * @summary Autosave in localStorage.
+ *
+ * @since 3.9.0
+ *
+ * @returns {
+ * {
+ * hasStorage: *,
+ * getSavedPostData: getSavedPostData,
+ * save: save,
+ * suspend: suspend,
+ * resume: resume
+ * }
+ * }
+ * The object with all functions for local storage autosave.
+ */
function autosaveLocal() {
- var restorePostData, undoPostData, blog_id, post_id, hasStorage, intervalTimer,
+ var blog_id, post_id, hasStorage, intervalTimer,
lastCompareString,
isSuspended = false;
- // Check if the browser supports sessionStorage and it's not disabled
+ /**
+ * @summary Checks if the browser supports sessionStorage and it's not disabled.
+ *
+ * @since 3.9.0
+ *
+ * @returns {boolean} True if the sessionStorage is supported and enabled.
+ */
function checkStorage() {
var test = Math.random().toString(),
result = false;
@@ -110,9 +197,12 @@
}
/**
- * Initialize the local storage
+ * @summary Initializes the local storage.
*
- * @return mixed False if no sessionStorage in the browser or an Object containing all postData for this blog
+ * @since 3.9.0
+ *
+ * @returns {boolean|Object} False if no sessionStorage in the browser or an Object
+ * containing all postData for this blog.
*/
function getStorage() {
var stored_obj = false;
@@ -131,11 +221,13 @@
}
/**
- * Set the storage for this blog
+ * @summary Sets the storage for this blog.
*
* Confirms that the data was saved successfully.
*
- * @return bool
+ * @since 3.9.0
+ *
+ * @returns {boolean} True if the data was saved successfully, false if it wasn't saved.
*/
function setStorage( stored_obj ) {
var key;
@@ -150,9 +242,11 @@
}
/**
- * Get the saved post data for the current post
+ * @summary Gets the saved post data for the current post.
*
- * @return mixed False if no storage or no data or the postData as an Object
+ * @since 3.9.0
+ *
+ * @returns {boolean|Object} False if no storage or no data or the postData as an Object.
*/
function getSavedPostData() {
var stored = getStorage();
@@ -165,12 +259,15 @@
}
/**
- * Set (save or delete) post data in the storage.
+ * @summary Sets (save or delete) post data in the storage.
+ *
+ * If stored_data evaluates to 'false' the storage key for the current post will be removed.
*
- * If stored_data evaluates to 'false' the storage key for the current post will be removed
+ * @since 3.9.0
*
- * $param stored_data The post data to store or null/false/empty to delete the key
- * @return bool
+ * @param {Object|boolean|null} stored_data The post data to store or null/false/empty to delete the key.
+ *
+ * @returns {boolean} True if data is stored, false if data was removed.
*/
function setData( stored_data ) {
var stored = getStorage();
@@ -190,22 +287,39 @@
return setStorage( stored );
}
+ /**
+ * @summary Sets isSuspended to true.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function suspend() {
isSuspended = true;
}
+ /**
+ * @summary Sets isSuspended to false.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function resume() {
isSuspended = false;
}
/**
- * Save post data for the current post
+ * @summary Saves post data for the current post.
*
* Runs on a 15 sec. interval, 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
+ * @since 3.9.0
+ *
+ * @param {Object} data The post data for saving, minimum 'post_title' and 'content'.
+ *
+ * @returns {boolean} Returns true when data has been saved, otherwise it returns false.
*/
function save( data ) {
var postData, compareString,
@@ -228,7 +342,7 @@
lastCompareString = initialCompareString;
}
- // If the content, title and excerpt did not change since the last save, don't save again
+ // If the content, title and excerpt did not change since the last save, don't save again.
if ( compareString === lastCompareString ) {
return false;
}
@@ -244,12 +358,24 @@
return result;
}
- // Run on DOM ready
+ /**
+ * @summary Initializes the auto save function.
+ *
+ * Checks whether the editor is active or not to use the editor events
+ * to autosave, or uses the values from the elements to autosave.
+ *
+ * Runs on DOM ready.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function run() {
post_id = $('#post_ID').val() || 0;
// Check if the local post data is different than the loaded post data.
if ( $( '#wp-content-wrap' ).hasClass( 'tmce-active' ) ) {
+
// If TinyMCE loads first, check the post 1.5 sec. after it is ready.
// By this time the content has been loaded in the editor and 'saved' to the textarea.
// This prevents false positives.
@@ -266,10 +392,11 @@
intervalTimer = window.setInterval( save, 15000 );
$( 'form#post' ).on( 'submit.autosave-local', function() {
- var editor = typeof tinymce !== 'undefined' && tinymce.get('content'),
+ var editor = getEditor(),
post_id = $('#post_ID').val() || 0;
if ( editor && ! editor.isHidden() ) {
+
// Last onSubmit event in the editor, needs to run after the content has been moved to the textarea.
editor.on( 'submit', function() {
save({
@@ -286,11 +413,22 @@
});
}
- wpCookies.set( 'wp-saving-post', post_id + '-check', 24 * 60 * 60 );
+ var secure = ( 'https:' === window.location.protocol );
+ wpCookies.set( 'wp-saving-post', post_id + '-check', 24 * 60 * 60, false, false, secure );
});
}
- // Strip whitespace and compare two strings
+ /**
+ * @summary Compares 2 strings.
+ *
+ * Removes whitespaces in the strings before comparing them.
+ *
+ * @since 3.9.0
+ *
+ * @param {string} str1 The first string.
+ * @param {string} str2 The second string.
+ * @returns {boolean} True if the strings are the same.
+ */
function compare( str1, str2 ) {
function removeSpaces( string ) {
return string.toString().replace(/[\x20\t\r\n\f]+/g, '');
@@ -300,16 +438,21 @@
}
/**
- * Check if the saved data for the current post (if any) is different than the loaded post data on the screen
+ * @summary Checks 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
+ * @since 3.9.0
+ *
+ * @returns {void}
*/
function checkPost() {
var content, post_title, excerpt, $notice,
postData = getSavedPostData(),
- cookie = wpCookies.get( 'wp-saving-post' );
+ cookie = wpCookies.get( 'wp-saving-post' ),
+ $newerAutosaveNotice = $( '#has-newer-autosave' ).parent( '.notice' ),
+ $headerEnd = $( '.wp-header-end' );
if ( cookie === post_id + '-saved' ) {
wpCookies.remove( 'wp-saving-post' );
@@ -322,11 +465,6 @@
return;
}
- // There is a newer autosave. Don't show two "restore" notices at the same time.
- if ( $( '#has-newer-autosave' ).length ) {
- return;
- }
-
content = $( '#content' ).val() || '';
post_title = $( '#title' ).val() || '';
excerpt = $( '#excerpt' ).val() || '';
@@ -337,36 +475,46 @@
return;
}
- restorePostData = postData;
- undoPostData = {
- content: content,
- post_title: post_title,
- excerpt: excerpt
- };
+ /*
+ * If '.wp-header-end' is found, append the notices after it otherwise
+ * after the first h1 or h2 heading found within the main content.
+ */
+ if ( ! $headerEnd.length ) {
+ $headerEnd = $( '.wrap h1, .wrap h2' ).first();
+ }
- $notice = $( '#local-storage-notice' );
- $('.wrap h2').first().after( $notice.addClass( 'notice-warning' ).show() );
+ $notice = $( '#local-storage-notice' )
+ .insertAfter( $headerEnd )
+ .addClass( 'notice-warning' );
- $notice.on( 'click.autosave-local', function( event ) {
- var $target = $( event.target );
+ if ( $newerAutosaveNotice.length ) {
- if ( $target.hasClass( 'restore-backup' ) ) {
- restorePost( restorePostData );
- $target.parent().hide();
- $(this).find( 'p.undo-restore' ).show();
- $notice.removeClass( 'notice-warning' ).addClass( 'notice-success' );
- } else if ( $target.hasClass( 'undo-restore-backup' ) ) {
- restorePost( undoPostData );
- $target.parent().hide();
- $(this).find( 'p.local-restore' ).show();
- $notice.removeClass( 'notice-success' ).addClass( 'notice-warning' );
- }
+ // If there is a "server" autosave notice, hide it.
+ // The data in the session storage is either the same or newer.
+ $newerAutosaveNotice.slideUp( 150, function() {
+ $notice.slideDown( 150 );
+ });
+ } else {
+ $notice.slideDown( 200 );
+ }
- event.preventDefault();
+ $notice.find( '.restore-backup' ).on( 'click.autosave-local', function() {
+ restorePost( postData );
+ $notice.fadeTo( 250, 0, function() {
+ $notice.slideUp( 150 );
+ });
});
}
- // Restore the current title, content and excerpt from postData.
+ /**
+ * @summary Restores the current title, content and excerpt from postData.
+ *
+ * @since 3.9.0
+ *
+ * @param {Object} postData The object containing all post data.
+ *
+ * @returns {boolean} True if the post is restored.
+ */
function restorePost( postData ) {
var editor;
@@ -379,16 +527,27 @@
}
$( '#excerpt' ).val( postData.excerpt || '' );
- editor = typeof tinymce !== 'undefined' && tinymce.get('content');
+ editor = getEditor();
if ( editor && ! editor.isHidden() && typeof switchEditors !== 'undefined' ) {
+ if ( editor.settings.wpautop && postData.content ) {
+ postData.content = switchEditors.wpautop( postData.content );
+ }
+
// Make sure there's an undo level in the editor
- editor.undoManager.add();
- editor.setContent( postData.content ? switchEditors.wpautop( postData.content ) : '' );
+ editor.undoManager.transact( function() {
+ editor.setContent( postData.content || '' );
+ editor.nodeChanged();
+ });
} else {
+
// Make sure the Text editor is selected
$( '#content-html' ).click();
- $( '#content' ).val( postData.content );
+ $( '#content' ).focus();
+
+ // Using document.execCommand() will let the user undo.
+ document.execCommand( 'selectAll' );
+ document.execCommand( 'insertText', false, postData.content || '' );
}
return true;
@@ -415,13 +574,34 @@
};
}
- // Autosave on the server
+ /**
+ * @summary Auto saves the post on the server.
+ *
+ * @since 3.9.0
+ *
+ * @returns {Object} {
+ * {
+ * tempBlockSave: tempBlockSave,
+ * triggerSave: triggerSave,
+ * postChanged: postChanged,
+ * suspend: suspend,
+ * resume: resume
+ * }
+ * } The object all functions for autosave.
+ */
function autosaveServer() {
var _blockSave, _blockSaveTimer, previousCompareString, lastCompareString,
nextRun = 0,
isSuspended = false;
- // Block saving for the next 10 sec.
+
+ /**
+ * @summary Blocks saving for the next 10 seconds.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function tempBlockSave() {
_blockSave = true;
window.clearTimeout( _blockSaveTimer );
@@ -431,15 +611,37 @@
}, 10000 );
}
+ /**
+ * @summary Sets isSuspended to true.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function suspend() {
isSuspended = true;
}
+ /**
+ * @summary Sets isSuspended to false.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function resume() {
isSuspended = false;
}
- // Runs on heartbeat-response
+ /**
+ * @summary Triggers the autosave with the post data.
+ *
+ * @since 3.9.0
+ *
+ * @param {Object} data The post data.
+ *
+ * @returns {void}
+ */
function response( data ) {
_schedule();
_blockSave = false;
@@ -456,11 +658,13 @@
}
/**
- * Save immediately
+ * @summary Saves immediately.
+ *
+ * Resets the timing and tells heartbeat to connect now.
*
- * Resets the timing and tells heartbeat to connect now
+ * @since 3.9.0
*
- * @return void
+ * @returns {void}
*/
function triggerSave() {
nextRun = 0;
@@ -468,18 +672,29 @@
}
/**
- * Checks if the post content in the textarea has changed since page load.
+ * @summary Checks if the post content in the textarea has changed since page load.
*
* This also happens when TinyMCE is active and editor.save() is triggered by
* wp.autosave.getPostData().
*
- * @return bool
+ * @since 3.9.0
+ *
+ * @return {boolean} True if the post has been changed.
*/
function postChanged() {
return getCompareString() !== initialCompareString;
}
- // Runs on 'heartbeat-send'
+ /**
+ * @summary Checks if the post can be saved or not.
+ *
+ * If the post hasn't changed or it cannot be updated,
+ * because the autosave is blocked or suspended, the function returns false.
+ *
+ * @since 3.9.0
+ *
+ * @returns {Object} Returns the post data.
+ */
function save() {
var postData, compareString;
@@ -517,21 +732,54 @@
return postData;
}
+ /**
+ * @summary Sets the next run, based on the autosave interval.
+ *
+ * @private
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
function _schedule() {
nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000;
}
+ /**
+ * @summary Sets the autosaveData on the autosave heartbeat.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
$document.on( 'heartbeat-send.autosave', function( event, data ) {
var autosaveData = save();
if ( autosaveData ) {
data.wp_autosave = autosaveData;
}
+
+ /**
+ * @summary Triggers the autosave of the post with the autosave data
+ * on the autosave heartbeat.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
}).on( 'heartbeat-tick.autosave', function( event, data ) {
if ( data.wp_autosave ) {
response( data.wp_autosave );
}
+ /**
+ * @summary Disables buttons and throws a notice when the connection is lost.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
}).on( 'heartbeat-connection-lost.autosave', function( event, error, status ) {
+
// When connection is lost, keep user from submitting changes.
if ( 'timeout' === error || 603 === status ) {
var $notice = $('#lost-connection-notice');
@@ -543,6 +791,14 @@
$notice.show();
disableButtons();
}
+
+ /**
+ * @summary Enables buttons when the connection is restored.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
}).on( 'heartbeat-connection-restored.autosave', function() {
$('#lost-connection-notice').hide();
enableButtons();
@@ -559,10 +815,18 @@
};
}
- // Wait for TinyMCE to initialize plus 1 sec. for any external css to finish loading,
- // then 'save' to the textarea before setting initialCompareString.
- // This avoids any insignificant differences between the initial textarea content and the content
- // extracted from the editor.
+ /**
+ * @summary Sets the autosave time out.
+ *
+ * Wait for TinyMCE to initialize plus 1 second. for any external css to finish loading,
+ * then save to the textarea before setting initialCompareString.
+ * This avoids any insignificant differences between the initial textarea content and the content
+ * extracted from the editor.
+ *
+ * @since 3.9.0
+ *
+ * @returns {void}
+ */
$document.on( 'tinymce-editor-init.autosave', function( event, editor ) {
if ( editor.id === 'content' ) {
window.setTimeout( function() {
@@ -571,6 +835,7 @@
}, 1000 );
}
}).ready( function() {
+
// Set the initial compare string in case TinyMCE is not used or not loaded first
initialCompareString = getCompareString();
});
@@ -585,6 +850,7 @@
};
}
+ /** @namespace wp */
window.wp = window.wp || {};
window.wp.autosave = autosave();