wp-emoji-loader.js (6966B)
1 /** 2 * @output wp-includes/js/wp-emoji-loader.js 3 */ 4 5 ( function( window, document, settings ) { 6 var src, ready, ii, tests; 7 8 // Create a canvas element for testing native browser support of emoji. 9 var canvas = document.createElement( 'canvas' ); 10 var context = canvas.getContext && canvas.getContext( '2d' ); 11 12 /** 13 * Checks if two sets of Emoji characters render the same visually. 14 * 15 * @since 4.9.0 16 * 17 * @private 18 * 19 * @param {number[]} set1 Set of Emoji character codes. 20 * @param {number[]} set2 Set of Emoji character codes. 21 * 22 * @return {boolean} True if the two sets render the same. 23 */ 24 function emojiSetsRenderIdentically( set1, set2 ) { 25 var stringFromCharCode = String.fromCharCode; 26 27 // Cleanup from previous test. 28 context.clearRect( 0, 0, canvas.width, canvas.height ); 29 context.fillText( stringFromCharCode.apply( this, set1 ), 0, 0 ); 30 var rendered1 = canvas.toDataURL(); 31 32 // Cleanup from previous test. 33 context.clearRect( 0, 0, canvas.width, canvas.height ); 34 context.fillText( stringFromCharCode.apply( this, set2 ), 0, 0 ); 35 var rendered2 = canvas.toDataURL(); 36 37 return rendered1 === rendered2; 38 } 39 40 /** 41 * Detects if the browser supports rendering emoji or flag emoji. 42 * 43 * Flag emoji are a single glyph made of two characters, so some browsers 44 * (notably, Firefox OS X) don't support them. 45 * 46 * @since 4.2.0 47 * 48 * @private 49 * 50 * @param {string} type Whether to test for support of "flag" or "emoji". 51 * 52 * @return {boolean} True if the browser can render emoji, false if it cannot. 53 */ 54 function browserSupportsEmoji( type ) { 55 var isIdentical; 56 57 if ( ! context || ! context.fillText ) { 58 return false; 59 } 60 61 /* 62 * Chrome on OS X added native emoji rendering in M41. Unfortunately, 63 * it doesn't work when the font is bolder than 500 weight. So, we 64 * check for bold rendering support to avoid invisible emoji in Chrome. 65 */ 66 context.textBaseline = 'top'; 67 context.font = '600 32px Arial'; 68 69 switch ( type ) { 70 case 'flag': 71 /* 72 * Test for Transgender flag compatibility. This flag is shortlisted for the Emoji 13 spec, 73 * but has landed in Twemoji early, so we can add support for it, too. 74 * 75 * To test for support, we try to render it, and compare the rendering to how it would look if 76 * the browser doesn't render it correctly (white flag emoji + transgender symbol). 77 */ 78 isIdentical = emojiSetsRenderIdentically( 79 [ 0x1F3F3, 0xFE0F, 0x200D, 0x26A7, 0xFE0F ], 80 [ 0x1F3F3, 0xFE0F, 0x200B, 0x26A7, 0xFE0F ] 81 ); 82 83 if ( isIdentical ) { 84 return false; 85 } 86 87 /* 88 * Test for UN flag compatibility. This is the least supported of the letter locale flags, 89 * so gives us an easy test for full support. 90 * 91 * To test for support, we try to render it, and compare the rendering to how it would look if 92 * the browser doesn't render it correctly ([U] + [N]). 93 */ 94 isIdentical = emojiSetsRenderIdentically( 95 [ 0xD83C, 0xDDFA, 0xD83C, 0xDDF3 ], 96 [ 0xD83C, 0xDDFA, 0x200B, 0xD83C, 0xDDF3 ] 97 ); 98 99 if ( isIdentical ) { 100 return false; 101 } 102 103 /* 104 * Test for English flag compatibility. England is a country in the United Kingdom, it 105 * does not have a two letter locale code but rather an five letter sub-division code. 106 * 107 * To test for support, we try to render it, and compare the rendering to how it would look if 108 * the browser doesn't render it correctly (black flag emoji + [G] + [B] + [E] + [N] + [G]). 109 */ 110 isIdentical = emojiSetsRenderIdentically( 111 [ 0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40, 0xDC65, 0xDB40, 0xDC6E, 0xDB40, 0xDC67, 0xDB40, 0xDC7F ], 112 [ 0xD83C, 0xDFF4, 0x200B, 0xDB40, 0xDC67, 0x200B, 0xDB40, 0xDC62, 0x200B, 0xDB40, 0xDC65, 0x200B, 0xDB40, 0xDC6E, 0x200B, 0xDB40, 0xDC67, 0x200B, 0xDB40, 0xDC7F ] 113 ); 114 115 return ! isIdentical; 116 case 'emoji': 117 /* 118 * Burning Love: Just a hunk, a hunk of burnin' love. 119 * 120 * To test for Emoji 13.1 support, try to render a new emoji: Heart on Fire! 121 * 122 * The Heart on Fire emoji is a ZWJ sequence combining ❤️ Red Heart, a Zero Width Joiner and 🔥 Fire. 123 * 124 * 0x2764, 0xfe0f == Red Heart emoji. 125 * 0x200D == Zero-Width Joiner (ZWJ) that links the two code points for the new emoji or 126 * 0x200B == Zero-Width Space (ZWS) that is rendered for clients not supporting the new emoji. 127 * 0xD83D, 0xDD25 == Fire. 128 * 129 * When updating this test for future Emoji releases, ensure that individual emoji that make up the 130 * sequence come from older emoji standards. 131 */ 132 isIdentical = emojiSetsRenderIdentically( 133 [0x2764, 0xfe0f, 0x200D, 0xD83D, 0xDD25], 134 [0x2764, 0xfe0f, 0x200B, 0xD83D, 0xDD25] 135 ); 136 137 return ! isIdentical; 138 } 139 140 return false; 141 } 142 143 /** 144 * Adds a script to the head of the document. 145 * 146 * @ignore 147 * 148 * @since 4.2.0 149 * 150 * @param {Object} src The url where the script is located. 151 * @return {void} 152 */ 153 function addScript( src ) { 154 var script = document.createElement( 'script' ); 155 156 script.src = src; 157 script.defer = script.type = 'text/javascript'; 158 document.getElementsByTagName( 'head' )[0].appendChild( script ); 159 } 160 161 tests = Array( 'flag', 'emoji' ); 162 163 settings.supports = { 164 everything: true, 165 everythingExceptFlag: true 166 }; 167 168 /* 169 * Tests the browser support for flag emojis and other emojis, and adjusts the 170 * support settings accordingly. 171 */ 172 for( ii = 0; ii < tests.length; ii++ ) { 173 settings.supports[ tests[ ii ] ] = browserSupportsEmoji( tests[ ii ] ); 174 175 settings.supports.everything = settings.supports.everything && settings.supports[ tests[ ii ] ]; 176 177 if ( 'flag' !== tests[ ii ] ) { 178 settings.supports.everythingExceptFlag = settings.supports.everythingExceptFlag && settings.supports[ tests[ ii ] ]; 179 } 180 } 181 182 settings.supports.everythingExceptFlag = settings.supports.everythingExceptFlag && ! settings.supports.flag; 183 184 // Sets DOMReady to false and assigns a ready function to settings. 185 settings.DOMReady = false; 186 settings.readyCallback = function() { 187 settings.DOMReady = true; 188 }; 189 190 // When the browser can not render everything we need to load a polyfill. 191 if ( ! settings.supports.everything ) { 192 ready = function() { 193 settings.readyCallback(); 194 }; 195 196 /* 197 * Cross-browser version of adding a dom ready event. 198 */ 199 if ( document.addEventListener ) { 200 document.addEventListener( 'DOMContentLoaded', ready, false ); 201 window.addEventListener( 'load', ready, false ); 202 } else { 203 window.attachEvent( 'onload', ready ); 204 document.attachEvent( 'onreadystatechange', function() { 205 if ( 'complete' === document.readyState ) { 206 settings.readyCallback(); 207 } 208 } ); 209 } 210 211 src = settings.source || {}; 212 213 if ( src.concatemoji ) { 214 addScript( src.concatemoji ); 215 } else if ( src.wpemoji && src.twemoji ) { 216 addScript( src.twemoji ); 217 addScript( src.wpemoji ); 218 } 219 } 220 221 } )( window, document, window._wpemojiSettings );