| 427 | | })(jQuery); |
| | 429 | // Autosave in localStorage |
| | 430 | // set as simple object/mixin for now |
| | 431 | wp.autosave = wp.autosave || {}; |
| | 432 | |
| | 433 | // Returns only post ID, title, content and excerpt for local autosaves, the rest of the fields for remote |
| | 434 | wp.autosave.getPostData = function( local ) { |
| | 435 | var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [], |
| | 436 | data = { |
| | 437 | autosave: true, |
| | 438 | post_id: $('#post_ID').val() || 0 |
| | 439 | }; |
| | 440 | |
| | 441 | if ( ed && !ed.isHidden() ) { |
| | 442 | // Don't run while the tinymce spellcheck is on. It resets all found words. |
| | 443 | if ( ed.plugins.spellchecker && ed.plugins.spellchecker.active ) { |
| | 444 | data.autosave = false; |
| | 445 | return data; |
| | 446 | } else { |
| | 447 | if ( 'mce_fullscreen' == ed.id ) |
| | 448 | tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'}); |
| | 449 | |
| | 450 | tinymce.triggerSave(); |
| | 451 | } |
| | 452 | } |
| | 453 | |
| | 454 | if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) { |
| | 455 | data['post_title'] = $('#wp-fullscreen-title').val() || ''; |
| | 456 | data['content'] = $('#wp_mce_fullscreen').val() || ''; |
| | 457 | } else { |
| | 458 | data['post_title'] = $('#title').val() || ''; |
| | 459 | data['content'] = $('#content').val() || ''; |
| | 460 | } |
| | 461 | |
| | 462 | data['excerpt'] = $('#excerpt').val() || ''; |
| | 463 | |
| | 464 | if ( local ) |
| | 465 | return data; |
| | 466 | |
| | 467 | $.extend( data, { |
| | 468 | action: 'autosave', |
| | 469 | autosavenonce: $('#autosavenonce').val() || '', |
| | 470 | post_type: $('#post_type').val() || '', |
| | 471 | post_author: $('#post_author').val() || '' |
| | 472 | }); |
| | 473 | |
| | 474 | $('.tags-input').each( function() { |
| | 475 | data[this.name] = this.value; |
| | 476 | }); |
| | 477 | |
| | 478 | $('input[id^="in-category-"]:checked').each( function() { |
| | 479 | cats.push(this.value); |
| | 480 | }); |
| | 481 | data['catslist'] = cats.join(','); |
| | 482 | |
| | 483 | if ( post_name = $('#post_name').val() ) |
| | 484 | data['post_name'] = post_name; |
| | 485 | |
| | 486 | if ( parent_id = $('#parent_id').val() ) |
| | 487 | data['parent_id'] = parent_id; |
| | 488 | |
| | 489 | if ( $('#comment_status').prop('checked') ) |
| | 490 | data['comment_status'] = 'open'; |
| | 491 | |
| | 492 | if ( $('#ping_status').prop('checked') ) |
| | 493 | data['ping_status'] = 'open'; |
| | 494 | |
| | 495 | if ( $('#auto_draft').val() == '1' ) |
| | 496 | data['auto_draft'] = '1'; |
| | 497 | |
| | 498 | return data; |
| | 499 | } |
| | 500 | |
| | 501 | // simple object/mixin for now |
| | 502 | wp.autosave.local = { |
| | 503 | |
| | 504 | lastsaveddata: '', |
| | 505 | unexpired: {}, |
| | 506 | blog_id: 0, |
| | 507 | hasStorage: (function() { |
| | 508 | var test = Math.random(), result; |
| | 509 | // Check if the browser supports localStorage and it's not disabled |
| | 510 | try { |
| | 511 | localStorage.setItem('wp-test', test); |
| | 512 | result = localStorage.getItem('wp-test') == test; |
| | 513 | localStorage.removeItem('wp-test'); |
| | 514 | return result; |
| | 515 | } catch(e) { |
| | 516 | return false; |
| | 517 | } |
| | 518 | }()), |
| | 519 | |
| | 520 | /** |
| | 521 | * Initialize the local storage |
| | 522 | * @return mixed simple object or false if no localStorage in the browser |
| | 523 | */ |
| | 524 | getStorage: function() { |
| | 525 | var storage = false; |
| | 526 | |
| | 527 | // separate local storage containers for each blog_id |
| | 528 | if ( this.hasStorage && this.blog_id ) { |
| | 529 | storage = localStorage.getItem( 'wp_autosave_' + this.blog_id ); |
| | 530 | |
| | 531 | if ( storage ) |
| | 532 | storage = JSON.parse( storage ); |
| | 533 | else |
| | 534 | storage = {}; |
| | 535 | } |
| | 536 | |
| | 537 | return storage; |
| | 538 | }, |
| | 539 | |
| | 540 | setStorage: function( storage_obj ) { |
| | 541 | if ( this.hasStorage ) |
| | 542 | return localStorage.setItem( 'wp_autosave_' + this.blog_id, JSON.stringify( storage_obj ) ); |
| | 543 | }, |
| | 544 | |
| | 545 | getData: function( post_id ) { |
| | 546 | var storage = this.getStorage(); |
| | 547 | |
| | 548 | post_id = post_id || $('#post_ID').val(); |
| | 549 | |
| | 550 | if ( !storage || !post_id ) |
| | 551 | return false; |
| | 552 | |
| | 553 | return storage[ 'post_' + post_id ] || []; |
| | 554 | }, |
| | 555 | |
| | 556 | setData: function( stored_data ) { |
| | 557 | var storage = this.getStorage(), post_id = $('#post_ID').val(), result; |
| | 558 | |
| | 559 | if ( !storage || !post_id ) |
| | 560 | return false; |
| | 561 | |
| | 562 | storage[ 'post_' + post_id ] = stored_data; |
| | 563 | result = this.setStorage(storage); |
| | 564 | |
| | 565 | // TODO do we need this? |
| | 566 | /* |
| | 567 | if ( result === false && stored_data.length > 1 ) { |
| | 568 | // localStorage is full, pop the oldest revision and try again |
| | 569 | while ( result === false && stored_data.length > 1 ) { |
| | 570 | stored_data.pop(); |
| | 571 | storage[ 'post_' + post_id ] = stored_data; |
| | 572 | result = this.setStorage(storage); |
| | 573 | } |
| | 574 | } |
| | 575 | */ |
| | 576 | |
| | 577 | if ( result === false ) { |
| | 578 | // localStorage is still full, throw error? |
| | 579 | return false; |
| | 580 | } |
| | 581 | |
| | 582 | return true; |
| | 583 | }, |
| | 584 | |
| | 585 | /** |
| | 586 | * Set post data for particular post id |
| | 587 | */ |
| | 588 | save: function() { |
| | 589 | var stored_data, post_data, result = false; |
| | 590 | |
| | 591 | // Prepares data for saving in local storage. |
| | 592 | post_data = wp.autosave.getPostData( true ); |
| | 593 | |
| | 594 | // If the content and title are empty or did not change since the last save, don't save again |
| | 595 | if ( ( ! post_data.post_title && ! post_data.content ) || post_data.post_title + post_data.content + post_data.excerpt == this.lastsaveddata ) |
| | 596 | return false; |
| | 597 | |
| | 598 | // cannot save at this moment |
| | 599 | if ( !post_data.autosave ) |
| | 600 | return false; |
| | 601 | |
| | 602 | $.extend( post_data, { |
| | 603 | save_time: (new Date()).getTime(), |
| | 604 | storage_status: 'fresh' |
| | 605 | }); |
| | 606 | |
| | 607 | stored_data = this.getData(); |
| | 608 | |
| | 609 | if ( stored_data ) { |
| | 610 | stored_data.unshift( post_data ); |
| | 611 | |
| | 612 | if ( stored_data.length > 5 ) { |
| | 613 | // Store only the 5 newest revisions |
| | 614 | stored_data.pop(); |
| | 615 | } |
| | 616 | console.log('saved at: '+post_data.save_time) |
| | 617 | result = this.setData( stored_data ); |
| | 618 | |
| | 619 | if ( result ) { |
| | 620 | this.lastsaveddata = post_data.post_title + post_data.content; |
| | 621 | // Update the local revisions as they are added |
| | 622 | this.showItems(); |
| | 623 | } |
| | 624 | } |
| | 625 | |
| | 626 | return result; |
| | 627 | }, |
| | 628 | |
| | 629 | /** |
| | 630 | * Checks if all revisions have expired. Removes expired revisions from local storage after 24 hours. |
| | 631 | */ |
| | 632 | cleanup: function() { |
| | 633 | var self = this, storage = this.getStorage(), current_time = (new Date()).getTime(), deleted = false, has_unexpired = false; |
| | 634 | |
| | 635 | if ( !storage ) |
| | 636 | return; |
| | 637 | |
| | 638 | $.each( storage, function( key, value ) { |
| | 639 | var new_arr; |
| | 640 | |
| | 641 | if ( !value ) |
| | 642 | return; |
| | 643 | |
| | 644 | new_arr = $.grep( value, function( post_data ) { |
| | 645 | if ( post_data.storage_status ) { |
| | 646 | if ( !self.unexpired[key] ) |
| | 647 | self.unexpired[key] = post_data; |
| | 648 | |
| | 649 | has_unexpired = true; |
| | 650 | return true; |
| | 651 | } |
| | 652 | // keep if less than 24 hr |
| | 653 | if ( ( current_time - post_data.save_time ) < 86400000 ) |
| | 654 | return true; |
| | 655 | |
| | 656 | return false; |
| | 657 | }); |
| | 658 | |
| | 659 | if ( new_arr.length != value.length ) |
| | 660 | deleted = true; |
| | 661 | |
| | 662 | if ( new_arr.length ) |
| | 663 | storage[key] = new_arr; |
| | 664 | else |
| | 665 | delete storage[key]; |
| | 666 | }); |
| | 667 | |
| | 668 | if ( deleted ) |
| | 669 | this.setStorage( storage ); |
| | 670 | |
| | 671 | if ( has_unexpired ) { |
| | 672 | // show warning about unexpired post data |
| | 673 | this.showWarning(); |
| | 674 | } |
| | 675 | }, |
| | 676 | |
| | 677 | /** |
| | 678 | * Marks post data as expired |
| | 679 | * |
| | 680 | * For use after remote autosave has completed |
| | 681 | */ |
| | 682 | expire: function() { |
| | 683 | var stored_data = this.getData(); |
| | 684 | |
| | 685 | $.each( stored_data, function( key, post_data ) { |
| | 686 | delete post_data.storage_status; |
| | 687 | }); |
| | 688 | |
| | 689 | return this.setData( stored_data ); |
| | 690 | }, |
| | 691 | |
| | 692 | showItems: function() { |
| | 693 | var self = this, data; |
| | 694 | |
| | 695 | function formatTime( milisec ) { |
| | 696 | var sec = parseInt( milisec / 1000, 10 ) || 0, |
| | 697 | h = Math.floor( sec / 3600 ), |
| | 698 | m = Math.floor( (sec % 3600) / 60 ), |
| | 699 | s = sec - ( (h * 3600) + (m * 60) ); |
| | 700 | |
| | 701 | function zeroize(n) { |
| | 702 | if ( !n ) |
| | 703 | return '00'; |
| | 704 | |
| | 705 | if ( n < 10 ) |
| | 706 | return '0' + n.toString(); |
| | 707 | |
| | 708 | return n.toString(); |
| | 709 | } |
| | 710 | |
| | 711 | return { |
| | 712 | h: zeroize(h), |
| | 713 | m: zeroize(m), |
| | 714 | s: zeroize(s) |
| | 715 | }; |
| | 716 | } |
| | 717 | |
| | 718 | if ( window.pagenow && window.pagenow == 'post' ) { |
| | 719 | data = this.getData(), current_time = (new Date()).getTime(); |
| | 720 | |
| | 721 | if ( data && data.length ) { |
| | 722 | $.each( data, function( key, post_data ) { |
| | 723 | var timeSaved = formatTime( current_time - post_data.save_time ), |
| | 724 | element = $('#localsave-items #localsave-item-' + key); |
| | 725 | console.log(element) |
| | 726 | $('.localsave-no-revisions').remove(); |
| | 727 | |
| | 728 | // can do something here with post_data.storage_status to show non-expired |
| | 729 | // (not saved to the server) backups with different color |
| | 730 | |
| | 731 | if ( ! element.length ) { |
| | 732 | element = $('.localsave-item-main').clone().removeClass('localsave-item-main').attr( 'id', 'localsave-item-' + key ).data('key', key); |
| | 733 | |
| | 734 | $('.localsave-text', element).html( $('.localsave-text', element).text().replace( /%s/, function(){ |
| | 735 | return '<span class="localsave-h">' + timeSaved.h + '</span>:' + |
| | 736 | '<span class="localsave-m">' + timeSaved.m + '</span>:' + |
| | 737 | '<span class="localsave-s">' + timeSaved.s + '</span>'; |
| | 738 | } ) ); |
| | 739 | |
| | 740 | $('#localsave-items').append( element.data('key', key).show() ); |
| | 741 | } else { |
| | 742 | element.data('key', key); |
| | 743 | $('.localsave-h', element).text( timeSaved.h ); |
| | 744 | $('.localsave-m', element).text( timeSaved.m ); |
| | 745 | $('.localsave-s', element).text( timeSaved.s ); |
| | 746 | } |
| | 747 | }); |
| | 748 | } |
| | 749 | } |
| | 750 | }, |
| | 751 | |
| | 752 | preview: function( key ) { |
| | 753 | var data = this.getData(), content, element; |
| | 754 | |
| | 755 | if ( !data[key] ) |
| | 756 | return; // error message? |
| | 757 | |
| | 758 | content = data[key].content; |
| | 759 | |
| | 760 | if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) { |
| | 761 | content = '<div>' + switchEditors.wpautop( content ) + '</div>'; |
| | 762 | } else if ( $('#wp-content-wrap').hasClass('html-active') ) |
| | 763 | content = '<textarea readonly>' + this.esc_html( content ) + '</textarea>'; |
| | 764 | |
| | 765 | element = $('.localsave-preview-wrap').show(); |
| | 766 | element.find('div.localsave-preview').html( content ); |
| | 767 | element.find('a.localsave-do-restore').data('key', key); |
| | 768 | $('#localsave-item-' + key).append( element ); |
| | 769 | }, |
| | 770 | |
| | 771 | closePreview: function() { |
| | 772 | var element = $('.localsave-preview-wrap').css('display', ''); |
| | 773 | |
| | 774 | element.find('div.localsave-preview').empty(); |
| | 775 | element.find('a.localsave-do-restore').removeData('key'); |
| | 776 | $('#localsave-items').append( element ); |
| | 777 | }, |
| | 778 | |
| | 779 | restore: function( key ) { |
| | 780 | var data = this.getData(), content, editor; |
| | 781 | |
| | 782 | if ( !data[key] ) |
| | 783 | return; // error message? |
| | 784 | |
| | 785 | content = data[key].content; |
| | 786 | |
| | 787 | if ( typeof tinymce != 'undefined' ) |
| | 788 | editor = tinymce.get('content'); |
| | 789 | |
| | 790 | if ( typeof switchEditors != 'undefined' && editor && ! editor.isHidden() ) { |
| | 791 | editor.setContent( switchEditors.wpautop( content ) ); |
| | 792 | } else if ( ( editor = $('textarea#content') ) && editor.length ) { |
| | 793 | editor.val( content ); |
| | 794 | } |
| | 795 | |
| | 796 | this.closePreview(); |
| | 797 | }, |
| | 798 | |
| | 799 | init: function() { |
| | 800 | var self = this, post_data = wp.autosave.getPostData( true ); |
| | 801 | |
| | 802 | this.blog_id = typeof window.autosaveL10n != 'undefined' ? window.autosaveL10n.blog_id : 0; |
| | 803 | |
| | 804 | $('#localsave-items').on( 'click.localsave-items', function(e) { |
| | 805 | var target = $(e.target); |
| | 806 | |
| | 807 | if ( target.hasClass('localsave-text') ) { |
| | 808 | self.preview( target.parent().data('key') ); |
| | 809 | } else if ( target.hasClass('localsave-do-close') ) { |
| | 810 | self.closePreview(); |
| | 811 | } else if ( target.hasClass('localsave-do-restore') ) { |
| | 812 | self.restore( target.data('key') ); |
| | 813 | } |
| | 814 | // Stop all links, including links in the preview |
| | 815 | e.preventDefault(); |
| | 816 | }); |
| | 817 | |
| | 818 | // Set the comparison string |
| | 819 | if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) |
| | 820 | this.lastsaveddata = post_data.post_title + switchEditors.pre_wpautop(post_data.content); |
| | 821 | else |
| | 822 | this.lastsaveddata = post_data.post_title + post_data.content; |
| | 823 | |
| | 824 | // Set the schedule |
| | 825 | this.schedule = $.schedule({ |
| | 826 | time: 15 * 1000, |
| | 827 | func: function() { wp.autosave.local.save(); }, |
| | 828 | repeat: true, |
| | 829 | protect: true |
| | 830 | }); |
| | 831 | |
| | 832 | $('form#post').on( 'submit.localsave-cancel', function() { |
| | 833 | $.cancel( self.schedule ); |
| | 834 | }); |
| | 835 | |
| | 836 | // Show the local revisions |
| | 837 | this.showItems(); |
| | 838 | }, |
| | 839 | |
| | 840 | /** |
| | 841 | * Escape html so we can show in div |
| | 842 | * @param string str |
| | 843 | * @return string |
| | 844 | */ |
| | 845 | esc_html: function( str ) { |
| | 846 | return $('<div/>').text(str).html(); |
| | 847 | }, |
| | 848 | |
| | 849 | /** |
| | 850 | * Show warning that one has historical saved data available for recovery in case one wants. |
| | 851 | */ |
| | 852 | showWarning: function() { |
| | 853 | var self = this, current_post_id = $('#post_ID').val() || '', html = '', same_screen = false; |
| | 854 | |
| | 855 | $.each( this.unexpired, function( key, post_data ) { |
| | 856 | var title = post_data.post_title || '#' + post_data.post_id; |
| | 857 | |
| | 858 | if ( window.pagenow && window.pagenow == 'post' && current_post_id == post_data.post_id ) { |
| | 859 | html += '<span class="localsave-recover"> <a class="localsave-recover-local" href="#revisionsdiv">' + self.esc_html(title) + '</a> </span> '; |
| | 860 | same_screen = true; |
| | 861 | } else { |
| | 862 | html += '<span class="localsave-recover"> <a href="post.php?post='+post_data.post_id+'&action=edit&localsave-recover=1#revisionsdiv">' + |
| | 863 | self.esc_html(title) + '</a> </span> '; |
| | 864 | } |
| | 865 | }); |
| | 866 | |
| | 867 | if ( html ) { |
| | 868 | $('#wpbody .wrap h2:first').after( |
| | 869 | '<div class="error" id="localsave-warning">' + |
| | 870 | '<p><strong>Data saved in your browser</strong></p>' + |
| | 871 | '<p>' + html + '</p>' + |
| | 872 | '</div>' |
| | 873 | ); |
| | 874 | |
| | 875 | if ( same_screen ) { |
| | 876 | $('#localsave-warning a.localsave-recover-local').click( function(){ |
| | 877 | // Make sure the Revisions postbox is open and not hidden |
| | 878 | $('#revisionsdiv').removeClass('closed').show(); |
| | 879 | // Hide the warning |
| | 880 | $('#localsave-warning').slideUp('fast', function(){ $(this).remove(); }); |
| | 881 | }); |
| | 882 | } |
| | 883 | } |
| | 884 | } |
| | 885 | } |
| | 886 | |
| | 887 | $(document).ready( function() { |
| | 888 | wp.autosave.local.cleanup(); |
| | 889 | // run on post.php and post-new.php |
| | 890 | if ( 'post' == window.pagenow ) |
| | 891 | wp.autosave.local.init(); |
| | 892 | }); |
| | 893 | |
| | 894 | }(jQuery)); |
| | 895 | |