balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

post.js (39845B)


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