manetta
3 years ago
75 changed files with 1351 additions and 13 deletions
@ -0,0 +1,934 @@ |
|||
/** |
|||
* @license Hyphenopoly 4.12.0 - client side hyphenation for webbrowsers |
|||
* ©2021 Mathias Nater, Güttingen (mathiasnater at gmail dot com) |
|||
* https://github.com/mnater/Hyphenopoly
|
|||
* |
|||
* Released under the MIT license |
|||
* http://mnater.github.io/Hyphenopoly/LICENSE
|
|||
*/ |
|||
|
|||
/* globals Hyphenopoly:readonly */ |
|||
((w, o) => { |
|||
"use strict"; |
|||
const SOFTHYPHEN = "\u00AD"; |
|||
|
|||
/** |
|||
* Event |
|||
*/ |
|||
const event = ((H) => { |
|||
const knownEvents = new Map([ |
|||
["afterElementHyphenation", []], |
|||
["beforeElementHyphenation", []], |
|||
["engineReady", []], |
|||
[ |
|||
"error", [ |
|||
(e) => { |
|||
if (e.runDefault) { |
|||
w.console.warn(e); |
|||
} |
|||
} |
|||
] |
|||
], |
|||
["hyphenopolyEnd", []], |
|||
["hyphenopolyStart", []] |
|||
]); |
|||
if (H.handleEvent) { |
|||
const userEvents = new Map(o.entries(H.handleEvent)); |
|||
knownEvents.forEach((eventFuncs, eventName) => { |
|||
if (userEvents.has(eventName)) { |
|||
eventFuncs.unshift(userEvents.get(eventName)); |
|||
} |
|||
}); |
|||
} |
|||
return { |
|||
"fire": ((eventName, eventData) => { |
|||
eventData.runDefault = true; |
|||
eventData.preventDefault = () => { |
|||
eventData.runDefault = false; |
|||
}; |
|||
knownEvents.get(eventName).forEach((eventFn) => { |
|||
eventFn(eventData); |
|||
}); |
|||
}) |
|||
}; |
|||
})(Hyphenopoly); |
|||
|
|||
/** |
|||
* Register copy event on element |
|||
* @param {Object} el The element |
|||
* @returns {undefined} |
|||
*/ |
|||
function registerOnCopy(el) { |
|||
el.addEventListener( |
|||
"copy", |
|||
(e) => { |
|||
e.preventDefault(); |
|||
const sel = w.getSelection(); |
|||
const div = document.createElement("div"); |
|||
div.appendChild(sel.getRangeAt(0).cloneContents()); |
|||
e.clipboardData.setData("text/plain", sel.toString().replace(RegExp(SOFTHYPHEN, "g"), "")); |
|||
e.clipboardData.setData("text/html", div.innerHTML.replace(RegExp(SOFTHYPHEN, "g"), "")); |
|||
}, |
|||
true |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Convert settings from H.setup-Object to Map |
|||
* This is a IIFE to keep complexity low. |
|||
*/ |
|||
((H) => { |
|||
/** |
|||
* Create a Map with a default Map behind the scenes. This mimics |
|||
* kind of a prototype chain of an object, but without the object- |
|||
* injection security risk. |
|||
* |
|||
* @param {Map} defaultsMap - A Map with default values |
|||
* @returns {Proxy} - A Proxy for the Map (dot-notation or get/set) |
|||
*/ |
|||
function createMapWithDefaults(defaultsMap) { |
|||
const userMap = new Map(); |
|||
|
|||
/** |
|||
* The get-trap: get the value from userMap or else from defaults |
|||
* @param {Sring} key - The key to retrieve the value for |
|||
* @returns {*} |
|||
*/ |
|||
function get(key) { |
|||
return (userMap.has(key)) |
|||
? userMap.get(key) |
|||
: defaultsMap.get(key); |
|||
} |
|||
|
|||
/** |
|||
* The set-trap: set the value to userMap and don't touch defaults |
|||
* @param {Sring} key - The key for the value |
|||
* @param {*} value - The value |
|||
* @returns {*} |
|||
*/ |
|||
function set(key, value) { |
|||
userMap.set(key, value); |
|||
} |
|||
return new Proxy(defaultsMap, { |
|||
"get": (_target, prop) => { |
|||
if (prop === "set") { |
|||
return set; |
|||
} |
|||
if (prop === "get") { |
|||
return get; |
|||
} |
|||
return get(prop); |
|||
}, |
|||
"ownKeys": () => { |
|||
return [ |
|||
...new Set( |
|||
[...defaultsMap.keys(), ...userMap.keys()] |
|||
) |
|||
]; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
const settings = createMapWithDefaults(new Map([ |
|||
["defaultLanguage", "en-us"], |
|||
[ |
|||
"dontHyphenate", (() => { |
|||
const list = "abbr,acronym,audio,br,button,code,img,input,kbd,label,math,option,pre,samp,script,style,sub,sup,svg,textarea,var,video"; |
|||
return createMapWithDefaults( |
|||
new Map(list.split(",").map((val) => { |
|||
return [val, true]; |
|||
})) |
|||
); |
|||
})() |
|||
], |
|||
["dontHyphenateClass", "donthyphenate"], |
|||
["exceptions", new Map()], |
|||
["keepAlive", true], |
|||
["normalize", false], |
|||
["processShadows", false], |
|||
["safeCopy", true], |
|||
["substitute", new Map()], |
|||
["timeout", 1000] |
|||
])); |
|||
o.entries(H.s).forEach(([key, value]) => { |
|||
switch (key) { |
|||
case "selectors": |
|||
// Set settings.selectors to array of selectors
|
|||
settings.set("selectors", o.keys(value)); |
|||
|
|||
/* |
|||
* For each selector add a property to settings with |
|||
* selector specific settings |
|||
*/ |
|||
o.entries(value).forEach(([sel, selSettings]) => { |
|||
const selectorSettings = createMapWithDefaults(new Map([ |
|||
["compound", "hyphen"], |
|||
["hyphen", SOFTHYPHEN], |
|||
["leftmin", 0], |
|||
["leftminPerLang", 0], |
|||
["minWordLength", 6], |
|||
["mixedCase", true], |
|||
["orphanControl", 1], |
|||
["rightmin", 0], |
|||
["rightminPerLang", 0] |
|||
])); |
|||
o.entries(selSettings).forEach( |
|||
([selSetting, setVal]) => { |
|||
if (typeof setVal === "object") { |
|||
selectorSettings.set( |
|||
selSetting, |
|||
new Map(o.entries(setVal)) |
|||
); |
|||
} else { |
|||
selectorSettings.set(selSetting, setVal); |
|||
} |
|||
} |
|||
); |
|||
settings.set(sel, selectorSettings); |
|||
}); |
|||
break; |
|||
case "dontHyphenate": |
|||
case "exceptions": |
|||
o.entries(value).forEach(([k, v]) => { |
|||
settings.get(key).set(k, v); |
|||
}); |
|||
break; |
|||
case "substitute": |
|||
o.entries(value).forEach(([lang, subst]) => { |
|||
settings.substitute.set( |
|||
lang, |
|||
new Map(o.entries(subst)) |
|||
); |
|||
}); |
|||
break; |
|||
default: |
|||
settings.set(key, value); |
|||
} |
|||
}); |
|||
H.c = settings; |
|||
})(Hyphenopoly); |
|||
|
|||
((H) => { |
|||
const C = H.c; |
|||
let mainLanguage = null; |
|||
|
|||
event.fire( |
|||
"hyphenopolyStart", |
|||
{ |
|||
"msg": "hyphenopolyStart" |
|||
} |
|||
); |
|||
|
|||
/** |
|||
* 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) { |
|||
event.fire( |
|||
"hyphenopolyEnd", |
|||
{ |
|||
"msg": "hyphenopolyEnd" |
|||
} |
|||
); |
|||
if (!C.keepAlive) { |
|||
window.Hyphenopoly = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
add, |
|||
counter, |
|||
list, |
|||
rem |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Get language of element by searching its parents or fallback |
|||
* @param {Object} el The element |
|||
* @param {string} parentLang Lang of parent if available |
|||
* @param {boolean} fallback Will falback to mainlanguage |
|||
* @returns {string|null} The language or null |
|||
*/ |
|||
function getLang(el, parentLang = "", fallback = true) { |
|||
// Find closest el with lang attr not empty
|
|||
el = el.closest("[lang]:not([lang=''])"); |
|||
if (el && el.lang) { |
|||
return el.lang.toLowerCase(); |
|||
} |
|||
if (parentLang) { |
|||
return parentLang; |
|||
} |
|||
return (fallback) |
|||
? mainLanguage |
|||
: null; |
|||
} |
|||
|
|||
/** |
|||
* Collect elements that have a selector defined in C.selectors |
|||
* and add them to elements. |
|||
* @param {Object} [parent = null] The start point element |
|||
* @param {string} [selector = null] The selector matching the parent |
|||
* @returns {Object} elements-object |
|||
*/ |
|||
function collectElements(parent = null, selector = null) { |
|||
const elements = makeElementCollection(); |
|||
|
|||
const dontHyphenateSelector = (() => { |
|||
let s = "." + C.dontHyphenateClass; |
|||
o.getOwnPropertyNames(C.dontHyphenate).forEach((tag) => { |
|||
if (C.dontHyphenate.get(tag)) { |
|||
s += "," + tag; |
|||
} |
|||
}); |
|||
return s; |
|||
})(); |
|||
const matchingSelectors = C.selectors.join(",") + "," + dontHyphenateSelector; |
|||
|
|||
/** |
|||
* 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 parent 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 = false) { |
|||
const eLang = getLang(el, pLang); |
|||
const langDef = H.cf.langs.get(eLang); |
|||
if (langDef === "H9Y") { |
|||
elements.add(el, eLang, sel); |
|||
if (!isChild && C.safeCopy) { |
|||
registerOnCopy(el); |
|||
} |
|||
} else if (!langDef && eLang !== "zxx") { |
|||
event.fire( |
|||
"error", |
|||
Error(`Element with '${eLang}' found, but '${eLang}.wasm' not loaded. Check language tags!`) |
|||
); |
|||
} |
|||
el.childNodes.forEach((n) => { |
|||
if (n.nodeType === 1 && !n.matches(matchingSelectors)) { |
|||
processElements(n, eLang, sel, true); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Searches the DOM for each sel |
|||
* @param {object} root The DOM root |
|||
* @returns {undefined} |
|||
*/ |
|||
function getElems(root) { |
|||
C.selectors.forEach((sel) => { |
|||
root.querySelectorAll(sel).forEach((n) => { |
|||
processElements(n, getLang(n), sel, false); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
if (parent === null) { |
|||
if (C.processShadows) { |
|||
w.document.querySelectorAll("*").forEach((m) => { |
|||
if (m.shadowRoot) { |
|||
getElems(m.shadowRoot); |
|||
} |
|||
}); |
|||
} |
|||
getElems(w.document); |
|||
} else { |
|||
processElements(parent, getLang(parent), selector); |
|||
} |
|||
return elements; |
|||
} |
|||
|
|||
const wordHyphenatorPool = new Map(); |
|||
|
|||
/** |
|||
* Factory for hyphenatorFunctions for a specific language and selector |
|||
* @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) { |
|||
const poolKey = lang + "-" + sel; |
|||
if (wordHyphenatorPool.has(poolKey)) { |
|||
return wordHyphenatorPool.get(poolKey); |
|||
} |
|||
|
|||
const selSettings = C.get(sel); |
|||
lo.cache.set(sel, new Map()); |
|||
|
|||
/** |
|||
* HyphenateFunction for non-compound words |
|||
* @param {string} word The word |
|||
* @returns {string} The hyphenated word |
|||
*/ |
|||
function hyphenateNormal(word) { |
|||
if (word.length > 61) { |
|||
event.fire( |
|||
"error", |
|||
Error("Found word longer than 61 characters") |
|||
); |
|||
} else if (!lo.reNotAlphabet.test(word)) { |
|||
return lo.hyphenate( |
|||
word, |
|||
selSettings.hyphen.charCodeAt(0), |
|||
selSettings.leftminPerLang.get(lang), |
|||
selSettings.rightminPerLang.get(lang) |
|||
); |
|||
} |
|||
return word; |
|||
} |
|||
|
|||
/** |
|||
* HyphenateFunction for compound words |
|||
* @param {string} word The word |
|||
* @returns {string} The hyphenated compound word |
|||
*/ |
|||
function hyphenateCompound(word) { |
|||
const zeroWidthSpace = "\u200B"; |
|||
let parts = null; |
|||
let wordHyphenator = null; |
|||
if (selSettings.compound === "auto" || |
|||
selSettings.compound === "all") { |
|||
wordHyphenator = createWordHyphenator(lo, lang, sel); |
|||
parts = word.split("-").map((p) => { |
|||
if (p.length >= selSettings.minWordLength) { |
|||
return wordHyphenator(p); |
|||
} |
|||
return p; |
|||
}); |
|||
if (selSettings.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 [...s].map((c) => { |
|||
return (c === c.toLowerCase()); |
|||
}).some((v, i, a) => { |
|||
return (v !== a[0]); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 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 (!hw) { |
|||
if (lo.exc.has(word)) { |
|||
hw = lo.exc.get(word).replace( |
|||
/-/g, |
|||
selSettings.hyphen |
|||
); |
|||
} else if (!selSettings.mixedCase && isMixedCase(word)) { |
|||
hw = word; |
|||
} else if (word.indexOf("-") === -1) { |
|||
hw = hyphenateNormal(word); |
|||
} else { |
|||
hw = hyphenateCompound(word); |
|||
} |
|||
lo.cache.get(sel).set(word, hw); |
|||
} |
|||
return hw; |
|||
} |
|||
wordHyphenatorPool.set(poolKey, 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) { |
|||
if (orphanControllerPool.has(sel)) { |
|||
return orphanControllerPool.get(sel); |
|||
} |
|||
const selSettings = C.get(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 |
|||
) { |
|||
if (selSettings.orphanControl === 3 && leadingWhiteSpace === " ") { |
|||
// \u00A0 = no-break space (nbsp)
|
|||
leadingWhiteSpace = "\u00A0"; |
|||
} |
|||
return leadingWhiteSpace + lastWord.replace(RegExp(selSettings.hyphen, "g"), "") + trailingWhiteSpace; |
|||
} |
|||
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); |
|||
const selSettings = C.get(sel); |
|||
const minWordLength = selSettings.minWordLength; |
|||
|
|||
/* |
|||
* Transpiled RegExp of |
|||
* /[${alphabet}\p{Mn}Subset\p{Letter}\00AD-]{${minwordlength},}/gui |
|||
*/ |
|||
const reWord = RegExp( |
|||
`[${lo.alphabet}a-z\u0300-\u036F\u0483-\u0487\u00DF-\u00F6\u00F8-\u00FE\u0101\u0103\u0105\u0107\u0109\u010D\u010F\u0111\u0113\u0117\u0119\u011B\u011D\u011F\u0123\u0125\u012B\u012F\u0131\u0135\u0137\u013C\u013E\u0142\u0144\u0146\u0148\u014D\u0151\u0153\u0155\u0159\u015B\u015D\u015F\u0161\u0165\u016B\u016D\u016F\u0171\u0173\u017A\u017C\u017E\u017F\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC\u0219\u021B\u02BC\u0390\u03AC-\u03CE\u03D0\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF\u03F2\u0430-\u044F\u0451-\u045C\u045E\u045F\u0491\u04AF\u04E9\u0561-\u0585\u0587\u0905-\u090C\u090F\u0910\u0913-\u0928\u092A-\u0930\u0932\u0933\u0935-\u0939\u093D\u0960\u0961\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A85-\u0A8B\u0A8F\u0A90\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B60\u0B61\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60\u0D61\u0D7A-\u0D7F\u0E01-\u0E2E\u0E30\u0E32\u0E33\u0E40-\u0E45\u10D0-\u10F0\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1E0D\u1E37\u1E41\u1E43\u1E45\u1E47\u1E6D\u1F00-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB2-\u1FB4\u1FB6\u1FB7\u1FC2-\u1FC4\u1FC6\u1FC7\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2-\u1FE7\u1FF2-\u1FF4\u1FF6\u1FF7\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CC9\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\u00AD\u200B-\u200D-]{${minWordLength},}`, "gui" |
|||
); |
|||
|
|||
/** |
|||
* 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) { |
|||
if (C.normalize) { |
|||
text = text.normalize("NFC"); |
|||
} |
|||
let tn = text.replace( |
|||
reWord, |
|||
createWordHyphenator(lo, lang, sel) |
|||
); |
|||
if (selSettings.orphanControl !== 1) { |
|||
tn = tn.replace( |
|||
/(\u0020*)(\S+)(\s*)$/, |
|||
createOrphanController(sel) |
|||
); |
|||
} |
|||
return tn; |
|||
} |
|||
|
|||
/** |
|||
* Hyphenate element according to setting in sel |
|||
* @param {object} el - the HTMLElement to be hyphenated |
|||
* @returns {undefined} |
|||
*/ |
|||
function hyphenateElement(el) { |
|||
event.fire( |
|||
"beforeElementHyphenation", |
|||
{ |
|||
el, |
|||
lang |
|||
} |
|||
); |
|||
el.childNodes.forEach((n) => { |
|||
if ( |
|||
n.nodeType === 3 && |
|||
(/\S/).test(n.data) && |
|||
n.data.length >= minWordLength |
|||
) { |
|||
n.data = hyphenateText(n.data); |
|||
} |
|||
}); |
|||
H.res.els.counter[0] -= 1; |
|||
event.fire( |
|||
"afterElementHyphenation", |
|||
{ |
|||
el, |
|||
lang |
|||
} |
|||
); |
|||
} |
|||
let r = null; |
|||
if (typeof entity === "string") { |
|||
r = hyphenateText(entity); |
|||
} else if (entity instanceof HTMLElement) { |
|||
hyphenateElement(entity); |
|||
} |
|||
return r; |
|||
} |
|||
|
|||
/** |
|||
* Creates a language-specific string hyphenator |
|||
* @param {String} lang - The language this hyphenator hyphenates |
|||
*/ |
|||
function createStringHyphenator(lang) { |
|||
return ((entity, sel = ".hyphenate") => { |
|||
if (typeof entity !== "string") { |
|||
event.fire( |
|||
"error", |
|||
Error("This use of hyphenators is deprecated. See https://mnater.github.io/Hyphenopoly/Hyphenators.html") |
|||
); |
|||
} |
|||
return hyphenate(lang, sel, entity); |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Creates a polyglot HTML hyphenator |
|||
*/ |
|||
function createDOMHyphenator() { |
|||
return ((entity, sel = ".hyphenate") => { |
|||
collectElements(entity, sel).list.forEach((els, l) => { |
|||
els.forEach((elo) => { |
|||
hyphenate(l, elo.selector, elo.element); |
|||
}); |
|||
}); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
H.unhyphenate = () => { |
|||
H.res.els.list.forEach((els) => { |
|||
els.forEach((elo) => { |
|||
const n = elo.element.firstChild; |
|||
n.data = n.data.replace(RegExp(C[elo.selector].hyphen, "g"), ""); |
|||
}); |
|||
}); |
|||
return Promise.resolve(H.res.els); |
|||
}; |
|||
|
|||
/** |
|||
* Hyphenate all elements with a given language |
|||
* @param {string} lang The language |
|||
* @param {Array} elArr Array of elements |
|||
* @returns {undefined} |
|||
*/ |
|||
function hyphenateLangElements(lang, elements) { |
|||
const elArr = elements.list.get(lang); |
|||
if (elArr) { |
|||
elArr.forEach((elo) => { |
|||
hyphenate(lang, elo.selector, elo.element); |
|||
}); |
|||
} else { |
|||
event.fire( |
|||
"error", |
|||
Error(`Engine for language '${lang}' loaded, but no elements found.`) |
|||
); |
|||
} |
|||
if (elements.counter[0] === 0) { |
|||
w.clearTimeout(H.timeOutHandler); |
|||
if (C.hide !== -1) { |
|||
H.hide(0, null); |
|||
} |
|||
event.fire( |
|||
"hyphenopolyEnd", |
|||
{ |
|||
"msg": "hyphenopolyEnd" |
|||
} |
|||
); |
|||
if (!C.keepAlive) { |
|||
window.Hyphenopoly = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Convert the exceptions from user input to Map |
|||
* @param {string} lang – The language for which the Map is created |
|||
* @return {Map} |
|||
*/ |
|||
function createExceptionMap(lang) { |
|||
let exc = ""; |
|||
if (C.exceptions.has(lang)) { |
|||
exc = C.exceptions.get(lang); |
|||
} |
|||
if (C.exceptions.has("global")) { |
|||
if (exc === "") { |
|||
exc = C.exceptions.get("global"); |
|||
} else { |
|||
exc += ", " + C.exceptions.get("global"); |
|||
} |
|||
} |
|||
if (exc === "") { |
|||
return new Map(); |
|||
} |
|||
return new Map(exc.split(", ").map((e) => { |
|||
return [e.replace(/-/g, ""), e]; |
|||
})); |
|||
} |
|||
|
|||
/** |
|||
* 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, |
|||
patternLeftmin, |
|||
patternRightmin |
|||
) { |
|||
C.selectors.forEach((sel) => { |
|||
const selSettings = C.get(sel); |
|||
if (selSettings.leftminPerLang === 0) { |
|||
selSettings.set("leftminPerLang", new Map()); |
|||
} |
|||
if (selSettings.rightminPerLang === 0) { |
|||
selSettings.set("rightminPerLang", new Map()); |
|||
} |
|||
selSettings.leftminPerLang.set(lang, Math.max( |
|||
patternLeftmin, |
|||
selSettings.leftmin, |
|||
Number(selSettings.leftminPerLang.get(lang)) || 0 |
|||
)); |
|||
|
|||
selSettings.rightminPerLang.set(lang, Math.max( |
|||
patternRightmin, |
|||
selSettings.rightmin, |
|||
Number(selSettings.rightminPerLang.get(lang)) || 0 |
|||
)); |
|||
}); |
|||
if (!H.languages) { |
|||
H.languages = new Map(); |
|||
} |
|||
alphabet = alphabet.replace(/\\*-/g, "\\-"); |
|||
H.languages.set(lang, { |
|||
alphabet, |
|||
"cache": new Map(), |
|||
"exc": createExceptionMap(lang), |
|||
"hyphenate": hyphenateFunction, |
|||
"ready": true, |
|||
"reNotAlphabet": RegExp(`[^${alphabet}]`, "i") |
|||
}); |
|||
H.hy6ors.get(lang).resolve(createStringHyphenator(lang)); |
|||
event.fire( |
|||
"engineReady", |
|||
{ |
|||
lang |
|||
} |
|||
); |
|||
if (H.res.els) { |
|||
hyphenateLangElements(lang, H.res.els); |
|||
} |
|||
} |
|||
|
|||
const decode = (() => { |
|||
if (w.TextDecoder) { |
|||
const utf16ledecoder = new TextDecoder("utf-16le"); |
|||
return ((ui16) => { |
|||
return utf16ledecoder.decode(ui16); |
|||
}); |
|||
} |
|||
return ((ui16) => { |
|||
return String.fromCharCode.apply(null, ui16); |
|||
}); |
|||
})(); |
|||
|
|||
/** |
|||
* Setup env for hyphenateFunction |
|||
* @param {Object} baseData baseData |
|||
* @param {function} hyphenateFunc hyphenateFunction |
|||
* @returns {function} hyphenateFunction with closured environment |
|||
*/ |
|||
function encloseHyphenateFunction(baseData, hyphenateFunc) { |
|||
const wordStore = new Uint16Array(baseData.buf, baseData.wo, 64); |
|||
return ((word, hyphencc, leftmin, rightmin) => { |
|||
wordStore.set([ |
|||
95, |
|||
...[...word].map((c) => { |
|||
return c.charCodeAt(0); |
|||
}), |
|||
95, |
|||
0 |
|||
]); |
|||
const len = hyphenateFunc(leftmin, rightmin, hyphencc); |
|||
if (len > 0) { |
|||
word = decode( |
|||
new Uint16Array(baseData.buf, baseData.hw, len) |
|||
); |
|||
} |
|||
return word; |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Instantiate Wasm Engine |
|||
* @param {string} lang The language |
|||
* @returns {undefined} |
|||
*/ |
|||
function instantiateWasmEngine(heProm, lang) { |
|||
const wa = window.WebAssembly; |
|||
|
|||
/** |
|||
* Register character substitutions in the .wasm-hyphenEngine |
|||
* @param {number} alphalen - The length of the alphabet |
|||
* @param {object} exp - Export-object of the hyphenEngine |
|||
*/ |
|||
function registerSubstitutions(alphalen, exp) { |
|||
if (C.substitute.has(lang)) { |
|||
const subst = C.substitute.get(lang); |
|||
subst.forEach((substituer, substituted) => { |
|||
const substitutedU = substituted.toUpperCase(); |
|||
const substitutedUcc = (substitutedU === substituted) |
|||
? 0 |
|||
: substitutedU.charCodeAt(0); |
|||
alphalen = exp.subst( |
|||
substituted.charCodeAt(0), |
|||
substitutedUcc, |
|||
substituer.charCodeAt(0) |
|||
); |
|||
}); |
|||
} |
|||
return alphalen; |
|||
} |
|||
|
|||
/** |
|||
* Instantiate the hyphenEngine |
|||
* @param {object} res - The fetched ressource |
|||
*/ |
|||
function handleWasm(res) { |
|||
const exp = res.instance.exports; |
|||
let alphalen = exp.conv(); |
|||
alphalen = registerSubstitutions(alphalen, exp); |
|||
const baseData = { |
|||
/* eslint-disable multiline-ternary */ |
|||
"buf": exp.mem.buffer, |
|||
"hw": (wa.Global) ? exp.hwo.value : exp.hwo, |
|||
"lm": (wa.Global) ? exp.lmi.value : exp.lmi, |
|||
"rm": (wa.Global) ? exp.rmi.value : exp.rmi, |
|||
"wo": (wa.Global) ? exp.uwo.value : exp.uwo |
|||
/* eslint-enable multiline-ternary */ |
|||
}; |
|||
prepareLanguagesObj( |
|||
lang, |
|||
encloseHyphenateFunction( |
|||
baseData, |
|||
exp.hyphenate |
|||
), |
|||
decode(new Uint16Array(exp.mem.buffer, 1026, alphalen - 1)), |
|||
baseData.lm, |
|||
baseData.rm |
|||
); |
|||
} |
|||
heProm.w.then((response) => { |
|||
if (response.ok) { |
|||
let r2 = response; |
|||
if (heProm.c) { |
|||
r2 = response.clone(); |
|||
} |
|||
if ( |
|||
wa.instantiateStreaming && |
|||
(response.headers.get("Content-Type") === "application/wasm") |
|||
) { |
|||
return wa.instantiateStreaming(r2); |
|||
} |
|||
return r2.arrayBuffer().then((ab) => { |
|||
return wa.instantiate(ab); |
|||
}); |
|||
} |
|||
return Promise.reject(Error(`File ${lang}.wasm can't be loaded from ${H.paths.patterndir}`)); |
|||
}).then(handleWasm, (e) => { |
|||
event.fire("error", e); |
|||
H.res.els.rem(lang); |
|||
}); |
|||
} |
|||
|
|||
H.res.DOM.then(() => { |
|||
mainLanguage = getLang(w.document.documentElement, "", false); |
|||
if (!mainLanguage && C.defaultLanguage !== "") { |
|||
mainLanguage = C.defaultLanguage; |
|||
} |
|||
const elements = collectElements(); |
|||
H.res.els = elements; |
|||
elements.list.forEach((ignore, lang) => { |
|||
if (H.languages && |
|||
H.languages.has(lang) && |
|||
H.languages.get(lang).ready |
|||
) { |
|||
hyphenateLangElements(lang, elements); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
H.res.he.forEach((heProm, lang) => { |
|||
instantiateWasmEngine(heProm, lang); |
|||
}); |
|||
|
|||
Promise.all( |
|||
// Make sure all lang specific hyphenators and DOM are ready
|
|||
[...H.hy6ors.entries()]. |
|||
reduce((accumulator, value) => { |
|||
if (value[0] !== "HTML") { |
|||
return accumulator.concat(value[1]); |
|||
} |
|||
return accumulator; |
|||
}, []). |
|||
concat(H.res.DOM) |
|||
).then(() => { |
|||
H.hy6ors.get("HTML").resolve(createDOMHyphenator()); |
|||
}, (e) => { |
|||
event.fire("error", e); |
|||
}); |
|||
})(Hyphenopoly); |
|||
})(window, Object); |
@ -0,0 +1,349 @@ |
|||
/** |
|||
* @license Hyphenopoly_Loader 4.12.0 - client side hyphenation |
|||
* ©2021 Mathias Nater, Güttingen (mathiasnater at gmail dot com) |
|||
* https://github.com/mnater/Hyphenopoly
|
|||
* |
|||
* Released under the MIT license |
|||
* http://mnater.github.io/Hyphenopoly/LICENSE
|
|||
*/ |
|||
/* globals Hyphenopoly:readonly */ |
|||
((w, d, H, o) => { |
|||
"use strict"; |
|||
|
|||
/** |
|||
* Shortcut for new Map |
|||
* @param {any} init - initialiser for new Map |
|||
* @returns {Map} |
|||
*/ |
|||
const mp = (init) => { |
|||
return new Map(init); |
|||
}; |
|||
|
|||
/** |
|||
* Sets default properties for an Object |
|||
* @param {object} obj - The object to set defaults to |
|||
* @param {object} defaults - The defaults to set |
|||
* @returns {object} |
|||
*/ |
|||
const setDefaults = (obj, defaults) => { |
|||
if (obj) { |
|||
o.entries(defaults).forEach(([k, v]) => { |
|||
// eslint-disable-next-line security/detect-object-injection
|
|||
obj[k] = obj[k] || v; |
|||
}); |
|||
return obj; |
|||
} |
|||
return defaults; |
|||
}; |
|||
|
|||
const store = sessionStorage; |
|||
const scriptName = "Hyphenopoly_Loader.js"; |
|||
const lcRequire = mp(); |
|||
|
|||
const shortcuts = { |
|||
"ac": "appendChild", |
|||
"ce": "createElement", |
|||
"ct": "createTextNode" |
|||
}; |
|||
|
|||
/** |
|||
* Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously |
|||
* computed settings from sessionStorage or creating a template object. |
|||
* This is in an iife to keep complexity low. |
|||
*/ |
|||
(() => { |
|||
if (H.cacheFeatureTests && store.getItem(scriptName)) { |
|||
H.cf = JSON.parse(store.getItem(scriptName)); |
|||
H.cf.langs = mp(H.cf.langs); |
|||
} else { |
|||
H.cf = { |
|||
"langs": mp(), |
|||
"pf": false |
|||
}; |
|||
} |
|||
})(); |
|||
|
|||
/** |
|||
* Set H.paths and some H.s (setup) fields to defaults or |
|||
* overwrite with user settings. |
|||
* These is an iife to keep complexity low. |
|||
*/ |
|||
(() => { |
|||
const thisScript = d.currentScript.src; |
|||
const maindir = thisScript.slice(0, (thisScript.lastIndexOf("/") + 1)); |
|||
const patterndir = maindir + "patterns/"; |
|||
H.paths = setDefaults(H.paths, { |
|||
maindir, |
|||
patterndir |
|||
}); |
|||
|
|||
H.s = setDefaults(H.setup, { |
|||
"CORScredentials": "include", |
|||
"hide": "all", |
|||
"selectors": {".hyphenate": {}}, |
|||
"timeout": 1000 |
|||
}); |
|||
// Change mode string to mode int
|
|||
H.s.hide = ["all", "element", "text"].indexOf(H.s.hide); |
|||
})(); |
|||
|
|||
/** |
|||
* Copy required languages to local lcRequire (lowercaseRequire) and |
|||
* eventually fallbacks to local lcFallbacks (lowercaseFallbacks). |
|||
* This is in an iife to keep complexity low. |
|||
*/ |
|||
(() => { |
|||
const fallbacks = mp(o.entries(H.fallbacks || {})); |
|||
o.entries(H.require).forEach(([lang, wo]) => { |
|||
lcRequire.set(lang.toLowerCase(), { |
|||
"fn": fallbacks.get(lang) || lang, |
|||
wo |
|||
}); |
|||
}); |
|||
})(); |
|||
|
|||
/** |
|||
* Create deferred Promise |
|||
* |
|||
* Kudos to http://lea.verou.me/2016/12/resolve-promises-externally-with-
|
|||
* this-one-weird-trick/ |
|||
* @return {promise} |
|||
*/ |
|||
const defProm = () => { |
|||
let res = null; |
|||
let rej = null; |
|||
const promise = new Promise((resolve, reject) => { |
|||
res = resolve; |
|||
rej = reject; |
|||
}); |
|||
promise.resolve = res; |
|||
promise.reject = rej; |
|||
|
|||
return promise; |
|||
}; |
|||
|
|||
let stylesNode = null; |
|||
|
|||
/** |
|||
* Define function H.hide. |
|||
* This function hides (state = 1) or unhides (state = 0) |
|||
* the whole document (mode == 0) or |
|||
* each selected element (mode == 1) or |
|||
* text of each selected element (mode == 2) or |
|||
* nothing (mode == -1) |
|||
* @param {integer} state - State |
|||
* @param {integer} mode - Mode |
|||
*/ |
|||
H.hide = (state, mode) => { |
|||
if (state === 0) { |
|||
if (stylesNode) { |
|||
stylesNode.remove(); |
|||
} |
|||
} else { |
|||
const vis = "{visibility:hidden!important}"; |
|||
stylesNode = d[shortcuts.ce]("style"); |
|||
let myStyle = ""; |
|||
if (mode === 0) { |
|||
myStyle = "html" + vis; |
|||
} else { |
|||
o.keys(H.s.selectors).forEach((sel) => { |
|||
if (mode === 1) { |
|||
myStyle += sel + vis; |
|||
} else { |
|||
myStyle += sel + "{color:transparent!important}"; |
|||
} |
|||
}); |
|||
} |
|||
stylesNode[shortcuts.ac](d[shortcuts.ct](myStyle)); |
|||
d.head[shortcuts.ac](stylesNode); |
|||
} |
|||
}; |
|||
|
|||
const tester = (() => { |
|||
let fakeBody = null; |
|||
return { |
|||
|
|||
/** |
|||
* Append fakeBody with tests to document |
|||
* @returns {Object|null} The body element or null, if no tests |
|||
*/ |
|||
"ap": () => { |
|||
if (fakeBody) { |
|||
d.documentElement[shortcuts.ac](fakeBody); |
|||
return fakeBody; |
|||
} |
|||
return null; |
|||
}, |
|||
|
|||
/** |
|||
* Remove fakeBody |
|||
* @returns {undefined} |
|||
*/ |
|||
"cl": () => { |
|||
if (fakeBody) { |
|||
fakeBody.remove(); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* Create and append div with CSS-hyphenated word |
|||
* @param {string} lang Language |
|||
* @returns {undefined} |
|||
*/ |
|||
"cr": (lang) => { |
|||
if (H.cf.langs.has(lang)) { |
|||
return; |
|||
} |
|||
fakeBody = fakeBody || d[shortcuts.ce]("body"); |
|||
const testDiv = d[shortcuts.ce]("div"); |
|||
const ha = "hyphens:auto"; |
|||
testDiv.lang = lang; |
|||
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`; |
|||
testDiv[shortcuts.ac]( |
|||
d[shortcuts.ct](lcRequire.get(lang).wo.toLowerCase()) |
|||
); |
|||
fakeBody[shortcuts.ac](testDiv); |
|||
} |
|||
}; |
|||
})(); |
|||
|
|||
/** |
|||
* Checks if hyphens (ev.prefixed) is set to auto for the element. |
|||
* @param {Object} elm - the element |
|||
* @returns {Boolean} result of the check |
|||
*/ |
|||
const checkCSSHyphensSupport = (elmStyle) => { |
|||
const h = elmStyle.hyphens || |
|||
elmStyle.webkitHyphens || |
|||
elmStyle.msHyphens; |
|||
return (h === "auto"); |
|||
}; |
|||
|
|||
H.res = { |
|||
"he": mp() |
|||
}; |
|||
const fw = mp(); |
|||
|
|||
/** |
|||
* Load hyphenEngines |
|||
* |
|||
* Make sure each .wasm is loaded exactly once, even for fallbacks |
|||
* fw: fetched wasm (maps filename to language) |
|||
* he: hyphenEngines (maps lang to wasm and counter) |
|||
* c (counter) is needed in Hyphenopoly.js to decide |
|||
* if wasm needs to be cloned |
|||
* @param {string} lang The language |
|||
* @returns {undefined} |
|||
*/ |
|||
const loadhyphenEngine = (lang) => { |
|||
const filename = lcRequire.get(lang).fn + ".wasm"; |
|||
H.cf.pf = true; |
|||
H.cf.langs.set(lang, "H9Y"); |
|||
if (fw.has(filename)) { |
|||
const hyphenEngineWrapper = H.res.he.get(fw.get(filename)); |
|||
hyphenEngineWrapper.c = true; |
|||
H.res.he.set(lang, hyphenEngineWrapper); |
|||
} else { |
|||
H.res.he.set( |
|||
lang, |
|||
{ |
|||
"w": w.fetch(H.paths.patterndir + filename, {"credentials": H.s.CORScredentials}) |
|||
} |
|||
); |
|||
fw.set(filename, lang); |
|||
} |
|||
}; |
|||
lcRequire.forEach((value, lang) => { |
|||
if (value.wo === "FORCEHYPHENOPOLY" || H.cf.langs.get(lang) === "H9Y") { |
|||
loadhyphenEngine(lang); |
|||
} else { |
|||
tester.cr(lang); |
|||
} |
|||
}); |
|||
const testContainer = tester.ap(); |
|||
if (testContainer) { |
|||
testContainer.querySelectorAll("div").forEach((n) => { |
|||
if (checkCSSHyphensSupport(n.style) && n.offsetHeight > 12) { |
|||
H.cf.langs.set(n.lang, "CSS"); |
|||
} else { |
|||
loadhyphenEngine(n.lang); |
|||
} |
|||
}); |
|||
tester.cl(); |
|||
} |
|||
const he = H.handleEvent; |
|||
if (H.cf.pf) { |
|||
H.res.DOM = new Promise((res) => { |
|||
if (d.readyState === "loading") { |
|||
d.addEventListener( |
|||
"DOMContentLoaded", |
|||
res, |
|||
{ |
|||
"once": true, |
|||
"passive": true |
|||
} |
|||
); |
|||
} else { |
|||
res(); |
|||
} |
|||
}); |
|||
const hide = H.s.hide; |
|||
if (hide === 0) { |
|||
H.hide(1, 0); |
|||
} |
|||
if (hide !== -1) { |
|||
H.timeOutHandler = w.setTimeout(() => { |
|||
H.hide(0, null); |
|||
// eslint-disable-next-line no-console
|
|||
console.info(scriptName + " timed out."); |
|||
}, H.s.timeout); |
|||
} |
|||
H.res.DOM.then(() => { |
|||
if (hide > 0) { |
|||
H.hide(1, hide); |
|||
} |
|||
}); |
|||
// Load main script
|
|||
const script = d[shortcuts.ce]("script"); |
|||
script.src = H.paths.maindir + "Hyphenopoly.js"; |
|||
d.head[shortcuts.ac](script); |
|||
H.hy6ors = mp(); |
|||
H.cf.langs.forEach((langDef, lang) => { |
|||
if (langDef === "H9Y") { |
|||
H.hy6ors.set(lang, defProm()); |
|||
} |
|||
}); |
|||
H.hy6ors.set("HTML", defProm()); |
|||
H.hyphenators = new Proxy(H.hy6ors, { |
|||
"get": (target, key) => { |
|||
return target.get(key); |
|||
}, |
|||
"set": () => { |
|||
// Inhibit setting of hyphenators
|
|||
return true; |
|||
} |
|||
}); |
|||
(() => { |
|||
if (he && he.polyfill) { |
|||
he.polyfill(); |
|||
} |
|||
})(); |
|||
} else { |
|||
(() => { |
|||
if (he && he.tearDown) { |
|||
he.tearDown(); |
|||
} |
|||
w.Hyphenopoly = null; |
|||
})(); |
|||
} |
|||
(() => { |
|||
if (H.cacheFeatureTests) { |
|||
store.setItem(scriptName, JSON.stringify( |
|||
{ |
|||
"langs": [...H.cf.langs.entries()], |
|||
"pf": H.cf.pf |
|||
} |
|||
)); |
|||
} |
|||
})(); |
|||
})(window, document, Hyphenopoly, Object); |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@ |
|||
class hyphenopoly extends Paged.Handler { |
|||
constructor(chunker, polisher, caller) { |
|||
super(chunker, polisher, caller); |
|||
} |
|||
beforeParsed(content) { |
|||
|
|||
// add lang fr to all sections
|
|||
content.querySelectorAll("section").forEach(section => { |
|||
section.setAttribute("lang", "en-us") |
|||
}) |
|||
|
|||
Object.keys(Hyphenopoly.setup.selectors).forEach(sel => { |
|||
content.querySelectorAll(sel).forEach(elem => { |
|||
Hyphenopoly.hyphenators["HTML"].then((hyn) => { |
|||
hyn(elem, sel); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
Paged.registerHandlers(hyphenopoly); |
Loading…
Reference in new issue