diff --git a/wp-includes/js/autosave.js b/wp-includes/js/autosave.js index e88943d6d4..c0851ecd37 100644 --- a/wp-includes/js/autosave.js +++ b/wp-includes/js/autosave.js @@ -4,16 +4,45 @@ window.autosave = function() { 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); /** - * Returns the data saved in both local and remote autosave + * @summary Returns the data saved in both local and remote autosave. * - * @return object Object containing the post data + * @since 3.9.0 + * + * @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, @@ -21,7 +50,7 @@ window.autosave = function() { cats = [], 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.isDirty() && ! editor.isHidden() && time - 3000 > lastTriggerSave ) { editor.save(); @@ -69,7 +98,17 @@ window.autosave = function() { 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,27 +117,71 @@ window.autosave = function() { 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' ); } + /** + * @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'); } - // Autosave in localStorage + /** + * @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 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; @@ -114,9 +197,12 @@ window.autosave = function() { } /** - * 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; @@ -135,11 +221,13 @@ window.autosave = function() { } /** - * 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; @@ -154,9 +242,11 @@ window.autosave = function() { } /** - * 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(); @@ -169,12 +259,15 @@ window.autosave = function() { } /** - * 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. * - * $param stored_data The post data to store or null/false/empty to delete the key - * @return bool + * @since 3.9.0 + * + * @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(); @@ -194,22 +287,39 @@ window.autosave = function() { 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, @@ -232,7 +342,7 @@ window.autosave = function() { 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; } @@ -248,12 +358,24 @@ window.autosave = function() { 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. @@ -274,6 +396,7 @@ window.autosave = function() { 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({ @@ -295,7 +418,17 @@ window.autosave = function() { }); } - // 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, ''); @@ -305,11 +438,14 @@ window.autosave = function() { } /** - * 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, @@ -352,6 +488,7 @@ window.autosave = function() { .addClass( 'notice-warning' ); if ( $newerAutosaveNotice.length ) { + // 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() { @@ -369,7 +506,15 @@ window.autosave = function() { }); } - // 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; @@ -395,9 +540,11 @@ window.autosave = function() { editor.nodeChanged(); }); } else { + // Make sure the Text editor is selected $( '#content-html' ).click(); $( '#content' ).focus(); + // Using document.execCommand() will let the user undo. document.execCommand( 'selectAll' ); document.execCommand( 'insertText', false, postData.content || '' ); @@ -427,13 +574,34 @@ window.autosave = function() { }; } - // 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 ); @@ -443,15 +611,37 @@ window.autosave = function() { }, 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; @@ -468,11 +658,13 @@ window.autosave = function() { } /** - * Save immediately + * @summary Saves immediately. * - * Resets the timing and tells heartbeat to connect now + * Resets the timing and tells heartbeat to connect now. * - * @return void + * @since 3.9.0 + * + * @returns {void} */ function triggerSave() { nextRun = 0; @@ -480,18 +672,29 @@ window.autosave = function() { } /** - * 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; @@ -529,21 +732,54 @@ window.autosave = function() { 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'); @@ -555,6 +791,14 @@ window.autosave = function() { $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(); @@ -571,10 +815,18 @@ window.autosave = function() { }; } - // 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() { @@ -583,6 +835,7 @@ window.autosave = function() { }, 1000 ); } }).ready( function() { + // Set the initial compare string in case TinyMCE is not used or not loaded first initialCompareString = getCompareString(); }); diff --git a/wp-includes/version.php b/wp-includes/version.php index 1ce697ed92..6936820ffe 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '4.9-alpha-41264'; +$wp_version = '4.9-alpha-41265'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.