autosave.js 21 KB

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