home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-includes / js / autosave.js < prev    next >
Encoding:
JavaScript  |  2017-09-08  |  21.1 KB  |  858 lines

  1. /* global tinymce, wpCookies, autosaveL10n, switchEditors */
  2. // Back-compat
  3. window.autosave = function() {
  4.     return true;
  5. };
  6.  
  7. /**
  8.  * @summary Adds autosave to the window object on dom ready.
  9.  *
  10.  * @since 3.9.0
  11.  *
  12.  * @param {jQuery} $ jQuery object.
  13.  * @param {window} The window object.
  14.  *
  15.  */
  16. ( function( $, window ) {
  17.     /**
  18.      * @summary Auto saves the post.
  19.      *
  20.      * @since 3.9.0
  21.      *
  22.      * @returns {Object}
  23.      *     {{
  24.      *         getPostData: getPostData,
  25.      *         getCompareString: getCompareString,
  26.      *         disableButtons: disableButtons,
  27.      *         enableButtons: enableButtons,
  28.      *         local: ({hasStorage, getSavedPostData, save, suspend, resume}|*),
  29.      *         server: ({tempBlockSave, triggerSave, postChanged, suspend, resume}|*)}
  30.      *     }
  31.      *     The object with all functions for autosave.
  32.      */
  33.     function autosave() {
  34.         var initialCompareString,
  35.             lastTriggerSave = 0,
  36.             $document = $(document);
  37.  
  38.         /**
  39.          * @summary Returns the data saved in both local and remote autosave.
  40.          *
  41.          * @since 3.9.0
  42.          *
  43.          * @param {string} type The type of autosave either local or remote.
  44.          *
  45.          * @returns {Object} Object containing the post data.
  46.          */
  47.         function getPostData( type ) {
  48.             var post_name, parent_id, data,
  49.                 time = ( new Date() ).getTime(),
  50.                 cats = [],
  51.                 editor = getEditor();
  52.  
  53.             // Don't run editor.save() more often than every 3 seconds.
  54.             // It is resource intensive and might slow down typing in long posts on slow devices.
  55.             if ( editor && editor.isDirty() && ! editor.isHidden() && time - 3000 > lastTriggerSave ) {
  56.                 editor.save();
  57.                 lastTriggerSave = time;
  58.             }
  59.  
  60.             data = {
  61.                 post_id: $( '#post_ID' ).val() || 0,
  62.                 post_type: $( '#post_type' ).val() || '',
  63.                 post_author: $( '#post_author' ).val() || '',
  64.                 post_title: $( '#title' ).val() || '',
  65.                 content: $( '#content' ).val() || '',
  66.                 excerpt: $( '#excerpt' ).val() || ''
  67.             };
  68.  
  69.             if ( type === 'local' ) {
  70.                 return data;
  71.             }
  72.  
  73.             $( 'input[id^="in-category-"]:checked' ).each( function() {
  74.                 cats.push( this.value );
  75.             });
  76.             data.catslist = cats.join(',');
  77.  
  78.             if ( post_name = $( '#post_name' ).val() ) {
  79.                 data.post_name = post_name;
  80.             }
  81.  
  82.             if ( parent_id = $( '#parent_id' ).val() ) {
  83.                 data.parent_id = parent_id;
  84.             }
  85.  
  86.             if ( $( '#comment_status' ).prop( 'checked' ) ) {
  87.                 data.comment_status = 'open';
  88.             }
  89.  
  90.             if ( $( '#ping_status' ).prop( 'checked' ) ) {
  91.                 data.ping_status = 'open';
  92.             }
  93.  
  94.             if ( $( '#auto_draft' ).val() === '1' ) {
  95.                 data.auto_draft = '1';
  96.             }
  97.  
  98.             return data;
  99.         }
  100.  
  101.         /**
  102.          * @summary Concatenates the title, content and excerpt.
  103.          *
  104.          * This is used to track changes when auto-saving.
  105.          *
  106.          * @since 3.9.0
  107.          *
  108.          * @param {Object} postData The object containing the post data.
  109.          *
  110.          * @returns {string} A concatenated string with title, content and excerpt.
  111.          */
  112.         function getCompareString( postData ) {
  113.             if ( typeof postData === 'object' ) {
  114.                 return ( postData.post_title || '' ) + '::' + ( postData.content || '' ) + '::' + ( postData.excerpt || '' );
  115.             }
  116.  
  117.             return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' );
  118.         }
  119.  
  120.         /**
  121.          * @summary Disables save buttons.
  122.          *
  123.          * @since 3.9.0
  124.          *
  125.          * @returns {void}
  126.          */
  127.         function disableButtons() {
  128.             $document.trigger('autosave-disable-buttons');
  129.  
  130.             // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions.
  131.             setTimeout( enableButtons, 5000 );
  132.         }
  133.  
  134.         /**
  135.          * @summary Enables save buttons.
  136.          *
  137.          * @since 3.9.0
  138.          *
  139.          * @returns {void}
  140.          */
  141.         function enableButtons() {
  142.             $document.trigger( 'autosave-enable-buttons' );
  143.         }
  144.  
  145.         /**
  146.          * @summary Gets the content editor.
  147.          *
  148.          * @since 4.6.0
  149.          *
  150.          * @returns {boolean|*} Returns either false if the editor is undefined,
  151.          *                         or the instance of the content editor.
  152.          */
  153.         function getEditor() {
  154.             return typeof tinymce !== 'undefined' && tinymce.get('content');
  155.         }
  156.  
  157.         /**
  158.          * @summary Autosave in localStorage.
  159.          *
  160.          * @since 3.9.0
  161.          *
  162.          * @returns {
  163.          * {
  164.          *     hasStorage: *,
  165.          *     getSavedPostData: getSavedPostData,
  166.          *     save: save,
  167.          *     suspend: suspend,
  168.          *     resume: resume
  169.          *     }
  170.          * }
  171.          * The object with all functions for local storage autosave.
  172.          */
  173.         function autosaveLocal() {
  174.             var blog_id, post_id, hasStorage, intervalTimer,
  175.                 lastCompareString,
  176.                 isSuspended = false;
  177.  
  178.             /**
  179.              * @summary Checks if the browser supports sessionStorage and it's not disabled.
  180.              *
  181.              * @since 3.9.0
  182.              *
  183.              * @returns {boolean} True if the sessionStorage is supported and enabled.
  184.              */
  185.             function checkStorage() {
  186.                 var test = Math.random().toString(),
  187.                     result = false;
  188.  
  189.                 try {
  190.                     window.sessionStorage.setItem( 'wp-test', test );
  191.                     result = window.sessionStorage.getItem( 'wp-test' ) === test;
  192.                     window.sessionStorage.removeItem( 'wp-test' );
  193.                 } catch(e) {}
  194.  
  195.                 hasStorage = result;
  196.                 return result;
  197.             }
  198.  
  199.             /**
  200.              * @summary Initializes the local storage.
  201.              *
  202.              * @since 3.9.0
  203.              *
  204.              * @returns {boolean|Object} False if no sessionStorage in the browser or an Object
  205.              *                           containing all postData for this blog.
  206.              */
  207.             function getStorage() {
  208.                 var stored_obj = false;
  209.                 // Separate local storage containers for each blog_id
  210.                 if ( hasStorage && blog_id ) {
  211.                     stored_obj = sessionStorage.getItem( 'wp-autosave-' + blog_id );
  212.  
  213.                     if ( stored_obj ) {
  214.                         stored_obj = JSON.parse( stored_obj );
  215.                     } else {
  216.                         stored_obj = {};
  217.                     }
  218.                 }
  219.  
  220.                 return stored_obj;
  221.             }
  222.  
  223.             /**
  224.              * @summary Sets the storage for this blog.
  225.              *
  226.              * Confirms that the data was saved successfully.
  227.              *
  228.              * @since 3.9.0
  229.              *
  230.              * @returns {boolean} True if the data was saved successfully, false if it wasn't saved.
  231.              */
  232.             function setStorage( stored_obj ) {
  233.                 var key;
  234.  
  235.                 if ( hasStorage && blog_id ) {
  236.                     key = 'wp-autosave-' + blog_id;
  237.                     sessionStorage.setItem( key, JSON.stringify( stored_obj ) );
  238.                     return sessionStorage.getItem( key ) !== null;
  239.                 }
  240.  
  241.                 return false;
  242.             }
  243.  
  244.             /**
  245.              * @summary Gets the saved post data for the current post.
  246.              *
  247.              * @since 3.9.0
  248.              *
  249.              * @returns {boolean|Object} False if no storage or no data or the postData as an Object.
  250.              */
  251.             function getSavedPostData() {
  252.                 var stored = getStorage();
  253.  
  254.                 if ( ! stored || ! post_id ) {
  255.                     return false;
  256.                 }
  257.  
  258.                 return stored[ 'post_' + post_id ] || false;
  259.             }
  260.  
  261.             /**
  262.              * @summary Sets (save or delete) post data in the storage.
  263.              *
  264.              * If stored_data evaluates to 'false' the storage key for the current post will be removed.
  265.              *
  266.              * @since 3.9.0
  267.              *
  268.              * @param {Object|boolean|null} stored_data The post data to store or null/false/empty to delete the key.
  269.              *
  270.              * @returns {boolean} True if data is stored, false if data was removed.
  271.              */
  272.             function setData( stored_data ) {
  273.                 var stored = getStorage();
  274.  
  275.                 if ( ! stored || ! post_id ) {
  276.                     return false;
  277.                 }
  278.  
  279.                 if ( stored_data ) {
  280.                     stored[ 'post_' + post_id ] = stored_data;
  281.                 } else if ( stored.hasOwnProperty( 'post_' + post_id ) ) {
  282.                     delete stored[ 'post_' + post_id ];
  283.                 } else {
  284.                     return false;
  285.                 }
  286.  
  287.                 return setStorage( stored );
  288.             }
  289.  
  290.             /**
  291.              * @summary Sets isSuspended to true.
  292.              *
  293.              * @since 3.9.0
  294.              *
  295.              * @returns {void}
  296.              */
  297.             function suspend() {
  298.                 isSuspended = true;
  299.             }
  300.  
  301.             /**
  302.              * @summary Sets isSuspended to false.
  303.              *
  304.              * @since 3.9.0
  305.              *
  306.              * @returns {void}
  307.              */
  308.             function resume() {
  309.                 isSuspended = false;
  310.             }
  311.  
  312.             /**
  313.              * @summary Saves post data for the current post.
  314.              *
  315.              * Runs on a 15 sec. interval, saves when there are differences in the post title or content.
  316.              * When the optional data is provided, updates the last saved post data.
  317.              *
  318.              * @since 3.9.0
  319.              *
  320.              * @param {Object} data The post data for saving, minimum 'post_title' and 'content'.
  321.              *
  322.              * @returns {boolean} Returns true when data has been saved, otherwise it returns false.
  323.              */
  324.             function save( data ) {
  325.                 var postData, compareString,
  326.                     result = false;
  327.  
  328.                 if ( isSuspended || ! hasStorage ) {
  329.                     return false;
  330.                 }
  331.  
  332.                 if ( data ) {
  333.                     postData = getSavedPostData() || {};
  334.                     $.extend( postData, data );
  335.                 } else {
  336.                     postData = getPostData('local');
  337.                 }
  338.  
  339.                 compareString = getCompareString( postData );
  340.  
  341.                 if ( typeof lastCompareString === 'undefined' ) {
  342.                     lastCompareString = initialCompareString;
  343.                 }
  344.  
  345.                 // If the content, title and excerpt did not change since the last save, don't save again.
  346.                 if ( compareString === lastCompareString ) {
  347.                     return false;
  348.                 }
  349.  
  350.                 postData.save_time = ( new Date() ).getTime();
  351.                 postData.status = $( '#post_status' ).val() || '';
  352.                 result = setData( postData );
  353.  
  354.                 if ( result ) {
  355.                     lastCompareString = compareString;
  356.                 }
  357.  
  358.                 return result;
  359.             }
  360.  
  361.             /**
  362.              * @summary Initializes the auto save function.
  363.              *
  364.              * Checks whether the editor is active or not to use the editor events
  365.              * to autosave, or uses the values from the elements to autosave.
  366.              *
  367.              * Runs on DOM ready.
  368.              *
  369.              * @since 3.9.0
  370.              *
  371.              * @returns {void}
  372.              */
  373.             function run() {
  374.                 post_id = $('#post_ID').val() || 0;
  375.  
  376.                 // Check if the local post data is different than the loaded post data.
  377.                 if ( $( '#wp-content-wrap' ).hasClass( 'tmce-active' ) ) {
  378.  
  379.                     // If TinyMCE loads first, check the post 1.5 sec. after it is ready.
  380.                     // By this time the content has been loaded in the editor and 'saved' to the textarea.
  381.                     // This prevents false positives.
  382.                     $document.on( 'tinymce-editor-init.autosave', function() {
  383.                         window.setTimeout( function() {
  384.                             checkPost();
  385.                         }, 1500 );
  386.                     });
  387.                 } else {
  388.                     checkPost();
  389.                 }
  390.  
  391.                 // Save every 15 sec.
  392.                 intervalTimer = window.setInterval( save, 15000 );
  393.  
  394.                 $( 'form#post' ).on( 'submit.autosave-local', function() {
  395.                     var editor = getEditor(),
  396.                         post_id = $('#post_ID').val() || 0;
  397.  
  398.                     if ( editor && ! editor.isHidden() ) {
  399.  
  400.                         // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea.
  401.                         editor.on( 'submit', function() {
  402.                             save({
  403.                                 post_title: $( '#title' ).val() || '',
  404.                                 content: $( '#content' ).val() || '',
  405.                                 excerpt: $( '#excerpt' ).val() || ''
  406.                             });
  407.                         });
  408.                     } else {
  409.                         save({
  410.                             post_title: $( '#title' ).val() || '',
  411.                             content: $( '#content' ).val() || '',
  412.                             excerpt: $( '#excerpt' ).val() || ''
  413.                         });
  414.                     }
  415.  
  416.                     var secure = ( 'https:' === window.location.protocol );
  417.                     wpCookies.set( 'wp-saving-post', post_id + '-check', 24 * 60 * 60, false, false, secure );
  418.                 });
  419.             }
  420.  
  421.             /**
  422.              * @summary Compares 2 strings.
  423.              *
  424.              * Removes whitespaces in the strings before comparing them.
  425.              *
  426.              * @since 3.9.0
  427.              *
  428.              * @param {string} str1 The first string.
  429.              * @param {string} str2 The second string.
  430.              * @returns {boolean} True if the strings are the same.
  431.              */
  432.             function compare( str1, str2 ) {
  433.                 function removeSpaces( string ) {
  434.                     return string.toString().replace(/[\x20\t\r\n\f]+/g, '');
  435.                 }
  436.  
  437.                 return ( removeSpaces( str1 || '' ) === removeSpaces( str2 || '' ) );
  438.             }
  439.  
  440.             /**
  441.              * @summary Checks if the saved data for the current post (if any) is different
  442.              * than the loaded post data on the screen.
  443.              *
  444.              * Shows a standard message letting the user restore the post data if different.
  445.              *
  446.              * @since 3.9.0
  447.              *
  448.              * @returns {void}
  449.              */
  450.             function checkPost() {
  451.                 var content, post_title, excerpt, $notice,
  452.                     postData = getSavedPostData(),
  453.                     cookie = wpCookies.get( 'wp-saving-post' ),
  454.                     $newerAutosaveNotice = $( '#has-newer-autosave' ).parent( '.notice' ),
  455.                     $headerEnd = $( '.wp-header-end' );
  456.  
  457.                 if ( cookie === post_id + '-saved' ) {
  458.                     wpCookies.remove( 'wp-saving-post' );
  459.                     // The post was saved properly, remove old data and bail
  460.                     setData( false );
  461.                     return;
  462.                 }
  463.  
  464.                 if ( ! postData ) {
  465.                     return;
  466.                 }
  467.  
  468.                 content = $( '#content' ).val() || '';
  469.                 post_title = $( '#title' ).val() || '';
  470.                 excerpt = $( '#excerpt' ).val() || '';
  471.  
  472.                 if ( compare( content, postData.content ) && compare( post_title, postData.post_title ) &&
  473.                     compare( excerpt, postData.excerpt ) ) {
  474.  
  475.                     return;
  476.                 }
  477.  
  478.                 /*
  479.                  * If '.wp-header-end' is found, append the notices after it otherwise
  480.                  * after the first h1 or h2 heading found within the main content.
  481.                  */
  482.                 if ( ! $headerEnd.length ) {
  483.                     $headerEnd = $( '.wrap h1, .wrap h2' ).first();
  484.                 }
  485.  
  486.                 $notice = $( '#local-storage-notice' )
  487.                     .insertAfter( $headerEnd )
  488.                     .addClass( 'notice-warning' );
  489.  
  490.                 if ( $newerAutosaveNotice.length ) {
  491.  
  492.                     // If there is a "server" autosave notice, hide it.
  493.                     // The data in the session storage is either the same or newer.
  494.                     $newerAutosaveNotice.slideUp( 150, function() {
  495.                         $notice.slideDown( 150 );
  496.                     });
  497.                 } else {
  498.                     $notice.slideDown( 200 );
  499.                 }
  500.  
  501.                 $notice.find( '.restore-backup' ).on( 'click.autosave-local', function() {
  502.                     restorePost( postData );
  503.                     $notice.fadeTo( 250, 0, function() {
  504.                         $notice.slideUp( 150 );
  505.                     });
  506.                 });
  507.             }
  508.  
  509.             /**
  510.              * @summary Restores the current title, content and excerpt from postData.
  511.              *
  512.              * @since 3.9.0
  513.              *
  514.              * @param {Object} postData The object containing all post data.
  515.              *
  516.              * @returns {boolean} True if the post is restored.
  517.              */
  518.             function restorePost( postData ) {
  519.                 var editor;
  520.  
  521.                 if ( postData ) {
  522.                     // Set the last saved data
  523.                     lastCompareString = getCompareString( postData );
  524.  
  525.                     if ( $( '#title' ).val() !== postData.post_title ) {
  526.                         $( '#title' ).focus().val( postData.post_title || '' );
  527.                     }
  528.  
  529.                     $( '#excerpt' ).val( postData.excerpt || '' );
  530.                     editor = getEditor();
  531.  
  532.                     if ( editor && ! editor.isHidden() && typeof switchEditors !== 'undefined' ) {
  533.                         if ( editor.settings.wpautop && postData.content ) {
  534.                             postData.content = switchEditors.wpautop( postData.content );
  535.                         }
  536.  
  537.                         // Make sure there's an undo level in the editor
  538.                         editor.undoManager.transact( function() {
  539.                             editor.setContent( postData.content || '' );
  540.                             editor.nodeChanged();
  541.                         });
  542.                     } else {
  543.  
  544.                         // Make sure the Text editor is selected
  545.                         $( '#content-html' ).click();
  546.                         $( '#content' ).focus();
  547.  
  548.                         // Using document.execCommand() will let the user undo.
  549.                         document.execCommand( 'selectAll' );
  550.                         document.execCommand( 'insertText', false, postData.content || '' );
  551.                     }
  552.  
  553.                     return true;
  554.                 }
  555.  
  556.                 return false;
  557.             }
  558.  
  559.             blog_id = typeof window.autosaveL10n !== 'undefined' && window.autosaveL10n.blog_id;
  560.  
  561.             // Check if the browser supports sessionStorage and it's not disabled,
  562.             // then initialize and run checkPost().
  563.             // Don't run if the post type supports neither 'editor' (textarea#content) nor 'excerpt'.
  564.             if ( checkStorage() && blog_id && ( $('#content').length || $('#excerpt').length ) ) {
  565.                 $document.ready( run );
  566.             }
  567.  
  568.             return {
  569.                 hasStorage: hasStorage,
  570.                 getSavedPostData: getSavedPostData,
  571.                 save: save,
  572.                 suspend: suspend,
  573.                 resume: resume
  574.             };
  575.         }
  576.  
  577.         /**
  578.          * @summary Auto saves the post on the server.
  579.          *
  580.          * @since 3.9.0
  581.          *
  582.          * @returns {Object} {
  583.          *     {
  584.          *         tempBlockSave: tempBlockSave,
  585.          *         triggerSave: triggerSave,
  586.          *         postChanged: postChanged,
  587.          *         suspend: suspend,
  588.          *         resume: resume
  589.          *         }
  590.          *     } The object all functions for autosave.
  591.          */
  592.         function autosaveServer() {
  593.             var _blockSave, _blockSaveTimer, previousCompareString, lastCompareString,
  594.                 nextRun = 0,
  595.                 isSuspended = false;
  596.  
  597.  
  598.             /**
  599.              * @summary  Blocks saving for the next 10 seconds.
  600.              *
  601.              * @since 3.9.0
  602.              *
  603.              * @returns {void}
  604.              */
  605.             function tempBlockSave() {
  606.                 _blockSave = true;
  607.                 window.clearTimeout( _blockSaveTimer );
  608.  
  609.                 _blockSaveTimer = window.setTimeout( function() {
  610.                     _blockSave = false;
  611.                 }, 10000 );
  612.             }
  613.  
  614.             /**
  615.              * @summary Sets isSuspended to true.
  616.              *
  617.              * @since 3.9.0
  618.              *
  619.              * @returns {void}
  620.              */
  621.             function suspend() {
  622.                 isSuspended = true;
  623.             }
  624.  
  625.             /**
  626.              * @summary Sets isSuspended to false.
  627.              *
  628.              * @since 3.9.0
  629.              *
  630.              * @returns {void}
  631.              */
  632.             function resume() {
  633.                 isSuspended = false;
  634.             }
  635.  
  636.             /**
  637.              * @summary Triggers the autosave with the post data.
  638.              *
  639.              * @since 3.9.0
  640.              *
  641.              * @param {Object} data The post data.
  642.              *
  643.              * @returns {void}
  644.              */
  645.             function response( data ) {
  646.                 _schedule();
  647.                 _blockSave = false;
  648.                 lastCompareString = previousCompareString;
  649.                 previousCompareString = '';
  650.  
  651.                 $document.trigger( 'after-autosave', [data] );
  652.                 enableButtons();
  653.  
  654.                 if ( data.success ) {
  655.                     // No longer an auto-draft
  656.                     $( '#auto_draft' ).val('');
  657.                 }
  658.             }
  659.  
  660.             /**
  661.              * @summary Saves immediately.
  662.              *
  663.              * Resets the timing and tells heartbeat to connect now.
  664.              *
  665.              * @since 3.9.0
  666.              *
  667.              * @returns {void}
  668.              */
  669.             function triggerSave() {
  670.                 nextRun = 0;
  671.                 wp.heartbeat.connectNow();
  672.             }
  673.  
  674.             /**
  675.              * @summary Checks if the post content in the textarea has changed since page load.
  676.              *
  677.              * This also happens when TinyMCE is active and editor.save() is triggered by
  678.              * wp.autosave.getPostData().
  679.              *
  680.              * @since 3.9.0
  681.              *
  682.              * @return {boolean} True if the post has been changed.
  683.              */
  684.             function postChanged() {
  685.                 return getCompareString() !== initialCompareString;
  686.             }
  687.  
  688.             /**
  689.              * @summary Checks if the post can be saved or not.
  690.              *
  691.              * If the post hasn't changed or it cannot be updated,
  692.              * because the autosave is blocked or suspended, the function returns false.
  693.              *
  694.              * @since 3.9.0
  695.              *
  696.              * @returns {Object} Returns the post data.
  697.              */
  698.             function save() {
  699.                 var postData, compareString;
  700.  
  701.                 // window.autosave() used for back-compat
  702.                 if ( isSuspended || _blockSave || ! window.autosave() ) {
  703.                     return false;
  704.                 }
  705.  
  706.                 if ( ( new Date() ).getTime() < nextRun ) {
  707.                     return false;
  708.                 }
  709.  
  710.                 postData = getPostData();
  711.                 compareString = getCompareString( postData );
  712.  
  713.                 // First check
  714.                 if ( typeof lastCompareString === 'undefined' ) {
  715.                     lastCompareString = initialCompareString;
  716.                 }
  717.  
  718.                 // No change
  719.                 if ( compareString === lastCompareString ) {
  720.                     return false;
  721.                 }
  722.  
  723.                 previousCompareString = compareString;
  724.                 tempBlockSave();
  725.                 disableButtons();
  726.  
  727.                 $document.trigger( 'wpcountwords', [ postData.content ] )
  728.                     .trigger( 'before-autosave', [ postData ] );
  729.  
  730.                 postData._wpnonce = $( '#_wpnonce' ).val() || '';
  731.  
  732.                 return postData;
  733.             }
  734.  
  735.             /**
  736.              * @summary Sets the next run, based on the autosave interval.
  737.              *
  738.              * @private
  739.              *
  740.              * @since 3.9.0
  741.              *
  742.              * @returns {void}
  743.              */
  744.             function _schedule() {
  745.                 nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000;
  746.             }
  747.  
  748.             /**
  749.              * @summary Sets the autosaveData on the autosave heartbeat.
  750.              *
  751.              * @since 3.9.0
  752.              *
  753.              * @returns {void}
  754.              */
  755.             $document.on( 'heartbeat-send.autosave', function( event, data ) {
  756.                 var autosaveData = save();
  757.  
  758.                 if ( autosaveData ) {
  759.                     data.wp_autosave = autosaveData;
  760.                 }
  761.  
  762.                 /**
  763.                  * @summary Triggers the autosave of the post with the autosave data
  764.                  * on the autosave heartbeat.
  765.                  *
  766.                  * @since 3.9.0
  767.                  *
  768.                  * @returns {void}
  769.                  */
  770.             }).on( 'heartbeat-tick.autosave', function( event, data ) {
  771.                 if ( data.wp_autosave ) {
  772.                     response( data.wp_autosave );
  773.                 }
  774.                 /**
  775.                  * @summary Disables buttons and throws a notice when the connection is lost.
  776.                  *
  777.                  * @since 3.9.0
  778.                  *
  779.                  * @returns {void}
  780.                  */
  781.             }).on( 'heartbeat-connection-lost.autosave', function( event, error, status ) {
  782.  
  783.                 // When connection is lost, keep user from submitting changes.
  784.                 if ( 'timeout' === error || 603 === status ) {
  785.                     var $notice = $('#lost-connection-notice');
  786.  
  787.                     if ( ! wp.autosave.local.hasStorage ) {
  788.                         $notice.find('.hide-if-no-sessionstorage').hide();
  789.                     }
  790.  
  791.                     $notice.show();
  792.                     disableButtons();
  793.                 }
  794.  
  795.                 /**
  796.                  * @summary Enables buttons when the connection is restored.
  797.                  *
  798.                  * @since 3.9.0
  799.                  *
  800.                  * @returns {void}
  801.                  */
  802.             }).on( 'heartbeat-connection-restored.autosave', function() {
  803.                 $('#lost-connection-notice').hide();
  804.                 enableButtons();
  805.             }).ready( function() {
  806.                 _schedule();
  807.             });
  808.  
  809.             return {
  810.                 tempBlockSave: tempBlockSave,
  811.                 triggerSave: triggerSave,
  812.                 postChanged: postChanged,
  813.                 suspend: suspend,
  814.                 resume: resume
  815.             };
  816.         }
  817.  
  818.         /**
  819.          * @summary Sets the autosave time out.
  820.          *
  821.          * Wait for TinyMCE to initialize plus 1 second. for any external css to finish loading,
  822.          * then save to the textarea before setting initialCompareString.
  823.          * This avoids any insignificant differences between the initial textarea content and the content
  824.          * extracted from the editor.
  825.          *
  826.          * @since 3.9.0
  827.          *
  828.          * @returns {void}
  829.          */
  830.         $document.on( 'tinymce-editor-init.autosave', function( event, editor ) {
  831.             if ( editor.id === 'content' ) {
  832.                 window.setTimeout( function() {
  833.                     editor.save();
  834.                     initialCompareString = getCompareString();
  835.                 }, 1000 );
  836.             }
  837.         }).ready( function() {
  838.  
  839.             // Set the initial compare string in case TinyMCE is not used or not loaded first
  840.             initialCompareString = getCompareString();
  841.         });
  842.  
  843.         return {
  844.             getPostData: getPostData,
  845.             getCompareString: getCompareString,
  846.             disableButtons: disableButtons,
  847.             enableButtons: enableButtons,
  848.             local: autosaveLocal(),
  849.             server: autosaveServer()
  850.         };
  851.     }
  852.  
  853.     /** @namespace wp */
  854.     window.wp = window.wp || {};
  855.     window.wp.autosave = autosave();
  856.  
  857. }( jQuery, window ));
  858.