[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> wp-emoji.js (source)

   1  /**
   2   * @output wp-includes/js/wp-emoji.js
   3   */
   4  
   5  /**
   6   * Additional options accepted by wp.emoji.parse().
   7   *
   8   * @typedef WPEmojiParseArgs
   9   * @type {Object}
  10   * @property {string}                 [className] Class name to give each generated image.
  11   * @property {Record<string, string>} [imgAttr]   Attributes to set on each generated image, in
  12   *                                                place of the default ones.
  13   */
  14  
  15  /**
  16   * wp-emoji.js is used to replace emoji with images in browsers when the browser
  17   * doesn't support emoji natively.
  18   *
  19   * @param {Window}          window   The global window object.
  20   * @param {WPEmojiSettings} settings The settings object.
  21   */
  22  ( function ( window, settings ) {
  23      /**
  24       * Replaces emoji with images when browsers don't support emoji.
  25       *
  26       * @since 4.2.0
  27       * @access private
  28       *
  29       * @see  Twemoji
  30       * @link https://github.com/jdecked/twemoji
  31       *
  32       * @return {{
  33       *     parse: ( object: HTMLElement|string, args?: WPEmojiParseArgs ) => HTMLElement|string,
  34       *     test: ( text: ?string ) => boolean
  35       * }} The wpEmoji parse and test functions.
  36       */
  37  	function wpEmoji() {
  38          // Compression and maintain local scope.
  39          const document = window.document;
  40  
  41          // Private.
  42          /** @type {Twemoji|undefined} */
  43          let twemoji;
  44  
  45          /** @type {number|undefined} */
  46          let timer;
  47  
  48          let loaded = false;
  49          let count = 0;
  50  
  51          /**
  52           * Runs when the document load event is fired, so we can do our first parse of
  53           * the page.
  54           *
  55           * Listens to all the DOM mutations and checks for added nodes that contain
  56           * emoji characters and replaces those with twitter emoji images.
  57           *
  58           * @since 4.2.0
  59           * @private
  60           */
  61  		function load() {
  62              if ( loaded ) {
  63                  return;
  64              }
  65  
  66              // Ensure twemoji is available on the global window before proceeding.
  67              if ( typeof window.twemoji === 'undefined' ) {
  68                  // Break if waiting for longer than 30 seconds.
  69                  if ( count > 600 ) {
  70                      return;
  71                  }
  72  
  73                  // Still waiting.
  74                  window.clearTimeout( timer );
  75                  timer = window.setTimeout( load, 50 );
  76                  count++;
  77  
  78                  return;
  79              }
  80  
  81              twemoji = window.twemoji;
  82              loaded = true;
  83  
  84              // Initialize the mutation observer, which checks all added nodes for
  85              // replaceable emoji characters.
  86              new MutationObserver( ( mutationRecords ) => {
  87                  for ( const { addedNodes, removedNodes } of mutationRecords ) {
  88                      const addedNode = addedNodes[ 0 ];
  89                      const removedNode = removedNodes[ 0 ];
  90  
  91                      /*
  92                       * Checks if an image has been replaced by a text element
  93                       * with the same text as the alternate description of the replaced image.
  94                       * (presumably because the image could not be loaded).
  95                       * If it is, leave this record alone, so that the text is not turned
  96                       * straight back into the image which just failed to load.
  97                       */
  98                      if (
  99                          addedNodes.length === 1 && removedNodes.length === 1 &&
 100                          addedNode instanceof Text &&
 101                          removedNode instanceof HTMLImageElement &&
 102                          addedNode.data === removedNode.alt &&
 103                          'load-failed' === removedNode.dataset.error
 104                      ) {
 105                          continue;
 106                      }
 107  
 108                      // Loop through all the added nodes.
 109                      for ( const addedNode of addedNodes ) {
 110                          // Emoji in a text node are replaced by parsing the element which contains it.
 111                          const node = addedNode instanceof Text ? addedNode.parentElement : addedNode;
 112  
 113                          if ( node instanceof HTMLElement && test( node.textContent ) ) {
 114                              parse( node );
 115                          }
 116                      }
 117                  }
 118              } ).observe( document.body, {
 119                  childList: true,
 120                  subtree: true
 121              } );
 122  
 123              parse( document.body );
 124          }
 125  
 126          /**
 127           * Tests if a text string contains emoji characters.
 128           *
 129           * @since 4.3.0
 130           *
 131           * @memberOf wp.emoji
 132           *
 133           * @param {?string} text The string to test.
 134           *
 135           * @return {boolean} Whether the string contains emoji characters.
 136           */
 137  		function test( text ) {
 138              // Single char. U+20E3 to detect keycaps. U+00A9 "copyright sign" and U+00AE "registered sign" not included.
 139              const single = /[\u203C\u2049\u20E3\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2300\u231A\u231B\u2328\u2388\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638\u2639\u263A\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692\u2693\u2694\u2696\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753\u2754\u2755\u2757\u2763\u2764\u2795\u2796\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05\u2B06\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]/;
 140  
 141              // Surrogate pair range. Only tests for the second half.
 142              const pair = /[\uDC00-\uDFFF]/;
 143  
 144              if ( text ) {
 145                  return pair.test( text ) || single.test( text );
 146              }
 147  
 148              return false;
 149          }
 150  
 151          /**
 152           * Parses any emoji characters into Twemoji images.
 153           *
 154           * - When passed an element the emoji characters are replaced inline.
 155           * - When passed a string the emoji characters are replaced and the result is
 156           * returned.
 157           *
 158           * @since 4.2.0
 159           *
 160           * @memberOf wp.emoji
 161           *
 162           * @param {HTMLElement|string} object The element or string to parse.
 163           * @param {WPEmojiParseArgs}   [args] Additional options for Twemoji.
 164           *
 165           * @return {HTMLElement|string} A string where all emoji are now image tags of
 166           *                              emoji. Or the element that was passed as the first argument.
 167           */
 168  		function parse( object, args ) {
 169              /*
 170               * If the browser has full support, twemoji is not loaded or our
 171               * object is not what was expected, we do not parse anything.
 172               */
 173              if ( settings.supports.everything || ! twemoji || ! object ||
 174                  ( 'string' !== typeof object && ( ! object.childNodes || ! object.childNodes.length ) ) ) {
 175  
 176                  return object;
 177              }
 178  
 179              // Compose the params for the twitter emoji library.
 180              args = args || {};
 181  
 182              // The caller may replace the attributes given to every generated image.
 183              const attributes = typeof args.imgAttr === 'object' ? args.imgAttr : { role: 'img' };
 184  
 185              /** @type {TwemojiParseOptions} */
 186              const params = {
 187                  base: settings.svgUrl,
 188                  ext: settings.svgExt,
 189                  className: args.className || 'emoji',
 190                  callback: ( icon, options ) => {
 191                      // Ignore some standard characters that TinyMCE recommends in its character map.
 192                      switch ( icon ) {
 193                          case 'a9':
 194                          case 'ae':
 195                          case '2122':
 196                          case '2194':
 197                          case '2660':
 198                          case '2663':
 199                          case '2665':
 200                          case '2666':
 201                              return false;
 202                      }
 203  
 204                      if ( settings.supports.everythingExceptFlag &&
 205                          ! /^1f1(?:e[6-9a-f]|f[0-9a-f])-1f1(?:e[6-9a-f]|f[0-9a-f])$/.test( icon ) && // Country flags.
 206                          ! /^(1f3f3-fe0f-200d-1f308|1f3f4-200d-2620-fe0f)$/.test( icon )             // Rainbow and pirate flags.
 207                      ) {
 208                          return false;
 209                      }
 210  
 211                      return ''.concat( options.base, icon, options.ext );
 212                  },
 213                  attributes: () => attributes,
 214                  onerror: function () {
 215                      /*
 216                       * Put the emoji character back in place of the image which failed to load. The
 217                       * attribute is what tells the MutationObserver above that this replacement is
 218                       * the one it must not turn straight back into an image.
 219                       */
 220                      if ( this.parentNode ) {
 221                          this.dataset.error = 'load-failed';
 222                          this.parentNode.replaceChild( document.createTextNode( this.alt ), this );
 223                      }
 224                  },
 225                  doNotParse: ( element ) => {
 226                      // Emoji will not be replaced in this element, nor in any of its descendants.
 227                      return element.classList.contains( 'wp-exclude-emoji' );
 228                  }
 229              };
 230  
 231              return twemoji.parse( object, params );
 232          }
 233  
 234          load();
 235  
 236          return { parse, test };
 237      }
 238  
 239      window.wp = window.wp || {};
 240  
 241      /**
 242       * @namespace wp.emoji
 243       */
 244      window.wp.emoji = wpEmoji();
 245  
 246  } )( window, window._wpemojiSettings );


Generated : Sun Oct 25 08:20:35 2026 Cross-referenced by PHPXref