1/** 2 * @license Hyphenopoly_Loader 5.3.0 - client side hyphenation 3 * ©2024 Mathias Nater, Güttingen (mathiasnater at gmail dot com) 4 * https://github.com/mnater/Hyphenopoly 5 * 6 * Released under the MIT license 7 * http://mnater.github.io/Hyphenopoly/LICENSE 8 */ 9/* globals Hyphenopoly:readonly */ 10window.Hyphenopoly = {}; 11 12((w, d, H, o) => { 13 'use strict'; 14 15 /** 16 * Shortcut for new Map 17 * @param {any} init - initialiser for new Map 18 * @returns {Map} 19 */ 20 const mp = (init) => { 21 return new Map(init); 22 }; 23 24 const scriptName = 'Hyphenopoly_Loader.js'; 25 const thisScript = d.currentScript.src; 26 const store = sessionStorage; 27 let mainScriptLoaded = false; 28 29 /** 30 * The main function runs the feature test and loads Hyphenopoly if 31 * necessary. 32 */ 33 const main = () => { 34 const shortcuts = { 35 ac: 'appendChild', 36 ce: 'createElement', 37 ct: 'createTextNode', 38 }; 39 40 /** 41 * Create deferred Promise 42 * 43 * From http://lea.verou.me/2016/12/resolve-promises-externally-with- 44 * this-one-weird-trick/ 45 * @return {promise} 46 */ 47 const defProm = () => { 48 let res = null; 49 let rej = null; 50 const promise = new Promise((resolve, reject) => { 51 res = resolve; 52 rej = reject; 53 }); 54 promise.resolve = res; 55 promise.reject = rej; 56 return promise; 57 }; 58 59 H.ac = new AbortController(); 60 const fetchOptions = { 61 credentials: H.s.CORScredentials, 62 signal: H.ac.signal, 63 }; 64 65 let stylesNode = null; 66 67 /** 68 * Define function H.hide. 69 * This function hides (state = 1) or unhides (state = 0) 70 * the whole document (mode == 0) or 71 * each selected element (mode == 1) or 72 * text of each selected element (mode == 2) or 73 * nothing (mode == -1) 74 * @param {integer} state - State 75 * @param {integer} mode - Mode 76 */ 77 H.hide = (state, mode) => { 78 if (state) { 79 let vis = '{visibility:hidden!important}'; 80 stylesNode = d[shortcuts.ce]('style'); 81 let myStyle = ''; 82 if (mode === 0) { 83 myStyle = 'html' + vis; 84 } else if (mode !== -1) { 85 if (mode === 2) { 86 vis = '{color:transparent!important}'; 87 } 88 o.keys(H.s.selectors).forEach((sel) => { 89 myStyle += sel + vis; 90 }); 91 } 92 stylesNode[shortcuts.ac](d[shortcuts.ct](myStyle)); 93 d.head[shortcuts.ac](stylesNode); 94 } else if (stylesNode) { 95 stylesNode.remove(); 96 } 97 }; 98 99 const tester = (() => { 100 let fakeBody = null; 101 return { 102 /** 103 * Append fakeBody with tests to document 104 * @returns {Object|null} The body element or null, if no tests 105 */ 106 ap: () => { 107 if (fakeBody) { 108 d.documentElement[shortcuts.ac](fakeBody); 109 return fakeBody; 110 } 111 return null; 112 }, 113 114 /** 115 * Remove fakeBody 116 * @returns {undefined} 117 */ 118 cl: () => { 119 if (fakeBody) { 120 fakeBody.remove(); 121 } 122 }, 123 124 /** 125 * Create and append div with CSS-hyphenated word 126 * @param {string} lang Language 127 * @returns {undefined} 128 */ 129 cr: (lang) => { 130 if (H.cf.langs.has(lang)) { 131 return; 132 } 133 fakeBody ||= d[shortcuts.ce]('body'); 134 const testDiv = d[shortcuts.ce]('div'); 135 const ha = 'hyphens:auto'; 136 testDiv.lang = lang; 137 testDiv.style.cssText = `visibility:hidden;-webkit-${ha};-ms-${ha};${ha};width:48px;font-size:12px;line-height:12px;border:none;padding:0;word-wrap:normal`; 138 testDiv[shortcuts.ac]( 139 d[shortcuts.ct](H.lrq.get(lang).wo.toLowerCase()) 140 ); 141 fakeBody[shortcuts.ac](testDiv); 142 }, 143 }; 144 })(); 145 146 /** 147 * Checks if hyphens (ev.prefixed) is set to auto for the element. 148 * @param {Object} elm - the element 149 * @returns {Boolean} result of the check 150 */ 151 const checkCSSHyphensSupport = (elmStyle) => { 152 const h = 153 elmStyle.hyphens || 154 elmStyle.webkitHyphens || 155 elmStyle.msHyphens; 156 return h === 'auto'; 157 }; 158 159 H.res = { 160 he: mp(), 161 }; 162 163 /** 164 * Load hyphenEngines to H.res.he 165 * 166 * Make sure each .wasm is loaded exactly once, even for fallbacks 167 * Store a list of languages to by hyphenated with each .wasm 168 * @param {string} lang The language 169 * @returns {undefined} 170 */ 171 const loadhyphenEngine = (lang) => { 172 const fn = H.lrq.get(lang).fn; 173 H.cf.pf = true; 174 H.cf.langs.set(lang, 'H9Y'); 175 if (H.res.he.has(fn)) { 176 H.res.he.get(fn).l.push(lang); 177 } else { 178 H.res.he.set(fn, { 179 l: [lang], 180 w: w.fetch(H.paths.patterndir + fn + '.wasm', fetchOptions), 181 }); 182 } 183 }; 184 H.lrq.forEach((value, lang) => { 185 if ( 186 value.wo === 'FORCEHYPHENOPOLY' || 187 H.cf.langs.get(lang) === 'H9Y' 188 ) { 189 loadhyphenEngine(lang); 190 } else { 191 tester.cr(lang); 192 } 193 });
vendor: 1,335 bytes, lines 194-245
194 const testContainer = tester.ap(); 195 if (testContainer) { 196 testContainer.querySelectorAll('div').forEach((n) => { 197 if (checkCSSHyphensSupport(n.style) && n.offsetHeight > 12) { 198 H.cf.langs.set(n.lang, 'CSS'); 199 } else { 200 loadhyphenEngine(n.lang); 201 } 202 }); 203 tester.cl(); 204 } 205 const hev = H.hev; 206 if (H.cf.pf) { 207 H.res.DOM = new Promise((res) => { 208 if (d.readyState === 'loading') { 209 d.addEventListener('DOMContentLoaded', res, { 210 once: true, 211 passive: true, 212 }); 213 } else { 214 res(); 215 } 216 }); 217 H.hide(1, H.s.hide); 218 H.timeOutHandler = w.setTimeout(() => { 219 H.hide(0, null); 220 // eslint-disable-next-line no-bitwise 221 if (H.s.timeout & 1) { 222 H.ac.abort(); 223 } 224 // eslint-disable-next-line no-console 225 console.info(scriptName + ' timed out.'); 226 }, H.s.timeout); 227 if (mainScriptLoaded) { 228 H.main(); 229 } else { 230 // Load main script 231 fetch(H.paths.maindir + 'Hyphenopoly.js', fetchOptions).then( 232 (response) => { 233 if (response.ok) { 234 response.blob().then((blb) => { 235 const script = d[shortcuts.ce]('script'); 236 script.src = URL.createObjectURL(blb); 237 d.head[shortcuts.ac](script); 238 mainScriptLoaded = true; 239 URL.revokeObjectURL(script.src); 240 }); 241 } 242 } 243 ); 244 } 245 H.hy6ors = mp();
246 H.cf.langs.forEach((langDef, lang) => { 247 if (langDef === 'H9Y') { 248 H.hy6ors.set(lang, defProm()); 249 } 250 }); 251 H.hy6ors.set('HTML', defProm()); 252 H.hyphenators = new Proxy(H.hy6ors, { 253 get: (target, key) => { 254 return target.get(key); 255 }, 256 set: () => { 257 // Inhibit setting of hyphenators 258 return true; 259 }, 260 }); 261 (() => { 262 if (hev && hev.polyfill) { 263 hev.polyfill(); 264 } 265 })(); 266 } else { 267 (() => { 268 if (hev && hev.tearDown) { 269 hev.tearDown(); 270 } 271 w.Hyphenopoly = null; 272 })(); 273 } 274 (() => { 275 if (H.cft) { 276 store.setItem( 277 scriptName, 278 JSON.stringify({ 279 langs: [...H.cf.langs.entries()], 280 pf: H.cf.pf, 281 }) 282 ); 283 } 284 })(); 285 }; 286 287 H.config = (c) => { 288 /** 289 * Sets default properties for an Object 290 * @param {object} obj - The object to set defaults to 291 * @param {object} defaults - The defaults to set 292 * @returns {object} 293 */ 294 const setDefaults = (obj, defaults) => { 295 if (obj) { 296 o.entries(defaults).forEach(([k, v]) => { 297 // eslint-disable-next-line security/detect-object-injection 298 obj[k] ||= v; 299 }); 300 return obj; 301 } 302 return defaults; 303 }; 304 305 H.cft = Boolean(c.cacheFeatureTests); 306 if (H.cft && store.getItem(scriptName)) { 307 H.cf = JSON.parse(store.getItem(scriptName)); 308 H.cf.langs = mp(H.cf.langs); 309 } else { 310 H.cf = { 311 langs: mp(), 312 pf: false, 313 }; 314 } 315 316 const maindir = thisScript.slice(0, thisScript.lastIndexOf('/') + 1); 317 const patterndir = maindir + 'patterns/'; 318 H.paths = setDefaults(c.paths, { 319 maindir, 320 patterndir, 321 }); 322 H.s = setDefaults(c.setup, { 323 CORScredentials: 'include', 324 hide: 'all', 325 selectors: { '.hyphenate': {} }, 326 timeout: 1000, 327 }); 328 // Change mode string to mode int 329 H.s.hide = ['all', 'element', 'text'].indexOf(H.s.hide); 330 if (c.handleEvent) { 331 H.hev = c.handleEvent; 332 } 333 334 const fallbacks = mp(o.entries(c.fallbacks || {})); 335 H.lrq = mp(); 336 o.entries(c.require).forEach(([lang, wo]) => { 337 H.lrq.set(lang.toLowerCase(), { 338 fn: fallbacks.get(lang) || lang, 339 wo, 340 }); 341 }); 342 343 main(); 344 }; 345})(window, document, Hyphenopoly, Object);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.