manetta
3 years ago
8 changed files with 1473 additions and 109697 deletions
@ -0,0 +1,13 @@ |
|||
./command-line/.etherpump/ |
|||
./command-line/Unfolded* |
|||
./command-line/images* |
|||
./command-line/venv* |
|||
./command-line/multi* |
|||
./snapshots/*.pdf |
|||
.etherpump/ |
|||
__pycache__/ |
|||
web-interface/.etherpump/ |
|||
web-interface/__pycache__/ |
|||
web-interface/static/Unfolded* |
|||
web-interface/static/images* |
|||
web-interface/venv* |
@ -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.
File diff suppressed because one or more lines are too long
@ -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