[ 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   * @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                              if ( test( node.textContent ) ) {
 149                                  parse( node );
 150                              }
 151                          }
 152                      }
 153                  } ).observe( document.body, {
 154                      childList: true,
 155                      subtree: true
 156                  } );
 157              }
 158  
 159              parse( document.body );
 160          }
 161  
 162          /**
 163           * Tests if a text string contains emoji characters.
 164           *
 165           * @since 4.3.0
 166           *
 167           * @memberOf wp.emoji
 168           *
 169           * @param {string} text The string to test.
 170           *
 171           * @return {boolean} Whether the string contains emoji characters.
 172           */
 173  		function test( text ) {
 174              // Single char. U+20E3 to detect keycaps. U+00A9 "copyright sign" and U+00AE "registered sign" not included.
 175              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]/,
 176              // Surrogate pair range. Only tests for the second half.
 177              pair = /[\uDC00-\uDFFF]/;
 178  
 179              if ( text ) {
 180                  return  pair.test( text ) || single.test( text );
 181              }
 182  
 183              return false;
 184          }
 185  
 186          /**
 187           * Parses any emoji characters into Twemoji images.
 188           *
 189           * - When passed an element the emoji characters are replaced inline.
 190           * - When passed a string the emoji characters are replaced and the result is
 191           *   returned.
 192           *
 193           * @since 4.2.0
 194           *
 195           * @memberOf wp.emoji
 196           *
 197           * @param {HTMLElement|string} object The element or string to parse.
 198           * @param {Object}             args   Additional options for Twemoji.
 199           *
 200           * @return {HTMLElement|string} A string where all emoji are now image tags of
 201           *                              emoji. Or the element that was passed as the first argument.
 202           */
 203  		function parse( object, args ) {
 204              var params;
 205  
 206              /*
 207               * If the browser has full support, twemoji is not loaded or our
 208               * object is not what was expected, we do not parse anything.
 209               */
 210              if ( settings.supports.everything || ! twemoji || ! object ||
 211                  ( 'string' !== typeof object && ( ! object.childNodes || ! object.childNodes.length ) ) ) {
 212  
 213                  return object;
 214              }
 215  
 216              // Compose the params for the twitter emoji library.
 217              args = args || {};
 218              params = {
 219                  base: browserSupportsSvgAsImage() ? settings.svgUrl : settings.baseUrl,
 220                  ext:  browserSupportsSvgAsImage() ? settings.svgExt : settings.ext,
 221                  className: args.className || 'emoji',
 222                  callback: function( icon, options ) {
 223                      // Ignore some standard characters that TinyMCE recommends in its character map.
 224                      switch ( icon ) {
 225                          case 'a9':
 226                          case 'ae':
 227                          case '2122':
 228                          case '2194':
 229                          case '2660':
 230                          case '2663':
 231                          case '2665':
 232                          case '2666':
 233                              return false;
 234                      }
 235  
 236                      if ( settings.supports.everythingExceptFlag &&
 237                          ! /^1f1(?:e[6-9a-f]|f[0-9a-f])-1f1(?:e[6-9a-f]|f[0-9a-f])$/.test( icon ) && // Country flags.
 238                          ! /^(1f3f3-fe0f-200d-1f308|1f3f4-200d-2620-fe0f)$/.test( icon )             // Rainbow and pirate flags.
 239                      ) {
 240                          return false;
 241                      }
 242  
 243                      return ''.concat( options.base, icon, options.ext );
 244                  },
 245                  attributes: function() {
 246                      return {
 247                          role: 'img'
 248                      };
 249                  },
 250                  onerror: function() {
 251                      if ( twemoji.parentNode ) {
 252                          this.setAttribute( 'data-error', 'load-failed' );
 253                          twemoji.parentNode.replaceChild( document.createTextNode( twemoji.alt ), twemoji );
 254                      }
 255                  },
 256                  doNotParse: function( node ) {
 257                      if (
 258                          node &&
 259                          node.className &&
 260                          typeof node.className === 'string' &&
 261                          node.className.indexOf( 'wp-exclude-emoji' ) !== -1
 262                      ) {
 263                          // Do not parse this node. Emojis will not be replaced in this node and all sub-nodes.
 264                          return true;
 265                      }
 266  
 267                      return false;
 268                  }
 269              };
 270  
 271              if ( typeof args.imgAttr === 'object' ) {
 272                  params.attributes = function() {
 273                      return args.imgAttr;
 274                  };
 275              }
 276  
 277              return twemoji.parse( object, params );
 278          }
 279  
 280          /**
 281           * Initialize our emoji support, and set up listeners.
 282           */
 283          if ( settings ) {
 284              if ( settings.DOMReady ) {
 285                  load();
 286              } else {
 287                  settings.readyCallback = load;
 288              }
 289          }
 290  
 291          return {
 292              parse: parse,
 293              test: test
 294          };
 295      }
 296  
 297      window.wp = window.wp || {};
 298  
 299      /**
 300       * @namespace wp.emoji
 301       */
 302      window.wp.emoji = new wpEmoji();
 303  
 304  } )( window, window._wpemojiSettings );


Generated : Mon Mar 18 08:20:01 2024 Cross-referenced by PHPXref