balmet.com

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

inline-edit-post.js (16526B)


      1 /**
      2  * This file contains the functions needed for the inline editing of posts.
      3  *
      4  * @since 2.7.0
      5  * @output wp-admin/js/inline-edit-post.js
      6  */
      7 
      8 /* global ajaxurl, typenow, inlineEditPost */
      9 
     10 window.wp = window.wp || {};
     11 
     12 /**
     13  * Manages the quick edit and bulk edit windows for editing posts or pages.
     14  *
     15  * @namespace inlineEditPost
     16  *
     17  * @since 2.7.0
     18  *
     19  * @type {Object}
     20  *
     21  * @property {string} type The type of inline editor.
     22  * @property {string} what The prefix before the post ID.
     23  *
     24  */
     25 ( function( $, wp ) {
     26 
     27 	window.inlineEditPost = {
     28 
     29 	/**
     30 	 * Initializes the inline and bulk post editor.
     31 	 *
     32 	 * Binds event handlers to the Escape key to close the inline editor
     33 	 * and to the save and close buttons. Changes DOM to be ready for inline
     34 	 * editing. Adds event handler to bulk edit.
     35 	 *
     36 	 * @since 2.7.0
     37 	 *
     38 	 * @memberof inlineEditPost
     39 	 *
     40 	 * @return {void}
     41 	 */
     42 	init : function(){
     43 		var t = this, qeRow = $('#inline-edit'), bulkRow = $('#bulk-edit');
     44 
     45 		t.type = $('table.widefat').hasClass('pages') ? 'page' : 'post';
     46 		// Post ID prefix.
     47 		t.what = '#post-';
     48 
     49 		/**
     50 		 * Binds the Escape key to revert the changes and close the quick editor.
     51 		 *
     52 		 * @return {boolean} The result of revert.
     53 		 */
     54 		qeRow.on( 'keyup', function(e){
     55 			// Revert changes if Escape key is pressed.
     56 			if ( e.which === 27 ) {
     57 				return inlineEditPost.revert();
     58 			}
     59 		});
     60 
     61 		/**
     62 		 * Binds the Escape key to revert the changes and close the bulk editor.
     63 		 *
     64 		 * @return {boolean} The result of revert.
     65 		 */
     66 		bulkRow.on( 'keyup', function(e){
     67 			// Revert changes if Escape key is pressed.
     68 			if ( e.which === 27 ) {
     69 				return inlineEditPost.revert();
     70 			}
     71 		});
     72 
     73 		/**
     74 		 * Reverts changes and close the quick editor if the cancel button is clicked.
     75 		 *
     76 		 * @return {boolean} The result of revert.
     77 		 */
     78 		$( '.cancel', qeRow ).on( 'click', function() {
     79 			return inlineEditPost.revert();
     80 		});
     81 
     82 		/**
     83 		 * Saves changes in the quick editor if the save(named: update) button is clicked.
     84 		 *
     85 		 * @return {boolean} The result of save.
     86 		 */
     87 		$( '.save', qeRow ).on( 'click', function() {
     88 			return inlineEditPost.save(this);
     89 		});
     90 
     91 		/**
     92 		 * If Enter is pressed, and the target is not the cancel button, save the post.
     93 		 *
     94 		 * @return {boolean} The result of save.
     95 		 */
     96 		$('td', qeRow).on( 'keydown', function(e){
     97 			if ( e.which === 13 && ! $( e.target ).hasClass( 'cancel' ) ) {
     98 				return inlineEditPost.save(this);
     99 			}
    100 		});
    101 
    102 		/**
    103 		 * Reverts changes and close the bulk editor if the cancel button is clicked.
    104 		 *
    105 		 * @return {boolean} The result of revert.
    106 		 */
    107 		$( '.cancel', bulkRow ).on( 'click', function() {
    108 			return inlineEditPost.revert();
    109 		});
    110 
    111 		/**
    112 		 * Disables the password input field when the private post checkbox is checked.
    113 		 */
    114 		$('#inline-edit .inline-edit-private input[value="private"]').on( 'click', function(){
    115 			var pw = $('input.inline-edit-password-input');
    116 			if ( $(this).prop('checked') ) {
    117 				pw.val('').prop('disabled', true);
    118 			} else {
    119 				pw.prop('disabled', false);
    120 			}
    121 		});
    122 
    123 		/**
    124 		 * Binds click event to the .editinline button which opens the quick editor.
    125 		 */
    126 		$( '#the-list' ).on( 'click', '.editinline', function() {
    127 			$( this ).attr( 'aria-expanded', 'true' );
    128 			inlineEditPost.edit( this );
    129 		});
    130 
    131 		$('#bulk-edit').find('fieldset:first').after(
    132 			$('#inline-edit fieldset.inline-edit-categories').clone()
    133 		).siblings( 'fieldset:last' ).prepend(
    134 			$('#inline-edit label.inline-edit-tags').clone()
    135 		);
    136 
    137 		$('select[name="_status"] option[value="future"]', bulkRow).remove();
    138 
    139 		/**
    140 		 * Adds onclick events to the apply buttons.
    141 		 */
    142 		$('#doaction').on( 'click', function(e){
    143 			var n;
    144 
    145 			t.whichBulkButtonId = $( this ).attr( 'id' );
    146 			n = t.whichBulkButtonId.substr( 2 );
    147 
    148 			if ( 'edit' === $( 'select[name="' + n + '"]' ).val() ) {
    149 				e.preventDefault();
    150 				t.setBulk();
    151 			} else if ( $('form#posts-filter tr.inline-editor').length > 0 ) {
    152 				t.revert();
    153 			}
    154 		});
    155 	},
    156 
    157 	/**
    158 	 * Toggles the quick edit window, hiding it when it's active and showing it when
    159 	 * inactive.
    160 	 *
    161 	 * @since 2.7.0
    162 	 *
    163 	 * @memberof inlineEditPost
    164 	 *
    165 	 * @param {Object} el Element within a post table row.
    166 	 */
    167 	toggle : function(el){
    168 		var t = this;
    169 		$( t.what + t.getId( el ) ).css( 'display' ) === 'none' ? t.revert() : t.edit( el );
    170 	},
    171 
    172 	/**
    173 	 * Creates the bulk editor row to edit multiple posts at once.
    174 	 *
    175 	 * @since 2.7.0
    176 	 *
    177 	 * @memberof inlineEditPost
    178 	 */
    179 	setBulk : function(){
    180 		var te = '', type = this.type, c = true;
    181 		this.revert();
    182 
    183 		$( '#bulk-edit td' ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );
    184 
    185 		// Insert the editor at the top of the table with an empty row above to maintain zebra striping.
    186 		$('table.widefat tbody').prepend( $('#bulk-edit') ).prepend('<tr class="hidden"></tr>');
    187 		$('#bulk-edit').addClass('inline-editor').show();
    188 
    189 		/**
    190 		 * Create a HTML div with the title and a link(delete-icon) for each selected
    191 		 * post.
    192 		 *
    193 		 * Get the selected posts based on the checked checkboxes in the post table.
    194 		 */
    195 		$( 'tbody th.check-column input[type="checkbox"]' ).each( function() {
    196 
    197 			// If the checkbox for a post is selected, add the post to the edit list.
    198 			if ( $(this).prop('checked') ) {
    199 				c = false;
    200 				var id = $(this).val(), theTitle;
    201 				theTitle = $('#inline_'+id+' .post_title').html() || wp.i18n.__( '(no title)' );
    202 				te += '<div id="ttle'+id+'"><a id="_'+id+'" class="ntdelbutton" title="'+ wp.i18n.__( 'Remove From Bulk Edit' ) +'">X</a>'+theTitle+'</div>';
    203 			}
    204 		});
    205 
    206 		// If no checkboxes where checked, just hide the quick/bulk edit rows.
    207 		if ( c ) {
    208 			return this.revert();
    209 		}
    210 
    211 		// Add onclick events to the delete-icons in the bulk editors the post title list.
    212 		$('#bulk-titles').html(te);
    213 		/**
    214 		 * Binds on click events to the checkboxes before the posts in the table.
    215 		 *
    216 		 * @listens click
    217 		 */
    218 		$('#bulk-titles a').on( 'click', function(){
    219 			var id = $(this).attr('id').substr(1);
    220 
    221 			$('table.widefat input[value="' + id + '"]').prop('checked', false);
    222 			$('#ttle'+id).remove();
    223 		});
    224 
    225 		// Enable auto-complete for tags when editing posts.
    226 		if ( 'post' === type ) {
    227 			$( 'tr.inline-editor textarea[data-wp-taxonomy]' ).each( function ( i, element ) {
    228 				/*
    229 				 * While Quick Edit clones the form each time, Bulk Edit always re-uses
    230 				 * the same form. Let's check if an autocomplete instance already exists.
    231 				 */
    232 				if ( $( element ).autocomplete( 'instance' ) ) {
    233 					// jQuery equivalent of `continue` within an `each()` loop.
    234 					return;
    235 				}
    236 
    237 				$( element ).wpTagsSuggest();
    238 			} );
    239 		}
    240 
    241 		// Scrolls to the top of the table where the editor is rendered.
    242 		$('html, body').animate( { scrollTop: 0 }, 'fast' );
    243 	},
    244 
    245 	/**
    246 	 * Creates a quick edit window for the post that has been clicked.
    247 	 *
    248 	 * @since 2.7.0
    249 	 *
    250 	 * @memberof inlineEditPost
    251 	 *
    252 	 * @param {number|Object} id The ID of the clicked post or an element within a post
    253 	 *                           table row.
    254 	 * @return {boolean} Always returns false at the end of execution.
    255 	 */
    256 	edit : function(id) {
    257 		var t = this, fields, editRow, rowData, status, pageOpt, pageLevel, nextPage, pageLoop = true, nextLevel, f, val, pw;
    258 		t.revert();
    259 
    260 		if ( typeof(id) === 'object' ) {
    261 			id = t.getId(id);
    262 		}
    263 
    264 		fields = ['post_title', 'post_name', 'post_author', '_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password', 'post_format', 'menu_order', 'page_template'];
    265 		if ( t.type === 'page' ) {
    266 			fields.push('post_parent');
    267 		}
    268 
    269 		// Add the new edit row with an extra blank row underneath to maintain zebra striping.
    270 		editRow = $('#inline-edit').clone(true);
    271 		$( 'td', editRow ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length );
    272 
    273 		$(t.what+id).removeClass('is-expanded').hide().after(editRow).after('<tr class="hidden"></tr>');
    274 
    275 		// Populate fields in the quick edit window.
    276 		rowData = $('#inline_'+id);
    277 		if ( !$(':input[name="post_author"] option[value="' + $('.post_author', rowData).text() + '"]', editRow).val() ) {
    278 
    279 			// The post author no longer has edit capabilities, so we need to add them to the list of authors.
    280 			$(':input[name="post_author"]', editRow).prepend('<option value="' + $('.post_author', rowData).text() + '">' + $('#' + t.type + '-' + id + ' .author').text() + '</option>');
    281 		}
    282 		if ( $( ':input[name="post_author"] option', editRow ).length === 1 ) {
    283 			$('label.inline-edit-author', editRow).hide();
    284 		}
    285 
    286 		for ( f = 0; f < fields.length; f++ ) {
    287 			val = $('.'+fields[f], rowData);
    288 
    289 			/**
    290 			 * Replaces the image for a Twemoji(Twitter emoji) with it's alternate text.
    291 			 *
    292 			 * @return {string} Alternate text from the image.
    293 			 */
    294 			val.find( 'img' ).replaceWith( function() { return this.alt; } );
    295 			val = val.text();
    296 			$(':input[name="' + fields[f] + '"]', editRow).val( val );
    297 		}
    298 
    299 		if ( $( '.comment_status', rowData ).text() === 'open' ) {
    300 			$( 'input[name="comment_status"]', editRow ).prop( 'checked', true );
    301 		}
    302 		if ( $( '.ping_status', rowData ).text() === 'open' ) {
    303 			$( 'input[name="ping_status"]', editRow ).prop( 'checked', true );
    304 		}
    305 		if ( $( '.sticky', rowData ).text() === 'sticky' ) {
    306 			$( 'input[name="sticky"]', editRow ).prop( 'checked', true );
    307 		}
    308 
    309 		/**
    310 		 * Creates the select boxes for the categories.
    311 		 */
    312 		$('.post_category', rowData).each(function(){
    313 			var taxname,
    314 				term_ids = $(this).text();
    315 
    316 			if ( term_ids ) {
    317 				taxname = $(this).attr('id').replace('_'+id, '');
    318 				$('ul.'+taxname+'-checklist :checkbox', editRow).val(term_ids.split(','));
    319 			}
    320 		});
    321 
    322 		/**
    323 		 * Gets all the taxonomies for live auto-fill suggestions when typing the name
    324 		 * of a tag.
    325 		 */
    326 		$('.tags_input', rowData).each(function(){
    327 			var terms = $(this),
    328 				taxname = $(this).attr('id').replace('_' + id, ''),
    329 				textarea = $('textarea.tax_input_' + taxname, editRow),
    330 				comma = wp.i18n._x( ',', 'tag delimiter' ).trim();
    331 
    332 			// Ensure the textarea exists.
    333 			if ( ! textarea.length ) {
    334 				return;
    335 			}
    336 
    337 			terms.find( 'img' ).replaceWith( function() { return this.alt; } );
    338 			terms = terms.text();
    339 
    340 			if ( terms ) {
    341 				if ( ',' !== comma ) {
    342 					terms = terms.replace(/,/g, comma);
    343 				}
    344 				textarea.val(terms);
    345 			}
    346 
    347 			textarea.wpTagsSuggest();
    348 		});
    349 
    350 		// Handle the post status.
    351 		status = $('._status', rowData).text();
    352 		if ( 'future' !== status ) {
    353 			$('select[name="_status"] option[value="future"]', editRow).remove();
    354 		}
    355 
    356 		pw = $( '.inline-edit-password-input' ).prop( 'disabled', false );
    357 		if ( 'private' === status ) {
    358 			$('input[name="keep_private"]', editRow).prop('checked', true);
    359 			pw.val( '' ).prop( 'disabled', true );
    360 		}
    361 
    362 		// Remove the current page and children from the parent dropdown.
    363 		pageOpt = $('select[name="post_parent"] option[value="' + id + '"]', editRow);
    364 		if ( pageOpt.length > 0 ) {
    365 			pageLevel = pageOpt[0].className.split('-')[1];
    366 			nextPage = pageOpt;
    367 			while ( pageLoop ) {
    368 				nextPage = nextPage.next('option');
    369 				if ( nextPage.length === 0 ) {
    370 					break;
    371 				}
    372 
    373 				nextLevel = nextPage[0].className.split('-')[1];
    374 
    375 				if ( nextLevel <= pageLevel ) {
    376 					pageLoop = false;
    377 				} else {
    378 					nextPage.remove();
    379 					nextPage = pageOpt;
    380 				}
    381 			}
    382 			pageOpt.remove();
    383 		}
    384 
    385 		$(editRow).attr('id', 'edit-'+id).addClass('inline-editor').show();
    386 		$('.ptitle', editRow).trigger( 'focus' );
    387 
    388 		return false;
    389 	},
    390 
    391 	/**
    392 	 * Saves the changes made in the quick edit window to the post.
    393 	 * Ajax saving is only for Quick Edit and not for bulk edit.
    394 	 *
    395 	 * @since 2.7.0
    396 	 *
    397 	 * @param {number} id The ID for the post that has been changed.
    398 	 * @return {boolean} False, so the form does not submit when pressing
    399 	 *                   Enter on a focused field.
    400 	 */
    401 	save : function(id) {
    402 		var params, fields, page = $('.post_status_page').val() || '';
    403 
    404 		if ( typeof(id) === 'object' ) {
    405 			id = this.getId(id);
    406 		}
    407 
    408 		$( 'table.widefat .spinner' ).addClass( 'is-active' );
    409 
    410 		params = {
    411 			action: 'inline-save',
    412 			post_type: typenow,
    413 			post_ID: id,
    414 			edit_date: 'true',
    415 			post_status: page
    416 		};
    417 
    418 		fields = $('#edit-'+id).find(':input').serialize();
    419 		params = fields + '&' + $.param(params);
    420 
    421 		// Make Ajax request.
    422 		$.post( ajaxurl, params,
    423 			function(r) {
    424 				var $errorNotice = $( '#edit-' + id + ' .inline-edit-save .notice-error' ),
    425 					$error = $errorNotice.find( '.error' );
    426 
    427 				$( 'table.widefat .spinner' ).removeClass( 'is-active' );
    428 
    429 				if (r) {
    430 					if ( -1 !== r.indexOf( '<tr' ) ) {
    431 						$(inlineEditPost.what+id).siblings('tr.hidden').addBack().remove();
    432 						$('#edit-'+id).before(r).remove();
    433 						$( inlineEditPost.what + id ).hide().fadeIn( 400, function() {
    434 							// Move focus back to the Quick Edit button. $( this ) is the row being animated.
    435 							$( this ).find( '.editinline' )
    436 								.attr( 'aria-expanded', 'false' )
    437 								.trigger( 'focus' );
    438 							wp.a11y.speak( wp.i18n.__( 'Changes saved.' ) );
    439 						});
    440 					} else {
    441 						r = r.replace( /<.[^<>]*?>/g, '' );
    442 						$errorNotice.removeClass( 'hidden' );
    443 						$error.html( r );
    444 						wp.a11y.speak( $error.text() );
    445 					}
    446 				} else {
    447 					$errorNotice.removeClass( 'hidden' );
    448 					$error.text( wp.i18n.__( 'Error while saving the changes.' ) );
    449 					wp.a11y.speak( wp.i18n.__( 'Error while saving the changes.' ) );
    450 				}
    451 			},
    452 		'html');
    453 
    454 		// Prevent submitting the form when pressing Enter on a focused field.
    455 		return false;
    456 	},
    457 
    458 	/**
    459 	 * Hides and empties the Quick Edit and/or Bulk Edit windows.
    460 	 *
    461 	 * @since 2.7.0
    462 	 *
    463 	 * @memberof inlineEditPost
    464 	 *
    465 	 * @return {boolean} Always returns false.
    466 	 */
    467 	revert : function(){
    468 		var $tableWideFat = $( '.widefat' ),
    469 			id = $( '.inline-editor', $tableWideFat ).attr( 'id' );
    470 
    471 		if ( id ) {
    472 			$( '.spinner', $tableWideFat ).removeClass( 'is-active' );
    473 
    474 			if ( 'bulk-edit' === id ) {
    475 
    476 				// Hide the bulk editor.
    477 				$( '#bulk-edit', $tableWideFat ).removeClass( 'inline-editor' ).hide().siblings( '.hidden' ).remove();
    478 				$('#bulk-titles').empty();
    479 
    480 				// Store the empty bulk editor in a hidden element.
    481 				$('#inlineedit').append( $('#bulk-edit') );
    482 
    483 				// Move focus back to the Bulk Action button that was activated.
    484 				$( '#' + inlineEditPost.whichBulkButtonId ).trigger( 'focus' );
    485 			} else {
    486 
    487 				// Remove both the inline-editor and its hidden tr siblings.
    488 				$('#'+id).siblings('tr.hidden').addBack().remove();
    489 				id = id.substr( id.lastIndexOf('-') + 1 );
    490 
    491 				// Show the post row and move focus back to the Quick Edit button.
    492 				$( this.what + id ).show().find( '.editinline' )
    493 					.attr( 'aria-expanded', 'false' )
    494 					.trigger( 'focus' );
    495 			}
    496 		}
    497 
    498 		return false;
    499 	},
    500 
    501 	/**
    502 	 * Gets the ID for a the post that you want to quick edit from the row in the quick
    503 	 * edit table.
    504 	 *
    505 	 * @since 2.7.0
    506 	 *
    507 	 * @memberof inlineEditPost
    508 	 *
    509 	 * @param {Object} o DOM row object to get the ID for.
    510 	 * @return {string} The post ID extracted from the table row in the object.
    511 	 */
    512 	getId : function(o) {
    513 		var id = $(o).closest('tr').attr('id'),
    514 			parts = id.split('-');
    515 		return parts[parts.length - 1];
    516 	}
    517 };
    518 
    519 $( function() { inlineEditPost.init(); } );
    520 
    521 // Show/hide locks on posts.
    522 $( function() {
    523 
    524 	// Set the heartbeat interval to 15 seconds.
    525 	if ( typeof wp !== 'undefined' && wp.heartbeat ) {
    526 		wp.heartbeat.interval( 15 );
    527 	}
    528 }).on( 'heartbeat-tick.wp-check-locked-posts', function( e, data ) {
    529 	var locked = data['wp-check-locked-posts'] || {};
    530 
    531 	$('#the-list tr').each( function(i, el) {
    532 		var key = el.id, row = $(el), lock_data, avatar;
    533 
    534 		if ( locked.hasOwnProperty( key ) ) {
    535 			if ( ! row.hasClass('wp-locked') ) {
    536 				lock_data = locked[key];
    537 				row.find('.column-title .locked-text').text( lock_data.text );
    538 				row.find('.check-column checkbox').prop('checked', false);
    539 
    540 				if ( lock_data.avatar_src ) {
    541 					avatar = $( '<img />', {
    542 						'class': 'avatar avatar-18 photo',
    543 						width: 18,
    544 						height: 18,
    545 						alt: '',
    546 						src: lock_data.avatar_src,
    547 						srcset: lock_data.avatar_src_2x ? lock_data.avatar_src_2x + ' 2x' : undefined
    548 					} );
    549 					row.find('.column-title .locked-avatar').empty().append( avatar );
    550 				}
    551 				row.addClass('wp-locked');
    552 			}
    553 		} else if ( row.hasClass('wp-locked') ) {
    554 			row.removeClass( 'wp-locked' ).find( '.locked-info span' ).empty();
    555 		}
    556 	});
    557 }).on( 'heartbeat-send.wp-check-locked-posts', function( e, data ) {
    558 	var check = [];
    559 
    560 	$('#the-list tr').each( function(i, el) {
    561 		if ( el.id ) {
    562 			check.push( el.id );
    563 		}
    564 	});
    565 
    566 	if ( check.length ) {
    567 		data['wp-check-locked-posts'] = check;
    568 	}
    569 });
    570 
    571 })( jQuery, window.wp );