2 // Back-compat |
2 // Back-compat |
3 window.autosave = function() { |
3 window.autosave = function() { |
4 return true; |
4 return true; |
5 }; |
5 }; |
6 |
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 */ |
7 ( function( $, window ) { |
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 */ |
8 function autosave() { |
33 function autosave() { |
9 var initialCompareString, |
34 var initialCompareString, |
10 lastTriggerSave = 0, |
35 lastTriggerSave = 0, |
11 $document = $(document); |
36 $document = $(document); |
12 |
37 |
13 /** |
38 /** |
14 * Returns the data saved in both local and remote autosave |
39 * @summary Returns the data saved in both local and remote autosave. |
15 * |
40 * |
16 * @return object Object containing the post data |
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. |
17 */ |
46 */ |
18 function getPostData( type ) { |
47 function getPostData( type ) { |
19 var post_name, parent_id, data, |
48 var post_name, parent_id, data, |
20 time = ( new Date() ).getTime(), |
49 time = ( new Date() ).getTime(), |
21 cats = [], |
50 cats = [], |
22 editor = typeof tinymce !== 'undefined' && tinymce.get('content'); |
51 editor = getEditor(); |
23 |
52 |
24 // Don't run editor.save() more often than every 3 sec. |
53 // Don't run editor.save() more often than every 3 seconds. |
25 // It is resource intensive and might slow down typing in long posts on slow devices. |
54 // It is resource intensive and might slow down typing in long posts on slow devices. |
26 if ( editor && ! editor.isHidden() && time - 3000 > lastTriggerSave ) { |
55 if ( editor && editor.isDirty() && ! editor.isHidden() && time - 3000 > lastTriggerSave ) { |
27 editor.save(); |
56 editor.save(); |
28 lastTriggerSave = time; |
57 lastTriggerSave = time; |
29 } |
58 } |
30 |
59 |
31 data = { |
60 data = { |
67 } |
96 } |
68 |
97 |
69 return data; |
98 return data; |
70 } |
99 } |
71 |
100 |
72 // Concatenate title, content and excerpt. Used to track changes when auto-saving. |
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 */ |
73 function getCompareString( postData ) { |
112 function getCompareString( postData ) { |
74 if ( typeof postData === 'object' ) { |
113 if ( typeof postData === 'object' ) { |
75 return ( postData.post_title || '' ) + '::' + ( postData.content || '' ) + '::' + ( postData.excerpt || '' ); |
114 return ( postData.post_title || '' ) + '::' + ( postData.content || '' ) + '::' + ( postData.excerpt || '' ); |
76 } |
115 } |
77 |
116 |
78 return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' ); |
117 return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' ); |
79 } |
118 } |
80 |
119 |
|
120 /** |
|
121 * @summary Disables save buttons. |
|
122 * |
|
123 * @since 3.9.0 |
|
124 * |
|
125 * @returns {void} |
|
126 */ |
81 function disableButtons() { |
127 function disableButtons() { |
82 $document.trigger('autosave-disable-buttons'); |
128 $document.trigger('autosave-disable-buttons'); |
|
129 |
83 // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions. |
130 // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions. |
84 setTimeout( enableButtons, 5000 ); |
131 setTimeout( enableButtons, 5000 ); |
85 } |
132 } |
86 |
133 |
|
134 /** |
|
135 * @summary Enables save buttons. |
|
136 * |
|
137 * @since 3.9.0 |
|
138 * |
|
139 * @returns {void} |
|
140 */ |
87 function enableButtons() { |
141 function enableButtons() { |
88 $document.trigger( 'autosave-enable-buttons' ); |
142 $document.trigger( 'autosave-enable-buttons' ); |
89 } |
143 } |
90 |
144 |
91 // Autosave in localStorage |
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 */ |
92 function autosaveLocal() { |
173 function autosaveLocal() { |
93 var restorePostData, undoPostData, blog_id, post_id, hasStorage, intervalTimer, |
174 var blog_id, post_id, hasStorage, intervalTimer, |
94 lastCompareString, |
175 lastCompareString, |
95 isSuspended = false; |
176 isSuspended = false; |
96 |
177 |
97 // Check if the browser supports sessionStorage and it's not disabled |
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 */ |
98 function checkStorage() { |
185 function checkStorage() { |
99 var test = Math.random().toString(), |
186 var test = Math.random().toString(), |
100 result = false; |
187 result = false; |
101 |
188 |
102 try { |
189 try { |
108 hasStorage = result; |
195 hasStorage = result; |
109 return result; |
196 return result; |
110 } |
197 } |
111 |
198 |
112 /** |
199 /** |
113 * Initialize the local storage |
200 * @summary Initializes the local storage. |
114 * |
201 * |
115 * @return mixed False if no sessionStorage in the browser or an Object containing all postData for this blog |
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. |
116 */ |
206 */ |
117 function getStorage() { |
207 function getStorage() { |
118 var stored_obj = false; |
208 var stored_obj = false; |
119 // Separate local storage containers for each blog_id |
209 // Separate local storage containers for each blog_id |
120 if ( hasStorage && blog_id ) { |
210 if ( hasStorage && blog_id ) { |
163 |
257 |
164 return stored[ 'post_' + post_id ] || false; |
258 return stored[ 'post_' + post_id ] || false; |
165 } |
259 } |
166 |
260 |
167 /** |
261 /** |
168 * Set (save or delete) post data in the storage. |
262 * @summary Sets (save or delete) post data in the storage. |
169 * |
263 * |
170 * If stored_data evaluates to 'false' the storage key for the current post will be removed |
264 * If stored_data evaluates to 'false' the storage key for the current post will be removed. |
171 * |
265 * |
172 * $param stored_data The post data to store or null/false/empty to delete the key |
266 * @since 3.9.0 |
173 * @return bool |
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. |
174 */ |
271 */ |
175 function setData( stored_data ) { |
272 function setData( stored_data ) { |
176 var stored = getStorage(); |
273 var stored = getStorage(); |
177 |
274 |
178 if ( ! stored || ! post_id ) { |
275 if ( ! stored || ! post_id ) { |
188 } |
285 } |
189 |
286 |
190 return setStorage( stored ); |
287 return setStorage( stored ); |
191 } |
288 } |
192 |
289 |
|
290 /** |
|
291 * @summary Sets isSuspended to true. |
|
292 * |
|
293 * @since 3.9.0 |
|
294 * |
|
295 * @returns {void} |
|
296 */ |
193 function suspend() { |
297 function suspend() { |
194 isSuspended = true; |
298 isSuspended = true; |
195 } |
299 } |
196 |
300 |
|
301 /** |
|
302 * @summary Sets isSuspended to false. |
|
303 * |
|
304 * @since 3.9.0 |
|
305 * |
|
306 * @returns {void} |
|
307 */ |
197 function resume() { |
308 function resume() { |
198 isSuspended = false; |
309 isSuspended = false; |
199 } |
310 } |
200 |
311 |
201 /** |
312 /** |
202 * Save post data for the current post |
313 * @summary Saves post data for the current post. |
203 * |
314 * |
204 * Runs on a 15 sec. interval, saves when there are differences in the post title or content. |
315 * Runs on a 15 sec. interval, saves when there are differences in the post title or content. |
205 * When the optional data is provided, updates the last saved post data. |
316 * When the optional data is provided, updates the last saved post data. |
206 * |
317 * |
207 * $param data optional Object The post data for saving, minimum 'post_title' and 'content' |
318 * @since 3.9.0 |
208 * @return bool |
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. |
209 */ |
323 */ |
210 function save( data ) { |
324 function save( data ) { |
211 var postData, compareString, |
325 var postData, compareString, |
212 result = false; |
326 result = false; |
213 |
327 |
242 } |
356 } |
243 |
357 |
244 return result; |
358 return result; |
245 } |
359 } |
246 |
360 |
247 // Run on DOM ready |
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 */ |
248 function run() { |
373 function run() { |
249 post_id = $('#post_ID').val() || 0; |
374 post_id = $('#post_ID').val() || 0; |
250 |
375 |
251 // Check if the local post data is different than the loaded post data. |
376 // Check if the local post data is different than the loaded post data. |
252 if ( $( '#wp-content-wrap' ).hasClass( 'tmce-active' ) ) { |
377 if ( $( '#wp-content-wrap' ).hasClass( 'tmce-active' ) ) { |
|
378 |
253 // If TinyMCE loads first, check the post 1.5 sec. after it is ready. |
379 // If TinyMCE loads first, check the post 1.5 sec. after it is ready. |
254 // By this time the content has been loaded in the editor and 'saved' to the textarea. |
380 // By this time the content has been loaded in the editor and 'saved' to the textarea. |
255 // This prevents false positives. |
381 // This prevents false positives. |
256 $document.on( 'tinymce-editor-init.autosave', function() { |
382 $document.on( 'tinymce-editor-init.autosave', function() { |
257 window.setTimeout( function() { |
383 window.setTimeout( function() { |
264 |
390 |
265 // Save every 15 sec. |
391 // Save every 15 sec. |
266 intervalTimer = window.setInterval( save, 15000 ); |
392 intervalTimer = window.setInterval( save, 15000 ); |
267 |
393 |
268 $( 'form#post' ).on( 'submit.autosave-local', function() { |
394 $( 'form#post' ).on( 'submit.autosave-local', function() { |
269 var editor = typeof tinymce !== 'undefined' && tinymce.get('content'), |
395 var editor = getEditor(), |
270 post_id = $('#post_ID').val() || 0; |
396 post_id = $('#post_ID').val() || 0; |
271 |
397 |
272 if ( editor && ! editor.isHidden() ) { |
398 if ( editor && ! editor.isHidden() ) { |
|
399 |
273 // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea. |
400 // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea. |
274 editor.on( 'submit', function() { |
401 editor.on( 'submit', function() { |
275 save({ |
402 save({ |
276 post_title: $( '#title' ).val() || '', |
403 post_title: $( '#title' ).val() || '', |
277 content: $( '#content' ).val() || '', |
404 content: $( '#content' ).val() || '', |
284 content: $( '#content' ).val() || '', |
411 content: $( '#content' ).val() || '', |
285 excerpt: $( '#excerpt' ).val() || '' |
412 excerpt: $( '#excerpt' ).val() || '' |
286 }); |
413 }); |
287 } |
414 } |
288 |
415 |
289 wpCookies.set( 'wp-saving-post', post_id + '-check', 24 * 60 * 60 ); |
416 var secure = ( 'https:' === window.location.protocol ); |
|
417 wpCookies.set( 'wp-saving-post', post_id + '-check', 24 * 60 * 60, false, false, secure ); |
290 }); |
418 }); |
291 } |
419 } |
292 |
420 |
293 // Strip whitespace and compare two strings |
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 */ |
294 function compare( str1, str2 ) { |
432 function compare( str1, str2 ) { |
295 function removeSpaces( string ) { |
433 function removeSpaces( string ) { |
296 return string.toString().replace(/[\x20\t\r\n\f]+/g, ''); |
434 return string.toString().replace(/[\x20\t\r\n\f]+/g, ''); |
297 } |
435 } |
298 |
436 |
299 return ( removeSpaces( str1 || '' ) === removeSpaces( str2 || '' ) ); |
437 return ( removeSpaces( str1 || '' ) === removeSpaces( str2 || '' ) ); |
300 } |
438 } |
301 |
439 |
302 /** |
440 /** |
303 * Check if the saved data for the current post (if any) is different than the loaded post data on the screen |
441 * @summary Checks if the saved data for the current post (if any) is different |
|
442 * than the loaded post data on the screen. |
304 * |
443 * |
305 * Shows a standard message letting the user restore the post data if different. |
444 * Shows a standard message letting the user restore the post data if different. |
306 * |
445 * |
307 * @return void |
446 * @since 3.9.0 |
|
447 * |
|
448 * @returns {void} |
308 */ |
449 */ |
309 function checkPost() { |
450 function checkPost() { |
310 var content, post_title, excerpt, $notice, |
451 var content, post_title, excerpt, $notice, |
311 postData = getSavedPostData(), |
452 postData = getSavedPostData(), |
312 cookie = wpCookies.get( 'wp-saving-post' ); |
453 cookie = wpCookies.get( 'wp-saving-post' ), |
|
454 $newerAutosaveNotice = $( '#has-newer-autosave' ).parent( '.notice' ), |
|
455 $headerEnd = $( '.wp-header-end' ); |
313 |
456 |
314 if ( cookie === post_id + '-saved' ) { |
457 if ( cookie === post_id + '-saved' ) { |
315 wpCookies.remove( 'wp-saving-post' ); |
458 wpCookies.remove( 'wp-saving-post' ); |
316 // The post was saved properly, remove old data and bail |
459 // The post was saved properly, remove old data and bail |
317 setData( false ); |
460 setData( false ); |
320 |
463 |
321 if ( ! postData ) { |
464 if ( ! postData ) { |
322 return; |
465 return; |
323 } |
466 } |
324 |
467 |
325 // There is a newer autosave. Don't show two "restore" notices at the same time. |
|
326 if ( $( '#has-newer-autosave' ).length ) { |
|
327 return; |
|
328 } |
|
329 |
|
330 content = $( '#content' ).val() || ''; |
468 content = $( '#content' ).val() || ''; |
331 post_title = $( '#title' ).val() || ''; |
469 post_title = $( '#title' ).val() || ''; |
332 excerpt = $( '#excerpt' ).val() || ''; |
470 excerpt = $( '#excerpt' ).val() || ''; |
333 |
471 |
334 if ( compare( content, postData.content ) && compare( post_title, postData.post_title ) && |
472 if ( compare( content, postData.content ) && compare( post_title, postData.post_title ) && |
335 compare( excerpt, postData.excerpt ) ) { |
473 compare( excerpt, postData.excerpt ) ) { |
336 |
474 |
337 return; |
475 return; |
338 } |
476 } |
339 |
477 |
340 restorePostData = postData; |
478 /* |
341 undoPostData = { |
479 * If '.wp-header-end' is found, append the notices after it otherwise |
342 content: content, |
480 * after the first h1 or h2 heading found within the main content. |
343 post_title: post_title, |
481 */ |
344 excerpt: excerpt |
482 if ( ! $headerEnd.length ) { |
345 }; |
483 $headerEnd = $( '.wrap h1, .wrap h2' ).first(); |
346 |
484 } |
347 $notice = $( '#local-storage-notice' ); |
485 |
348 $('.wrap h2').first().after( $notice.addClass( 'notice-warning' ).show() ); |
486 $notice = $( '#local-storage-notice' ) |
349 |
487 .insertAfter( $headerEnd ) |
350 $notice.on( 'click.autosave-local', function( event ) { |
488 .addClass( 'notice-warning' ); |
351 var $target = $( event.target ); |
489 |
352 |
490 if ( $newerAutosaveNotice.length ) { |
353 if ( $target.hasClass( 'restore-backup' ) ) { |
491 |
354 restorePost( restorePostData ); |
492 // If there is a "server" autosave notice, hide it. |
355 $target.parent().hide(); |
493 // The data in the session storage is either the same or newer. |
356 $(this).find( 'p.undo-restore' ).show(); |
494 $newerAutosaveNotice.slideUp( 150, function() { |
357 $notice.removeClass( 'notice-warning' ).addClass( 'notice-success' ); |
495 $notice.slideDown( 150 ); |
358 } else if ( $target.hasClass( 'undo-restore-backup' ) ) { |
496 }); |
359 restorePost( undoPostData ); |
497 } else { |
360 $target.parent().hide(); |
498 $notice.slideDown( 200 ); |
361 $(this).find( 'p.local-restore' ).show(); |
499 } |
362 $notice.removeClass( 'notice-success' ).addClass( 'notice-warning' ); |
500 |
363 } |
501 $notice.find( '.restore-backup' ).on( 'click.autosave-local', function() { |
364 |
502 restorePost( postData ); |
365 event.preventDefault(); |
503 $notice.fadeTo( 250, 0, function() { |
|
504 $notice.slideUp( 150 ); |
|
505 }); |
366 }); |
506 }); |
367 } |
507 } |
368 |
508 |
369 // Restore the current title, content and excerpt from postData. |
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 */ |
370 function restorePost( postData ) { |
518 function restorePost( postData ) { |
371 var editor; |
519 var editor; |
372 |
520 |
373 if ( postData ) { |
521 if ( postData ) { |
374 // Set the last saved data |
522 // Set the last saved data |
377 if ( $( '#title' ).val() !== postData.post_title ) { |
525 if ( $( '#title' ).val() !== postData.post_title ) { |
378 $( '#title' ).focus().val( postData.post_title || '' ); |
526 $( '#title' ).focus().val( postData.post_title || '' ); |
379 } |
527 } |
380 |
528 |
381 $( '#excerpt' ).val( postData.excerpt || '' ); |
529 $( '#excerpt' ).val( postData.excerpt || '' ); |
382 editor = typeof tinymce !== 'undefined' && tinymce.get('content'); |
530 editor = getEditor(); |
383 |
531 |
384 if ( editor && ! editor.isHidden() && typeof switchEditors !== 'undefined' ) { |
532 if ( editor && ! editor.isHidden() && typeof switchEditors !== 'undefined' ) { |
|
533 if ( editor.settings.wpautop && postData.content ) { |
|
534 postData.content = switchEditors.wpautop( postData.content ); |
|
535 } |
|
536 |
385 // Make sure there's an undo level in the editor |
537 // Make sure there's an undo level in the editor |
386 editor.undoManager.add(); |
538 editor.undoManager.transact( function() { |
387 editor.setContent( postData.content ? switchEditors.wpautop( postData.content ) : '' ); |
539 editor.setContent( postData.content || '' ); |
|
540 editor.nodeChanged(); |
|
541 }); |
388 } else { |
542 } else { |
|
543 |
389 // Make sure the Text editor is selected |
544 // Make sure the Text editor is selected |
390 $( '#content-html' ).click(); |
545 $( '#content-html' ).click(); |
391 $( '#content' ).val( postData.content ); |
546 $( '#content' ).focus(); |
|
547 |
|
548 // Using document.execCommand() will let the user undo. |
|
549 document.execCommand( 'selectAll' ); |
|
550 document.execCommand( 'insertText', false, postData.content || '' ); |
392 } |
551 } |
393 |
552 |
394 return true; |
553 return true; |
395 } |
554 } |
396 |
555 |
413 suspend: suspend, |
572 suspend: suspend, |
414 resume: resume |
573 resume: resume |
415 }; |
574 }; |
416 } |
575 } |
417 |
576 |
418 // Autosave on the server |
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 */ |
419 function autosaveServer() { |
592 function autosaveServer() { |
420 var _blockSave, _blockSaveTimer, previousCompareString, lastCompareString, |
593 var _blockSave, _blockSaveTimer, previousCompareString, lastCompareString, |
421 nextRun = 0, |
594 nextRun = 0, |
422 isSuspended = false; |
595 isSuspended = false; |
423 |
596 |
424 // Block saving for the next 10 sec. |
597 |
|
598 /** |
|
599 * @summary Blocks saving for the next 10 seconds. |
|
600 * |
|
601 * @since 3.9.0 |
|
602 * |
|
603 * @returns {void} |
|
604 */ |
425 function tempBlockSave() { |
605 function tempBlockSave() { |
426 _blockSave = true; |
606 _blockSave = true; |
427 window.clearTimeout( _blockSaveTimer ); |
607 window.clearTimeout( _blockSaveTimer ); |
428 |
608 |
429 _blockSaveTimer = window.setTimeout( function() { |
609 _blockSaveTimer = window.setTimeout( function() { |
430 _blockSave = false; |
610 _blockSave = false; |
431 }, 10000 ); |
611 }, 10000 ); |
432 } |
612 } |
433 |
613 |
|
614 /** |
|
615 * @summary Sets isSuspended to true. |
|
616 * |
|
617 * @since 3.9.0 |
|
618 * |
|
619 * @returns {void} |
|
620 */ |
434 function suspend() { |
621 function suspend() { |
435 isSuspended = true; |
622 isSuspended = true; |
436 } |
623 } |
437 |
624 |
|
625 /** |
|
626 * @summary Sets isSuspended to false. |
|
627 * |
|
628 * @since 3.9.0 |
|
629 * |
|
630 * @returns {void} |
|
631 */ |
438 function resume() { |
632 function resume() { |
439 isSuspended = false; |
633 isSuspended = false; |
440 } |
634 } |
441 |
635 |
442 // Runs on heartbeat-response |
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 */ |
443 function response( data ) { |
645 function response( data ) { |
444 _schedule(); |
646 _schedule(); |
445 _blockSave = false; |
647 _blockSave = false; |
446 lastCompareString = previousCompareString; |
648 lastCompareString = previousCompareString; |
447 previousCompareString = ''; |
649 previousCompareString = ''; |
454 $( '#auto_draft' ).val(''); |
656 $( '#auto_draft' ).val(''); |
455 } |
657 } |
456 } |
658 } |
457 |
659 |
458 /** |
660 /** |
459 * Save immediately |
661 * @summary Saves immediately. |
460 * |
662 * |
461 * Resets the timing and tells heartbeat to connect now |
663 * Resets the timing and tells heartbeat to connect now. |
462 * |
664 * |
463 * @return void |
665 * @since 3.9.0 |
|
666 * |
|
667 * @returns {void} |
464 */ |
668 */ |
465 function triggerSave() { |
669 function triggerSave() { |
466 nextRun = 0; |
670 nextRun = 0; |
467 wp.heartbeat.connectNow(); |
671 wp.heartbeat.connectNow(); |
468 } |
672 } |
469 |
673 |
470 /** |
674 /** |
471 * Checks if the post content in the textarea has changed since page load. |
675 * @summary Checks if the post content in the textarea has changed since page load. |
472 * |
676 * |
473 * This also happens when TinyMCE is active and editor.save() is triggered by |
677 * This also happens when TinyMCE is active and editor.save() is triggered by |
474 * wp.autosave.getPostData(). |
678 * wp.autosave.getPostData(). |
475 * |
679 * |
476 * @return bool |
680 * @since 3.9.0 |
|
681 * |
|
682 * @return {boolean} True if the post has been changed. |
477 */ |
683 */ |
478 function postChanged() { |
684 function postChanged() { |
479 return getCompareString() !== initialCompareString; |
685 return getCompareString() !== initialCompareString; |
480 } |
686 } |
481 |
687 |
482 // Runs on 'heartbeat-send' |
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 */ |
483 function save() { |
698 function save() { |
484 var postData, compareString; |
699 var postData, compareString; |
485 |
700 |
486 // window.autosave() used for back-compat |
701 // window.autosave() used for back-compat |
487 if ( isSuspended || _blockSave || ! window.autosave() ) { |
702 if ( isSuspended || _blockSave || ! window.autosave() ) { |
515 postData._wpnonce = $( '#_wpnonce' ).val() || ''; |
730 postData._wpnonce = $( '#_wpnonce' ).val() || ''; |
516 |
731 |
517 return postData; |
732 return postData; |
518 } |
733 } |
519 |
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 */ |
520 function _schedule() { |
744 function _schedule() { |
521 nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000; |
745 nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000; |
522 } |
746 } |
523 |
747 |
|
748 /** |
|
749 * @summary Sets the autosaveData on the autosave heartbeat. |
|
750 * |
|
751 * @since 3.9.0 |
|
752 * |
|
753 * @returns {void} |
|
754 */ |
524 $document.on( 'heartbeat-send.autosave', function( event, data ) { |
755 $document.on( 'heartbeat-send.autosave', function( event, data ) { |
525 var autosaveData = save(); |
756 var autosaveData = save(); |
526 |
757 |
527 if ( autosaveData ) { |
758 if ( autosaveData ) { |
528 data.wp_autosave = autosaveData; |
759 data.wp_autosave = autosaveData; |
529 } |
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 */ |
530 }).on( 'heartbeat-tick.autosave', function( event, data ) { |
770 }).on( 'heartbeat-tick.autosave', function( event, data ) { |
531 if ( data.wp_autosave ) { |
771 if ( data.wp_autosave ) { |
532 response( data.wp_autosave ); |
772 response( data.wp_autosave ); |
533 } |
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 */ |
534 }).on( 'heartbeat-connection-lost.autosave', function( event, error, status ) { |
781 }).on( 'heartbeat-connection-lost.autosave', function( event, error, status ) { |
|
782 |
535 // When connection is lost, keep user from submitting changes. |
783 // When connection is lost, keep user from submitting changes. |
536 if ( 'timeout' === error || 603 === status ) { |
784 if ( 'timeout' === error || 603 === status ) { |
537 var $notice = $('#lost-connection-notice'); |
785 var $notice = $('#lost-connection-notice'); |
538 |
786 |
539 if ( ! wp.autosave.local.hasStorage ) { |
787 if ( ! wp.autosave.local.hasStorage ) { |
557 suspend: suspend, |
813 suspend: suspend, |
558 resume: resume |
814 resume: resume |
559 }; |
815 }; |
560 } |
816 } |
561 |
817 |
562 // Wait for TinyMCE to initialize plus 1 sec. for any external css to finish loading, |
818 /** |
563 // then 'save' to the textarea before setting initialCompareString. |
819 * @summary Sets the autosave time out. |
564 // This avoids any insignificant differences between the initial textarea content and the content |
820 * |
565 // extracted from the editor. |
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 */ |
566 $document.on( 'tinymce-editor-init.autosave', function( event, editor ) { |
830 $document.on( 'tinymce-editor-init.autosave', function( event, editor ) { |
567 if ( editor.id === 'content' ) { |
831 if ( editor.id === 'content' ) { |
568 window.setTimeout( function() { |
832 window.setTimeout( function() { |
569 editor.save(); |
833 editor.save(); |
570 initialCompareString = getCompareString(); |
834 initialCompareString = getCompareString(); |
571 }, 1000 ); |
835 }, 1000 ); |
572 } |
836 } |
573 }).ready( function() { |
837 }).ready( function() { |
|
838 |
574 // Set the initial compare string in case TinyMCE is not used or not loaded first |
839 // Set the initial compare string in case TinyMCE is not used or not loaded first |
575 initialCompareString = getCompareString(); |
840 initialCompareString = getCompareString(); |
576 }); |
841 }); |
577 |
842 |
578 return { |
843 return { |