[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/js/ -> tags-suggest.js (source)

   1  /**
   2   * @output wp-admin/js/tags-suggest.js
   3   */
   4  
   5  /**
   6   * Default settings for jQuery UI Autocomplete for use with non-hierarchical taxonomies.
   7   *
   8   * @param {JQueryStatic} $ The jQuery object.
   9   */
  10  ( function( $ ) {
  11      var tempID = 0;
  12      var separator = wp.i18n._x( ',', 'tag delimiter' ) || ',';
  13      var __ = wp.i18n.__,
  14          _n = wp.i18n._n,
  15          sprintf = wp.i18n.sprintf;
  16  
  17      /**
  18       * Splits a string into an array of terms based on the separator.
  19       *
  20       * @param {string} val The string to split.
  21       * @return {string[]} An array of terms.
  22       */
  23  	function split( val ) {
  24          return val.split( new RegExp( separator + '\\s*' ) );
  25      }
  26  
  27      /**
  28       * Gets the last term from a string based on the separator.
  29       *
  30       * @param {string} term The string to get the last term from.
  31       * @return {string} The last term.
  32       */
  33  	function getLast( term ) {
  34          return split( term ).pop();
  35      }
  36  
  37      /**
  38       * Adds UI Autocomplete to an input or textarea element with presets for use
  39       * with non-hierarchical taxonomies.
  40       *
  41       * Example: `$( element ).wpTagsSuggest( options )`.
  42       *
  43       * The taxonomy can be passed in a `data-wp-taxonomy` attribute on the element or
  44       * can be in `options.taxonomy`.
  45       *
  46       * @since 4.7.0
  47       *
  48       * @param {Object} options Options that are passed to UI Autocomplete. Can be used to override the default settings.
  49       * @return {Object} jQuery instance.
  50       */
  51      $.fn.wpTagsSuggest = function( options ) {
  52          var cache;
  53          var last;
  54          var $element = $( this );
  55  
  56          // Do not initialize if the element doesn't exist.
  57          if ( ! $element.length ) {
  58              return this;
  59          }
  60  
  61          options = options || {};
  62  
  63          var taxonomy = options.taxonomy || $element.attr( 'data-wp-taxonomy' ) || 'post_tag';
  64  
  65          delete( options.taxonomy );
  66  
  67          options = $.extend( {
  68              source: function( request, response ) {
  69                  var term;
  70  
  71                  if ( last === request.term ) {
  72                      response( cache );
  73                      return;
  74                  }
  75  
  76                  term = getLast( request.term );
  77  
  78                  $.get( window.ajaxurl, {
  79                      action: 'ajax-tag-search',
  80                      tax: taxonomy,
  81                      q: term,
  82                      number: 20
  83                  } ).always( function() {
  84                      $element.removeClass( 'ui-autocomplete-loading' ); // UI fails to remove this sometimes?
  85                  } ).done( function( data ) {
  86                      var tagName;
  87                      var tags = [];
  88  
  89                      if ( data ) {
  90                          data = data.split( '\n' );
  91  
  92                          for ( tagName in data ) {
  93                              var id = ++tempID;
  94  
  95                              tags.push({
  96                                  id: id,
  97                                  name: data[tagName]
  98                              });
  99                          }
 100  
 101                          cache = tags;
 102                          response( tags );
 103                      } else {
 104                          response( tags );
 105                      }
 106                  } );
 107  
 108                  last = request.term;
 109              },
 110              focus: function( event, ui ) {
 111                  $element.attr( 'aria-activedescendant', 'wp-tags-autocomplete-' + ui.item.id );
 112  
 113                  // Don't empty the input field when using the arrow keys
 114                  // to highlight items. See api.jqueryui.com/autocomplete/#event-focus
 115                  event.preventDefault();
 116              },
 117              select: function( event, ui ) {
 118                  var tags = split( $element.val() );
 119                  // Remove the last user input.
 120                  tags.pop();
 121                  // Append the new tag and an empty element to get one more separator at the end.
 122                  tags.push( ui.item.name, '' );
 123  
 124                  $element.val( tags.join( separator + ' ' ) );
 125  
 126                  if ( $.ui.keyCode.TAB === event.keyCode ) {
 127                      // Audible confirmation message when a tag has been selected.
 128                      window.wp.a11y.speak( wp.i18n.__( 'Term selected.' ), 'assertive' );
 129                      event.preventDefault();
 130                  } else if ( $.ui.keyCode.ENTER === event.keyCode ) {
 131                      // If we're in the edit post Tags meta box, add the tag.
 132                      if ( window.tagBox ) {
 133                          window.tagBox.userAction = 'add';
 134                          window.tagBox.flushTags( $( this ).closest( '.tagsdiv' ) );
 135                      }
 136  
 137                      // Do not close Quick Edit / Bulk Edit.
 138                      event.preventDefault();
 139                      event.stopPropagation();
 140                  }
 141  
 142                  return false;
 143              },
 144              open: function() {
 145                  $element.attr( 'aria-expanded', 'true' );
 146              },
 147              close: function() {
 148                  $element.attr( 'aria-expanded', 'false' );
 149              },
 150              minLength: 2,
 151              position: {
 152                  my: 'left top+2',
 153                  at: 'left bottom',
 154                  collision: 'none'
 155              },
 156              messages: {
 157                  noResults: __( 'No results found.' ),
 158                  results: function( number ) {
 159                      return sprintf(
 160                          /* translators: %d: Number of search results found. */
 161                          _n(
 162                              '%d result found. Use up and down arrow keys to navigate.',
 163                              '%d results found. Use up and down arrow keys to navigate.',
 164                              number
 165                          ),
 166                          number
 167                      );
 168                  }
 169              }
 170          }, options );
 171  
 172          $element.on( 'keydown', function() {
 173              $element.removeAttr( 'aria-activedescendant' );
 174          } );
 175  
 176          $element.autocomplete( options );
 177  
 178          // Ensure the autocomplete instance exists.
 179          if ( ! $element.autocomplete( 'instance' ) ) {
 180              return this;
 181          }
 182  
 183          $element.autocomplete( 'instance' )._renderItem = function( ul, item ) {
 184              return $( '<li role="option" id="wp-tags-autocomplete-' + item.id + '">' )
 185                  .text( item.name )
 186                  .appendTo( ul );
 187          };
 188  
 189          $element.attr( {
 190              'role': 'combobox',
 191              'aria-autocomplete': 'list',
 192              'aria-expanded': 'false',
 193              'aria-owns': $element.autocomplete( 'widget' ).attr( 'id' )
 194          } )
 195          .on( 'focus', function() {
 196              var inputValue = split( $element.val() ).pop();
 197  
 198              // Don't trigger a search if the field is empty.
 199              // Also, avoids screen readers announce `No search results`.
 200              if ( inputValue ) {
 201                  $element.autocomplete( 'search' );
 202              }
 203          } );
 204  
 205          // Returns a jQuery object containing the menu element.
 206          $element.autocomplete( 'widget' )
 207              .addClass( 'wp-tags-autocomplete' )
 208              .attr( 'role', 'listbox' )
 209              .removeAttr( 'tabindex' ) // Remove the `tabindex=0` attribute added by jQuery UI.
 210  
 211              /*
 212               * Looks like Safari and VoiceOver need an `aria-selected` attribute. See ticket #33301.
 213               * The `menufocus` and `menublur` events are the same events used to add and remove
 214               * the `ui-state-focus` CSS class on the menu items. See jQuery UI Menu Widget.
 215               */
 216              .on( 'menufocus', function( event, ui ) {
 217                  ui.item.attr( 'aria-selected', 'true' );
 218              })
 219              .on( 'menublur', function() {
 220                  // The `menublur` event returns an object where the item is `null`,
 221                  // so we need to find the active item with other means.
 222                  $( this ).find( '[aria-selected="true"]' ).removeAttr( 'aria-selected' );
 223              });
 224  
 225          return this;
 226      };
 227  
 228  }( jQuery ) );


Generated : Fri Sep 11 08:20:31 2026 Cross-referenced by PHPXref