balmet.com

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

tags-box.js (11140B)


      1 /**
      2  * @output wp-admin/js/tags-box.js
      3  */
      4 
      5 /* jshint curly: false, eqeqeq: false */
      6 /* global ajaxurl, tagBox, array_unique_noempty */
      7 
      8 ( function( $ ) {
      9 	var tagDelimiter = wp.i18n._x( ',', 'tag delimiter' ) || ',';
     10 
     11 	/**
     12 	 * Filters unique items and returns a new array.
     13 	 *
     14 	 * Filters all items from an array into a new array containing only the unique
     15 	 * items. This also excludes whitespace or empty values.
     16 	 *
     17 	 * @since 2.8.0
     18 	 *
     19 	 * @global
     20 	 *
     21 	 * @param {Array} array The array to filter through.
     22 	 *
     23 	 * @return {Array} A new array containing only the unique items.
     24 	 */
     25 	window.array_unique_noempty = function( array ) {
     26 		var out = [];
     27 
     28 		// Trim the values and ensure they are unique.
     29 		$.each( array, function( key, val ) {
     30 			val = val || '';
     31 			val = val.trim();
     32 
     33 			if ( val && $.inArray( val, out ) === -1 ) {
     34 				out.push( val );
     35 			}
     36 		} );
     37 
     38 		return out;
     39 	};
     40 
     41 	/**
     42 	 * The TagBox object.
     43 	 *
     44 	 * Contains functions to create and manage tags that can be associated with a
     45 	 * post.
     46 	 *
     47 	 * @since 2.9.0
     48 	 *
     49 	 * @global
     50 	 */
     51 	window.tagBox = {
     52 		/**
     53 		 * Cleans up tags by removing redundant characters.
     54 		 *
     55 		 * @since 2.9.0
     56 		 *
     57 		 * @memberOf tagBox
     58 		 *
     59 		 * @param {string} tags Comma separated tags that need to be cleaned up.
     60 		 *
     61 		 * @return {string} The cleaned up tags.
     62 		 */
     63 		clean : function( tags ) {
     64 			if ( ',' !== tagDelimiter ) {
     65 				tags = tags.replace( new RegExp( tagDelimiter, 'g' ), ',' );
     66 			}
     67 
     68 			tags = tags.replace(/\s*,\s*/g, ',').replace(/,+/g, ',').replace(/[,\s]+$/, '').replace(/^[,\s]+/, '');
     69 
     70 			if ( ',' !== tagDelimiter ) {
     71 				tags = tags.replace( /,/g, tagDelimiter );
     72 			}
     73 
     74 			return tags;
     75 		},
     76 
     77 		/**
     78 		 * Parses tags and makes them editable.
     79 		 *
     80 		 * @since 2.9.0
     81 		 *
     82 		 * @memberOf tagBox
     83 		 *
     84 		 * @param {Object} el The tag element to retrieve the ID from.
     85 		 *
     86 		 * @return {boolean} Always returns false.
     87 		 */
     88 		parseTags : function(el) {
     89 			var id = el.id,
     90 				num = id.split('-check-num-')[1],
     91 				taxbox = $(el).closest('.tagsdiv'),
     92 				thetags = taxbox.find('.the-tags'),
     93 				current_tags = thetags.val().split( tagDelimiter ),
     94 				new_tags = [];
     95 
     96 			delete current_tags[num];
     97 
     98 			// Sanitize the current tags and push them as if they're new tags.
     99 			$.each( current_tags, function( key, val ) {
    100 				val = val || '';
    101 				val = val.trim();
    102 				if ( val ) {
    103 					new_tags.push( val );
    104 				}
    105 			});
    106 
    107 			thetags.val( this.clean( new_tags.join( tagDelimiter ) ) );
    108 
    109 			this.quickClicks( taxbox );
    110 			return false;
    111 		},
    112 
    113 		/**
    114 		 * Creates clickable links, buttons and fields for adding or editing tags.
    115 		 *
    116 		 * @since 2.9.0
    117 		 *
    118 		 * @memberOf tagBox
    119 		 *
    120 		 * @param {Object} el The container HTML element.
    121 		 *
    122 		 * @return {void}
    123 		 */
    124 		quickClicks : function( el ) {
    125 			var thetags = $('.the-tags', el),
    126 				tagchecklist = $('.tagchecklist', el),
    127 				id = $(el).attr('id'),
    128 				current_tags, disabled;
    129 
    130 			if ( ! thetags.length )
    131 				return;
    132 
    133 			disabled = thetags.prop('disabled');
    134 
    135 			current_tags = thetags.val().split( tagDelimiter );
    136 			tagchecklist.empty();
    137 
    138 			/**
    139 			 * Creates a delete button if tag editing is enabled, before adding it to the tag list.
    140 			 *
    141 			 * @since 2.5.0
    142 			 *
    143 			 * @memberOf tagBox
    144 			 *
    145 			 * @param {string} key The index of the current tag.
    146 			 * @param {string} val The value of the current tag.
    147 			 *
    148 			 * @return {void}
    149 			 */
    150 			$.each( current_tags, function( key, val ) {
    151 				var listItem, xbutton;
    152 
    153 				val = val || '';
    154 				val = val.trim();
    155 
    156 				if ( ! val )
    157 					return;
    158 
    159 				// Create a new list item, and ensure the text is properly escaped.
    160 				listItem = $( '<li />' ).text( val );
    161 
    162 				// If tags editing isn't disabled, create the X button.
    163 				if ( ! disabled ) {
    164 					/*
    165 					 * Build the X buttons, hide the X icon with aria-hidden and
    166 					 * use visually hidden text for screen readers.
    167 					 */
    168 					xbutton = $( '<button type="button" id="' + id + '-check-num-' + key + '" class="ntdelbutton">' +
    169 						'<span class="remove-tag-icon" aria-hidden="true"></span>' +
    170 						'<span class="screen-reader-text">' + wp.i18n.__( 'Remove term:' ) + ' ' + listItem.html() + '</span>' +
    171 						'</button>' );
    172 
    173 					/**
    174 					 * Handles the click and keypress event of the tag remove button.
    175 					 *
    176 					 * Makes sure the focus ends up in the tag input field when using
    177 					 * the keyboard to delete the tag.
    178 					 *
    179 					 * @since 4.2.0
    180 					 *
    181 					 * @param {Event} e The click or keypress event to handle.
    182 					 *
    183 					 * @return {void}
    184 					 */
    185 					xbutton.on( 'click keypress', function( e ) {
    186 						// On click or when using the Enter/Spacebar keys.
    187 						if ( 'click' === e.type || 13 === e.keyCode || 32 === e.keyCode ) {
    188 							/*
    189 							 * When using the keyboard, move focus back to the
    190 							 * add new tag field. Note: when releasing the pressed
    191 							 * key this will fire the `keyup` event on the input.
    192 							 */
    193 							if ( 13 === e.keyCode || 32 === e.keyCode ) {
    194  								$( this ).closest( '.tagsdiv' ).find( 'input.newtag' ).trigger( 'focus' );
    195  							}
    196 
    197 							tagBox.userAction = 'remove';
    198 							tagBox.parseTags( this );
    199 						}
    200 					});
    201 
    202 					listItem.prepend( '&nbsp;' ).prepend( xbutton );
    203 				}
    204 
    205 				// Append the list item to the tag list.
    206 				tagchecklist.append( listItem );
    207 			});
    208 
    209 			// The buttons list is built now, give feedback to screen reader users.
    210 			tagBox.screenReadersMessage();
    211 		},
    212 
    213 		/**
    214 		 * Adds a new tag.
    215 		 *
    216 		 * Also ensures that the quick links are properly generated.
    217 		 *
    218 		 * @since 2.9.0
    219 		 *
    220 		 * @memberOf tagBox
    221 		 *
    222 		 * @param {Object} el The container HTML element.
    223 		 * @param {Object|boolean} a When this is an HTML element the text of that
    224 		 *                           element will be used for the new tag.
    225 		 * @param {number|boolean} f If this value is not passed then the tag input
    226 		 *                           field is focused.
    227 		 *
    228 		 * @return {boolean} Always returns false.
    229 		 */
    230 		flushTags : function( el, a, f ) {
    231 			var tagsval, newtags, text,
    232 				tags = $( '.the-tags', el ),
    233 				newtag = $( 'input.newtag', el );
    234 
    235 			a = a || false;
    236 
    237 			text = a ? $(a).text() : newtag.val();
    238 
    239 			/*
    240 			 * Return if there's no new tag or if the input field is empty.
    241 			 * Note: when using the keyboard to add tags, focus is moved back to
    242 			 * the input field and the `keyup` event attached on this field will
    243 			 * fire when releasing the pressed key. Checking also for the field
    244 			 * emptiness avoids to set the tags and call quickClicks() again.
    245 			 */
    246 			if ( 'undefined' == typeof( text ) || '' === text ) {
    247 				return false;
    248 			}
    249 
    250 			tagsval = tags.val();
    251 			newtags = tagsval ? tagsval + tagDelimiter + text : text;
    252 
    253 			newtags = this.clean( newtags );
    254 			newtags = array_unique_noempty( newtags.split( tagDelimiter ) ).join( tagDelimiter );
    255 			tags.val( newtags );
    256 			this.quickClicks( el );
    257 
    258 			if ( ! a )
    259 				newtag.val('');
    260 			if ( 'undefined' == typeof( f ) )
    261 				newtag.trigger( 'focus' );
    262 
    263 			return false;
    264 		},
    265 
    266 		/**
    267 		 * Retrieves the available tags and creates a tagcloud.
    268 		 *
    269 		 * Retrieves the available tags from the database and creates an interactive
    270 		 * tagcloud. Clicking a tag will add it.
    271 		 *
    272 		 * @since 2.9.0
    273 		 *
    274 		 * @memberOf tagBox
    275 		 *
    276 		 * @param {string} id The ID to extract the taxonomy from.
    277 		 *
    278 		 * @return {void}
    279 		 */
    280 		get : function( id ) {
    281 			var tax = id.substr( id.indexOf('-') + 1 );
    282 
    283 			/**
    284 			 * Puts a received tag cloud into a DOM element.
    285 			 *
    286 			 * The tag cloud HTML is generated on the server.
    287 			 *
    288 			 * @since 2.9.0
    289 			 *
    290 			 * @param {number|string} r The response message from the Ajax call.
    291 			 * @param {string} stat The status of the Ajax request.
    292 			 *
    293 			 * @return {void}
    294 			 */
    295 			$.post( ajaxurl, { 'action': 'get-tagcloud', 'tax': tax }, function( r, stat ) {
    296 				if ( 0 === r || 'success' != stat ) {
    297 					return;
    298 				}
    299 
    300 				r = $( '<div id="tagcloud-' + tax + '" class="the-tagcloud">' + r + '</div>' );
    301 
    302 				/**
    303 				 * Adds a new tag when a tag in the tagcloud is clicked.
    304 				 *
    305 				 * @since 2.9.0
    306 				 *
    307 				 * @return {boolean} Returns false to prevent the default action.
    308 				 */
    309 				$( 'a', r ).on( 'click', function() {
    310 					tagBox.userAction = 'add';
    311 					tagBox.flushTags( $( '#' + tax ), this );
    312 					return false;
    313 				});
    314 
    315 				$( '#' + id ).after( r );
    316 			});
    317 		},
    318 
    319 		/**
    320 		 * Track the user's last action.
    321 		 *
    322 		 * @since 4.7.0
    323 		 */
    324 		userAction: '',
    325 
    326 		/**
    327 		 * Dispatches an audible message to screen readers.
    328 		 *
    329 		 * This will inform the user when a tag has been added or removed.
    330 		 *
    331 		 * @since 4.7.0
    332 		 *
    333 		 * @return {void}
    334 		 */
    335 		screenReadersMessage: function() {
    336 			var message;
    337 
    338 			switch ( this.userAction ) {
    339 				case 'remove':
    340 					message = wp.i18n.__( 'Term removed.' );
    341 					break;
    342 
    343 				case 'add':
    344 					message = wp.i18n.__( 'Term added.' );
    345 					break;
    346 
    347 				default:
    348 					return;
    349 			}
    350 
    351 			window.wp.a11y.speak( message, 'assertive' );
    352 		},
    353 
    354 		/**
    355 		 * Initializes the tags box by setting up the links, buttons. Sets up event
    356 		 * handling.
    357 		 *
    358 		 * This includes handling of pressing the enter key in the input field and the
    359 		 * retrieval of tag suggestions.
    360 		 *
    361 		 * @since 2.9.0
    362 		 *
    363 		 * @memberOf tagBox
    364 		 *
    365 		 * @return {void}
    366 		 */
    367 		init : function() {
    368 			var ajaxtag = $('div.ajaxtag');
    369 
    370 			$('.tagsdiv').each( function() {
    371 				tagBox.quickClicks( this );
    372 			});
    373 
    374 			$( '.tagadd', ajaxtag ).on( 'click', function() {
    375 				tagBox.userAction = 'add';
    376 				tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
    377 			});
    378 
    379 			/**
    380 			 * Handles pressing enter on the new tag input field.
    381 			 *
    382 			 * Prevents submitting the post edit form. Uses `keypress` to take
    383 			 * into account Input Method Editor (IME) converters.
    384 			 *
    385 			 * @since 2.9.0
    386 			 *
    387 			 * @param {Event} event The keypress event that occurred.
    388 			 *
    389 			 * @return {void}
    390 			 */
    391 			$( 'input.newtag', ajaxtag ).on( 'keypress', function( event ) {
    392 				if ( 13 == event.which ) {
    393 					tagBox.userAction = 'add';
    394 					tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
    395 					event.preventDefault();
    396 					event.stopPropagation();
    397 				}
    398 			}).each( function( i, element ) {
    399 				$( element ).wpTagsSuggest();
    400 			});
    401 
    402 			/**
    403 			 * Before a post is saved the value currently in the new tag input field will be
    404 			 * added as a tag.
    405 			 *
    406 			 * @since 2.9.0
    407 			 *
    408 			 * @return {void}
    409 			 */
    410 			$('#post').on( 'submit', function(){
    411 				$('div.tagsdiv').each( function() {
    412 					tagBox.flushTags(this, false, 1);
    413 				});
    414 			});
    415 
    416 			/**
    417 			 * Handles clicking on the tag cloud link.
    418 			 *
    419 			 * Makes sure the ARIA attributes are set correctly.
    420 			 *
    421 			 * @since 2.9.0
    422 			 *
    423 			 * @return {void}
    424 			 */
    425 			$('.tagcloud-link').on( 'click', function(){
    426 				// On the first click, fetch the tag cloud and insert it in the DOM.
    427 				tagBox.get( $( this ).attr( 'id' ) );
    428 				// Update button state, remove previous click event and attach a new one to toggle the cloud.
    429 				$( this )
    430 					.attr( 'aria-expanded', 'true' )
    431 					.off()
    432 					.on( 'click', function() {
    433 						$( this )
    434 							.attr( 'aria-expanded', 'false' === $( this ).attr( 'aria-expanded' ) ? 'true' : 'false' )
    435 							.siblings( '.the-tagcloud' ).toggle();
    436 					});
    437 			});
    438 		}
    439 	};
    440 }( jQuery ));