12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196 |
- /**
- * @license Hyphenopoly 3.4.0 - client side hyphenation for webbrowsers
- * ©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 asmHyphenEngine:readonly, Hyphenopoly:readonly */
- (function mainWrapper(w) {
- "use strict";
- const SOFTHYPHEN = String.fromCharCode(173);
- /**
- * Create Object without standard Object-prototype
- * @returns {Object} empty object
- */
- function empty() {
- return Object.create(null);
- }
- /**
- * Polyfill Math.imul
- * @param {number} a LHS
- * @param {number} b RHS
- * @returns {number} empty object
- */
- /* eslint-disable no-bitwise */
- Math.imul = Math.imul || function imul(a, b) {
- const aHi = (a >>> 16) & 0xffff;
- const aLo = a & 0xffff;
- const bHi = (b >>> 16) & 0xffff;
- const bLo = b & 0xffff;
- /*
- * The shift by 0 fixes the sign on the high part.
- * The final |0 converts the unsigned value into a signed value
- */
- return ((aLo * bLo) + ((((aHi * bLo) + (aLo * bHi)) << 16) >>> 0) | 0);
- };
- /* eslint-enable no-bitwise */
- /**
- * Set value and properties of object member
- * Argument <props> is a bit pattern:
- * 1. bit: configurable
- * 2. bit: enumerable
- * 3. bit writable
- * e.g. 011(2) = 3(10) => configurable: f, enumerable: t, writable: t
- * or 010(2) = 2(10) => configurable: f, enumerable: t, writable: f
- * @param {any} val The value
- * @param {number} props bitfield
- * @returns {Object} Property object
- */
- function setProp(val, props) {
- /* eslint-disable no-bitwise, sort-keys */
- return {
- "configurable": (props & 4) > 0,
- "enumerable": (props & 2) > 0,
- "writable": (props & 1) > 0,
- "value": val
- };
- /* eslint-enable no-bitwise, sort-keys */
- }
- /**
- * Register copy event on element
- * @param {Object} el The element
- * @returns {undefined}
- */
- function registerOnCopy(el) {
- el.addEventListener(
- "copy",
- function oncopy(e) {
- e.preventDefault();
- const sel = w.getSelection();
- const docFrag = sel.getRangeAt(0).cloneContents();
- const div = document.createElement("div");
- div.appendChild(docFrag);
- const selectedHTML = div.innerHTML;
- const selectedText = sel.toString();
- /* eslint-disable security/detect-non-literal-regexp */
- e.clipboardData.setData("text/plain", selectedText.replace(new RegExp(SOFTHYPHEN, "g"), ""));
- e.clipboardData.setData("text/html", selectedHTML.replace(new RegExp(SOFTHYPHEN, "g"), ""));
- /* eslint-enable security/detect-non-literal-regexp */
- },
- true
- );
- }
- (function configurationFactory(H) {
- const generalDefaults = Object.create(null, {
- "defaultLanguage": setProp("en-us", 2),
- "dontHyphenate": setProp((function createList() {
- const r = empty();
- const list = "abbr,acronym,audio,br,button,code,img,input,kbd,label,math,option,pre,samp,script,style,sub,sup,svg,textarea,var,video";
- list.split(",").forEach(function add(value) {
- /* eslint-disable security/detect-object-injection */
- r[value] = true;
- /* eslint-enable security/detect-object-injection */
- });
- return r;
- }()), 2),
- "dontHyphenateClass": setProp("donthyphenate", 2),
- "exceptions": setProp(empty(), 2),
- "keepAlive": setProp(true, 2),
- "normalize": setProp(false, 2),
- "safeCopy": setProp(true, 2),
- "timeout": setProp(1000, 2)
- });
- const settings = Object.create(generalDefaults);
- const perClassDefaults = Object.create(null, {
- "compound": setProp("hyphen", 2),
- "hyphen": setProp(SOFTHYPHEN, 2),
- "leftmin": setProp(0, 2),
- "leftminPerLang": setProp(0, 2),
- "minWordLength": setProp(6, 2),
- "mixedCase": setProp(true, 2),
- "orphanControl": setProp(1, 2),
- "rightmin": setProp(0, 2),
- "rightminPerLang": setProp(0, 2)
- });
- Object.keys(H.setup).forEach(function copySettings(key) {
- if (key === "selectors") {
- const selectors = Object.keys(H.setup.selectors);
- Object.defineProperty(
- settings,
- "selectors",
- setProp(selectors, 2)
- );
- selectors.forEach(function copySelectors(sel) {
- const tmp = empty();
- /* eslint-disable security/detect-object-injection */
- Object.keys(H.setup.selectors[sel]).forEach(
- function copySelectorSettings(k) {
- tmp[k] = setProp(H.setup.selectors[sel][k], 2);
- }
- );
- /* eslint-enable security/detect-object-injection */
- Object.defineProperty(
- settings,
- sel,
- setProp(Object.create(perClassDefaults, tmp), 2)
- );
- });
- } else if (key === "dontHyphenate") {
- const tmp = empty();
- Object.keys(H.setup.dontHyphenate).forEach(
- function copyTagNames(k) {
- /* eslint-disable security/detect-object-injection */
- tmp[k] = setProp(H.setup.dontHyphenate[k], 2);
- /* eslint-enable security/detect-object-injection */
- }
- );
- Object.defineProperty(
- settings,
- key,
- setProp(
- Object.create(generalDefaults.dontHyphenate, tmp), 3
- )
- );
- } else {
- /* eslint-disable security/detect-object-injection */
- Object.defineProperty(
- settings,
- key,
- setProp(H.setup[key], 3)
- );
- /* eslint-enable security/detect-object-injection */
- }
- });
- H.c = settings;
- }(Hyphenopoly));
- (function H9Y(H) {
- const C = H.c;
- let mainLanguage = null;
- let elements = null;
- /**
- * Factory for elements
- * @returns {Object} elements-object
- */
- function makeElementCollection() {
- const list = new Map();
- /*
- * Counter counts the elements to be hyphenated.
- * Needs to be an object (Pass by reference)
- */
- const counter = [0];
- /**
- * Add element to elements
- * @param {object} el The element
- * @param {string} lang The language of the element
- * @param {string} sel The selector of the element
- * @returns {Object} An element-object
- */
- function add(el, lang, sel) {
- const elo = {
- "element": el,
- "selector": sel
- };
- if (!list.has(lang)) {
- list.set(lang, []);
- }
- list.get(lang).push(elo);
- counter[0] += 1;
- return elo;
- }
- /**
- * Removes elements from the list and updates the counter
- * @param {string} lang - The lang of the elements to remove
- */
- function rem(lang) {
- let langCount = 0;
- if (list.has(lang)) {
- langCount = list.get(lang).length;
- list.delete(lang);
- counter[0] -= langCount;
- if (counter[0] === 0) {
- H.events.dispatch("hyphenopolyEnd");
- }
- }
- }
- /**
- * Execute fn for each element
- * @param {function} fn The function to execute
- * @returns {undefined}
- */
- function each(fn) {
- list.forEach(function eachElement(val, key) {
- fn(key, val);
- });
- }
- return {
- "add": add,
- "counter": counter,
- "each": each,
- "list": list,
- "rem": rem
- };
- }
- /**
- * Get language of element by searching its parents or fallback
- * @param {Object} el The element
- * @param {boolean} fallback Will falback to mainlanguage
- * @returns {string|null} The language or null
- */
- function getLang(el, fallback) {
- try {
- return (el.getAttribute("lang"))
- ? el.getAttribute("lang").toLowerCase()
- : el.tagName.toLowerCase() === "html"
- ? fallback
- ? mainLanguage
- : null
- : getLang(el.parentNode, fallback);
- } catch (ignore) {
- return null;
- }
- }
- /**
- * Set mainLanguage
- * @returns {undefined}
- */
- function autoSetMainLanguage() {
- const el = w.document.getElementsByTagName("html")[0];
- mainLanguage = getLang(el, false);
- if (!mainLanguage && C.defaultLanguage !== "") {
- mainLanguage = C.defaultLanguage;
- }
- }
- /**
- * Check if node is matched by a given selector
- * @param {Node} n The Node to check
- * @param {String} sel Selector(s)
- * @returns {Boolean} true if matched, false if not matched
- */
- function nodeMatchedBy(n, sel) {
- if (!n.matches) {
- n.matches = n.msMatchesSelector || n.webkitMatchesSelector;
- }
- return n.matches(sel);
- }
- /**
- * Collect elements that have a selector defined in C.selectors
- * and add them to elements.
- * @returns {undefined}
- */
- function collectElements() {
- elements = makeElementCollection();
- const dontHyphenateSelector = (function createSel() {
- let s = "." + H.c.dontHyphenateClass;
- let k = null;
- for (k in C.dontHyphenate) {
- /* eslint-disable security/detect-object-injection */
- if (C.dontHyphenate[k]) {
- s += ", " + k;
- }
- /* eslint-enable security/detect-object-injection */
- }
- return s;
- }());
- const matchingSelectors = C.selectors.join(", ") + ", " + dontHyphenateSelector;
- /**
- * Get Language of Element or of one of its ancestors.
- * @param {Object} el The element to scan
- * @param {string} pLang The language of the parent element
- * @returns {string} the language
- */
- function getElementLanguage(el, pLang) {
- if (el.lang && typeof el.lang === "string") {
- return el.lang.toLowerCase();
- } else if (pLang && pLang !== "") {
- return pLang.toLowerCase();
- }
- return getLang(el, true);
- }
- /**
- * Recursively walk all elements in el, lending lang and selName
- * add them to elements if necessary.
- * @param {Object} el The element to scan
- * @param {string} pLang The language of the oarent element
- * @param {string} sel The selector of the parent element
- * @param {boolean} isChild If el is a child element
- * @returns {undefined}
- */
- function processElements(el, pLang, sel, isChild) {
- isChild = isChild || false;
- const eLang = getElementLanguage(el, pLang);
- /* eslint-disable security/detect-object-injection */
- if (H.cf.langs[eLang] === "H9Y") {
- elements.add(el, eLang, sel);
- if (!isChild && C.safeCopy) {
- registerOnCopy(el);
- }
- } else if (!H.cf.langs[eLang]) {
- H.events.dispatch("error", {
- "lvl": "warn",
- "msg": "Element with '" + eLang + "' found, but '" + eLang + ".hpb' not loaded. Check language tags!"
- });
- }
- /* eslint-enable security/detect-object-injection */
- const cn = el.childNodes;
- Array.prototype.forEach.call(cn, function eachChildNode(n) {
- if (n.nodeType === 1 &&
- !nodeMatchedBy(n, matchingSelectors)) {
- processElements(n, eLang, sel, true);
- }
- });
- }
- C.selectors.forEach(function eachSelector(sel) {
- const nl = w.document.querySelectorAll(sel);
- Array.prototype.forEach.call(nl, function eachNode(n) {
- processElements(n, getLang(n, true), sel, false);
- });
- });
- H.elementsReady = true;
- }
- const wordHyphenatorPool = new Map();
- /**
- * Factory for hyphenatorFunctions for a specific language and class
- * @param {Object} lo Language-Object
- * @param {string} lang The language
- * @param {string} sel The selector
- * @returns {function} The hyphenate function
- */
- function createWordHyphenator(lo, lang, sel) {
- /* eslint-disable-next-line security/detect-object-injection */
- const classSettings = C[sel];
- const hyphen = classSettings.hyphen;
- lo.cache.set(sel, new Map());
- /**
- * HyphenateFunction for compound words
- * @param {string} word The word
- * @returns {string} The hyphenated compound word
- */
- function hyphenateCompound(word) {
- const zeroWidthSpace = String.fromCharCode(8203);
- let parts = null;
- let wordHyphenator = null;
- if (classSettings.compound === "auto" ||
- classSettings.compound === "all") {
- wordHyphenator = createWordHyphenator(lo, lang, sel);
- parts = word.split("-").map(function h7eParts(p) {
- if (p.length >= classSettings.minWordLength) {
- return wordHyphenator(p);
- }
- return p;
- });
- if (classSettings.compound === "auto") {
- word = parts.join("-");
- } else {
- word = parts.join("-" + zeroWidthSpace);
- }
- } else {
- word = word.replace("-", "-" + zeroWidthSpace);
- }
- return word;
- }
- /**
- * Checks if a string is mixed case
- * @param {string} s The string
- * @returns {boolean} true if s is mixed case
- */
- function isMixedCase(s) {
- return Array.prototype.map.call(s, function mapper(c) {
- return (c === c.toLowerCase());
- }).some(function checker(v, i, a) {
- return (v !== a[0]);
- });
- }
- /* eslint-disable complexity */
- /**
- * HyphenateFunction for words (compound or not)
- * @param {string} word The word
- * @returns {string} The hyphenated word
- */
- function hyphenator(word) {
- let hw = lo.cache.get(sel).get(word);
- if (!classSettings.mixedCase && isMixedCase(word)) {
- hw = word;
- }
- if (!hw) {
- if (lo.exceptions.has(word)) {
- hw = lo.exceptions.get(word).replace(
- /-/g,
- classSettings.hyphen
- );
- } else if (word.indexOf("-") === -1) {
- if (word.length > 61) {
- H.events.dispatch("error", {
- "lvl": "warn",
- "msg": "found word longer than 61 characters"
- });
- hw = word;
- } else {
- /* eslint-disable security/detect-object-injection */
- hw = lo.hyphenateFunction(
- word,
- hyphen.charCodeAt(0),
- classSettings.leftminPerLang[lang],
- classSettings.rightminPerLang[lang]
- );
- /* eslint-enable security/detect-object-injection */
- }
- } else {
- hw = hyphenateCompound(word);
- }
- lo.cache.get(sel).set(word, hw);
- }
- return hw;
- }
- /* eslint-enable complexity */
- wordHyphenatorPool.set(lang + "-" + sel, hyphenator);
- return hyphenator;
- }
- const orphanControllerPool = new Map();
- /**
- * Factory for function that handles orphans
- * @param {string} sel The selector
- * @returns {function} The function created
- */
- function createOrphanController(sel) {
- /**
- * Function template
- * @param {string} ignore unused result of replace
- * @param {string} leadingWhiteSpace The leading whiteSpace
- * @param {string} lastWord The last word
- * @param {string} trailingWhiteSpace The trailing whiteSpace
- * @returns {string} Treated end of text
- */
- function controlOrphans(
- ignore,
- leadingWhiteSpace,
- lastWord,
- trailingWhiteSpace
- ) {
- /* eslint-disable security/detect-object-injection */
- const classSettings = C[sel];
- /* eslint-enable security/detect-object-injection */
- let h = classSettings.hyphen;
- if (".\\+*?[^]$(){}=!<>|:-".indexOf(classSettings.hyphen) !== -1) {
- h = "\\" + classSettings.hyphen;
- }
- if (classSettings.orphanControl === 3 && leadingWhiteSpace === " ") {
- leadingWhiteSpace = String.fromCharCode(160);
- }
- /* eslint-disable security/detect-non-literal-regexp */
- return leadingWhiteSpace + lastWord.replace(new RegExp(h, "g"), "") + trailingWhiteSpace;
- /* eslint-enable security/detect-non-literal-regexp */
- }
- orphanControllerPool.set(sel, controlOrphans);
- return controlOrphans;
- }
- /**
- * Hyphenate an entitiy (text string or Element-Object)
- * @param {string} lang - the language of the string
- * @param {string} sel - the selectorName of settings
- * @param {string} entity - the entity to be hyphenated
- * @returns {string | null} hyphenated str according to setting of sel
- */
- function hyphenate(lang, sel, entity) {
- const lo = H.languages.get(lang);
- /* eslint-disable security/detect-object-injection */
- const classSettings = C[sel];
- /* eslint-enable security/detect-object-injection */
- const minWordLength = classSettings.minWordLength;
- const normalize = C.normalize &&
- Boolean(String.prototype.normalize);
- const poolKey = lang + "-" + sel;
- const wordHyphenator = (wordHyphenatorPool.has(poolKey))
- ? wordHyphenatorPool.get(poolKey)
- : createWordHyphenator(lo, lang, sel);
- const orphanController = (orphanControllerPool.has(sel))
- ? orphanControllerPool.get(sel)
- : createOrphanController(sel);
- const re = lo.genRegExps.get(sel);
- /**
- * Hyphenate text according to setting in sel
- * @param {string} text - the strint to be hyphenated
- * @returns {string} hyphenated string according to setting of sel
- */
- function hyphenateText(text) {
- let tn = null;
- if (normalize) {
- tn = text.normalize("NFC").replace(re, wordHyphenator);
- } else {
- tn = text.replace(re, wordHyphenator);
- }
- if (classSettings.orphanControl !== 1) {
- tn = tn.replace(
- // eslint-disable-next-line prefer-named-capture-group
- /(\u0020*)(\S+)(\s*)$/,
- orphanController
- );
- }
- return tn;
- }
- /**
- * Hyphenate element according to setting in sel
- * @param {object} el - the HTMLElement to be hyphenated
- * @returns {undefined}
- */
- function hyphenateElement(el) {
- H.events.dispatch("beforeElementHyphenation", {
- "el": el,
- "lang": lang
- });
- const cn = el.childNodes;
- Array.prototype.forEach.call(cn, function eachChildNode(n) {
- if (
- n.nodeType === 3 &&
- n.data.length >= minWordLength
- ) {
- n.data = hyphenateText(n.data);
- }
- });
- elements.counter[0] -= 1;
- H.events.dispatch("afterElementHyphenation", {
- "el": el,
- "lang": lang
- });
- }
- let r = null;
- if (typeof entity === "string") {
- r = hyphenateText(entity);
- } else if (entity instanceof HTMLElement) {
- hyphenateElement(entity);
- }
- return r;
- }
- H.createHyphenator = function createHyphenator(lang) {
- return function hyphenator(entity, sel) {
- sel = sel || ".hyphenate";
- return hyphenate(lang, sel, entity);
- };
- };
- H.unhyphenate = function unhyphenate() {
- elements.each(function eachLang(lang, els) {
- els.forEach(function eachElem(elo) {
- const n = elo.element.firstChild;
- const h = C[elo.selector].hyphen;
- /* eslint-disable security/detect-non-literal-regexp */
- n.data = n.data.replace(new RegExp(h, "g"), "");
- /* eslint-enable security/detect-non-literal-regexp */
- });
- });
- };
- /**
- * Hyphenate all elements with a given language
- * @param {string} lang The language
- * @param {Array} elArr Array of elements
- * @returns {undefined}
- */
- function hyphenateLangElements(lang, elArr) {
- if (elArr) {
- elArr.forEach(function eachElem(elo) {
- hyphenate(lang, elo.selector, elo.element);
- });
- } else {
- H.events.dispatch("error", {
- "lvl": "warn",
- "msg": "engine for language '" + lang + "' loaded, but no elements found."
- });
- }
- if (elements.counter[0] === 0) {
- H.events.dispatch("hyphenopolyEnd");
- }
- }
- /**
- * Convert exceptions to object
- * @param {string} exc comma separated list of exceptions
- * @returns {Object} Map of exceptions
- */
- function convertExceptions(exc) {
- const r = new Map();
- exc.split(", ").forEach(function eachExc(e) {
- const key = e.replace(/-/g, "");
- r.set(key, e);
- });
- return r;
- }
- /**
- * Create lang Object
- * @param {string} lang The language
- * @returns {Object} The newly
- */
- function createLangObj(lang) {
- if (!H.languages) {
- H.languages = new Map();
- }
- if (!H.languages.has(lang)) {
- H.languages.set(lang, empty());
- }
- return H.languages.get(lang);
- }
- /**
- * Setup lo
- * @param {string} lang The language
- * @param {function} hyphenateFunction The hyphenateFunction
- * @param {string} alphabet List of used characters
- * @param {number} leftmin leftmin
- * @param {number} rightmin rightmin
- * @returns {undefined}
- */
- function prepareLanguagesObj(
- lang,
- hyphenateFunction,
- alphabet,
- leftmin,
- rightmin
- ) {
- alphabet = alphabet.replace(/-/g, "");
- const lo = createLangObj(lang);
- if (!lo.engineReady) {
- lo.cache = new Map();
- /* eslint-disable security/detect-object-injection */
- if (H.c.exceptions.global) {
- if (H.c.exceptions[lang]) {
- H.c.exceptions[lang] += ", " + H.c.exceptions.global;
- } else {
- H.c.exceptions[lang] = H.c.exceptions.global;
- }
- }
- if (H.c.exceptions[lang]) {
- lo.exceptions = convertExceptions(H.c.exceptions[lang]);
- delete H.c.exceptions[lang];
- } else {
- lo.exceptions = new Map();
- }
- /* eslint-enable security/detect-object-injection */
- lo.genRegExps = new Map();
- lo.leftmin = leftmin;
- lo.rightmin = rightmin;
- lo.hyphenateFunction = hyphenateFunction;
- C.selectors.forEach(function eachSelector(sel) {
- /* eslint-disable security/detect-object-injection */
- const classSettings = C[sel];
- /* eslint-enable security/detect-object-injection */
- if (classSettings.leftminPerLang === 0) {
- Object.defineProperty(
- classSettings,
- "leftminPerLang",
- setProp(empty(), 2)
- );
- }
- if (classSettings.rightminPerLang === 0) {
- Object.defineProperty(
- classSettings,
- "rightminPerLang",
- setProp(empty(), 2)
- );
- }
- /* eslint-disable security/detect-object-injection */
- if (classSettings.leftminPerLang[lang]) {
- classSettings.leftminPerLang[lang] = Math.max(
- lo.leftmin,
- classSettings.leftmin,
- classSettings.leftminPerLang[lang]
- );
- } else {
- classSettings.leftminPerLang[lang] = Math.max(
- lo.leftmin,
- classSettings.leftmin
- );
- }
- if (classSettings.rightminPerLang[lang]) {
- classSettings.rightminPerLang[lang] = Math.max(
- lo.rightmin,
- classSettings.rightmin,
- classSettings.rightminPerLang[lang]
- );
- } else {
- classSettings.rightminPerLang[lang] = Math.max(
- lo.rightmin,
- classSettings.rightmin
- );
- }
- /* eslint-enable security/detect-object-injection */
- /*
- * Find words with characters from `alphabet` and
- * `Zero Width Non-Joiner` and `-` with a min length.
- *
- * This regexp is not perfect. It also finds parts of words
- * that follow a character that is not in the `alphabet`.
- * Word delimiters are not taken in account.
- */
- /* eslint-disable security/detect-non-literal-regexp */
- lo.genRegExps.set(sel, new RegExp("[\\w" + alphabet + String.fromCharCode(8204) + "-]{" + classSettings.minWordLength + ",}", "gi"));
- /* eslint-enable security/detect-non-literal-regexp */
- });
- lo.engineReady = true;
- }
- Hyphenopoly.events.dispatch("engineReady", {"msg": lang});
- }
- /**
- * Calculate heap size for (w)asm
- * wasm page size: 65536 = 64 Ki
- * asm: http://asmjs.org/spec/latest/#linking-0
- * @param {number} targetSize The targetet Size
- * @returns {number} The necessary heap size
- */
- function calculateHeapSize(targetSize) {
- /* eslint-disable no-bitwise */
- if (H.cf.wasm) {
- return Math.ceil(targetSize / 65536) * 65536;
- }
- const exp = Math.ceil(Math.log(targetSize) * Math.LOG2E);
- if (exp <= 12) {
- return 1 << 12;
- }
- if (exp < 24) {
- return 1 << exp;
- }
- return Math.ceil(targetSize / (1 << 24)) * (1 << 24);
- /* eslint-enable no-bitwise */
- }
- /**
- * Polyfill for TextDecoder
- */
- const decode = (function makeDecoder() {
- if (w.TextDecoder) {
- const utf16ledecoder = new TextDecoder("utf-16le");
- return function decoder(ui16) {
- return utf16ledecoder.decode(ui16);
- };
- }
- return function decoder(ui16) {
- return String.fromCharCode.apply(null, ui16);
- };
- }());
- /**
- * Calculate Base Data
- *
- * Build Heap (the heap object's byteLength must be
- * either 2^n for n in [12, 24)
- * or 2^24 · n for n ≥ 1;)
- *
- * MEMORY LAYOUT:
- *
- * -------------------- <- Offset 0
- * | translateMap |
- * | keys: |
- * |256 chars * 2Bytes|
- * | + |
- * | values: |
- * |256 chars * 1Byte |
- * -------------------- <- 768 Bytes
- * | alphabet |
- * |256 chars * 2Bytes|
- * -------------------- <- valueStoreOffset (vs) = 1280
- * | valueStore |
- * | 1 Byte |
- * |* valueStoreLength|
- * --------------------
- * | align to 4Bytes |
- * -------------------- <- patternTrieOffset (pt)
- * | patternTrie |
- * | 4 Bytes |
- * |*patternTrieLength|
- * -------------------- <- wordOffset (wo)
- * | wordStore |
- * | Uint16[64] | 128 bytes
- * -------------------- <- translatedWordOffset (tw)
- * | transl.WordStore |
- * | Uint8[64] | 64 bytes
- * -------------------- <- hyphenPointsOffset (hp)
- * | hyphenPoints |
- * | Uint8[64] | 64 bytes
- * -------------------- <- hyphenatedWordOffset (hw)
- * | hyphenatedWord |
- * | Uint16[128] | 256 Bytes
- * -------------------- <- hpbOffset (ho) -
- * | HEADER | |
- * | 6*4 Bytes | |
- * | 24 Bytes | |
- * -------------------- |
- * | PATTERN LIC | |
- * | variable Length | |
- * -------------------- |
- * | align to 4Bytes | } this is the .hpb-file
- * -------------------- <- hpbTranslateOffset |
- * | TRANSLATE | |
- * | 2 + [0] * 2Bytes | |
- * -------------------- <-hpbPatternsOffset(po)|
- * | PATTERNS | |
- * | patternsLength | |
- * -------------------- <- heapEnd -
- * | align to 4Bytes |
- * -------------------- <- heapSize (hs)
- * @param {Object} hpbBuf FileBuffer from .hpb-file
- * @returns {Object} baseData-object
- */
- function calculateBaseData(hpbBuf) {
- const hpbMetaData = new Uint32Array(hpbBuf).subarray(0, 8);
- if (hpbMetaData[0] !== 40005736) {
- /*
- * Pattern files must begin with "hpb2"
- * Get current utf8 values with
- * `new Uint8Array(Uint32Array.of(hpbMetaData[0]).buffer)`
- */
- H.events.dispatch("error", {
- "lvl": "error",
- "msg": "Pattern file format error: " + new Uint8Array(Uint32Array.of(hpbMetaData[0]).buffer)
- });
- throw new Error("Pattern file format error!");
- }
- const valueStoreLength = hpbMetaData[7];
- const valueStoreOffset = 1280;
- const patternTrieOffset = valueStoreOffset + valueStoreLength +
- (4 - ((valueStoreOffset + valueStoreLength) % 4));
- const wordOffset = patternTrieOffset + (hpbMetaData[6] * 4);
- return {
- // Set hpbOffset
- "ho": wordOffset + 512,
- // Set hyphenPointsOffset
- "hp": wordOffset + 192,
- // Set heapSize
- "hs": Math.max(calculateHeapSize(wordOffset + 512 + hpbMetaData[2] + hpbMetaData[3]), 32 * 1024 * 64),
- // Set hyphenatedWordOffset
- "hw": wordOffset + 256,
- // Set leftmin
- "lm": hpbMetaData[4],
- // Set patternsLength
- "pl": hpbMetaData[3],
- // Set hpbPatternsOffset
- "po": wordOffset + 512 + hpbMetaData[2],
- // Set patternTrieOffset
- "pt": patternTrieOffset,
- // Set rightmin
- "rm": hpbMetaData[5],
- // Set translateOffset
- "to": wordOffset + 512 + hpbMetaData[1],
- // Set translatedWordOffset
- "tw": wordOffset + 128,
- // Set valueStoreOffset
- "vs": valueStoreOffset,
- // Set wordOffset
- "wo": wordOffset
- };
- }
- /**
- * Setup env for hyphenateFunction
- * @param {Object} baseData baseData
- * @param {function} hyphenateFunc hyphenateFunction
- * @returns {function} hyphenateFunction with closured environment
- */
- function encloseHyphenateFunction(baseData, hyphenateFunc) {
- /* eslint-disable no-bitwise */
- const heapBuffer = H.cf.wasm
- ? baseData.wasmMemory.buffer
- : baseData.heapBuffer;
- const wordStore = (new Uint16Array(heapBuffer)).subarray(
- baseData.wo >> 1,
- (baseData.wo >> 1) + 64
- );
- const hydWrdStore = (new Uint16Array(heapBuffer)).subarray(
- baseData.hw >> 1,
- (baseData.hw >> 1) + 128
- );
- /* eslint-enable no-bitwise */
- wordStore[0] = 95;
- return function enclHyphenate(word, hyphencc, leftmin, rightmin) {
- let i = 0;
- let cc = 0;
- do {
- cc = word.charCodeAt(i);
- i += 1;
- // eslint-disable-next-line security/detect-object-injection
- wordStore[i] = cc;
- } while (cc);
- /* eslint-disable security/detect-object-injection */
- wordStore[i] = 95;
- wordStore[i + 1] = 0;
- /* eslint-enable security/detect-object-injection */
- if (hyphenateFunc(leftmin, rightmin, hyphencc) === 1) {
- word = decode(hydWrdStore.subarray(1, hydWrdStore[0] + 1));
- }
- return word;
- };
- }
- /**
- * Instantiate Wasm Engine
- * @param {string} lang The language
- * @returns {undefined}
- */
- function instantiateWasmEngine(lang) {
- Promise.all([H.bins.get(lang), H.bins.get("hyphenEngine")]).then(
- function onAll(binaries) {
- const hpbBuf = binaries[0];
- const baseData = calculateBaseData(hpbBuf);
- const wasmModule = binaries[1];
- const specMem = H.specMems.get(lang);
- const wasmMemory = (
- specMem.buffer.byteLength >= baseData.hs
- )
- ? specMem
- : new WebAssembly.Memory({
- "initial": baseData.hs / 65536,
- "maximum": 256
- });
- const ui32wasmMemory = new Uint32Array(wasmMemory.buffer);
- ui32wasmMemory.set(
- new Uint32Array(hpbBuf),
- // eslint-disable-next-line no-bitwise
- baseData.ho >> 2
- );
- baseData.wasmMemory = wasmMemory;
- WebAssembly.instantiate(wasmModule, {
- "env": {
- "memory": baseData.wasmMemory,
- "memoryBase": 0
- },
- "x": baseData
- }).then(
- function runWasm(result) {
- const alphalen = result.exports.convert();
- prepareLanguagesObj(
- lang,
- encloseHyphenateFunction(
- baseData,
- result.exports.hyphenate
- ),
- decode(
- (new Uint16Array(wasmMemory.buffer)).
- subarray(385, 384 + alphalen)
- ),
- baseData.lm,
- baseData.rm
- );
- }
- );
- }
- );
- }
- /**
- * Instantiate asm Engine
- * @param {string} lang The language
- * @returns {undefined}
- */
- function instantiateAsmEngine(lang) {
- const hpbBuf = H.bins.get(lang);
- const baseData = calculateBaseData(hpbBuf);
- const specMem = H.specMems.get(lang);
- const heapBuffer = (specMem.byteLength >= baseData.hs)
- ? specMem
- : new ArrayBuffer(baseData.hs);
- const ui8Heap = new Uint8Array(heapBuffer);
- const ui8Patterns = new Uint8Array(hpbBuf);
- ui8Heap.set(ui8Patterns, baseData.ho);
- baseData.heapBuffer = heapBuffer;
- const theHyphenEngine = asmHyphenEngine(
- {
- "Int32Array": w.Int32Array,
- "Math": Math,
- "Uint16Array": w.Uint16Array,
- "Uint8Array": w.Uint8Array
- },
- baseData,
- baseData.heapBuffer
- );
- const alphalen = theHyphenEngine.convert();
- prepareLanguagesObj(
- lang,
- encloseHyphenateFunction(baseData, theHyphenEngine.hyphenate),
- decode(
- (new Uint16Array(heapBuffer)).
- subarray(385, 384 + alphalen)
- ),
- baseData.lm,
- baseData.rm
- );
- }
- let engineInstantiator = null;
- const hpb = [];
- /**
- * Instantiate hyphenEngines for languages
- * @param {string} lang The language
- * @param {string} engineType The engineType: "wasm" or "asm"
- * @returns {undefined}
- */
- function prepare(lang, engineType) {
- if (lang === "*") {
- if (engineType === "wasm") {
- engineInstantiator = instantiateWasmEngine;
- } else if (engineType === "asm") {
- engineInstantiator = instantiateAsmEngine;
- }
- hpb.forEach(function eachHbp(hpbLang) {
- engineInstantiator(hpbLang);
- });
- } else if (engineInstantiator) {
- engineInstantiator(lang);
- } else {
- hpb.push(lang);
- }
- }
- H.events.define(
- "contentLoaded",
- function onContentLoaded() {
- autoSetMainLanguage();
- collectElements();
- H.events.dispatch("elementsReady");
- },
- false
- );
- H.events.define(
- "elementsReady",
- function onElementsReady() {
- elements.each(function eachElem(lang, values) {
- if (H.languages &&
- H.languages.has(lang) &&
- H.languages.get(lang).engineReady
- ) {
- hyphenateLangElements(lang, values);
- }
- });
- },
- false
- );
- H.events.define(
- "engineLoaded",
- function onEngineLoaded(e) {
- prepare("*", e.msg);
- },
- false
- );
- H.events.define(
- "hpbLoaded",
- function onHpbLoaded(e) {
- prepare(e.msg, "*");
- },
- false
- );
- H.events.addListener(
- "loadError",
- function onLoadError(e) {
- if (e.msg !== "wasm") {
- elements.rem(e.name);
- }
- },
- false
- );
- H.events.define(
- "engineReady",
- function onEngineReady(e) {
- if (H.elementsReady) {
- hyphenateLangElements(e.msg, elements.list.get(e.msg));
- }
- },
- false
- );
- H.events.define(
- "hyphenopolyStart",
- null,
- true
- );
- H.events.define(
- "hyphenopolyEnd",
- function def() {
- w.clearTimeout(C.timeOutHandler);
- if (C.hide !== "none") {
- H.toggle("on");
- }
- if (!C.keepAlive) {
- window.Hyphenopoly = null;
- }
- },
- false
- );
- H.events.define(
- "beforeElementHyphenation",
- null,
- true
- );
- H.events.define(
- "afterElementHyphenation",
- null,
- true
- );
- H.events.tempRegister.forEach(function eachEo(eo) {
- H.events.addListener(eo.name, eo.handler, false);
- });
- delete H.events.tempRegister;
- H.events.dispatch("hyphenopolyStart", {"msg": "Hyphenopoly started"});
- w.clearTimeout(H.c.timeOutHandler);
- Object.defineProperty(C, "timeOutHandler", setProp(
- w.setTimeout(function ontimeout() {
- H.events.dispatch("timeout", {"delay": C.timeout});
- }, C.timeout),
- 2
- ));
- H.events.deferred.forEach(function eachDeferred(deferredeo) {
- H.events.dispatch(deferredeo.name, deferredeo.data);
- });
- delete H.events.deferred;
- }(Hyphenopoly));
- }(window));
|