[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  /**
   2   * wp-emoji.js is used to replace emoji with images in browsers when the browser
   3   * doesn't support emoji natively.
   4   *
   5   * @param {Window} window The global window object.
   6   * @param {Object} settings The settings object.
   7   * @output wp-includes/js/wp-emoji.js
   8   */
   9  
  10  ( function( window, settings ) {
  11      /**
  12       * Replaces emoji with images when browsers don't support emoji.
  13       *
  14       * @since 4.2.0
  15       * @access private
  16       *
  17       * @class
  18       *
  19       * @see  Twitter Emoji library
  20       * @link https://github.com/twitter/twemoji
  21       *
  22       * @return {Object} The wpEmoji parse and test functions.
  23       */
  24  	function wpEmoji() {
  25          var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
  26  
  27          // Compression and maintain local scope.
  28          document = window.document,
  29  
  30          // Private.
  31          twemoji, timer,
  32          loaded = false,
  33          count = 0,
  34          ie11 = window.navigator.userAgent.indexOf( 'Trident/7.0' ) > 0;
  35  
  36          /**
  37           * Detect if the browser supports SVG.
  38           *
  39           * @since 4.6.0
  40           * @private
  41           *
  42           * @see Modernizr
  43           * @link https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg/asimg.js
  44           *
  45           * @return {boolean} True if the browser supports svg, false if not.
  46           */
  47  		function browserSupportsSvgAsImage() {
  48              if ( !! document.implementation.hasFeature ) {
  49                  return document.implementation.hasFeature( 'http://www.w3.org/TR/SVG11/feature#Image', '1.1' );
  50              }
  51  
  52              // document.implementation.hasFeature is deprecated. It can be presumed
  53              // if future browsers remove it, the browser will support SVGs as images.
  54              return true;
  55          }
  56  
  57          /**
  58           * Runs when the document load event is fired, so we can do our first parse of
  59           * the page.
  60           *
  61           * Listens to all the DOM mutations and checks for added nodes that contain
  62           * emoji characters and replaces those with twitter emoji images.
  63           *
  64           * @since 4.2.0
  65           * @private
  66           */
  67  		function load() {
  68              if ( loaded ) {
  69                  return;
  70              }
  71  
  72              // Ensure twemoji is available on the global window before proceeding.
  73              if ( typeof window.twemoji === 'undefined' ) {
  74                  // Break if waiting for longer than 30 seconds.
  75                  if ( count > 600 ) {
  76                      return;
  77                  }
  78  
  79                  // Still waiting.
  80                  window.clearTimeout( timer );
  81                  timer = window.setTimeout( load, 50 );
  82                  count++;
  83  
  84                  return;
  85              }
  86  
  87              twemoji = window.twemoji;
  88              loaded = true;
  89  
  90              // Initialize the mutation observer, which checks all added nodes for
  91              // replaceable emoji characters.
  92              if ( MutationObserver ) {
  93                  new MutationObserver( function( mutationRecords ) {
  94                      var i = mutationRecords.length,
  95                          addedNodes, removedNodes, ii, node;
  96  
  97                      while ( i-- ) {
  98                          addedNodes = mutationRecords[ i ].addedNodes;
  99                          removedNodes = mutationRecords[ i ].removedNodes;
 100                          ii = addedNodes.length;
 101  
 102                          /*
 103                           * Checks if an image has been replaced by a text element
 104                           * with the same text as the alternate description of the replaced image.
 105                           * (presumably because the image could not be loaded).
 106                           * If it is, do absolutely nothing.
 107                           *
 108                           * Node type 3 is a TEXT_NODE.
 109                           *
 110                           * @link https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
 111                           */
 112                          if (
 113                              ii === 1 && removedNodes.length === 1 &&
 114                              addedNodes[0].nodeType === 3 &&
 115                              removedNodes[0].nodeName === 'IMG' &&
 116                              addedNodes[0].data === removedNodes[0].alt &&
 117                              'load-failed' === removedNodes[0].getAttribute( 'data-error' )
 118                          ) {
 119                              return;
 120                          }
 121  
 122                          // Loop through all the added nodes.
 123                          while ( ii-- ) {
 124                              node = addedNodes[ ii ];
 125  
 126                              // Node type 3 is a TEXT_NODE.
 127                              if ( node.nodeType === 3 ) {
 128                                  if ( ! node.parentNode ) {
 129                                      continue;
 130                                  }
 131  
 132                                  if ( ie11 ) {
 133                                      /*
 134                                       * IE 11's implementation of MutationObserver is buggy.
 135                                       * It unnecessarily splits text nodes when it encounters a HTML
 136                                       * template interpolation symbol ( "{{", for example ). So, we
 137                                       * join the text nodes back together as a work-around.
 138                                       *
 139                                       * Node type 3 is a TEXT_NODE.
 140                                       */
 141                                      while( node.nextSibling && 3 === node.nextSibling.nodeType ) {
 142                                          node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
 143                                          node.parentNode.removeChild( node.nextSibling );
 144                                      }
 145                                  }
 146  
 147                                  node = node.parentNode;
 148                              }
 149  
 150                              if ( test( node.textContent ) ) {
 151                                  parse( node );
 152                              }
 153                          }
 154                      }
 155                  } ).observe( document.body, {
 156                      childList: true,
 157                      subtree: true
 158                  } );
 159              }
 160  
 161              parse( document.body );
 162          }
 163  
 164          /**
 165           * Tests if a text string contains emoji characters.
 166           *
 167           * @since 4.3.0
 168           *
 169           * @memberOf wp.emoji
 170           *
 171           * @param {string} text The string to test.
 172           *
 173           * @return {boolean} Whether the string contains emoji characters.
 174           */
 175  		function test( text ) {
 176              // Single char. U+20E3 to detect keycaps. U+00A9 "copyright sign" and U+00AE "registered sign" not included.
 177              var 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]/,
 178              // Surrogate pair range. Only tests for the second half.
 179              pair = /[\uDC00-\uDFFF]/;
 180  
 181              if ( text ) {
 182                  return  pair.test( text ) || single.test( text );
 183              }
 184  
 185              return false;
 186          }
 187  
 188          /**
 189           * Parses any emoji characters into Twemoji images.
 190           *
 191           * - When passed an element the emoji characters are replaced inline.
 192           * - When passed a string the emoji characters are replaced and the result is
 193           *   returned.
 194           *
 195           * @since 4.2.0
 196           *
 197           * @memberOf wp.emoji
 198           *
 199           * @param {HTMLElement|string} object The element or string to parse.
 200           * @param {Object}             args   Additional options for Twemoji.
 201           *
 202           * @return {HTMLElement|string} A string where all emoji are now image tags of
 203           *                              emoji. Or the element that was passed as the first argument.
 204           */
 205  		function parse( object, args ) {
 206              var params;
 207  
 208              /*
 209               * If the browser has full support, twemoji is not loaded or our
 210               * object is not what was expected, we do not parse anything.
 211               */
 212              if ( settings.supports.everything || ! twemoji || ! object ||
 213                  ( 'string' !== typeof object && ( ! object.childNodes || ! object.childNodes.length ) ) ) {
 214  
 215                  return object;
 216              }
 217  
 218              // Compose the params for the twitter emoji library.
 219              args = args || {};
 220              params = {
 221                  base: browserSupportsSvgAsImage() ? settings.svgUrl : settings.baseUrl,
 222                  ext:  browserSupportsSvgAsImage() ? settings.svgExt : settings.ext,
 223                  className: args.className || 'emoji',
 224                  callback: function( icon, options ) {
 225                      // Ignore some standard characters that TinyMCE recommends in its character map.
 226                      switch ( icon ) {
 227                          case 'a9':
 228                          case 'ae':
 229                          case '2122':
 230                          case '2194':
 231                          case '2660':
 232                          case '2663':
 233                          case '2665':
 234                          case '2666':
 235                              return false;
 236                      }
 237  
 238                      if ( settings.supports.everythingExceptFlag &&
 239                          ! /^1f1(?:e[6-9a-f]|f[0-9a-f])-1f1(?:e[6-9a-f]|f[0-9a-f])$/.test( icon ) && // Country flags.
 240                          ! /^(1f3f3-fe0f-200d-1f308|1f3f4-200d-2620-fe0f)$/.test( icon )             // Rainbow and pirate flags.
 241                      ) {
 242                          return false;
 243                      }
 244  
 245                      return ''.concat( options.base, icon, options.ext );
 246                  },
 247                  attributes: function() {
 248                      return {
 249                          role: 'img'
 250                      };
 251                  },
 252                  onerror: function() {
 253                      if ( twemoji.parentNode ) {
 254                          this.setAttribute( 'data-error', 'load-failed' );
 255                          twemoji.parentNode.replaceChild( document.createTextNode( twemoji.alt ), twemoji );
 256                      }
 257                  },
 258                  doNotParse: function( node ) {
 259                      if (
 260                          node &&
 261                          node.className &&
 262                          typeof node.className === 'string' &&
 263                          node.className.indexOf( 'wp-exclude-emoji' ) !== -1
 264                      ) {
 265                          // Do not parse this node. Emojis will not be replaced in this node and all sub-nodes.
 266                          return true;
 267                      }
 268  
 269                      return false;
 270                  }
 271              };
 272  
 273              if ( typeof args.imgAttr === 'object' ) {
 274                  params.attributes = function() {
 275                      return args.imgAttr;
 276                  };
 277              }
 278  
 279              return twemoji.parse( object, params );
 280          }
 281  
 282          load();
 283  
 284          return {
 285              parse: parse,
 286              test: test
 287          };
 288      }
 289  
 290      window.wp = window.wp || {};
 291  
 292      /**
 293       * @namespace wp.emoji
 294       */
 295      window.wp.emoji = new wpEmoji();
 296  
 297  } )( window, window._wpemojiSettings );


Generated : Sun Sep 6 08:20:27 2026 Cross-referenced by PHPXref