/** * @license Hyphenopoly_Loader 3.4.0 - client side hyphenation * ©2019 Mathias Nater, Zürich (mathiasnater at gmail dot com) * https://github.com/mnater/Hyphenopoly * * Released under the MIT license * http://mnater.github.io/Hyphenopoly/LICENSE */ /* globals Hyphenopoly:readonly */ /** * Wrap all code in an iife to keep a scope. Important objects are parameters * of this iife to keep codesize low. * @param {Object} w shorthand for window * @param {Object} d shorthand for document * @param {Object} H shorthand for Hyphenopoly * @param {Object} o shorthand for object */ (function H9YL(w, d, H, o) { "use strict"; const store = sessionStorage; const wa = w.WebAssembly; const lcFallbacks = new Map(); const lcRequire = new Map(); /** * Create Object without standard Object-prototype * @returns {Object} empty object */ function empty() { return o.create(null); } /** * Shorthand for Object.keys(obj).forEach(function () {}) * @param {Object} obj the object to iterate * @param {function} fn the function to execute * @returns {undefined} */ function eachKey(obj, fn) { o.keys(obj).forEach(fn); } /** * Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously * computed settings from sessionStorage or creating an template object. * This is in an iife to keep complexity low. */ (function configFeat() { if (H.cacheFeatureTests && store.getItem("Hyphenopoly_Loader")) { H.cf = JSON.parse(store.getItem("Hyphenopoly_Loader")); } else { H.cf = { "langs": empty(), "polyfill": false, "wasm": null }; } }()); /** * Set H.paths defaults or overwrite with user settings. * This is in an iife to keep complexity low. */ (function configPaths() { const maindir = (d.currentScript) ? d.currentScript.src.replace(/Hyphenopoly_Loader.js/i, "") : "../"; const patterndir = maindir + "patterns/"; if (H.paths) { H.paths.maindir = H.paths.maindir || maindir; H.paths.patterndir = H.paths.patterndir || patterndir; } else { H.paths = o.create({ "maindir": maindir, "patterndir": patterndir }); } }()); /** * Set some H.setup fields to defaults or overwrite with user settings. * This is in an iife to keep complexity low. */ (function configSetup() { if (H.setup) { H.setup.selectors = H.setup.selectors || {".hyphenate": {}}; H.setup.timeout = H.setup.timeout || 1000; H.setup.hide = H.setup.hide || "all"; } else { H.setup = { "hide": "all", "selectors": {".hyphenate": {}}, "timeout": 1000 }; } }()); /** * Copy required languages to local lcRequire and * eventually fallbacks to local lcFallbacks. * This is in an iife to keep complexity low. */ (function configRequire() { eachKey(H.require, function copyRequire(k) { // eslint-disable-next-line security/detect-object-injection lcRequire.set(k.toLowerCase(), H.require[k]); }); if (H.fallbacks) { eachKey(H.fallbacks, function copyFallbacks(k) { lcFallbacks.set( k.toLowerCase(), // eslint-disable-next-line security/detect-object-injection H.fallbacks[k].toLowerCase() ); }); } }()); /** * Define function H.toggle. * This function hides or unhides (depending of the parameter state) * the whole document (H.setup.hide == "all") or * each selected element (H.setup.hide == "element") or * text of each selected element (H.setup.hide == "text") * @param {string} state State: either on (visible) or off (hidden) */ H.toggle = function toggle(state) { if (state === "on") { const stylesNode = d.getElementById("H9Y_Styles"); if (stylesNode) { stylesNode.parentNode.removeChild(stylesNode); } } else { const vis = " {visibility: hidden !important}\n"; const sc = d.createElement("style"); let myStyle = ""; sc.id = "H9Y_Styles"; switch (H.setup.hide) { case "all": myStyle = "html" + vis; break; case "element": eachKey(H.setup.selectors, function eachSelector(sel) { myStyle += sel + vis; }); break; case "text": eachKey(H.setup.selectors, function eachSelector(sel) { myStyle += sel + " {color: transparent !important}\n"; }); break; // No Default } sc.appendChild(d.createTextNode(myStyle)); d.head.appendChild(sc); } }; /** * Setup basic event system. Some events are defined but the definition of * what happens when they are triggered is deferred to Hyphenopoly.js * This is in an iife to keep complexity low. */ (function setupEvents() { // Events known to the system const definedEvents = new Map(); // Default events, execution deferred to Hyphenopoly.js const deferred = []; /* * Register for custom event handlers, where event is not yet defined * these events will be correctly registered in Hyphenopoly.js */ const tempRegister = []; /** * Create Event Object * @param {string} name The Name of the event * @param {function} defFunc The default method of the event * @param {boolean} cancellable Is the default cancellable * @returns {undefined} */ function define(name, defFunc, cancellable) { definedEvents.set(name, { "cancellable": cancellable, "default": defFunc, "register": [] }); } define( "timeout", function def(e) { H.toggle("on"); w.console.info( "Hyphenopolys 'FOUHC'-prevention timed out after %dms", e.delay ); }, false ); define( "error", function def(e) { switch (e.lvl) { case "info": w.console.info(e.msg); break; case "warn": w.console.warn(e.msg); break; default: w.console.error(e.msg); } }, true ); define( "contentLoaded", function def(e) { deferred.push({ "data": e, "name": "contentLoaded" }); }, false ); define( "engineLoaded", function def(e) { deferred.push({ "data": e, "name": "engineLoaded" }); }, false ); define( "hpbLoaded", function def(e) { deferred.push({ "data": e, "name": "hpbLoaded" }); }, false ); define( "loadError", function def(e) { deferred.push({ "data": e, "name": "loadError" }); }, false ); define( "tearDown", null, true ); /** * Dispatch event with arguments * @param {string} name The name of the event * @param {Object|undefined} data Data of the event * @returns {undefined} */ function dispatch(name, data) { data = data || empty(); let defaultPrevented = false; definedEvents.get(name).register.forEach( function call(currentHandler) { data.preventDefault = function preventDefault() { if (definedEvents.get(name).cancellable) { defaultPrevented = true; } }; currentHandler(data); } ); if ( !defaultPrevented && definedEvents.get(name).default ) { definedEvents.get(name).default(data); } } /** * Add EventListender to event * @param {string} name The name of the event * @param {function} handler Function to register * @param {boolean} defer If the registration is deferred * @returns {undefined} */ function addListener(name, handler, defer) { if (definedEvents.has(name)) { definedEvents.get(name).register.push(handler); } else if (defer) { tempRegister.push({ "handler": handler, "name": name }); } else { H.events.dispatch( "error", { "lvl": "warn", "msg": "unknown Event \"" + name + "\" discarded" } ); } } if (H.handleEvent) { eachKey(H.handleEvent, function add(name) { /* eslint-disable security/detect-object-injection */ addListener(name, H.handleEvent[name], true); /* eslint-enable security/detect-object-injection */ }); } H.events = empty(); H.events.deferred = deferred; H.events.tempRegister = tempRegister; H.events.dispatch = dispatch; H.events.define = define; H.events.addListener = addListener; }()); /** * Feature test for wasm. * @returns {boolean} support */ function runWasmTest() { /* * Wasm feature test with iOS bug detection * (https://bugs.webkit.org/show_bug.cgi?id=181781) */ if ( typeof wa === "object" && typeof wa.Instance === "function" ) { /* eslint-disable array-element-newline */ const module = new wa.Module(Uint8Array.from([ 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127, 3, 2, 1, 0, 5, 3, 1, 0, 1, 7, 5, 1, 1, 116, 0, 0, 10, 16, 1, 14, 0, 32, 0, 65, 1, 54, 2, 0, 32, 0, 40, 2, 0, 11 ])); /* eslint-enable array-element-newline */ return (new wa.Instance(module).exports.t(4) !== 0); } return false; } /** * Load script by adding