| | 298 | |
| | 299 | // Autosave in localStorage |
| | 300 | // set as simple object/mixin for now |
| | 301 | window.wp = window.wp || {}; |
| | 302 | wp.autosave = wp.autosave || {}; |
| | 303 | |
| | 304 | (function($){ |
| | 305 | // Returns the data for saving in both localStorage and autosaves to the server |
| | 306 | wp.autosave.getPostData = function() { |
| | 307 | var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [], |
| | 308 | data = { |
| | 309 | action: 'autosave', |
| | 310 | autosave: true, |
| | 311 | post_id: $('#post_ID').val() || 0, |
| | 312 | autosavenonce: $('#autosavenonce').val() || '', |
| | 313 | post_type: $('#post_type').val() || '', |
| | 314 | post_author: $('#post_author').val() || '', |
| | 315 | excerpt: $('#excerpt').val() || '' |
| | 316 | }; |
| | 317 | |
| | 318 | if ( ed && !ed.isHidden() ) { |
| | 319 | // Don't run while the tinymce spellcheck is on. It resets all found words. |
| | 320 | if ( ed.plugins.spellchecker && ed.plugins.spellchecker.active ) { |
| | 321 | data.autosave = false; |
| | 322 | return data; |
| | 323 | } else { |
| | 324 | if ( 'mce_fullscreen' == ed.id ) |
| | 325 | tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'}); |
| | 326 | |
| | 327 | tinymce.triggerSave(); |
| | 328 | } |
| | 329 | } |
| | 330 | |
| | 331 | if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) { |
| | 332 | data['post_title'] = $('#wp-fullscreen-title').val() || ''; |
| | 333 | data['content'] = $('#wp_mce_fullscreen').val() || ''; |
| | 334 | } else { |
| | 335 | data['post_title'] = $('#title').val() || ''; |
| | 336 | data['content'] = $('#content').val() || ''; |
| | 337 | } |
| | 338 | |
| | 339 | /* |
| | 340 | // We haven't been saving tags with autosave since 2.8... Start again? |
| | 341 | $('.the-tags').each( function() { |
| | 342 | data[this.name] = this.value; |
| | 343 | }); |
| | 344 | */ |
| | 345 | |
| | 346 | $('input[id^="in-category-"]:checked').each( function() { |
| | 347 | cats.push(this.value); |
| | 348 | }); |
| | 349 | data['catslist'] = cats.join(','); |
| | 350 | |
| | 351 | if ( post_name = $('#post_name').val() ) |
| | 352 | data['post_name'] = post_name; |
| | 353 | |
| | 354 | if ( parent_id = $('#parent_id').val() ) |
| | 355 | data['parent_id'] = parent_id; |
| | 356 | |
| | 357 | if ( $('#comment_status').prop('checked') ) |
| | 358 | data['comment_status'] = 'open'; |
| | 359 | |
| | 360 | if ( $('#ping_status').prop('checked') ) |
| | 361 | data['ping_status'] = 'open'; |
| | 362 | |
| | 363 | if ( $('#auto_draft').val() == '1' ) |
| | 364 | data['auto_draft'] = '1'; |
| | 365 | |
| | 366 | return data; |
| | 367 | } |
| | 368 | |
| | 369 | wp.autosave.local = { |
| | 370 | |
| | 371 | lastsaveddata: '', |
| | 372 | blog_id: 0, |
| | 373 | ajaxurl: window.ajaxurl || 'wp-admin/admin-ajax.php', |
| | 374 | hasStorage: false, |
| | 375 | |
| | 376 | // Check if the browser supports sessionStorage and it's not disabled |
| | 377 | checkStorage: function() { |
| | 378 | var test = Math.random(), result = false; |
| | 379 | |
| | 380 | try { |
| | 381 | sessionStorage.setItem('wp-test', test); |
| | 382 | result = sessionStorage.getItem('wp-test') == test; |
| | 383 | sessionStorage.removeItem('wp-test'); |
| | 384 | } catch(e) {} |
| | 385 | |
| | 386 | this.hasStorage = result; |
| | 387 | return result; |
| | 388 | }, |
| | 389 | |
| | 390 | /** |
| | 391 | * Initialize the local storage |
| | 392 | * |
| | 393 | * @return mixed False if no sessionStorage in the browser or an Object containing all post_data for this blog |
| | 394 | */ |
| | 395 | getStorage: function() { |
| | 396 | var stored_obj = false; |
| | 397 | // Separate local storage containers for each blog_id |
| | 398 | if ( this.hasStorage && this.blog_id ) { |
| | 399 | stored_obj = sessionStorage.getItem( 'wp-autosave-' + this.blog_id ); |
| | 400 | |
| | 401 | if ( stored_obj ) |
| | 402 | stored_obj = JSON.parse( stored_obj ); |
| | 403 | else |
| | 404 | stored_obj = {}; |
| | 405 | } |
| | 406 | |
| | 407 | return stored_obj; |
| | 408 | }, |
| | 409 | |
| | 410 | /** |
| | 411 | * Set the storage for this blog |
| | 412 | * |
| | 413 | * Confirms that the data was saved successfully. |
| | 414 | * |
| | 415 | * @return bool |
| | 416 | */ |
| | 417 | setStorage: function( stored_obj ) { |
| | 418 | var key; |
| | 419 | |
| | 420 | if ( this.hasStorage && this.blog_id ) { |
| | 421 | key = 'wp-autosave-' + this.blog_id; |
| | 422 | sessionStorage.setItem( key, JSON.stringify( stored_obj ) ); |
| | 423 | return sessionStorage.getItem( key ) !== null; |
| | 424 | } |
| | 425 | |
| | 426 | return false; |
| | 427 | }, |
| | 428 | |
| | 429 | /** |
| | 430 | * Get the saved post data for the current post |
| | 431 | * |
| | 432 | * @return mixed False if no storage or no data or the post_data as an Object |
| | 433 | */ |
| | 434 | getData: function() { |
| | 435 | var stored = this.getStorage(), post_id = $('#post_ID').val(); |
| | 436 | |
| | 437 | if ( !stored || !post_id ) |
| | 438 | return false; |
| | 439 | |
| | 440 | return stored[ 'post_' + post_id ] || false; |
| | 441 | }, |
| | 442 | |
| | 443 | /** |
| | 444 | * Set (save) post data in the storage |
| | 445 | * |
| | 446 | * @return bool |
| | 447 | */ |
| | 448 | setData: function( stored_data ) { |
| | 449 | var stored = this.getStorage(), post_id = $('#post_ID').val(); |
| | 450 | |
| | 451 | if ( !stored || !post_id ) |
| | 452 | return false; |
| | 453 | |
| | 454 | stored[ 'post_' + post_id ] = stored_data; |
| | 455 | |
| | 456 | return this.setStorage(stored); |
| | 457 | }, |
| | 458 | |
| | 459 | /** |
| | 460 | * Save post data for the current post |
| | 461 | * |
| | 462 | * Runs on a 15 sec. schedule, saves when there are differences in the post title or content. |
| | 463 | * When the optional data is provided, updates the last saved post data. |
| | 464 | * |
| | 465 | * $param data optional Object The post data for saving, minimum 'post_title' and 'content' |
| | 466 | * @return bool |
| | 467 | */ |
| | 468 | save: function( data ) { |
| | 469 | var result = false; |
| | 470 | |
| | 471 | if ( ! data ) { |
| | 472 | post_data = wp.autosave.getPostData(); |
| | 473 | } else { |
| | 474 | post_data = this.getData() || {}; |
| | 475 | $.extend( post_data, data ); |
| | 476 | } |
| | 477 | |
| | 478 | // If the content and title are empty or did not change since the last save, don't save again |
| | 479 | if ( post_data.post_title + ': ' + post_data.content == this.lastsaveddata ) |
| | 480 | return false; |
| | 481 | |
| | 482 | // Cannot get the post data at the moment |
| | 483 | if ( !post_data.autosave ) |
| | 484 | return false; |
| | 485 | |
| | 486 | post_data['save_time'] = (new Date()).getTime(); |
| | 487 | post_data['status'] = $('#post_status').val() || ''; |
| | 488 | result = this.setData( post_data ); |
| | 489 | |
| | 490 | if ( result ) |
| | 491 | this.lastsaveddata = post_data.post_title + ': ' + post_data.content; |
| | 492 | |
| | 493 | return result; |
| | 494 | }, |
| | 495 | |
| | 496 | // Initialize and run checkPost() on loading the script (before TinyMCE init) |
| | 497 | init: function( settings ) { |
| | 498 | var self = this; |
| | 499 | |
| | 500 | // Run only on the Add/Edit Post screens and in browsers that have sessionStorage |
| | 501 | if ( 'post' != window.pagenow || ! this.checkStorage() ) |
| | 502 | return; |
| | 503 | // editor.js has to be loaded before autosave.js |
| | 504 | if ( typeof switchEditors == 'undefined' ) |
| | 505 | return; |
| | 506 | |
| | 507 | if ( settings ) |
| | 508 | $.extend( this, settings ); |
| | 509 | |
| | 510 | if ( !this.blog_id ) |
| | 511 | this.blog_id = typeof window.autosaveL10n != 'undefined' ? window.autosaveL10n.blog_id : 0; |
| | 512 | |
| | 513 | this.checkPost(); |
| | 514 | $(document).ready( self.run ); |
| | 515 | }, |
| | 516 | |
| | 517 | // Run on DOM ready |
| | 518 | run: function() { |
| | 519 | var self = this, post_data; |
| | 520 | |
| | 521 | // Set the comparison string |
| | 522 | if ( !this.lastsaveddata ) { |
| | 523 | post_data = wp.autosave.getPostData(); |
| | 524 | |
| | 525 | if ( post_data.content && $('#wp-content-wrap').hasClass('tmce-active') ) |
| | 526 | this.lastsaveddata = post_data.post_title + ': ' + switchEditors.pre_wpautop( post_data.content ); |
| | 527 | else |
| | 528 | this.lastsaveddata = post_data.post_title + ': ' + post_data.content; |
| | 529 | } |
| | 530 | |
| | 531 | // Set the schedule |
| | 532 | this.schedule = $.schedule({ |
| | 533 | time: 15 * 1000, |
| | 534 | func: function() { wp.autosave.local.save(); }, |
| | 535 | repeat: true, |
| | 536 | protect: true |
| | 537 | }); |
| | 538 | |
| | 539 | $('form#post').on('submit.autosave-local', function() { |
| | 540 | var editor = typeof tinymce != 'undefined' && tinymce.get('content'); |
| | 541 | |
| | 542 | if ( editor && ! editor.isHidden() ) { |
| | 543 | // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea. |
| | 544 | editor.onSubmit.add( function() { |
| | 545 | wp.autosave.local.save({ |
| | 546 | post_title: $('#title').val() || '', |
| | 547 | content: $('#content').val() || '', |
| | 548 | excerpt: $('#excerpt').val() || '' |
| | 549 | }); |
| | 550 | }); |
| | 551 | } else { |
| | 552 | self.save({ |
| | 553 | post_title: $('#title').val() || '', |
| | 554 | content: $('#content').val() || '', |
| | 555 | excerpt: $('#excerpt').val() || '' |
| | 556 | }); |
| | 557 | } |
| | 558 | }); |
| | 559 | }, |
| | 560 | |
| | 561 | // Strip whitespace and compare two strings |
| | 562 | compare: function( str1, str2 ) { |
| | 563 | function strip_space( string ) { |
| | 564 | return string.toString().replace(/[\x20\t\r\n\f]+/g, ''); |
| | 565 | } |
| | 566 | |
| | 567 | return ( strip_space( str1 || '' ) == strip_space( str2 || '' ) ); |
| | 568 | }, |
| | 569 | |
| | 570 | /** |
| | 571 | * Check if the saved data for the current post (if any) is different than the loaded post data on the screen |
| | 572 | * |
| | 573 | * Shows a standard message letting the user restore the post data if different. |
| | 574 | * |
| | 575 | * @return void |
| | 576 | */ |
| | 577 | checkPost: function() { |
| | 578 | var self = this, post_data = this.getData(), content, match = false, check_data, notice; |
| | 579 | |
| | 580 | if ( post_data ) { |
| | 581 | content = $('#content').val(); |
| | 582 | check_data = $.extend( {}, post_data ); |
| | 583 | |
| | 584 | if ( $('#wp-content-wrap').hasClass('tmce-active') ) |
| | 585 | content = switchEditors.pre_wpautop( content ); |
| | 586 | |
| | 587 | if ( this.compare( content, check_data.content ) && this.compare( $('#title').val(), check_data.post_title ) && this.compare( $('#excerpt').val(), check_data.excerpt ) ) |
| | 588 | match = true; |
| | 589 | |
| | 590 | if ( ! match ) { |
| | 591 | // We have three choices here: |
| | 592 | // - Do an autosave and then show the standard notice "There is an autosave newer than...". |
| | 593 | // - Offer to load/restore the backed up post data. |
| | 594 | // - Restore the post_data without asking, then show a notice with an Undo link/button. |
| | 595 | // Doing an autosave will take few seconds and may take up to 30 and fail if network connectivity is bad |
| | 596 | // Restoring the post will leave the user with the proper content, but it won't be saved to the server until the next autosave. |
| | 597 | |
| | 598 | this.restore_post_data = post_data; |
| | 599 | this.undo_post_data = wp.autosave.getPostData(); |
| | 600 | |
| | 601 | /* |
| | 602 | if ( $('#post_status').val() == 'publish' ) { |
| | 603 | // Different message when a post is published? |
| | 604 | // Comparing the current and saved post data may fail (false positive) when the post is published |
| | 605 | // as in some cases there are changes to post_content on publishing and updating before saving to the DB. |
| | 606 | } |
| | 607 | */ |
| | 608 | |
| | 609 | notice = $('#local-storage-notice'); |
| | 610 | $('form#post').before( notice.addClass('updated').show() ); |
| | 611 | |
| | 612 | notice.on( 'click', function(e) { |
| | 613 | var target = $( e.target ); |
| | 614 | |
| | 615 | if ( target.hasClass('restore-backup') ) { |
| | 616 | self.restorePost( self.restore_post_data ); |
| | 617 | target.parent().hide(); |
| | 618 | $(this).find('p.undo-restore').show(); |
| | 619 | } else if ( target.hasClass('undo-restore-backup') ) { |
| | 620 | self.restorePost( self.undo_post_data ); |
| | 621 | target.parent().hide(); |
| | 622 | $(this).find('p.local-restore').show(); |
| | 623 | } |
| | 624 | |
| | 625 | e.preventDefault(); |
| | 626 | }); |
| | 627 | } |
| | 628 | } |
| | 629 | }, |
| | 630 | |
| | 631 | // Restore the current title, content and excerpt from post_data. |
| | 632 | restorePost: function( post_data ) { |
| | 633 | var editor; |
| | 634 | |
| | 635 | if ( post_data ) { |
| | 636 | // Set the last saved data |
| | 637 | this.lastsaveddata = post_data.post_title + ': ' + post_data.content; |
| | 638 | |
| | 639 | if ( $('#title').val() != post_data.post_title ) |
| | 640 | $('#title').focus().val( post_data.post_title || '' ); |
| | 641 | |
| | 642 | $('#excerpt').val( post_data.excerpt || '' ); |
| | 643 | editor = typeof tinymce != 'undefined' && tinymce.get('content'); |
| | 644 | |
| | 645 | if ( editor && ! editor.isHidden() ) { |
| | 646 | // Make sure there's an undo level in the editor |
| | 647 | editor.undoManager.add(); |
| | 648 | editor.setContent( post_data.content ? switchEditors.wpautop( post_data.content ) : '' ); |
| | 649 | } else { |
| | 650 | // Make sure the Text editor is selected |
| | 651 | $('#content-html').click(); |
| | 652 | $('#content').val( post_data.content ); |
| | 653 | } |
| | 654 | |
| | 655 | return true; |
| | 656 | } |
| | 657 | |
| | 658 | return false; |
| | 659 | } |
| | 660 | } |
| | 661 | |
| | 662 | wp.autosave.local.init(); |
| | 663 | |
| | 664 | }(jQuery)); |