| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /** 2 * @file Contains all dynamic functionality needed on post and term pages. 3 * 4 * @output wp-admin/js/post.js 5 */ 6 7 /* global ajaxurl, wpAjax, postboxes, pagenow, tinymce, alert, deleteUserSetting, ClipboardJS */ 8 /* global theList:true, theExtraList:true, getUserSetting, setUserSetting, commentReply, commentsBox */ 9 /* global WPSetThumbnailHTML, wptitlehint */ 10 11 // Backward compatibility: prevent fatal errors. 12 window.makeSlugeditClickable = window.editPermalink = function(){}; 13 14 // Make sure the wp object exists. 15 window.wp = window.wp || {}; 16 17 /** 18 * Handles the dynamic functionality needed on post and term pages. 19 * 20 * @param {JQueryStatic} $ The jQuery object. 21 */ 22 ( function( $ ) { 23 var titleHasFocus = false, 24 __ = wp.i18n.__; 25 26 /** 27 * Control loading of comments on the post and term edit pages. 28 * 29 * @type {{st: number, get: commentsBox.get, load: commentsBox.load}} 30 * 31 * @namespace commentsBox 32 */ 33 window.commentsBox = { 34 // Comment offset to use when fetching new comments. 35 st : 0, 36 37 /** 38 * Fetch comments using Ajax and display them in the box. 39 * 40 * @memberof commentsBox 41 * 42 * @param {number} total Total number of comments for this post. 43 * @param {number} num Optional. Number of comments to fetch, defaults to 10. 44 * @return {boolean} Always returns false. 45 */ 46 get : function(total, num) { 47 var st = this.st, data; 48 if ( ! num ) 49 num = 10; 50 51 this.st += num; 52 this.total = total; 53 $( '#commentsdiv .spinner' ).addClass( 'is-active' ); 54 55 data = { 56 'action' : 'get-comments', 57 'mode' : 'single', 58 '_ajax_nonce' : $('#add_comment_nonce').val(), 59 'p' : $('#post_ID').val(), 60 'start' : st, 61 'number' : num 62 }; 63 64 $.post( 65 ajaxurl, 66 data, 67 function(r) { 68 r = wpAjax.parseAjaxResponse(r); 69 $('#commentsdiv .widefat').show(); 70 $( '#commentsdiv .spinner' ).removeClass( 'is-active' ); 71 72 if ( 'object' == typeof r && r.responses[0] ) { 73 $('#the-comment-list').append( r.responses[0].data ); 74 75 theList = theExtraList = null; 76 $( 'a[className*=\':\']' ).off(); 77 78 // If the offset is over the total number of comments we cannot fetch any more, so hide the button. 79 if ( commentsBox.st > commentsBox.total ) 80 $('#show-comments').hide(); 81 else 82 $('#show-comments').show().children('a').text( __( 'Show more comments' ) ); 83 84 return; 85 } else if ( 1 == r ) { 86 $('#show-comments').text( __( 'No more comments found.' ) ); 87 return; 88 } 89 90 $('#the-comment-list').append('<tr><td colspan="2">'+wpAjax.broken+'</td></tr>'); 91 } 92 ); 93 94 return false; 95 }, 96 97 /** 98 * Load the next batch of comments. 99 * 100 * @memberof commentsBox 101 * 102 * @param {number} total Total number of comments to load. 103 */ 104 load: function(total){ 105 this.st = jQuery('#the-comment-list tr[id^="comment-"]:visible').length; 106 this.get(total); 107 } 108 }; 109 110 /** 111 * Overwrite the content of the Featured Image postbox 112 * 113 * @param {string} html New HTML to be displayed in the content area of the postbox. 114 * 115 * @global 116 */ 117 window.WPSetThumbnailHTML = function(html){ 118 $('.inside', '#postimagediv').html(html); 119 }; 120 121 /** 122 * Set the Image ID of the Featured Image 123 * 124 * @param {number} id The post_id of the image to use as Featured Image. 125 * 126 * @global 127 */ 128 window.WPSetThumbnailID = function(id){ 129 var field = $('input[value="_thumbnail_id"]', '#list-table'); 130 if ( field.length > 0 ) { 131 $('#meta\\[' + field.attr('id').match(/[0-9]+/) + '\\]\\[value\\]').text(id); 132 } 133 }; 134 135 /** 136 * Remove the Featured Image 137 * 138 * @param {string} nonce Nonce to use in the request. 139 * 140 * @global 141 */ 142 window.WPRemoveThumbnail = function(nonce){ 143 $.post( 144 ajaxurl, { 145 action: 'set-post-thumbnail', 146 post_id: $( '#post_ID' ).val(), 147 thumbnail_id: -1, 148 _ajax_nonce: nonce, 149 cookie: encodeURIComponent( document.cookie ) 150 }, 151 /** 152 * Handle server response 153 * 154 * @param {string} str Response, will be '0' when an error occurred otherwise contains link to add Featured Image. 155 */ 156 function(str){ 157 if ( str == '0' ) { 158 alert( __( 'Could not set that as the thumbnail image. Try a different attachment.' ) ); 159 } else { 160 WPSetThumbnailHTML(str); 161 } 162 } 163 ); 164 }; 165 166 /** 167 * Heartbeat locks. 168 * 169 * Used to lock editing of an object by only one user at a time. 170 * 171 * When the user does not send a heartbeat in a heartbeat-time 172 * the user is no longer editing and another user can start editing. 173 */ 174 $(document).on( 'heartbeat-send.refresh-lock', function( e, data ) { 175 var lock = $('#active_post_lock').val(), 176 post_id = $('#post_ID').val(), 177 send = {}; 178 179 if ( ! post_id || ! $('#post-lock-dialog').length ) 180 return; 181 182 send.post_id = post_id; 183 184 if ( lock ) 185 send.lock = lock; 186 187 data['wp-refresh-post-lock'] = send; 188 189 }).on( 'heartbeat-tick.refresh-lock', function( e, data ) { 190 // Post locks: update the lock string or show the dialog if somebody has taken over editing. 191 var received, wrap, avatar; 192 193 if ( data['wp-refresh-post-lock'] ) { 194 received = data['wp-refresh-post-lock']; 195 196 if ( received.lock_error ) { 197 // Show "editing taken over" message. 198 wrap = $('#post-lock-dialog'); 199 200 if ( wrap.length && ! wrap.is(':visible') ) { 201 if ( wp.autosave ) { 202 // Save the latest changes and disable. 203 $(document).one( 'heartbeat-tick', function() { 204 wp.autosave.server.suspend(); 205 wrap.removeClass('saving').addClass('saved'); 206 $(window).off( 'beforeunload.edit-post' ); 207 }); 208 209 wrap.addClass('saving'); 210 wp.autosave.server.triggerSave(); 211 } 212 213 if ( received.lock_error.avatar_src ) { 214 avatar = $( '<img />', { 215 'class': 'avatar avatar-64 photo', 216 width: 64, 217 height: 64, 218 alt: '', 219 src: received.lock_error.avatar_src, 220 srcset: received.lock_error.avatar_src_2x ? 221 received.lock_error.avatar_src_2x + ' 2x' : 222 undefined 223 } ); 224 wrap.find('div.post-locked-avatar').empty().append( avatar ); 225 } 226 227 wrap.show().find('.currently-editing').text( received.lock_error.text ); 228 wrap.find('.wp-tab-first').trigger( 'focus' ); 229 } 230 } else if ( received.new_lock ) { 231 $('#active_post_lock').val( received.new_lock ); 232 } 233 } 234 }).on( 'before-autosave.update-post-slug', function() { 235 titleHasFocus = document.activeElement && document.activeElement.id === 'title'; 236 }).on( 'after-autosave.update-post-slug', function() { 237 238 /* 239 * Create slug area only if not already there 240 * and the title field was not focused (user was not typing a title) when autosave ran. 241 */ 242 if ( ! $('#edit-slug-box > *').length && ! titleHasFocus ) { 243 $.post( ajaxurl, { 244 action: 'sample-permalink', 245 post_id: $('#post_ID').val(), 246 new_title: $('#title').val(), 247 samplepermalinknonce: $('#samplepermalinknonce').val() 248 }, 249 function( data ) { 250 if ( data != '-1' ) { 251 $('#edit-slug-box').html(data); 252 } 253 } 254 ); 255 } 256 }); 257 258 }(jQuery)); 259 260 /** 261 * Handles the Heartbeat refresh nonces. 262 * 263 * @param {JQueryStatic} $ The jQuery object. 264 */ 265 (function($) { 266 var check, timeout; 267 268 /** 269 * Only allow to check for nonce refresh every 30 seconds. 270 */ 271 function schedule() { 272 check = false; 273 window.clearTimeout( timeout ); 274 timeout = window.setTimeout( function(){ check = true; }, 300000 ); 275 } 276 277 $( function() { 278 schedule(); 279 }).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { 280 var post_id, 281 $authCheck = $('#wp-auth-check-wrap'); 282 283 if ( check || ( $authCheck.length && ! $authCheck.hasClass( 'hidden' ) ) ) { 284 if ( ( post_id = $('#post_ID').val() ) && $('#_wpnonce').val() ) { 285 data['wp-refresh-post-nonces'] = { 286 post_id: post_id 287 }; 288 } 289 } 290 }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { 291 var nonces = data['wp-refresh-post-nonces']; 292 293 if ( nonces ) { 294 schedule(); 295 296 if ( nonces.replace ) { 297 $.each( nonces.replace, function( selector, value ) { 298 $( '#' + selector ).val( value ); 299 }); 300 } 301 302 if ( nonces.heartbeatNonce ) 303 window.heartbeatSettings.nonce = nonces.heartbeatNonce; 304 } 305 }); 306 }(jQuery)); 307 308 /** 309 * Handles all post and postbox controls and functionality. 310 * 311 * @param {JQueryStatic} $ The jQuery object. 312 */ 313 jQuery( function($) { 314 var stamp, visibility, $submitButtons, updateVisibility, updateText, 315 $textarea = $('#content'), 316 $document = $(document), 317 postId = $('#post_ID').val() || 0, 318 $submitpost = $('#submitpost'), 319 releaseLock = true, 320 $postVisibilitySelect = $('#post-visibility-select'), 321 $timestampdiv = $('#timestampdiv'), 322 $postStatusSelect = $('#post-status-select'), 323 isMac = window.navigator.platform ? window.navigator.platform.indexOf( 'Mac' ) !== -1 : false, 324 copyAttachmentURLClipboard = new ClipboardJS( '.copy-attachment-url.edit-media' ), 325 copyAttachmentURLSuccessTimeout, 326 __ = wp.i18n.__, _x = wp.i18n._x; 327 328 postboxes.add_postbox_toggles(pagenow); 329 330 /* 331 * Clear the window name. Otherwise if this is a former preview window where the user navigated to edit another post, 332 * and the first post is still being edited, clicking Preview there will use this window to show the preview. 333 */ 334 window.name = ''; 335 336 // Post locks: contain focus inside the dialog. If the dialog is shown, focus the first item. 337 $('#post-lock-dialog .notification-dialog').on( 'keydown', function(e) { 338 // Don't do anything when [Tab] is pressed. 339 if ( e.which != 9 ) 340 return; 341 342 var target = $(e.target); 343 344 // [Shift] + [Tab] on first tab cycles back to last tab. 345 if ( target.hasClass('wp-tab-first') && e.shiftKey ) { 346 $(this).find('.wp-tab-last').trigger( 'focus' ); 347 e.preventDefault(); 348 // [Tab] on last tab cycles back to first tab. 349 } else if ( target.hasClass('wp-tab-last') && ! e.shiftKey ) { 350 $(this).find('.wp-tab-first').trigger( 'focus' ); 351 e.preventDefault(); 352 } 353 }).filter(':visible').find('.wp-tab-first').trigger( 'focus' ); 354 355 // Set the heartbeat interval to 10 seconds if post lock dialogs are enabled. 356 if ( wp.heartbeat && $('#post-lock-dialog').length ) { 357 wp.heartbeat.interval( 10 ); 358 } 359 360 // The form is being submitted by the user. 361 $submitButtons = $submitpost.find( ':submit, a.submitdelete, #post-preview' ).on( 'click.edit-post', function( event ) { 362 var $button = $(this); 363 364 if ( $button.hasClass('disabled') ) { 365 event.preventDefault(); 366 return; 367 } 368 369 if ( $button.hasClass('submitdelete') || $button.is( '#post-preview' ) ) { 370 return; 371 } 372 373 // The form submission can be blocked from JS or by using HTML 5.0 validation on some fields. 374 // Run this only on an actual 'submit'. 375 $('form#post').off( 'submit.edit-post' ).on( 'submit.edit-post', function( event ) { 376 if ( event.isDefaultPrevented() ) { 377 return; 378 } 379 380 // Stop auto save. 381 if ( wp.autosave ) { 382 wp.autosave.server.suspend(); 383 } 384 385 if ( typeof commentReply !== 'undefined' ) { 386 /* 387 * Warn the user they have an unsaved comment before submitting 388 * the post data for update. 389 */ 390 if ( ! commentReply.discardCommentChanges() ) { 391 return false; 392 } 393 394 /* 395 * Close the comment edit/reply form if open to stop the form 396 * action from interfering with the post's form action. 397 */ 398 commentReply.close(); 399 } 400 401 releaseLock = false; 402 $(window).off( 'beforeunload.edit-post' ); 403 404 $submitButtons.addClass( 'disabled' ); 405 406 if ( $button.attr('id') === 'publish' ) { 407 $submitpost.find( '#major-publishing-actions .spinner' ).addClass( 'is-active' ); 408 } else { 409 $submitpost.find( '#minor-publishing .spinner' ).addClass( 'is-active' ); 410 } 411 }); 412 }); 413 414 // Submit the form saving a draft or an autosave, and show a preview in a new tab. 415 $('#post-preview').on( 'click.post-preview', function( event ) { 416 var $this = $(this), 417 $form = $('form#post'), 418 $previewField = $('input#wp-preview'), 419 target = $this.attr('target') || 'wp-preview', 420 ua = navigator.userAgent.toLowerCase(); 421 422 event.preventDefault(); 423 424 if ( $this.hasClass('disabled') ) { 425 return; 426 } 427 428 if ( wp.autosave ) { 429 wp.autosave.server.tempBlockSave(); 430 } 431 432 $previewField.val('dopreview'); 433 $form.attr( 'target', target ).trigger( 'submit' ).attr( 'target', '' ); 434 435 // Workaround for WebKit bug preventing a form submitting twice to the same action. 436 // https://bugs.webkit.org/show_bug.cgi?id=28633 437 if ( ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1 ) { 438 $form.attr( 'action', function( index, value ) { 439 return value + '?t=' + ( new Date() ).getTime(); 440 }); 441 } 442 443 $previewField.val(''); 444 }); 445 446 // Auto save new posts after a title is typed. 447 if ( $( '#auto_draft' ).val() ) { 448 $( '#title' ).on( 'blur', function() { 449 var cancel; 450 451 if ( ! this.value || $('#edit-slug-box > *').length ) { 452 return; 453 } 454 455 // Cancel the auto save when the blur was triggered by the user submitting the form. 456 $('form#post').one( 'submit', function() { 457 cancel = true; 458 }); 459 460 window.setTimeout( function() { 461 if ( ! cancel && wp.autosave ) { 462 wp.autosave.server.triggerSave(); 463 } 464 }, 200 ); 465 }); 466 } 467 468 $document.on( 'autosave-disable-buttons.edit-post', function() { 469 $submitButtons.addClass( 'disabled' ); 470 }).on( 'autosave-enable-buttons.edit-post', function() { 471 if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) { 472 $submitButtons.removeClass( 'disabled' ); 473 } 474 }).on( 'before-autosave.edit-post', function() { 475 $( '.autosave-message' ).text( __( 'Saving Draft…' ) ); 476 }).on( 'after-autosave.edit-post', function( event, data ) { 477 $( '.autosave-message' ).text( data.message ); 478 479 if ( $( document.body ).hasClass( 'post-new-php' ) ) { 480 $( '.submitbox .submitdelete' ).show(); 481 } 482 }); 483 484 /* 485 * When the user is trying to load another page, or reloads current page 486 * show a confirmation dialog when there are unsaved changes. 487 */ 488 $( window ).on( 'beforeunload.edit-post', function( event ) { 489 var editor = window.tinymce && window.tinymce.get( 'content' ); 490 var changed = false; 491 492 if ( wp.autosave ) { 493 changed = wp.autosave.server.postChanged(); 494 } else if ( editor ) { 495 changed = ( ! editor.isHidden() && editor.isDirty() ); 496 } 497 498 if ( changed ) { 499 event.preventDefault(); 500 // The return string is needed for browser compat. 501 // See https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event. 502 return __( 'The changes you made will be lost if you navigate away from this page.' ); 503 } 504 }).on( 'pagehide.edit-post', function( event ) { 505 if ( ! releaseLock ) { 506 return; 507 } 508 509 /* 510 * Unload is triggered (by hand) on removing the Thickbox iframe. 511 * Make sure we process only the main document unload. 512 */ 513 if ( event.target && event.target.nodeName != '#document' ) { 514 return; 515 } 516 517 var postID = $('#post_ID').val(); 518 var postLock = $('#active_post_lock').val(); 519 520 if ( ! postID || ! postLock ) { 521 return; 522 } 523 524 var data = { 525 action: 'wp-remove-post-lock', 526 _wpnonce: $('#_wpnonce').val(), 527 post_ID: postID, 528 active_post_lock: postLock 529 }; 530 531 if ( window.FormData && window.navigator.sendBeacon ) { 532 var formData = new window.FormData(); 533 534 $.each( data, function( key, value ) { 535 formData.append( key, value ); 536 }); 537 538 if ( window.navigator.sendBeacon( ajaxurl, formData ) ) { 539 return; 540 } 541 } 542 543 // Fall back to a synchronous POST request. 544 // See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon 545 $.post({ 546 async: false, 547 data: data, 548 url: ajaxurl 549 }); 550 }); 551 552 // Multiple taxonomies. 553 if ( $('#tagsdiv-post_tag').length ) { 554 window.tagBox && window.tagBox.init(); 555 } else { 556 $('.meta-box-sortables').children('div.postbox').each(function(){ 557 if ( this.id.indexOf('tagsdiv-') === 0 ) { 558 window.tagBox && window.tagBox.init(); 559 return false; 560 } 561 }); 562 } 563 564 // Handle categories. 565 $('.categorydiv').each( function(){ 566 var this_id = $(this).attr('id'), catAddBefore, catAddAfter, taxonomyParts, taxonomy, settingName; 567 568 taxonomyParts = this_id.split('-'); 569 taxonomyParts.shift(); 570 taxonomy = taxonomyParts.join('-'); 571 settingName = taxonomy + '_tab'; 572 573 if ( taxonomy == 'category' ) { 574 settingName = 'cats'; 575 } 576 577 // @todo Move to jQuery 1.3+, support for multiple hierarchical taxonomies, see wp-lists.js. 578 $('a', '#' + taxonomy + '-tabs').on( 'click keyup keydown', function( event ) { 579 var t = $(this).attr('href'); 580 if ( event.type === 'keydown' && event.key === ' ' ) { 581 event.preventDefault(); 582 } 583 if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) { 584 event.preventDefault(); 585 $('#' + taxonomy + '-tabs a').removeAttr( 'aria-selected' ).attr( 'tabindex', '-1' ); 586 $(this).attr( 'aria-selected', 'true' ).removeAttr( 'tabindex' ); 587 $(this).parent().addClass('tabs').siblings('li').removeClass('tabs'); 588 $('#' + taxonomy + '-tabs').siblings('.tabs-panel').hide(); 589 $(t).show(); 590 if ( '#' + taxonomy + '-all' == t ) { 591 deleteUserSetting( settingName ); 592 } else { 593 setUserSetting( settingName, 'pop' ); 594 } 595 } 596 if ( event.type === 'keyup' && ( event.key === 'ArrowRight' || event.key === 'ArrowLeft' ) ) { 597 $(this).attr( 'tabindex', '-1' ); 598 let next = $(this).parent('li').next(); 599 let prev = $(this).parent('li').prev(); 600 if ( next.length > 0 ) { 601 next.find('a').removeAttr( 'tabindex'); 602 next.find('a').trigger( 'focus' ); 603 } else { 604 prev.find('a').removeAttr( 'tabindex'); 605 prev.find('a').trigger( 'focus' ); 606 } 607 } 608 }); 609 610 if ( getUserSetting( settingName ) ) 611 $('a[href="#' + taxonomy + '-pop"]', '#' + taxonomy + '-tabs').trigger( 'click' ); 612 613 // Add category button controls. 614 $('#new' + taxonomy).one( 'focus', function() { 615 $( this ).val( '' ).removeClass( 'form-input-tip' ); 616 }); 617 618 // On [Enter] submit the taxonomy. 619 $('#new' + taxonomy).on( 'keypress', function(event){ 620 if( 13 === event.keyCode ) { 621 event.preventDefault(); 622 $('#' + taxonomy + '-add-submit').trigger( 'click' ); 623 } 624 }); 625 626 // After submitting a new taxonomy, re-focus the input field. 627 $('#' + taxonomy + '-add-submit').on( 'click', function() { 628 $('#new' + taxonomy).trigger( 'focus' ); 629 }); 630 631 /** 632 * Disables the submit button before adding a new taxonomy. 633 * 634 * @param {Object} s Taxonomy object which will be added. 635 * 636 * @return {Object} Taxonomy object with additional data to be sent to the server. 637 */ 638 catAddBefore = function( s ) { 639 if ( !$('#new'+taxonomy).val() ) { 640 return false; 641 } 642 643 s.data += '&' + $( ':checked', '#'+taxonomy+'checklist' ).serialize(); 644 $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', true ); 645 return s; 646 }; 647 648 /** 649 * Re-enable submit button after a taxonomy has been added. 650 * 651 * Re-enable submit button. 652 * If the taxonomy has a parent place the taxonomy underneath the parent. 653 * 654 * @param {Object} r Response. 655 * @param {Object} s Taxonomy data. 656 * 657 * @return {void} 658 */ 659 catAddAfter = function( r, s ) { 660 var sup, drop = $('#new'+taxonomy+'_parent'); 661 662 $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', false ); 663 if ( 'undefined' != s.parsed.responses[0] && (sup = s.parsed.responses[0].supplemental.newcat_parent) ) { 664 drop.before(sup); 665 drop.remove(); 666 } 667 }; 668 669 $('#' + taxonomy + 'checklist').wpList({ 670 alt: '', 671 response: taxonomy + '-ajax-response', 672 addBefore: catAddBefore, 673 addAfter: catAddAfter 674 }); 675 676 // Add new taxonomy button toggles input form visibility. 677 $('#' + taxonomy + '-add-toggle').on( 'click', function( e ) { 678 e.preventDefault(); 679 $('#' + taxonomy + '-adder').toggleClass( 'wp-hidden-children' ); 680 $('a[href="#' + taxonomy + '-all"]', '#' + taxonomy + '-tabs').trigger( 'click' ); 681 $('#new'+taxonomy).trigger( 'focus' ); 682 }); 683 684 // Sync checked items between "All {taxonomy}" and "Most used" lists. 685 $('#' + taxonomy + 'checklist, #' + taxonomy + 'checklist-pop').on( 686 'click', 687 'li.popular-category > label input[type="checkbox"]', 688 function() { 689 var t = $(this), c = t.is(':checked'), id = t.val(); 690 if ( id && t.parents('#taxonomy-'+taxonomy).length ) { 691 $('input#in-' + taxonomy + '-' + id + ', input[id^="in-' + taxonomy + '-' + id + '-"]').prop('checked', c); 692 $('input#in-popular-' + taxonomy + '-' + id).prop('checked', c); 693 } 694 } 695 ); 696 697 }); // End cats. 698 699 // Custom Fields postbox. 700 if ( $('#postcustom').length ) { 701 $( '#the-list' ).wpList( { 702 /** 703 * Add current post_ID to request to fetch custom fields 704 * 705 * @ignore 706 * 707 * @param {Object} s Request object. 708 * 709 * @return {Object} Data modified with post_ID attached. 710 */ 711 addBefore: function( s ) { 712 s.data += '&post_id=' + $('#post_ID').val(); 713 return s; 714 }, 715 /** 716 * Show the listing of custom fields after fetching. 717 * 718 * @ignore 719 */ 720 addAfter: function() { 721 $('table#list-table').show(); 722 } 723 }); 724 } 725 726 /* 727 * Publish Post box (#submitdiv) 728 */ 729 if ( $('#submitdiv').length ) { 730 stamp = $('#timestamp').html(); 731 visibility = $('#post-visibility-display').html(); 732 733 /** 734 * When the visibility of a post changes sub-options should be shown or hidden. 735 * 736 * @ignore 737 * 738 * @return {void} 739 */ 740 updateVisibility = function() { 741 // Show sticky for public posts. 742 if ( $postVisibilitySelect.find('input:radio:checked').val() != 'public' ) { 743 $('#sticky').prop('checked', false); 744 $('#sticky-span').hide(); 745 } else { 746 $('#sticky-span').show(); 747 } 748 749 // Show password input field for password protected post. 750 if ( $postVisibilitySelect.find('input:radio:checked').val() != 'password' ) { 751 $('#password-span').hide(); 752 } else { 753 $('#password-span').show(); 754 } 755 }; 756 757 /** 758 * Make sure all labels represent the current settings. 759 * 760 * @ignore 761 * 762 * @return {boolean} False when an invalid timestamp has been selected, otherwise True. 763 */ 764 updateText = function() { 765 766 if ( ! $timestampdiv.length ) 767 return true; 768 769 var attemptedDate, originalDate, currentDate, publishOn, postStatus = $('#post_status'), 770 optPublish = $('option[value="publish"]', postStatus), aa = $('#aa').val(), 771 mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val(); 772 773 attemptedDate = new Date( aa, mm - 1, jj, hh, mn ); 774 originalDate = new Date( 775 $('#hidden_aa').val(), 776 $('#hidden_mm').val() -1, 777 $('#hidden_jj').val(), 778 $('#hidden_hh').val(), 779 $('#hidden_mn').val() 780 ); 781 currentDate = new Date( 782 $('#cur_aa').val(), 783 $('#cur_mm').val() -1, 784 $('#cur_jj').val(), 785 $('#cur_hh').val(), 786 $('#cur_mn').val() 787 ); 788 789 // Catch unexpected date problems. 790 if ( 791 attemptedDate.getFullYear() != aa || 792 (1 + attemptedDate.getMonth()) != mm || 793 attemptedDate.getDate() != jj || 794 attemptedDate.getMinutes() != mn 795 ) { 796 $timestampdiv.find('.timestamp-wrap').addClass('form-invalid'); 797 return false; 798 } else { 799 $timestampdiv.find('.timestamp-wrap').removeClass('form-invalid'); 800 } 801 802 // Determine what the publish should be depending on the date and post status. 803 if ( attemptedDate > currentDate ) { 804 publishOn = __( 'Schedule for:' ); 805 $('#publish').val( _x( 'Schedule', 'post action/button label' ) ); 806 } else if ( attemptedDate <= currentDate && $('#original_post_status').val() != 'publish' ) { 807 publishOn = __( 'Publish on:' ); 808 $('#publish').val( __( 'Publish' ) ); 809 } else { 810 publishOn = __( 'Published on:' ); 811 $('#publish').val( __( 'Update' ) ); 812 } 813 814 // If the date is the same, set it to trigger update events. 815 if ( originalDate.toUTCString() == attemptedDate.toUTCString() ) { 816 // Re-set to the current value. 817 $('#timestamp').html(stamp); 818 } else { 819 $('#timestamp').html( 820 '\n' + publishOn + ' <b>' + 821 // translators: 1: Month, 2: Day, 3: Year, 4: Hour, 5: Minute. 822 __( '%1$s %2$s, %3$s at %4$s:%5$s' ) 823 .replace( '%1$s', $( 'option[value="' + mm + '"]', '#mm' ).attr( 'data-text' ) ) 824 .replace( '%2$s', parseInt( jj, 10 ) ) 825 .replace( '%3$s', aa ) 826 .replace( '%4$s', ( '00' + hh ).slice( -2 ) ) 827 .replace( '%5$s', ( '00' + mn ).slice( -2 ) ) + 828 '</b> ' 829 ); 830 } 831 832 // Add "privately published" to post status when applies. 833 if ( $postVisibilitySelect.find('input:radio:checked').val() == 'private' ) { 834 $('#publish').val( __( 'Update' ) ); 835 if ( 0 === optPublish.length ) { 836 postStatus.append('<option value="publish">' + __( 'Privately Published' ) + '</option>'); 837 } else { 838 optPublish.html( __( 'Privately Published' ) ); 839 } 840 $('option[value="publish"]', postStatus).prop('selected', true); 841 $('#misc-publishing-actions .edit-post-status').hide(); 842 } else { 843 if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) { 844 if ( optPublish.length ) { 845 optPublish.remove(); 846 postStatus.val($('#hidden_post_status').val()); 847 } 848 } else { 849 optPublish.html( __( 'Published' ) ); 850 } 851 if ( postStatus.is(':hidden') ) 852 $('#misc-publishing-actions .edit-post-status').show(); 853 } 854 855 // Update "Status:" to currently selected status. 856 $('#post-status-display').text( 857 // Remove any potential tags from post status text. 858 wp.sanitize.stripTagsAndEncodeText( $('option:selected', postStatus).text() ) 859 ); 860 861 // Show or hide the "Save Draft" button. 862 if ( 863 $('option:selected', postStatus).val() == 'private' || 864 $('option:selected', postStatus).val() == 'publish' 865 ) { 866 $('#save-post').hide(); 867 } else { 868 $('#save-post').show(); 869 if ( $('option:selected', postStatus).val() == 'pending' ) { 870 $('#save-post').show().val( __( 'Save as Pending' ) ); 871 } else { 872 $('#save-post').show().val( __( 'Save Draft' ) ); 873 } 874 } 875 return true; 876 }; 877 878 // Show the visibility options and hide the toggle button when opened. 879 $( '#visibility .edit-visibility').on( 'click', function( e ) { 880 e.preventDefault(); 881 if ( $postVisibilitySelect.is(':hidden') ) { 882 updateVisibility(); 883 $postVisibilitySelect.slideDown( 'fast', function() { 884 $postVisibilitySelect.find( 'input[type="radio"]' ).first().trigger( 'focus' ); 885 } ); 886 $(this).hide(); 887 } 888 }); 889 890 // Cancel visibility selection area and hide it from view. 891 $postVisibilitySelect.find('.cancel-post-visibility').on( 'click', function( event ) { 892 $postVisibilitySelect.slideUp('fast'); 893 $('#visibility-radio-' + $('#hidden-post-visibility').val()).prop('checked', true); 894 $('#post_password').val($('#hidden-post-password').val()); 895 $('#sticky').prop('checked', $('#hidden-post-sticky').prop('checked')); 896 $('#post-visibility-display').html(visibility); 897 $('#visibility .edit-visibility').show().trigger( 'focus' ); 898 updateText(); 899 event.preventDefault(); 900 }); 901 902 // Set the selected visibility as current. 903 $postVisibilitySelect.find('.save-post-visibility').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. 904 var visibilityLabel = '', selectedVisibility = $postVisibilitySelect.find('input:radio:checked').val(); 905 906 $postVisibilitySelect.slideUp('fast'); 907 $('#visibility .edit-visibility').show().trigger( 'focus' ); 908 updateText(); 909 910 if ( 'public' !== selectedVisibility ) { 911 $('#sticky').prop('checked', false); 912 } 913 914 switch ( selectedVisibility ) { 915 case 'public': 916 visibilityLabel = $( '#sticky' ).prop( 'checked' ) ? __( 'Public, Sticky' ) : __( 'Public' ); 917 break; 918 case 'private': 919 visibilityLabel = __( 'Private' ); 920 break; 921 case 'password': 922 visibilityLabel = __( 'Password Protected' ); 923 break; 924 } 925 926 $('#post-visibility-display').text( visibilityLabel ); 927 event.preventDefault(); 928 }); 929 930 // When the selection changes, update labels. 931 $postVisibilitySelect.find('input:radio').on( 'change', function() { 932 updateVisibility(); 933 }); 934 935 // Edit publish time click. 936 $timestampdiv.siblings('a.edit-timestamp').on( 'click', function( event ) { 937 if ( $timestampdiv.is( ':hidden' ) ) { 938 $timestampdiv.slideDown( 'fast', function() { 939 $( 'input, select', $timestampdiv.find( '.timestamp-wrap' ) ).first().trigger( 'focus' ); 940 } ); 941 $(this).hide(); 942 } 943 event.preventDefault(); 944 }); 945 946 // Cancel editing the publish time and hide the settings. 947 $timestampdiv.find('.cancel-timestamp').on( 'click', function( event ) { 948 $timestampdiv.slideUp('fast').siblings('a.edit-timestamp').show().trigger( 'focus' ); 949 $('#mm').val($('#hidden_mm').val()); 950 $('#jj').val($('#hidden_jj').val()); 951 $('#aa').val($('#hidden_aa').val()); 952 $('#hh').val($('#hidden_hh').val()); 953 $('#mn').val($('#hidden_mn').val()); 954 updateText(); 955 event.preventDefault(); 956 }); 957 958 // Save the changed timestamp. 959 $timestampdiv.find('.save-timestamp').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. 960 if ( updateText() ) { 961 $timestampdiv.slideUp('fast'); 962 $timestampdiv.siblings('a.edit-timestamp').show().trigger( 'focus' ); 963 } 964 event.preventDefault(); 965 }); 966 967 // Cancel submit when an invalid timestamp has been selected. 968 $('#post').on( 'submit', function( event ) { 969 if ( ! updateText() ) { 970 event.preventDefault(); 971 $timestampdiv.show(); 972 973 if ( wp.autosave ) { 974 wp.autosave.enableButtons(); 975 } 976 977 $( '#publishing-action .spinner' ).removeClass( 'is-active' ); 978 } 979 }); 980 981 // Post Status edit click. 982 $postStatusSelect.siblings('a.edit-post-status').on( 'click', function( event ) { 983 if ( $postStatusSelect.is( ':hidden' ) ) { 984 $postStatusSelect.slideDown( 'fast', function() { 985 $postStatusSelect.find('select').trigger( 'focus' ); 986 } ); 987 $(this).hide(); 988 } 989 event.preventDefault(); 990 }); 991 992 // Save the Post Status changes and hide the options. 993 $postStatusSelect.find('.save-post-status').on( 'click', function( event ) { 994 $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); 995 updateText(); 996 event.preventDefault(); 997 }); 998 999 // Cancel Post Status editing and hide the options. 1000 $postStatusSelect.find('.cancel-post-status').on( 'click', function( event ) { 1001 $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); 1002 $('#post_status').val( $('#hidden_post_status').val() ); 1003 updateText(); 1004 event.preventDefault(); 1005 }); 1006 } 1007 1008 /** 1009 * Handle the editing of the post_name. Create the required HTML elements and 1010 * update the changes via Ajax. 1011 * 1012 * @global 1013 * 1014 * @return {void} 1015 */ 1016 function editPermalink() { 1017 var i, slug_value, slug_label, 1018 $el, revert_e, 1019 c = 0, 1020 real_slug = $('#post_name'), 1021 revert_slug = real_slug.val(), 1022 permalink = $( '#sample-permalink' ), 1023 permalinkOrig = permalink.html(), 1024 permalinkInner = $( '#sample-permalink a' ).html(), 1025 buttons = $('#edit-slug-buttons'), 1026 buttonsOrig = buttons.html(), 1027 full = $('#editable-post-name-full'); 1028 1029 // Deal with Twemoji in the post-name. 1030 full.find( 'img' ).replaceWith( function() { return this.alt; } ); 1031 full = full.html(); 1032 1033 permalink.html( permalinkInner ); 1034 1035 // Save current content to revert to when cancelling. 1036 $el = $( '#editable-post-name' ); 1037 revert_e = $el.html(); 1038 1039 buttons.html( 1040 '<button type="button" class="save button button-compact">' + __( 'OK' ) + '</button> ' + 1041 '<button type="button" class="cancel button-link">' + __( 'Cancel' ) + '</button>' 1042 ); 1043 1044 // Save permalink changes. 1045 buttons.children( '.save' ).on( 'click', function() { 1046 var new_slug = $el.children( 'input' ).val(); 1047 1048 if ( new_slug == $('#editable-post-name-full').text() ) { 1049 buttons.children('.cancel').trigger( 'click' ); 1050 return; 1051 } 1052 1053 $.post( 1054 ajaxurl, 1055 { 1056 action: 'sample-permalink', 1057 post_id: postId, 1058 new_slug: new_slug, 1059 new_title: $('#title').val(), 1060 samplepermalinknonce: $('#samplepermalinknonce').val() 1061 }, 1062 function(data) { 1063 var box = $('#edit-slug-box'); 1064 box.html(data); 1065 if (box.hasClass('hidden')) { 1066 box.fadeIn('fast', function () { 1067 box.removeClass('hidden'); 1068 }); 1069 } 1070 1071 buttons.html(buttonsOrig); 1072 permalink.html(permalinkOrig); 1073 real_slug.val(new_slug); 1074 $( '.edit-slug' ).trigger( 'focus' ); 1075 wp.a11y.speak( __( 'Permalink saved' ) ); 1076 } 1077 ); 1078 }); 1079 1080 // Cancel editing of permalink. 1081 buttons.children( '.cancel' ).on( 'click', function() { 1082 $('#view-post-btn').show(); 1083 $el.html(revert_e); 1084 buttons.html(buttonsOrig); 1085 permalink.html(permalinkOrig); 1086 real_slug.val(revert_slug); 1087 $( '.edit-slug' ).trigger( 'focus' ); 1088 }); 1089 1090 // If more than 1/4th of 'full' is '%', make it empty. 1091 for ( i = 0; i < full.length; ++i ) { 1092 if ( '%' == full.charAt(i) ) 1093 c++; 1094 } 1095 slug_value = ( c > full.length / 4 ) ? '' : full; 1096 slug_label = __( 'URL Slug' ); 1097 1098 $el.html( 1099 '<label for="new-post-slug" class="screen-reader-text">' + slug_label + '</label>' + 1100 '<input type="text" id="new-post-slug" value="' + slug_value + '" autocomplete="off" spellcheck="false" />' 1101 ).children( 'input' ).on( 'keydown', function( e ) { 1102 var key = e.which; 1103 // On [Enter], just save the new slug, don't save the post. 1104 if ( 13 === key ) { 1105 e.preventDefault(); 1106 buttons.children( '.save' ).trigger( 'click' ); 1107 } 1108 // On [Esc] cancel the editing. 1109 if ( 27 === key ) { 1110 buttons.children( '.cancel' ).trigger( 'click' ); 1111 } 1112 } ).on( 'keyup', function() { 1113 real_slug.val( this.value ); 1114 }).trigger( 'focus' ); 1115 } 1116 1117 $( '#titlediv' ).on( 'click', '.edit-slug', function() { 1118 editPermalink(); 1119 }); 1120 1121 /** 1122 * Adds screen reader text to the title label when needed. 1123 * 1124 * Use the 'screen-reader-text' class to emulate a placeholder attribute 1125 * and hide the label when entering a value. 1126 * 1127 * @param {string} id Optional. HTML ID to add the screen reader helper text to. 1128 * 1129 * @global 1130 * 1131 * @return {void} 1132 */ 1133 window.wptitlehint = function( id ) { 1134 id = id || 'title'; 1135 1136 var title = $( '#' + id ), titleprompt = $( '#' + id + '-prompt-text' ); 1137 1138 if ( '' === title.val() ) { 1139 titleprompt.removeClass( 'screen-reader-text' ); 1140 } 1141 1142 title.on( 'input', function() { 1143 if ( '' === this.value ) { 1144 titleprompt.removeClass( 'screen-reader-text' ); 1145 return; 1146 } 1147 1148 titleprompt.addClass( 'screen-reader-text' ); 1149 } ); 1150 }; 1151 1152 wptitlehint(); 1153 1154 // Resize the WYSIWYG and plain text editors. 1155 ( function() { 1156 var editor, offset, mce, 1157 $handle = $('#post-status-info'), 1158 $postdivrich = $('#postdivrich'); 1159 1160 // If there are no textareas or we are on a touch device, we can't do anything. 1161 if ( ! $textarea.length || 'ontouchstart' in window ) { 1162 // Hide the resize handle. 1163 $('#content-resize-handle').hide(); 1164 return; 1165 } 1166 1167 /** 1168 * Handle drag event. 1169 * 1170 * @param {Object} event Event containing details about the drag. 1171 */ 1172 function dragging( event ) { 1173 if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { 1174 return; 1175 } 1176 1177 if ( mce ) { 1178 editor.theme.resizeTo( null, offset + event.pageY ); 1179 } else { 1180 $textarea.height( Math.max( 50, offset + event.pageY ) ); 1181 } 1182 1183 event.preventDefault(); 1184 } 1185 1186 /** 1187 * When the dragging stopped make sure we return focus and do a confidence check on the height. 1188 */ 1189 function endDrag() { 1190 var height, toolbarHeight; 1191 1192 if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { 1193 return; 1194 } 1195 1196 if ( mce ) { 1197 editor.focus(); 1198 toolbarHeight = parseInt( $( '#wp-content-editor-container .mce-toolbar-grp' ).height(), 10 ); 1199 1200 if ( toolbarHeight < 10 || toolbarHeight > 200 ) { 1201 toolbarHeight = 30; 1202 } 1203 1204 height = parseInt( $('#content_ifr').css('height'), 10 ) + toolbarHeight - 28; 1205 } else { 1206 $textarea.trigger( 'focus' ); 1207 height = parseInt( $textarea.css('height'), 10 ); 1208 } 1209 1210 $document.off( '.wp-editor-resize' ); 1211 1212 // Confidence check: normalize height to stay within acceptable ranges. 1213 if ( height && height > 50 && height < 5000 ) { 1214 setUserSetting( 'ed_size', height ); 1215 } 1216 } 1217 1218 $handle.on( 'mousedown.wp-editor-resize', function( event ) { 1219 if ( typeof tinymce !== 'undefined' ) { 1220 editor = tinymce.get('content'); 1221 } 1222 1223 if ( editor && ! editor.isHidden() ) { 1224 mce = true; 1225 offset = $('#content_ifr').height() - event.pageY; 1226 } else { 1227 mce = false; 1228 offset = $textarea.height() - event.pageY; 1229 $textarea.trigger( 'blur' ); 1230 } 1231 1232 $document.on( 'mousemove.wp-editor-resize', dragging ) 1233 .on( 'mouseup.wp-editor-resize mouseleave.wp-editor-resize', endDrag ); 1234 1235 event.preventDefault(); 1236 }).on( 'mouseup.wp-editor-resize', endDrag ); 1237 })(); 1238 1239 // TinyMCE specific handling of Post Format changes to reflect in the editor. 1240 if ( typeof tinymce !== 'undefined' ) { 1241 // When changing post formats, change the editor body class. 1242 $( '#post-formats-select input.post-format' ).on( 'change.set-editor-class', function() { 1243 var editor, body, format = this.id; 1244 1245 if ( format && $( this ).prop( 'checked' ) && ( editor = tinymce.get( 'content' ) ) ) { 1246 body = editor.getBody(); 1247 body.className = body.className.replace( /\bpost-format-[^ ]+/, '' ); 1248 editor.dom.addClass( body, format == 'post-format-0' ? 'post-format-standard' : format ); 1249 $( document ).trigger( 'editor-classchange' ); 1250 } 1251 }); 1252 1253 // When changing page template, change the editor body class. 1254 $( '#page_template' ).on( 'change.set-editor-class', function() { 1255 var editor, body, pageTemplate = $( this ).val() || ''; 1256 1257 pageTemplate = pageTemplate.substr( pageTemplate.lastIndexOf( '/' ) + 1, pageTemplate.length ) 1258 .replace( /\.php$/, '' ) 1259 .replace( /\./g, '-' ); 1260 1261 if ( pageTemplate && ( editor = tinymce.get( 'content' ) ) ) { 1262 body = editor.getBody(); 1263 body.className = body.className.replace( /\bpage-template-[^ ]+/, '' ); 1264 editor.dom.addClass( body, 'page-template-' + pageTemplate ); 1265 $( document ).trigger( 'editor-classchange' ); 1266 } 1267 }); 1268 1269 } 1270 1271 // Save on pressing [Ctrl]/[Command] + [S] in the Text editor. 1272 $textarea.on( 'keydown.wp-autosave', function( event ) { 1273 // Key [S] has code 83. 1274 if ( event.which === 83 ) { 1275 if ( 1276 event.shiftKey || 1277 event.altKey || 1278 ( isMac && ( ! event.metaKey || event.ctrlKey ) ) || 1279 ( ! isMac && ! event.ctrlKey ) 1280 ) { 1281 return; 1282 } 1283 1284 wp.autosave && wp.autosave.server.triggerSave(); 1285 event.preventDefault(); 1286 } 1287 }); 1288 1289 // If the last status was auto-draft and the save is triggered, edit the current URL. 1290 if ( $( '#original_post_status' ).val() === 'auto-draft' && window.history.replaceState ) { 1291 var location; 1292 1293 $( '#publish' ).on( 'click', function() { 1294 location = window.location.href; 1295 location += ( location.indexOf( '?' ) !== -1 ) ? '&' : '?'; 1296 location += 'wp-post-new-reload=true'; 1297 1298 window.history.replaceState( null, null, location ); 1299 }); 1300 } 1301 1302 /** 1303 * Copies the attachment URL in the Edit Media page to the clipboard. 1304 * 1305 * @since 5.5.0 1306 * 1307 * @param {MouseEvent} event A click event. 1308 * 1309 * @return {void} 1310 */ 1311 copyAttachmentURLClipboard.on( 'success', function( event ) { 1312 var triggerElement = $( event.trigger ), 1313 successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) ); 1314 1315 // Clear the selection and move focus back to the trigger. 1316 event.clearSelection(); 1317 1318 // Show success visual feedback. 1319 clearTimeout( copyAttachmentURLSuccessTimeout ); 1320 successElement.removeClass( 'hidden' ); 1321 1322 // Hide success visual feedback after 3 seconds since last success. 1323 copyAttachmentURLSuccessTimeout = setTimeout( function() { 1324 successElement.addClass( 'hidden' ); 1325 }, 3000 ); 1326 1327 // Handle success audible feedback. 1328 wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) ); 1329 } ); 1330 } ); 1331 1332 /** 1333 * Handles the TinyMCE word count display. 1334 * 1335 * @param {JQueryStatic} $ The jQuery object. 1336 * @param {wp.utils.WordCounter} counter The WordCounter object. 1337 */ 1338 ( function( $, counter ) { 1339 $( function() { 1340 var $content = $( '#content' ), 1341 $count = $( '#wp-word-count' ).find( '.word-count' ), 1342 prevCount = 0, 1343 contentEditor; 1344 1345 /** 1346 * Get the word count from TinyMCE and display it 1347 */ 1348 function update() { 1349 var text, count; 1350 1351 if ( ! contentEditor || contentEditor.isHidden() ) { 1352 text = $content.val(); 1353 } else { 1354 text = contentEditor.getContent( { format: 'raw' } ); 1355 } 1356 1357 count = counter.count( text ); 1358 1359 if ( count !== prevCount ) { 1360 $count.text( count ); 1361 } 1362 1363 prevCount = count; 1364 } 1365 1366 /** 1367 * Bind the word count update triggers. 1368 * 1369 * When a node change in the main TinyMCE editor has been triggered. 1370 * When a key has been released in the plain text content editor. 1371 */ 1372 $( document ).on( 'tinymce-editor-init', function( event, editor ) { 1373 if ( editor.id !== 'content' ) { 1374 return; 1375 } 1376 1377 contentEditor = editor; 1378 1379 editor.on( 'nodechange keyup', _.debounce( update, 1000 ) ); 1380 } ); 1381 1382 $content.on( 'input keyup', _.debounce( update, 1000 ) ); 1383 1384 update(); 1385 } ); 1386 1387 } )( jQuery, new wp.utils.WordCounter() );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Fri Sep 18 08:20:28 2026 | Cross-referenced by PHPXref |