[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
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 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |