wp/wp-includes/js/wp-emoji.js
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     1 
     1 
     2 ( function( window, settings ) {
     2 ( function( window, settings ) {
     3 	function wpEmoji() {
     3 	function wpEmoji() {
     4 		var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
     4 		var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
     5 
     5 
     6 		/**
     6 		// Compression and maintain local scope
     7 		 * Flag to determine if we should replace emoji characters with images.
     7 		document = window.document,
     8 		 *
       
     9 		 * @since 4.2.0
       
    10 		 *
       
    11 		 * @var Boolean
       
    12 		 */
       
    13 		replaceEmoji = false,
       
    14 
     8 
    15 		// Private
     9 		// Private
    16 		twemoji, timer,
    10 		twemoji, timer,
    17 		loaded = false,
    11 		loaded = false,
    18 		count = 0;
    12 		count = 0,
       
    13 		ie11 = window.navigator.userAgent.indexOf( 'Trident/7.0' ) > 0;
       
    14 
       
    15 		/**
       
    16 		 * Detect if the browser supports SVG.
       
    17 		 *
       
    18 		 * @since 4.6.0
       
    19 		 *
       
    20 		 * @return {Boolean} True if the browser supports svg, false if not.
       
    21 		 */
       
    22 		function browserSupportsSvgAsImage() {
       
    23 			if ( !! document.implementation.hasFeature ) {
       
    24 				// Source: Modernizr
       
    25 				// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg/asimg.js
       
    26 				return document.implementation.hasFeature( 'http://www.w3.org/TR/SVG11/feature#Image', '1.1' );
       
    27 			}
       
    28 
       
    29 			// document.implementation.hasFeature is deprecated. It can be presumed
       
    30 			// if future browsers remove it, the browser will support SVGs as images.
       
    31 			return true;
       
    32 		}
    19 
    33 
    20 		/**
    34 		/**
    21 		 * Runs when the document load event is fired, so we can do our first parse of the page.
    35 		 * Runs when the document load event is fired, so we can do our first parse of the page.
    22 		 *
    36 		 *
    23 		 * @since 4.2.0
    37 		 * @since 4.2.0
    56 
    70 
    57 						if (
    71 						if (
    58 							ii === 1 && removedNodes.length === 1 &&
    72 							ii === 1 && removedNodes.length === 1 &&
    59 							addedNodes[0].nodeType === 3 &&
    73 							addedNodes[0].nodeType === 3 &&
    60 							removedNodes[0].nodeName === 'IMG' &&
    74 							removedNodes[0].nodeName === 'IMG' &&
    61 							addedNodes[0].data === removedNodes[0].alt
    75 							addedNodes[0].data === removedNodes[0].alt &&
       
    76 							'load-failed' === removedNodes[0].getAttribute( 'data-error' )
    62 						) {
    77 						) {
    63 							return;
    78 							return;
    64 						}
    79 						}
    65 
    80 
    66 						while ( ii-- ) {
    81 						while ( ii-- ) {
    67 							node = addedNodes[ ii ];
    82 							node = addedNodes[ ii ];
    68 
    83 
    69 							if ( node.nodeType === 3 ) {
    84 							if ( node.nodeType === 3 ) {
       
    85 								if ( ! node.parentNode ) {
       
    86 									continue;
       
    87 								}
       
    88 
       
    89 								if ( ie11 ) {
       
    90 									/*
       
    91 									 * IE 11's implementation of MutationObserver is buggy.
       
    92 									 * It unnecessarily splits text nodes when it encounters a HTML
       
    93 									 * template interpolation symbol ( "{{", for example ). So, we
       
    94 									 * join the text nodes back together as a work-around.
       
    95 									 */
       
    96 									while( node.nextSibling && 3 === node.nextSibling.nodeType ) {
       
    97 										node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
       
    98 										node.parentNode.removeChild( node.nextSibling );
       
    99 									}
       
   100 								}
       
   101 
    70 								node = node.parentNode;
   102 								node = node.parentNode;
    71 							}
   103 							}
    72 
   104 
    73 							if ( ! node || ( node.className && node.className.indexOf( 'wp-exclude-emoji' ) !== -1 ) ) {
   105 							if ( ! node || node.nodeType !== 1 ||
       
   106 								( node.className && typeof node.className === 'string' && node.className.indexOf( 'wp-exclude-emoji' ) !== -1 ) ) {
       
   107 
    74 								continue;
   108 								continue;
    75 							}
   109 							}
    76 
   110 
    77 							if ( node && node.nodeType === 1 ) {
   111 							if ( test( node.textContent ) ) {
    78 								parse( node );
   112 								parse( node );
    79 							}
   113 							}
    80 						}
   114 						}
    81 					}
   115 					}
    82 				} ).observe( document.body, {
   116 				} ).observe( document.body, {
    87 
   121 
    88 			parse( document.body );
   122 			parse( document.body );
    89 		}
   123 		}
    90 
   124 
    91 		/**
   125 		/**
       
   126 		 * Test if a text string contains emoji characters.
       
   127 		 *
       
   128 		 * @since 4.3.0
       
   129 		 *
       
   130 		 * @param {String} text The string to test
       
   131 		 *
       
   132 		 * @return {Boolean} Whether the string contains emoji characters.
       
   133 		 */
       
   134 		function test( text ) {
       
   135 			// Single char. U+20E3 to detect keycaps. U+00A9 "copyright sign" and U+00AE "registered sign" not included.
       
   136 			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]/,
       
   137 			// Surrogate pair range. Only tests for the second half.
       
   138 			pair = /[\uDC00-\uDFFF]/;
       
   139 
       
   140 			if ( text ) {
       
   141 				return  pair.test( text ) || single.test( text );
       
   142 			}
       
   143 
       
   144 			return false;
       
   145 		}
       
   146 
       
   147 		/**
    92 		 * Given an element or string, parse any emoji characters into Twemoji images.
   148 		 * Given an element or string, parse any emoji characters into Twemoji images.
    93 		 *
   149 		 *
    94 		 * @since 4.2.0
   150 		 * @since 4.2.0
    95 		 *
   151 		 *
    96 		 * @param {HTMLElement|String} object The element or string to parse.
   152 		 * @param {HTMLElement|String} object The element or string to parse.
    97 		 * @param {Object} args Additional options for Twemoji.
   153 		 * @param {Object} args Additional options for Twemoji.
    98 		 */
   154 		 */
    99 		function parse( object, args ) {
   155 		function parse( object, args ) {
   100 			if ( ! replaceEmoji || ! twemoji ) {
   156 			var params;
       
   157 
       
   158 			if ( settings.supports.everything || ! twemoji || ! object ||
       
   159 				( 'string' !== typeof object && ( ! object.childNodes || ! object.childNodes.length ) ) ) {
       
   160 
   101 				return object;
   161 				return object;
   102 			}
   162 			}
   103 
   163 
   104 			args = args || {};
   164 			args = args || {};
   105 
   165 			params = {
   106 			return twemoji.parse( object, {
   166 				base: browserSupportsSvgAsImage() ? settings.svgUrl : settings.baseUrl,
   107 				base: settings.baseUrl,
   167 				ext:  browserSupportsSvgAsImage() ? settings.svgExt : settings.ext,
   108 				ext: settings.ext,
       
   109 				className: args.className || 'emoji',
   168 				className: args.className || 'emoji',
   110 				imgAttr: args.imgAttr,
       
   111 				callback: function( icon, options ) {
   169 				callback: function( icon, options ) {
   112 					// Ignore some standard characters that TinyMCE recommends in its character map.
   170 					// Ignore some standard characters that TinyMCE recommends in its character map.
   113 					switch ( icon ) {
   171 					switch ( icon ) {
   114 						case 'a9':
   172 						case 'a9':
   115 						case 'ae':
   173 						case 'ae':
   120 						case '2665':
   178 						case '2665':
   121 						case '2666':
   179 						case '2666':
   122 							return false;
   180 							return false;
   123 					}
   181 					}
   124 
   182 
   125 					if ( ! settings.supports.flag && settings.supports.simple &&
   183 					if ( settings.supports.everythingExceptFlag &&
   126 						! /^1f1(?:e[6-9a-f]|f[0-9a-f])-1f1(?:e[6-9a-f]|f[0-9a-f])$/.test( icon ) ) {
   184 						! /^1f1(?:e[6-9a-f]|f[0-9a-f])-1f1(?:e[6-9a-f]|f[0-9a-f])$/.test( icon ) && // Country flags
   127 
   185 						! /^(1f3f3-fe0f-200d-1f308|1f3f4-200d-2620-fe0f)$/.test( icon )             // Rainbow and pirate flags
       
   186 					) {
   128 						return false;
   187 						return false;
   129 					}
   188 					}
   130 
   189 
   131 					return ''.concat( options.base, icon, options.ext );
   190 					return ''.concat( options.base, icon, options.ext );
       
   191 				},
       
   192 				onerror: function() {
       
   193 					if ( twemoji.parentNode ) {
       
   194 						this.setAttribute( 'data-error', 'load-failed' );
       
   195 						twemoji.parentNode.replaceChild( document.createTextNode( twemoji.alt ), twemoji );
       
   196 					}
   132 				}
   197 				}
   133 			} );
   198 			};
       
   199 
       
   200 			if ( typeof args.imgAttr === 'object' ) {
       
   201 				params.attributes = function() {
       
   202 					return args.imgAttr;
       
   203 				};
       
   204 			}
       
   205 
       
   206 			return twemoji.parse( object, params );
   134 		}
   207 		}
   135 
   208 
   136 		/**
   209 		/**
   137 		 * Initialize our emoji support, and set up listeners.
   210 		 * Initialize our emoji support, and set up listeners.
   138 		 */
   211 		 */
   139 		if ( settings ) {
   212 		if ( settings ) {
   140 			replaceEmoji = ! settings.supports.simple || ! settings.supports.flag;
       
   141 
       
   142 			if ( settings.DOMReady ) {
   213 			if ( settings.DOMReady ) {
   143 				load();
   214 				load();
   144 			} else {
   215 			} else {
   145 				settings.readyCallback = load;
   216 				settings.readyCallback = load;
   146 			}
   217 			}
   147 		}
   218 		}
   148 
   219 
   149 		return {
   220 		return {
   150 			replaceEmoji: replaceEmoji,
   221 			parse: parse,
   151 			parse: parse
   222 			test: test
   152 		};
   223 		};
   153 	}
   224 	}
   154 
   225 
   155 	window.wp = window.wp || {};
   226 	window.wp = window.wp || {};
   156 	window.wp.emoji = new wpEmoji();
   227 	window.wp.emoji = new wpEmoji();