[ Index ]

PHP Cross Reference of WordPress

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


Generated: Fri Apr 19 01:00:02 2024 Cross-referenced by PHPXref 0.7.1