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