Hyphenopoly_Loader.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. /**
  2. * @license Hyphenopoly_Loader 3.4.0 - client side hyphenation
  3. * ©2019 Mathias Nater, Zürich (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 */
  10. /**
  11. * Wrap all code in an iife to keep a scope. Important objects are parameters
  12. * of this iife to keep codesize low.
  13. * @param {Object} w shorthand for window
  14. * @param {Object} d shorthand for document
  15. * @param {Object} H shorthand for Hyphenopoly
  16. * @param {Object} o shorthand for object
  17. */
  18. (function H9YL(w, d, H, o) {
  19. "use strict";
  20. const store = sessionStorage;
  21. const wa = w.WebAssembly;
  22. const lcFallbacks = new Map();
  23. const lcRequire = new Map();
  24. /**
  25. * Create Object without standard Object-prototype
  26. * @returns {Object} empty object
  27. */
  28. function empty() {
  29. return o.create(null);
  30. }
  31. /**
  32. * Shorthand for Object.keys(obj).forEach(function () {})
  33. * @param {Object} obj the object to iterate
  34. * @param {function} fn the function to execute
  35. * @returns {undefined}
  36. */
  37. function eachKey(obj, fn) {
  38. o.keys(obj).forEach(fn);
  39. }
  40. /**
  41. * Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously
  42. * computed settings from sessionStorage or creating an template object.
  43. * This is in an iife to keep complexity low.
  44. */
  45. (function configFeat() {
  46. if (H.cacheFeatureTests && store.getItem("Hyphenopoly_Loader")) {
  47. H.cf = JSON.parse(store.getItem("Hyphenopoly_Loader"));
  48. } else {
  49. H.cf = {
  50. "langs": empty(),
  51. "polyfill": false,
  52. "wasm": null
  53. };
  54. }
  55. }());
  56. /**
  57. * Set H.paths defaults or overwrite with user settings.
  58. * This is in an iife to keep complexity low.
  59. */
  60. (function configPaths() {
  61. const maindir = (d.currentScript)
  62. ? d.currentScript.src.replace(/Hyphenopoly_Loader.js/i, "")
  63. : "../";
  64. const patterndir = maindir + "patterns/";
  65. if (H.paths) {
  66. H.paths.maindir = H.paths.maindir || maindir;
  67. H.paths.patterndir = H.paths.patterndir || patterndir;
  68. } else {
  69. H.paths = o.create({
  70. "maindir": maindir,
  71. "patterndir": patterndir
  72. });
  73. }
  74. }());
  75. /**
  76. * Set some H.setup fields to defaults or overwrite with user settings.
  77. * This is in an iife to keep complexity low.
  78. */
  79. (function configSetup() {
  80. if (H.setup) {
  81. H.setup.selectors = H.setup.selectors || {".hyphenate": {}};
  82. H.setup.timeout = H.setup.timeout || 1000;
  83. H.setup.hide = H.setup.hide || "all";
  84. } else {
  85. H.setup = {
  86. "hide": "all",
  87. "selectors": {".hyphenate": {}},
  88. "timeout": 1000
  89. };
  90. }
  91. }());
  92. /**
  93. * Copy required languages to local lcRequire and
  94. * eventually fallbacks to local lcFallbacks.
  95. * This is in an iife to keep complexity low.
  96. */
  97. (function configRequire() {
  98. eachKey(H.require, function copyRequire(k) {
  99. // eslint-disable-next-line security/detect-object-injection
  100. lcRequire.set(k.toLowerCase(), H.require[k]);
  101. });
  102. if (H.fallbacks) {
  103. eachKey(H.fallbacks, function copyFallbacks(k) {
  104. lcFallbacks.set(
  105. k.toLowerCase(),
  106. // eslint-disable-next-line security/detect-object-injection
  107. H.fallbacks[k].toLowerCase()
  108. );
  109. });
  110. }
  111. }());
  112. /**
  113. * Define function H.toggle.
  114. * This function hides or unhides (depending of the parameter state)
  115. * the whole document (H.setup.hide == "all") or
  116. * each selected element (H.setup.hide == "element") or
  117. * text of each selected element (H.setup.hide == "text")
  118. * @param {string} state State: either on (visible) or off (hidden)
  119. */
  120. H.toggle = function toggle(state) {
  121. if (state === "on") {
  122. const stylesNode = d.getElementById("H9Y_Styles");
  123. if (stylesNode) {
  124. stylesNode.parentNode.removeChild(stylesNode);
  125. }
  126. } else {
  127. const vis = " {visibility: hidden !important}\n";
  128. const sc = d.createElement("style");
  129. let myStyle = "";
  130. sc.id = "H9Y_Styles";
  131. switch (H.setup.hide) {
  132. case "all":
  133. myStyle = "html" + vis;
  134. break;
  135. case "element":
  136. eachKey(H.setup.selectors, function eachSelector(sel) {
  137. myStyle += sel + vis;
  138. });
  139. break;
  140. case "text":
  141. eachKey(H.setup.selectors, function eachSelector(sel) {
  142. myStyle += sel + " {color: transparent !important}\n";
  143. });
  144. break;
  145. // No Default
  146. }
  147. sc.appendChild(d.createTextNode(myStyle));
  148. d.head.appendChild(sc);
  149. }
  150. };
  151. /**
  152. * Setup basic event system. Some events are defined but the definition of
  153. * what happens when they are triggered is deferred to Hyphenopoly.js
  154. * This is in an iife to keep complexity low.
  155. */
  156. (function setupEvents() {
  157. // Events known to the system
  158. const definedEvents = new Map();
  159. // Default events, execution deferred to Hyphenopoly.js
  160. const deferred = [];
  161. /*
  162. * Register for custom event handlers, where event is not yet defined
  163. * these events will be correctly registered in Hyphenopoly.js
  164. */
  165. const tempRegister = [];
  166. /**
  167. * Create Event Object
  168. * @param {string} name The Name of the event
  169. * @param {function} defFunc The default method of the event
  170. * @param {boolean} cancellable Is the default cancellable
  171. * @returns {undefined}
  172. */
  173. function define(name, defFunc, cancellable) {
  174. definedEvents.set(name, {
  175. "cancellable": cancellable,
  176. "default": defFunc,
  177. "register": []
  178. });
  179. }
  180. define(
  181. "timeout",
  182. function def(e) {
  183. H.toggle("on");
  184. w.console.info(
  185. "Hyphenopolys 'FOUHC'-prevention timed out after %dms",
  186. e.delay
  187. );
  188. },
  189. false
  190. );
  191. define(
  192. "error",
  193. function def(e) {
  194. switch (e.lvl) {
  195. case "info":
  196. w.console.info(e.msg);
  197. break;
  198. case "warn":
  199. w.console.warn(e.msg);
  200. break;
  201. default:
  202. w.console.error(e.msg);
  203. }
  204. },
  205. true
  206. );
  207. define(
  208. "contentLoaded",
  209. function def(e) {
  210. deferred.push({
  211. "data": e,
  212. "name": "contentLoaded"
  213. });
  214. },
  215. false
  216. );
  217. define(
  218. "engineLoaded",
  219. function def(e) {
  220. deferred.push({
  221. "data": e,
  222. "name": "engineLoaded"
  223. });
  224. },
  225. false
  226. );
  227. define(
  228. "hpbLoaded",
  229. function def(e) {
  230. deferred.push({
  231. "data": e,
  232. "name": "hpbLoaded"
  233. });
  234. },
  235. false
  236. );
  237. define(
  238. "loadError",
  239. function def(e) {
  240. deferred.push({
  241. "data": e,
  242. "name": "loadError"
  243. });
  244. },
  245. false
  246. );
  247. define(
  248. "tearDown",
  249. null,
  250. true
  251. );
  252. /**
  253. * Dispatch event <name> with arguments <data>
  254. * @param {string} name The name of the event
  255. * @param {Object|undefined} data Data of the event
  256. * @returns {undefined}
  257. */
  258. function dispatch(name, data) {
  259. data = data || empty();
  260. let defaultPrevented = false;
  261. definedEvents.get(name).register.forEach(
  262. function call(currentHandler) {
  263. data.preventDefault = function preventDefault() {
  264. if (definedEvents.get(name).cancellable) {
  265. defaultPrevented = true;
  266. }
  267. };
  268. currentHandler(data);
  269. }
  270. );
  271. if (
  272. !defaultPrevented &&
  273. definedEvents.get(name).default
  274. ) {
  275. definedEvents.get(name).default(data);
  276. }
  277. }
  278. /**
  279. * Add EventListender <handler> to event <name>
  280. * @param {string} name The name of the event
  281. * @param {function} handler Function to register
  282. * @param {boolean} defer If the registration is deferred
  283. * @returns {undefined}
  284. */
  285. function addListener(name, handler, defer) {
  286. if (definedEvents.has(name)) {
  287. definedEvents.get(name).register.push(handler);
  288. } else if (defer) {
  289. tempRegister.push({
  290. "handler": handler,
  291. "name": name
  292. });
  293. } else {
  294. H.events.dispatch(
  295. "error",
  296. {
  297. "lvl": "warn",
  298. "msg": "unknown Event \"" + name + "\" discarded"
  299. }
  300. );
  301. }
  302. }
  303. if (H.handleEvent) {
  304. eachKey(H.handleEvent, function add(name) {
  305. /* eslint-disable security/detect-object-injection */
  306. addListener(name, H.handleEvent[name], true);
  307. /* eslint-enable security/detect-object-injection */
  308. });
  309. }
  310. H.events = empty();
  311. H.events.deferred = deferred;
  312. H.events.tempRegister = tempRegister;
  313. H.events.dispatch = dispatch;
  314. H.events.define = define;
  315. H.events.addListener = addListener;
  316. }());
  317. /**
  318. * Feature test for wasm.
  319. * @returns {boolean} support
  320. */
  321. function runWasmTest() {
  322. /*
  323. * Wasm feature test with iOS bug detection
  324. * (https://bugs.webkit.org/show_bug.cgi?id=181781)
  325. */
  326. if (
  327. typeof wa === "object" &&
  328. typeof wa.Instance === "function"
  329. ) {
  330. /* eslint-disable array-element-newline */
  331. const module = new wa.Module(Uint8Array.from([
  332. 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127,
  333. 3, 2, 1, 0, 5, 3, 1, 0, 1, 7, 5, 1, 1, 116, 0, 0,
  334. 10, 16, 1, 14, 0, 32, 0, 65, 1, 54, 2, 0, 32, 0, 40, 2,
  335. 0, 11
  336. ]));
  337. /* eslint-enable array-element-newline */
  338. return (new wa.Instance(module).exports.t(4) !== 0);
  339. }
  340. return false;
  341. }
  342. /**
  343. * Load script by adding <script>-tag
  344. * @param {string} path Where the script is stored
  345. * @param {string} filename Filename of the script
  346. * @returns {undefined}
  347. */
  348. function loadScript(path, filename) {
  349. const script = d.createElement("script");
  350. script.src = path + filename;
  351. if (filename === "hyphenEngine.asm.js") {
  352. script.addEventListener("load", function listener() {
  353. H.events.dispatch("engineLoaded", {"msg": "asm"});
  354. });
  355. }
  356. d.head.appendChild(script);
  357. }
  358. const loadedBins = new Map();
  359. /**
  360. * Load binary files either with fetch (on new browsers that support wasm)
  361. * or with xmlHttpRequest
  362. * @param {string} path Where the script is stored
  363. * @param {string} fne Filename of the script with extension
  364. * @param {string} name Name of the ressource
  365. * @param {Object} msg Message
  366. * @returns {undefined}
  367. */
  368. function loadBinary(path, fne, name, msg) {
  369. /**
  370. * Get bin file using fetch
  371. * @param {string} p Where the script is stored
  372. * @param {string} f Filename of the script with extension
  373. * @param {string} n Name of the ressource
  374. * @param {Object} m Message
  375. * @returns {undefined}
  376. */
  377. function fetchBinary(p, f, n, m) {
  378. w.fetch(p + f, {"credentials": "include"}).then(
  379. function resolve(response) {
  380. if (response.ok) {
  381. if (n === "hyphenEngine") {
  382. H.bins.set(n, response.arrayBuffer().then(
  383. function getModule(buf) {
  384. return new wa.Module(buf);
  385. }
  386. ));
  387. H.events.dispatch("engineLoaded", {"msg": m});
  388. } else {
  389. const files = loadedBins.get(f);
  390. files.forEach(function eachHpb(rn) {
  391. H.bins.set(
  392. rn,
  393. (files.length > 1)
  394. ? response.clone().arrayBuffer()
  395. : response.arrayBuffer()
  396. );
  397. H.events.dispatch(
  398. "hpbLoaded",
  399. {"msg": rn}
  400. );
  401. });
  402. }
  403. } else {
  404. H.events.dispatch("loadError", {
  405. "file": f,
  406. "msg": m,
  407. "name": n,
  408. "path": p
  409. });
  410. }
  411. }
  412. );
  413. }
  414. /**
  415. * Get bin file using XHR
  416. * @param {string} p Where the script is stored
  417. * @param {string} f Filename of the script with extension
  418. * @param {string} n Name of the ressource
  419. * @param {Object} m Message
  420. * @returns {undefined}
  421. */
  422. function requestBinary(p, f, n, m) {
  423. const xhr = new XMLHttpRequest();
  424. xhr.onload = function onload() {
  425. if (xhr.status === 200) {
  426. loadedBins.get(f).
  427. forEach(function eachHpb(rn) {
  428. H.bins.set(
  429. rn,
  430. xhr.response
  431. );
  432. H.events.dispatch(
  433. "hpbLoaded",
  434. {"msg": rn}
  435. );
  436. });
  437. } else {
  438. H.events.dispatch("loadError", {
  439. "file": f,
  440. "msg": m,
  441. "name": n,
  442. "path": p
  443. });
  444. }
  445. };
  446. xhr.open("GET", p + f);
  447. xhr.responseType = "arraybuffer";
  448. xhr.send();
  449. }
  450. if (!loadedBins.has(fne)) {
  451. loadedBins.set(fne, [msg]);
  452. if (H.cf.wasm) {
  453. fetchBinary(path, fne, name, msg);
  454. } else {
  455. requestBinary(path, fne, name, msg);
  456. }
  457. } else if (name !== "hyphenEngine") {
  458. loadedBins.get(fne).push(msg);
  459. }
  460. }
  461. /**
  462. * Pre-Allocate memory for (w)asm
  463. * Default is 32 wasm Pages (). For languages with larger .hpb
  464. * files a higher value is needed.
  465. * Get the value from baseData.heapSize in Hyphenopoly.js
  466. * @param {string} lang Language
  467. * @returns {undefined}
  468. */
  469. function allocateMemory(lang) {
  470. const specVal = new Map(
  471. [["de", 54], ["hu", 205], ["nb-no", 91], ["nl", 41]]
  472. );
  473. const wasmPages = specVal.get(lang) || 32;
  474. H.specMems = H.specMems || new Map();
  475. if (H.cf.wasm) {
  476. H.specMems.set(lang, new wa.Memory({
  477. "initial": wasmPages,
  478. "maximum": 256
  479. }));
  480. } else {
  481. /* eslint-disable no-bitwise */
  482. const asmPages = (2 << Math.floor(
  483. Math.log(wasmPages) * Math.LOG2E
  484. )) << 16;
  485. /* eslint-enable no-bitwise */
  486. H.specMems.set(lang, new ArrayBuffer(asmPages));
  487. }
  488. }
  489. (function testClientFeatures() {
  490. const tester = (function tester() {
  491. let fakeBody = null;
  492. /* eslint-disable array-element-newline */
  493. const css = [
  494. "visibility:hidden",
  495. "-moz-hyphens:auto",
  496. "-webkit-hyphens:auto",
  497. "-ms-hyphens:auto",
  498. "hyphens:auto",
  499. "width:48px",
  500. "font-size:12px",
  501. "line-height:12px",
  502. "border:none",
  503. "padding:0",
  504. "word-wrap:normal"
  505. ].join(";");
  506. /* eslint-enable array-element-newline */
  507. /**
  508. * Create and append div with CSS-hyphenated word
  509. * @param {string} lang Language
  510. * @returns {undefined}
  511. */
  512. function create(lang) {
  513. /* eslint-disable security/detect-object-injection */
  514. if (H.cf.langs[lang]) {
  515. return;
  516. }
  517. /* eslint-enable security/detect-object-injection */
  518. fakeBody = fakeBody || d.createElement("body");
  519. const testDiv = d.createElement("div");
  520. testDiv.lang = lang;
  521. testDiv.style.cssText = css;
  522. testDiv.appendChild(
  523. d.createTextNode(lcRequire.get(lang).toLowerCase())
  524. );
  525. fakeBody.appendChild(testDiv);
  526. }
  527. /**
  528. * Append fakeBody with tests to target (document)
  529. * @param {Object} target Where to append fakeBody
  530. * @returns {Object|null} The body element or null, if no tests
  531. */
  532. function append(target) {
  533. if (fakeBody) {
  534. target.appendChild(fakeBody);
  535. return fakeBody;
  536. }
  537. return null;
  538. }
  539. /**
  540. * Remove fakeBody
  541. * @returns {undefined}
  542. */
  543. function clear() {
  544. if (fakeBody) {
  545. fakeBody.parentNode.removeChild(fakeBody);
  546. }
  547. }
  548. return {
  549. "append": append,
  550. "clear": clear,
  551. "create": create
  552. };
  553. }());
  554. /**
  555. * Checks if hyphens (ev.prefixed) is set to auto for the element.
  556. * @param {Object} elm - the element
  557. * @returns {Boolean} result of the check
  558. */
  559. function checkCSSHyphensSupport(elm) {
  560. return (
  561. elm.style.hyphens === "auto" ||
  562. elm.style.webkitHyphens === "auto" ||
  563. elm.style.msHyphens === "auto" ||
  564. elm.style["-moz-hyphens"] === "auto"
  565. );
  566. }
  567. /**
  568. * Expose the hyphenate-function of a specific language to
  569. * Hyphenopoly.hyphenators.<language>
  570. *
  571. * Hyphenopoly.hyphenators.<language> is a Promise that fullfills
  572. * to hyphenate(entity, sel) as soon as the ressources are loaded
  573. * and the engine is ready.
  574. * If Promises aren't supported (e.g. IE11) a error message is produced.
  575. *
  576. * @param {string} lang - the language
  577. * @returns {undefined}
  578. */
  579. function exposeHyphenateFunction(lang) {
  580. /* eslint-disable security/detect-object-injection */
  581. H.hyphenators = H.hyphenators || empty();
  582. if (!H.hyphenators[lang]) {
  583. if (w.Promise) {
  584. H.hyphenators[lang] = new Promise(function pro(rs, rj) {
  585. H.events.addListener(
  586. "engineReady",
  587. function handler(e) {
  588. if (e.msg === lang) {
  589. rs(H.createHyphenator(e.msg));
  590. }
  591. },
  592. true
  593. );
  594. H.events.addListener(
  595. "loadError",
  596. function handler(e) {
  597. if (e.name === lang || e.name === "hyphenEngine") {
  598. rj(new Error("File " + e.file + " can't be loaded from " + e.path));
  599. }
  600. },
  601. false
  602. );
  603. });
  604. H.hyphenators[lang].catch(function catchPromiseError(e) {
  605. H.events.dispatch(
  606. "error",
  607. {
  608. "lvl": "error",
  609. "msg": e.message
  610. }
  611. );
  612. });
  613. } else {
  614. H.hyphenators[lang] = {
  615. /**
  616. * Fires an error message, if then is called
  617. * @returns {undefined}
  618. */
  619. "then": function () {
  620. H.events.dispatch(
  621. "error",
  622. {"msg": "Promises not supported in this engine. Use a polyfill."}
  623. );
  624. }
  625. };
  626. }
  627. }
  628. /* eslint-enable security/detect-object-injection */
  629. }
  630. /**
  631. * Load .hpb files
  632. * @param {string} lang The language
  633. * @returns {undefined}
  634. */
  635. function loadPattern(lang) {
  636. let filename = lang + ".hpb";
  637. let langFallback = lang;
  638. H.cf.polyfill = true;
  639. // eslint-disable-next-line security/detect-object-injection
  640. H.cf.langs[lang] = "H9Y";
  641. if (lcFallbacks && lcFallbacks.has(lang)) {
  642. langFallback = lcFallbacks.get(lang);
  643. filename = langFallback + ".hpb";
  644. }
  645. H.bins = H.bins || new Map();
  646. loadBinary(H.paths.patterndir, filename, langFallback, lang);
  647. }
  648. if (H.cf.wasm === null) {
  649. H.cf.wasm = runWasmTest();
  650. }
  651. lcRequire.forEach(function eachReq(value, lang) {
  652. if (value === "FORCEHYPHENOPOLY" ||
  653. // eslint-disable-next-line security/detect-object-injection
  654. (H.cf.langs[lang] && H.cf.langs[lang] === "H9Y")
  655. ) {
  656. loadPattern(lang);
  657. } else {
  658. tester.create(lang);
  659. }
  660. });
  661. const testContainer = tester.append(d.documentElement);
  662. if (testContainer !== null) {
  663. const nl = testContainer.querySelectorAll("div");
  664. Array.prototype.forEach.call(nl, function eachNode(n) {
  665. if (checkCSSHyphensSupport(n) && n.offsetHeight > 12) {
  666. H.cf.langs[n.lang] = "CSS";
  667. } else {
  668. loadPattern(n.lang);
  669. }
  670. });
  671. tester.clear();
  672. }
  673. if (H.cf.polyfill) {
  674. loadScript(H.paths.maindir, "Hyphenopoly.js");
  675. if (H.cf.wasm) {
  676. loadBinary(
  677. H.paths.maindir,
  678. "hyphenEngine.wasm",
  679. "hyphenEngine",
  680. "wasm"
  681. );
  682. } else {
  683. loadScript(H.paths.maindir, "hyphenEngine.asm.js");
  684. }
  685. eachKey(H.cf.langs, function prepareEach(lang) {
  686. /* eslint-disable security/detect-object-injection */
  687. if (H.cf.langs[lang] === "H9Y") {
  688. allocateMemory(lang);
  689. exposeHyphenateFunction(lang);
  690. }
  691. /* eslint-enable security/detect-object-injection */
  692. });
  693. }
  694. }());
  695. /**
  696. * Hides the specified elements and starts the process by
  697. * dispatching a "contentLoaded"-event in Hyphenopoly
  698. * @returns {undefined}
  699. */
  700. function handleDCL() {
  701. if (H.setup.hide.match(/^(?:element|text)$/)) {
  702. H.toggle("off");
  703. }
  704. H.events.dispatch(
  705. "contentLoaded",
  706. {"msg": ["contentLoaded"]}
  707. );
  708. }
  709. if (H.cf.polyfill) {
  710. if (H.setup.hide === "all") {
  711. H.toggle("off");
  712. }
  713. if (H.setup.hide !== "none") {
  714. H.setup.timeOutHandler = w.setTimeout(function timedOut() {
  715. H.toggle("on");
  716. H.events.dispatch("timeout", {"delay": H.setup.timeout});
  717. }, H.setup.timeout);
  718. }
  719. if (d.readyState === "loading") {
  720. d.addEventListener(
  721. "DOMContentLoaded",
  722. handleDCL,
  723. {
  724. "once": true,
  725. "passive": true
  726. }
  727. );
  728. } else {
  729. handleDCL();
  730. }
  731. } else {
  732. H.events.dispatch("tearDown", {});
  733. w.Hyphenopoly = null;
  734. }
  735. if (H.cacheFeatureTests) {
  736. store.setItem(
  737. "Hyphenopoly_Loader",
  738. JSON.stringify(H.cf)
  739. );
  740. }
  741. }(window, document, Hyphenopoly, Object));