angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

wp-emoji.js (8989B)


      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 );