Hyphenopoly_Loader.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. /**
  2. * @license Hyphenopoly_Loader 3.1.1 - 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. /**
  10. * Wrap all code in an iife to keep a scope. Important objects are parameters
  11. * of this iife to keep codesize low.
  12. * @param {Object} w shorthand for window
  13. * @param {Object} d shorthand for document
  14. * @param {Object} H shorthand for Hyphenopoly
  15. * @param {Object} o shorthand for object
  16. */
  17. (function H9YL(w, d, H, o) {
  18. "use strict";
  19. const store = sessionStorage;
  20. const wa = w.WebAssembly;
  21. const lcFallbacks = new Map();
  22. const lcRequire = new Map();
  23. /**
  24. * Create Object without standard Object-prototype
  25. * @returns {Object} empty object
  26. */
  27. function empty() {
  28. return o.create(null);
  29. }
  30. /**
  31. * Shorthand for Object.keys(obj).forEach(function () {})
  32. * @param {Object} obj the object to iterate
  33. * @param {function} fn the function to execute
  34. * @returns {undefined}
  35. */
  36. function eachKey(obj, fn) {
  37. o.keys(obj).forEach(fn);
  38. }
  39. /**
  40. * Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously
  41. * computed settings from sessionStorage or creating an template object.
  42. * This is in an iife to keep complexity low.
  43. */
  44. (function configFeat() {
  45. if (H.cacheFeatureTests && store.getItem("Hyphenopoly_Loader")) {
  46. H.cf = JSON.parse(store.getItem("Hyphenopoly_Loader"));
  47. } else {
  48. H.cf = {
  49. "langs": empty(),
  50. "polyfill": false,
  51. "wasm": null
  52. };
  53. }
  54. }());
  55. /**
  56. * Set H.paths defaults or overwrite with user settings.
  57. * This is in an iife to keep complexity low.
  58. */
  59. (function configPaths() {
  60. const maindir = (d.currentScript)
  61. ? d.currentScript.src.replace(/Hyphenopoly_Loader.js/i, "")
  62. : "../";
  63. const patterndir = maindir + "patterns/";
  64. if (H.paths) {
  65. H.paths.maindir = H.paths.maindir || maindir;
  66. H.paths.patterndir = H.paths.patterndir || patterndir;
  67. } else {
  68. H.paths = o.create({
  69. "maindir": maindir,
  70. "patterndir": patterndir
  71. });
  72. }
  73. }());
  74. /**
  75. * Set some H.setup fields to defaults or overwrite with user settings.
  76. * This is in an iife to keep complexity low.
  77. */
  78. (function configSetup() {
  79. if (H.setup) {
  80. H.setup.selectors = H.setup.selectors || {".hyphenate": {}};
  81. H.setup.timeout = H.setup.timeout || 1000;
  82. H.setup.hide = H.setup.hide || "all";
  83. } else {
  84. H.setup = {
  85. "hide": "all",
  86. "selectors": {".hyphenate": {}},
  87. "timeout": 1000
  88. };
  89. }
  90. }());
  91. /**
  92. * Copy required languages to local lcRequire and
  93. * eventually fallbacks to local lcFallbacks.
  94. * This is in an iife to keep complexity low.
  95. */
  96. (function configRequire() {
  97. eachKey(H.require, function copyRequire(k) {
  98. // eslint-disable-next-line security/detect-object-injection
  99. lcRequire.set(k.toLowerCase(), H.require[k]);
  100. });
  101. if (H.fallbacks) {
  102. eachKey(H.fallbacks, function copyFallbacks(k) {
  103. lcFallbacks.set(
  104. k.toLowerCase(),
  105. // eslint-disable-next-line security/detect-object-injection
  106. H.fallbacks[k].toLowerCase()
  107. );
  108. });
  109. }
  110. }());
  111. /**
  112. * Define function H.toggle.
  113. * This function hides or unhides (depending of the parameter state)
  114. * the whole document (H.setup.hide == "all") or
  115. * each selected element (H.setup.hide == "element") or
  116. * text of each selected element (H.setup.hide == "text")
  117. * @param {string} state State: either on (visible) or off (hidden)
  118. */
  119. H.toggle = function toggle(state) {
  120. if (state === "on") {
  121. const stylesNode = d.getElementById("H9Y_Styles");
  122. if (stylesNode) {
  123. stylesNode.parentNode.removeChild(stylesNode);
  124. }
  125. } else {
  126. const vis = " {visibility: hidden !important}\n";
  127. const sc = d.createElement("style");
  128. let myStyle = "";
  129. sc.id = "H9Y_Styles";
  130. switch (H.setup.hide) {
  131. case "all":
  132. myStyle = "html" + vis;
  133. break;
  134. case "element":
  135. eachKey(H.setup.selectors, function eachSelector(sel) {
  136. myStyle += sel + vis;
  137. });
  138. break;
  139. case "text":
  140. eachKey(H.setup.selectors, function eachSelector(sel) {
  141. myStyle += sel + " {color: transparent !important}\n";
  142. });
  143. break;
  144. default:
  145. myStyle = "";
  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. * Eegister 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, 8, 1, 4, 116, 101, 115,
  334. 116, 0, 0, 10, 16, 1, 14, 0, 32, 0, 65, 1, 54, 2, 0, 32,
  335. 0, 40, 2, 0, 11
  336. ]));
  337. /* eslint-enable array-element-newline */
  338. return (new wa.Instance(module).exports.test(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).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. /* eslint-disable-next-line no-negated-condition */
  424. const xhr = new XMLHttpRequest();
  425. xhr.onload = function onload() {
  426. if (xhr.status === 200) {
  427. loadedBins.get(f).
  428. forEach(function eachHpb(rn) {
  429. H.bins.set(
  430. rn,
  431. xhr.response
  432. );
  433. H.events.dispatch(
  434. "hpbLoaded",
  435. {"msg": rn}
  436. );
  437. });
  438. } else {
  439. H.events.dispatch("loadError", {
  440. "file": f,
  441. "msg": m,
  442. "name": n,
  443. "path": p
  444. });
  445. }
  446. };
  447. xhr.open("GET", p + f);
  448. xhr.responseType = "arraybuffer";
  449. xhr.send();
  450. }
  451. if (!loadedBins.has(fne)) {
  452. loadedBins.set(fne, [msg]);
  453. if (H.cf.wasm) {
  454. fetchBinary(path, fne, name, msg);
  455. } else {
  456. requestBinary(path, fne, name, msg);
  457. }
  458. } else if (name !== "hyphenEngine") {
  459. loadedBins.get(fne).push(msg);
  460. }
  461. }
  462. /**
  463. * Pre-Allocate memory for (w)asm
  464. * Default is 32 wasm Pages (). For languages with larger .hpb
  465. * files a higher value is needed.
  466. * Get the value from baseData.heapSize in Hyphenopoly.js
  467. * @param {string} lang Language
  468. * @returns {undefined}
  469. */
  470. function allocateMemory(lang) {
  471. const specVal = new Map(
  472. [["de", 54], ["hu", 205], ["nb-no", 91], ["nl", 41]]
  473. );
  474. const wasmPages = specVal.get(lang) || 32;
  475. H.specMems = H.specMems || new Map();
  476. if (H.cf.wasm) {
  477. H.specMems.set(lang, new wa.Memory({
  478. "initial": wasmPages,
  479. "maximum": 256
  480. }));
  481. } else {
  482. /* eslint-disable no-bitwise */
  483. const asmPages = (2 << Math.floor(
  484. Math.log(wasmPages) * Math.LOG2E
  485. )) << 16;
  486. /* eslint-enable no-bitwise */
  487. H.specMems.set(lang, new ArrayBuffer(asmPages));
  488. }
  489. }
  490. (function testClientFeatures() {
  491. const tester = (function tester() {
  492. let fakeBody = null;
  493. /* eslint-disable array-element-newline */
  494. const css = [
  495. "visibility:hidden;",
  496. "-moz-hyphens:auto;",
  497. "-webkit-hyphens:auto;",
  498. "-ms-hyphens:auto;",
  499. "hyphens:auto;",
  500. "width:48px;",
  501. "font-size:12px;",
  502. "line-height:12px;",
  503. "border:none;",
  504. "padding:0;",
  505. "word-wrap:normal"
  506. ].join("");
  507. /* eslint-enable array-element-newline */
  508. /**
  509. * Create and append div with CSS-hyphenated word
  510. * @param {string} lang Language
  511. * @returns {undefined}
  512. */
  513. function create(lang) {
  514. /* eslint-disable security/detect-object-injection */
  515. if (H.cf.langs[lang]) {
  516. return;
  517. }
  518. /* eslint-enable security/detect-object-injection */
  519. fakeBody = fakeBody || d.createElement("body");
  520. const testDiv = d.createElement("div");
  521. testDiv.lang = lang;
  522. testDiv.style.cssText = css;
  523. testDiv.appendChild(d.createTextNode(lcRequire.get(lang)));
  524. fakeBody.appendChild(testDiv);
  525. }
  526. /**
  527. * Append fakeBody with tests to target (document)
  528. * @param {Object} target Where to append fakeBody
  529. * @returns {Object|null} The body element or null, if no tests
  530. */
  531. function append(target) {
  532. if (fakeBody) {
  533. target.appendChild(fakeBody);
  534. return fakeBody;
  535. }
  536. return null;
  537. }
  538. /**
  539. * Remove fakeBody
  540. * @returns {undefined}
  541. */
  542. function clear() {
  543. if (fakeBody) {
  544. fakeBody.parentNode.removeChild(fakeBody);
  545. }
  546. }
  547. return {
  548. "append": append,
  549. "clear": clear,
  550. "create": create
  551. };
  552. }());
  553. /**
  554. * Checks if hyphens (ev.prefixed) is set to auto for the element.
  555. * @param {Object} elm - the element
  556. * @returns {Boolean} result of the check
  557. */
  558. function checkCSSHyphensSupport(elm) {
  559. return (
  560. elm.style.hyphens === "auto" ||
  561. elm.style.webkitHyphens === "auto" ||
  562. elm.style.msHyphens === "auto" ||
  563. elm.style["-moz-hyphens"] === "auto"
  564. );
  565. }
  566. /**
  567. * Expose the hyphenate-function of a specific language to
  568. * Hyphenopoly.hyphenators.<language>
  569. *
  570. * Hyphenopoly.hyphenators.<language> is a Promise that fullfills
  571. * to hyphenate(entity, sel) as soon as the ressources are loaded
  572. * and the engine is ready.
  573. * If Promises aren't supported (e.g. IE11) a error message is produced.
  574. *
  575. * @param {string} lang - the language
  576. * @returns {undefined}
  577. */
  578. function exposeHyphenateFunction(lang) {
  579. /* eslint-disable security/detect-object-injection */
  580. H.hyphenators = H.hyphenators || empty();
  581. if (!H.hyphenators[lang]) {
  582. if (w.Promise) {
  583. H.hyphenators[lang] = new Promise(function pro(rs, rj) {
  584. H.events.addListener("engineReady", function handler(e) {
  585. if (e.msg === lang) {
  586. rs(H.createHyphenator(e.msg));
  587. }
  588. }, true);
  589. H.events.addListener("loadError", function handler(e) {
  590. if (e.name === lang || e.name === "hyphenEngine") {
  591. rj(new Error("File " + e.file + " can't be loaded from " + e.path));
  592. }
  593. }, false);
  594. });
  595. H.hyphenators[lang].catch(function catchPromiseError(e) {
  596. H.events.dispatch(
  597. "error",
  598. {
  599. "lvl": "error",
  600. "msg": e.message
  601. }
  602. );
  603. });
  604. } else {
  605. H.hyphenators[lang] = {
  606. /**
  607. * Fires an error message, if then is called
  608. * @returns {undefined}
  609. */
  610. "then": function () {
  611. H.events.dispatch(
  612. "error",
  613. {"msg": "Promises not supported in this engine. Use a polyfill."}
  614. );
  615. }
  616. };
  617. }
  618. }
  619. /* eslint-enable security/detect-object-injection */
  620. }
  621. /**
  622. * Load .hpb files
  623. * @param {string} lang The language
  624. * @returns {undefined}
  625. */
  626. function loadPattern(lang) {
  627. let filename = lang + ".hpb";
  628. let langFallback = lang;
  629. H.cf.polyfill = true;
  630. // eslint-disable-next-line security/detect-object-injection
  631. H.cf.langs[lang] = "H9Y";
  632. if (lcFallbacks && lcFallbacks.has(lang)) {
  633. langFallback = lcFallbacks.get(lang);
  634. filename = langFallback + ".hpb";
  635. }
  636. H.bins = H.bins || new Map();
  637. loadBinary(H.paths.patterndir, filename, langFallback, lang);
  638. }
  639. if (H.cf.wasm === null) {
  640. H.cf.wasm = runWasmTest();
  641. }
  642. lcRequire.forEach(function eachReq(value, lang) {
  643. if (value === "FORCEHYPHENOPOLY" ||
  644. // eslint-disable-next-line security/detect-object-injection
  645. (H.cf.langs[lang] && H.cf.langs[lang] === "H9Y")
  646. ) {
  647. loadPattern(lang);
  648. } else {
  649. tester.create(lang);
  650. }
  651. });
  652. const testContainer = tester.append(d.documentElement);
  653. if (testContainer !== null) {
  654. const nl = testContainer.getElementsByTagName("div");
  655. eachKey(nl, function eachNode(n) {
  656. /* eslint-disable security/detect-object-injection */
  657. if (checkCSSHyphensSupport(nl[n]) && nl[n].offsetHeight > 12) {
  658. H.cf.langs[nl[n].lang] = "CSS";
  659. } else {
  660. loadPattern(nl[n].lang);
  661. }
  662. /* eslint-enable security/detect-object-injection */
  663. });
  664. tester.clear();
  665. }
  666. if (H.cf.polyfill) {
  667. loadScript(H.paths.maindir, "Hyphenopoly.js");
  668. if (H.cf.wasm) {
  669. loadBinary(
  670. H.paths.maindir,
  671. "hyphenEngine.wasm",
  672. "hyphenEngine",
  673. "wasm"
  674. );
  675. } else {
  676. loadScript(H.paths.maindir, "hyphenEngine.asm.js");
  677. }
  678. eachKey(H.cf.langs, function prepareEach(lang) {
  679. /* eslint-disable security/detect-object-injection */
  680. if (H.cf.langs[lang] === "H9Y") {
  681. allocateMemory(lang);
  682. exposeHyphenateFunction(lang);
  683. }
  684. /* eslint-enable security/detect-object-injection */
  685. });
  686. }
  687. }());
  688. /**
  689. * Hides the specified elements and starts the process by
  690. * dispatching a "contentLoaded"-event in Hyphenopoly
  691. * @returns {undefined}
  692. */
  693. function handleDCL() {
  694. if (H.setup.hide.match(/^(?:element|text)$/)) {
  695. H.toggle("off");
  696. }
  697. H.events.dispatch(
  698. "contentLoaded",
  699. {"msg": ["contentLoaded"]}
  700. );
  701. }
  702. if (H.cf.polyfill) {
  703. if (H.setup.hide === "all") {
  704. H.toggle("off");
  705. }
  706. if (H.setup.hide !== "none") {
  707. H.setup.timeOutHandler = w.setTimeout(function timedOut() {
  708. H.toggle("on");
  709. H.events.dispatch("timeout", {"delay": H.setup.timeout});
  710. }, H.setup.timeout);
  711. }
  712. if (d.readyState === "loading") {
  713. d.addEventListener(
  714. "DOMContentLoaded",
  715. handleDCL,
  716. {
  717. "once": true,
  718. "passive": true
  719. }
  720. );
  721. } else {
  722. handleDCL();
  723. }
  724. } else {
  725. H.events.dispatch("tearDown", {});
  726. w.Hyphenopoly = null;
  727. }
  728. if (H.cacheFeatureTests) {
  729. store.setItem(
  730. "Hyphenopoly_Loader",
  731. JSON.stringify(H.cf)
  732. );
  733. }
  734. }(window, document, Hyphenopoly, Object));