WordPress/wp-admin/js/tags-box.js
omarreiss bdbaccce37 General: Explicitly assigns all JS globals to the window.
Many variables in the JavaScript were defined in the global scope without being explicitly assigned to the window. When built with Webpack, the code gets encapsulated in anonymous functions and those implicit globals get assigned to the wrong scope. This patch prevents that from happening.

Fixes #44371. See #43731.

Built from https://develop.svn.wordpress.org/trunk@43577


git-svn-id: http://core.svn.wordpress.org/trunk@43406 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2018-08-19 13:33:24 +00:00

435 lines
11 KiB
JavaScript

/**
* @output wp-admin/js/tags-box.js
*/
/* jshint curly: false, eqeqeq: false */
/* global ajaxurl, tagBox, array_unique_noempty */
( function( $ ) {
var tagDelimiter = ( window.tagsSuggestL10n && window.tagsSuggestL10n.tagDelimiter ) || ',';
/**
* Filters unique items and returns a new array.
*
* Filters all items from an array into a new array containing only the unique
* items. This also excludes whitespace or empty values.
*
* @since 2.8.0
*
* @global
*
* @param {Array} array The array to filter through.
*
* @return {Array} A new array containing only the unique items.
*/
window.array_unique_noempty = function( array ) {
var out = [];
// Trim the values and ensure they are unique.
$.each( array, function( key, val ) {
val = $.trim( val );
if ( val && $.inArray( val, out ) === -1 ) {
out.push( val );
}
} );
return out;
};
/**
* The TagBox object.
*
* Contains functions to create and manage tags that can be associated with a
* post.
*
* @since 2.9.0
*
* @global
*/
window.tagBox = {
/**
* Cleans up tags by removing redundant characters.
*
* @since 2.9.0
* @memberOf tagBox
*
* @param {string} tags Comma separated tags that need to be cleaned up.
*
* @return {string} The cleaned up tags.
*/
clean : function( tags ) {
if ( ',' !== tagDelimiter ) {
tags = tags.replace( new RegExp( tagDelimiter, 'g' ), ',' );
}
tags = tags.replace(/\s*,\s*/g, ',').replace(/,+/g, ',').replace(/[,\s]+$/, '').replace(/^[,\s]+/, '');
if ( ',' !== tagDelimiter ) {
tags = tags.replace( /,/g, tagDelimiter );
}
return tags;
},
/**
* Parses tags and makes them editable.
*
* @since 2.9.0
* @memberOf tagBox
*
* @param {Object} el The tag element to retrieve the ID from.
*
* @return {boolean} Always returns false.
*/
parseTags : function(el) {
var id = el.id,
num = id.split('-check-num-')[1],
taxbox = $(el).closest('.tagsdiv'),
thetags = taxbox.find('.the-tags'),
current_tags = thetags.val().split( tagDelimiter ),
new_tags = [];
delete current_tags[num];
// Sanitize the current tags and push them as if they're new tags.
$.each( current_tags, function( key, val ) {
val = $.trim( val );
if ( val ) {
new_tags.push( val );
}
});
thetags.val( this.clean( new_tags.join( tagDelimiter ) ) );
this.quickClicks( taxbox );
return false;
},
/**
* Creates clickable links, buttons and fields for adding or editing tags.
*
* @since 2.9.0
* @memberOf tagBox
*
* @param {Object} el The container HTML element.
*
* @return {void}
*/
quickClicks : function( el ) {
var thetags = $('.the-tags', el),
tagchecklist = $('.tagchecklist', el),
id = $(el).attr('id'),
current_tags, disabled;
if ( ! thetags.length )
return;
disabled = thetags.prop('disabled');
current_tags = thetags.val().split( tagDelimiter );
tagchecklist.empty();
/**
* Creates a delete button if tag editing is enabled, before adding it to the tag list.
*
* @since 2.5.0
* @memberOf tagBox
*
* @param {string} key The index of the current tag.
* @param {string} val The value of the current tag.
*
* @return {void}
*/
$.each( current_tags, function( key, val ) {
var listItem, xbutton;
val = $.trim( val );
if ( ! val )
return;
// Create a new list item, and ensure the text is properly escaped.
listItem = $( '<li />' ).text( val );
// If tags editing isn't disabled, create the X button.
if ( ! disabled ) {
/*
* Build the X buttons, hide the X icon with aria-hidden and
* use visually hidden text for screen readers.
*/
xbutton = $( '<button type="button" id="' + id + '-check-num-' + key + '" class="ntdelbutton">' +
'<span class="remove-tag-icon" aria-hidden="true"></span>' +
'<span class="screen-reader-text">' + window.tagsSuggestL10n.removeTerm + ' ' + listItem.html() + '</span>' +
'</button>' );
/**
* Handles the click and keypress event of the tag remove button.
*
* Makes sure the focus ends up in the tag input field when using
* the keyboard to delete the tag.
*
* @since 4.2.0
*
* @param {Event} e The click or keypress event to handle.
*
* @return {void}
*/
xbutton.on( 'click keypress', function( e ) {
// On click or when using the Enter/Spacebar keys.
if ( 'click' === e.type || 13 === e.keyCode || 32 === e.keyCode ) {
/*
* When using the keyboard, move focus back to the
* add new tag field. Note: when releasing the pressed
* key this will fire the `keyup` event on the input.
*/
if ( 13 === e.keyCode || 32 === e.keyCode ) {
$( this ).closest( '.tagsdiv' ).find( 'input.newtag' ).focus();
}
tagBox.userAction = 'remove';
tagBox.parseTags( this );
}
});
listItem.prepend( '&nbsp;' ).prepend( xbutton );
}
// Append the list item to the tag list.
tagchecklist.append( listItem );
});
// The buttons list is built now, give feedback to screen reader users.
tagBox.screenReadersMessage();
},
/**
* Adds a new tag.
*
* Also ensures that the quick links are properly generated.
*
* @since 2.9.0
* @memberOf tagBox
*
* @param {Object} el The container HTML element.
* @param {Object|boolean} a When this is an HTML element the text of that
* element will be used for the new tag.
* @param {number|boolean} f If this value is not passed then the tag input
* field is focused.
*
* @return {boolean} Always returns false.
*/
flushTags : function( el, a, f ) {
var tagsval, newtags, text,
tags = $( '.the-tags', el ),
newtag = $( 'input.newtag', el );
a = a || false;
text = a ? $(a).text() : newtag.val();
/*
* Return if there's no new tag or if the input field is empty.
* Note: when using the keyboard to add tags, focus is moved back to
* the input field and the `keyup` event attached on this field will
* fire when releasing the pressed key. Checking also for the field
* emptiness avoids to set the tags and call quickClicks() again.
*/
if ( 'undefined' == typeof( text ) || '' === text ) {
return false;
}
tagsval = tags.val();
newtags = tagsval ? tagsval + tagDelimiter + text : text;
newtags = this.clean( newtags );
newtags = array_unique_noempty( newtags.split( tagDelimiter ) ).join( tagDelimiter );
tags.val( newtags );
this.quickClicks( el );
if ( ! a )
newtag.val('');
if ( 'undefined' == typeof( f ) )
newtag.focus();
return false;
},
/**
* Retrieves the available tags and creates a tagcloud.
*
* Retrieves the available tags from the database and creates an interactive
* tagcloud. Clicking a tag will add it.
*
* @since 2.9.0
* @memberOf tagBox
*
* @param {string} id The ID to extract the taxonomy from.
*
* @return {void}
*/
get : function( id ) {
var tax = id.substr( id.indexOf('-') + 1 );
/**
* Puts a received tag cloud into a DOM element.
*
* The tag cloud HTML is generated on the server.
*
* @since 2.9.0
*
* @param {number|string} r The response message from the AJAX call.
* @param {string} stat The status of the AJAX request.
*
* @return {void}
*/
$.post( ajaxurl, { 'action': 'get-tagcloud', 'tax': tax }, function( r, stat ) {
if ( 0 === r || 'success' != stat ) {
return;
}
r = $( '<div id="tagcloud-' + tax + '" class="the-tagcloud">' + r + '</div>' );
/**
* Adds a new tag when a tag in the tagcloud is clicked.
*
* @since 2.9.0
*
* @return {boolean} Returns false to prevent the default action.
*/
$( 'a', r ).click( function() {
tagBox.userAction = 'add';
tagBox.flushTags( $( '#' + tax ), this );
return false;
});
$( '#' + id ).after( r );
});
},
/**
* Track the user's last action.
*
* @since 4.7.0
*/
userAction: '',
/**
* Dispatches an audible message to screen readers.
*
* This will inform the user when a tag has been added or removed.
*
* @since 4.7.0
*
* @return {void}
*/
screenReadersMessage: function() {
var message;
switch ( this.userAction ) {
case 'remove':
message = window.tagsSuggestL10n.termRemoved;
break;
case 'add':
message = window.tagsSuggestL10n.termAdded;
break;
default:
return;
}
window.wp.a11y.speak( message, 'assertive' );
},
/**
* Initializes the tags box by setting up the links, buttons. Sets up event
* handling.
*
* This includes handling of pressing the enter key in the input field and the
* retrieval of tag suggestions.
*
* @since 2.9.0
* @memberOf tagBox
*
* @return {void}
*/
init : function() {
var ajaxtag = $('div.ajaxtag');
$('.tagsdiv').each( function() {
tagBox.quickClicks( this );
});
$( '.tagadd', ajaxtag ).click( function() {
tagBox.userAction = 'add';
tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
});
/**
* Handles pressing enter on the new tag input field.
*
* Prevents submitting the post edit form.
*
* @since 2.9.0
*
* @param {Event} event The keypress event that occurred.
*
* @return {void}
*/
$( 'input.newtag', ajaxtag ).keypress( function( event ) {
if ( 13 == event.which ) {
tagBox.userAction = 'add';
tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
event.preventDefault();
event.stopPropagation();
}
}).keypress( function( event ) {
if ( 13 == event.which ) {
event.preventDefault();
event.stopPropagation();
}
}).each( function( i, element ) {
$( element ).wpTagsSuggest();
});
/**
* Before a post is saved the value currently in the new tag input field will be
* added as a tag.
*
* @since 2.9.0
*
* @return {void}
*/
$('#post').submit(function(){
$('div.tagsdiv').each( function() {
tagBox.flushTags(this, false, 1);
});
});
/**
* Handles clicking on the tag cloud link.
*
* Makes sure the ARIA attributes are set correctly.
*
* @since 2.9.0
*
* @return {void}
*/
$('.tagcloud-link').click(function(){
// On the first click, fetch the tag cloud and insert it in the DOM.
tagBox.get( $( this ).attr( 'id' ) );
// Update button state, remove previous click event and attach a new one to toggle the cloud.
$( this )
.attr( 'aria-expanded', 'true' )
.unbind()
.click( function() {
$( this )
.attr( 'aria-expanded', 'false' === $( this ).attr( 'aria-expanded' ) ? 'true' : 'false' )
.siblings( '.the-tagcloud' ).toggle();
});
});
}
};
}( jQuery ));