correction, ajout gitignore
This commit is contained in:
@@ -0,0 +1,871 @@
|
||||
/**
|
||||
* @license Hyphenopoly.module.js 3.1.1 - hyphenation for node
|
||||
* ©2018 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
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Use 'fs' in node environment and fallback to http if the module gets executed
|
||||
* in a browser environment (e.g. browserified)
|
||||
*/
|
||||
let loader = require("fs");
|
||||
|
||||
const {StringDecoder} = require("string_decoder");
|
||||
|
||||
const decode = (function makeDecoder() {
|
||||
const utf16ledecoder = new StringDecoder("utf-16le");
|
||||
return function dec(ui16) {
|
||||
return utf16ledecoder.write(Buffer.from(ui16));
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Create Object without standard Object-prototype
|
||||
* @returns {Object} empty object
|
||||
*/
|
||||
function empty() {
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
}
|
||||
|
||||
const H = empty();
|
||||
H.binaries = new Map();
|
||||
|
||||
H.supportedLanguages = [
|
||||
"af",
|
||||
"as",
|
||||
"be",
|
||||
"bg",
|
||||
"bn",
|
||||
"ca",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"el-monoton",
|
||||
"el-polyton",
|
||||
"en-gb",
|
||||
"en-us",
|
||||
"eo",
|
||||
"es",
|
||||
"et",
|
||||
"eu",
|
||||
"fi",
|
||||
"fr",
|
||||
"fur",
|
||||
"ga",
|
||||
"gl",
|
||||
"grc",
|
||||
"gu",
|
||||
"hi",
|
||||
"hr",
|
||||
"hsb",
|
||||
"hu",
|
||||
"hy",
|
||||
"ia",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"ka",
|
||||
"kmr",
|
||||
"kn",
|
||||
"la-x-liturgic",
|
||||
"la",
|
||||
"lt",
|
||||
"lv",
|
||||
"ml",
|
||||
"mn-cyrl",
|
||||
"mr",
|
||||
"mul-ethi",
|
||||
"nb-no",
|
||||
"nl",
|
||||
"nn",
|
||||
"oc",
|
||||
"or",
|
||||
"pa",
|
||||
"pl",
|
||||
"pms",
|
||||
"pt",
|
||||
"rm",
|
||||
"ro",
|
||||
"ru",
|
||||
"sh-cyrl",
|
||||
"sh-latn",
|
||||
"sk",
|
||||
"sl",
|
||||
"sr-cyrl",
|
||||
"sv",
|
||||
"ta",
|
||||
"te",
|
||||
"th",
|
||||
"tk",
|
||||
"tr",
|
||||
"uk",
|
||||
"zh-latn-pinyin"
|
||||
];
|
||||
|
||||
/**
|
||||
* Read a file and call callback
|
||||
* Use "fs" (node) or "http" (browser)
|
||||
* @param {string} file - the filename
|
||||
* @param {function} cb - callback function with args (error, data)
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function readFile(file, cb, sync) {
|
||||
if (H.c.loader === "fs") {
|
||||
/* eslint-disable security/detect-non-literal-fs-filename */
|
||||
if (sync) {
|
||||
// eslint-disable-next-line no-sync
|
||||
return loader.readFileSync(file);
|
||||
}
|
||||
loader.readFile(file, cb);
|
||||
/* eslint-enable security/detect-non-literal-fs-filename */
|
||||
} else {
|
||||
loader.get(file, function onData(res) {
|
||||
const rawData = [];
|
||||
res.on("data", function onChunk(chunk) {
|
||||
rawData.push(chunk);
|
||||
});
|
||||
res.on("end", function onEnd() {
|
||||
cb(null, Buffer.concat(rawData));
|
||||
});
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a wasm file, dispatch "engineLoaded" on success
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function loadWasm() {
|
||||
if (H.c.sync) {
|
||||
/* eslint-disable security/detect-non-literal-fs-filename */
|
||||
const data = readFile(`${H.c.paths.maindir}hyphenEngine.wasm`, null, true);
|
||||
/* eslint-enable security/detect-non-literal-fs-filename */
|
||||
H.binaries.set("hyphenEngine", new Uint8Array(data).buffer);
|
||||
H.events.dispatch("engineLoaded");
|
||||
} else {
|
||||
readFile(
|
||||
`${H.c.paths.maindir}hyphenEngine.wasm`,
|
||||
function cb(err, data) {
|
||||
if (err) {
|
||||
H.events.dispatch("error", {
|
||||
"key": "hyphenEngine",
|
||||
"msg": `${H.c.paths.maindir}hyphenEngine.wasm not found.`
|
||||
});
|
||||
} else {
|
||||
H.binaries.set("hyphenEngine", new Uint8Array(data).buffer);
|
||||
H.events.dispatch("engineLoaded");
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a hpb file, dispatch "hpbLoaded" on success
|
||||
* @param {string} lang - The language
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function loadHpb(lang) {
|
||||
if (H.c.sync) {
|
||||
const data = readFile(`${H.c.paths.patterndir}${lang}.hpb`, null, true);
|
||||
H.binaries.set(lang, new Uint8Array(data).buffer);
|
||||
H.events.dispatch("hpbLoaded", {"msg": lang});
|
||||
} else {
|
||||
readFile(
|
||||
`${H.c.paths.patterndir}${lang}.hpb`,
|
||||
function cb(err, data) {
|
||||
if (err) {
|
||||
H.events.dispatch("error", {
|
||||
"key": lang,
|
||||
"msg": `${H.c.paths.patterndir}${lang}.hpb not found.`
|
||||
});
|
||||
} else {
|
||||
H.binaries.set(lang, new Uint8Array(data).buffer);
|
||||
H.events.dispatch("hpbLoaded", {"msg": lang});
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate heap size for wasm
|
||||
* wasm page size: 65536 = 64 Ki
|
||||
* @param {number} targetSize The targetet Size
|
||||
* @returns {number} The necessary heap size
|
||||
*/
|
||||
function calculateHeapSize(targetSize) {
|
||||
return Math.ceil(targetSize / 65536) * 65536;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert exceptions to Map
|
||||
* @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 created lang object
|
||||
*/
|
||||
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 a language object (lo) and dispatch "engineReady"
|
||||
* @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 */
|
||||
/* eslint-disable security/detect-non-literal-regexp */
|
||||
lo.genRegExp = new RegExp(`[\\w${alphabet}${String.fromCharCode(8204)}-]{${H.c.minWordLength},}`, "gi");
|
||||
/* eslint-enable security/detect-non-literal-regexp */
|
||||
lo.leftmin = leftmin;
|
||||
lo.rightmin = rightmin;
|
||||
lo.hyphenateFunction = hyphenateFunction;
|
||||
lo.engineReady = true;
|
||||
}
|
||||
H.events.dispatch("engineReady", {"msg": lang});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = baseData.wasmMemory.buffer;
|
||||
const wordOffset = baseData.wo;
|
||||
const wordStore = (new Uint16Array(heapBuffer)).subarray(
|
||||
wordOffset >> 1,
|
||||
(wordOffset >> 1) + 64
|
||||
);
|
||||
const defLeftmin = baseData.lm;
|
||||
const defRightmin = baseData.rm;
|
||||
const hyphenatedWordStore = (new Uint16Array(heapBuffer)).subarray(
|
||||
baseData.hw >> 1,
|
||||
(baseData.hw >> 1) + 128
|
||||
);
|
||||
/* eslint-enable no-bitwise */
|
||||
|
||||
/**
|
||||
* The hyphenateFunction that encloses the env above
|
||||
* Copies the word to wasm-Memory, calls wasm.hyphenateFunc and reads
|
||||
* the hyphenated word from wasm-Memory (eventually replacing hyphenchar)
|
||||
* @param {String} word - the word that has to be hyphenated
|
||||
* @param {String} hyphenchar – the hyphenate character
|
||||
* @param {Number} leftmin – min number of chars to remain on line
|
||||
* @param {Number} rightmin – min number of chars to go to new line
|
||||
* @returns {String} the hyphenated word
|
||||
*/
|
||||
wordStore[0] = 95;
|
||||
return function enclHyphenate(word, hyphenchar, leftmin, rightmin) {
|
||||
let i = 0;
|
||||
let cc = word.charCodeAt(i);
|
||||
leftmin = leftmin || defLeftmin;
|
||||
rightmin = rightmin || defRightmin;
|
||||
while (cc) {
|
||||
i += 1;
|
||||
// eslint-disable-next-line security/detect-object-injection
|
||||
wordStore[i] = cc;
|
||||
cc = word.charCodeAt(i);
|
||||
}
|
||||
wordStore[i + 1] = 95;
|
||||
wordStore[i + 2] = 0;
|
||||
if (hyphenateFunc(leftmin, rightmin) === 1) {
|
||||
word = String.fromCharCode.apply(
|
||||
null,
|
||||
hyphenatedWordStore.subarray(
|
||||
1,
|
||||
hyphenatedWordStore[0] + 1
|
||||
)
|
||||
);
|
||||
if (hyphenchar !== "\u00AD") {
|
||||
word = word.replace(/\u00AD/g, hyphenchar);
|
||||
}
|
||||
}
|
||||
return word;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Wasm Engine, then compute the pattern trie and
|
||||
* call prepareLanguagesObj.
|
||||
* @param {string} lang The language
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function instantiateWasmEngine(lang) {
|
||||
const baseData = calculateBaseData(H.binaries.get(lang));
|
||||
const wasmMemory = new WebAssembly.Memory({
|
||||
"initial": baseData.hs / 65536,
|
||||
"maximum": 256
|
||||
});
|
||||
const ui32wasmMemory = new Uint32Array(wasmMemory.buffer);
|
||||
ui32wasmMemory.set(
|
||||
new Uint32Array(H.binaries.get(lang)),
|
||||
// eslint-disable-next-line no-bitwise
|
||||
baseData.ho >> 2
|
||||
);
|
||||
baseData.wasmMemory = wasmMemory;
|
||||
const importObj = {
|
||||
"env": {
|
||||
"memory": baseData.wasmMemory,
|
||||
"memoryBase": 0
|
||||
},
|
||||
"x": baseData
|
||||
};
|
||||
if (H.c.sync) {
|
||||
const heInstance = new WebAssembly.Instance(
|
||||
new WebAssembly.Module(H.binaries.get("hyphenEngine")),
|
||||
importObj
|
||||
);
|
||||
heInstance.exports.convert();
|
||||
prepareLanguagesObj(
|
||||
lang,
|
||||
encloseHyphenateFunction(
|
||||
baseData,
|
||||
heInstance.exports.hyphenate
|
||||
),
|
||||
decode(
|
||||
(new Uint8Array(wasmMemory.buffer)).
|
||||
subarray(768, 1280)
|
||||
),
|
||||
baseData.lm,
|
||||
baseData.rm
|
||||
);
|
||||
} else {
|
||||
WebAssembly.instantiate(H.binaries.get("hyphenEngine"), importObj).then(
|
||||
function runWasm(result) {
|
||||
result.instance.exports.convert();
|
||||
prepareLanguagesObj(
|
||||
lang,
|
||||
encloseHyphenateFunction(
|
||||
baseData,
|
||||
result.instance.exports.hyphenate
|
||||
),
|
||||
decode(
|
||||
(new Uint8Array(wasmMemory.buffer)).
|
||||
subarray(768, 1280)
|
||||
),
|
||||
baseData.lm,
|
||||
baseData.rm
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let engineInstantiator = null;
|
||||
const hpb = [];
|
||||
|
||||
/**
|
||||
* Instantiate hyphenEngines for languages
|
||||
* @param {string} lang The language
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function prepare(lang) {
|
||||
if (lang === "*") {
|
||||
engineInstantiator = instantiateWasmEngine;
|
||||
hpb.forEach(function eachHbp(hpbLang) {
|
||||
engineInstantiator(hpbLang);
|
||||
});
|
||||
} else if (engineInstantiator) {
|
||||
engineInstantiator(lang);
|
||||
} else {
|
||||
hpb.push(lang);
|
||||
}
|
||||
}
|
||||
|
||||
const wordHyphenatorPool = new Map();
|
||||
|
||||
/**
|
||||
* Factory for hyphenatorFunctions for a specific language and class
|
||||
* @param {Object} lo Language-Object
|
||||
* @param {string} lang The language
|
||||
* @returns {function} The hyphenate function
|
||||
*/
|
||||
function createWordHyphenator(lo, lang) {
|
||||
/**
|
||||
* 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 (H.c.compound === "auto" ||
|
||||
H.c.compound === "all") {
|
||||
wordHyphenator = createWordHyphenator(lo, lang);
|
||||
parts = word.split("-").map(function h7eParts(p) {
|
||||
if (p.length >= H.c.minWordLength) {
|
||||
return wordHyphenator(p);
|
||||
}
|
||||
return p;
|
||||
});
|
||||
if (H.c.compound === "auto") {
|
||||
word = parts.join("-");
|
||||
} else {
|
||||
word = parts.join("-" + zeroWidthSpace);
|
||||
}
|
||||
} else {
|
||||
word = word.replace("-", "-" + zeroWidthSpace);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
/**
|
||||
* HyphenateFunction for words (compound or not)
|
||||
* @param {string} word The word
|
||||
* @returns {string} The hyphenated word
|
||||
*/
|
||||
function hyphenator(word) {
|
||||
let hw = lo.cache.get(word);
|
||||
if (!hw) {
|
||||
if (lo.exceptions.has(word)) {
|
||||
hw = lo.exceptions.get(word).replace(
|
||||
/-/g,
|
||||
H.c.hyphen
|
||||
);
|
||||
} else if (word.indexOf("-") === -1) {
|
||||
if (word.length > 61) {
|
||||
H.events.dispatch("error", {"msg": "found word longer than 61 characters"});
|
||||
hw = word;
|
||||
} else {
|
||||
hw = lo.hyphenateFunction(
|
||||
word,
|
||||
H.c.hyphen,
|
||||
H.c.leftmin,
|
||||
H.c.rightmin
|
||||
);
|
||||
}
|
||||
} else {
|
||||
hw = hyphenateCompound(word);
|
||||
}
|
||||
lo.cache.set(word, hw);
|
||||
}
|
||||
return hw;
|
||||
}
|
||||
wordHyphenatorPool.set(lang, hyphenator);
|
||||
return hyphenator;
|
||||
}
|
||||
|
||||
const orphanController = (function createOrphanController() {
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
let h = H.c.hyphen;
|
||||
if (".\\+*?[^]$(){}=!<>|:-".indexOf(H.c.hyphen) !== -1) {
|
||||
h = `\\${H.c.hyphen}`;
|
||||
}
|
||||
if (H.c.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 */
|
||||
}
|
||||
return controlOrphans;
|
||||
}());
|
||||
|
||||
/**
|
||||
* Encloses hyphenateTextFunction
|
||||
* @param {string} lang - The language
|
||||
* @return {function} The hyphenateText-function
|
||||
*/
|
||||
function createTextHyphenator(lang) {
|
||||
const lo = H.languages.get(lang);
|
||||
const wordHyphenator = (wordHyphenatorPool.has(lang))
|
||||
? wordHyphenatorPool.get(lang)
|
||||
: createWordHyphenator(lo, lang);
|
||||
|
||||
/**
|
||||
* Hyphenate text
|
||||
* @param {string} text The text
|
||||
* @param {string} lang The language of the text
|
||||
* @returns {string} Hyphenated text
|
||||
*/
|
||||
return function hyphenateText(text) {
|
||||
if (H.c.normalize) {
|
||||
text = text.normalize("NFC");
|
||||
}
|
||||
let tn = text.replace(lo.genRegExp, wordHyphenator);
|
||||
if (H.c.orphanControl !== 1) {
|
||||
tn = tn.replace(
|
||||
// eslint-disable-next-line prefer-named-capture-group
|
||||
/(\u0020*)(\S+)(\s*)$/,
|
||||
orphanController
|
||||
);
|
||||
}
|
||||
return tn;
|
||||
};
|
||||
}
|
||||
|
||||
(function setupEvents() {
|
||||
// Events known to the system
|
||||
const definedEvents = new Map();
|
||||
|
||||
/**
|
||||
* Create Event Object
|
||||
* @param {string} name The Name of the event
|
||||
* @param {function|null} defFunc The default method of the event
|
||||
* @param {boolean} cancellable Is the default cancellable
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function define(name, defFunc, cancellable) {
|
||||
definedEvents.set(name, {
|
||||
"cancellable": cancellable,
|
||||
"default": defFunc,
|
||||
"register": []
|
||||
});
|
||||
}
|
||||
|
||||
define(
|
||||
"error",
|
||||
function def(e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e.msg);
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
define(
|
||||
"engineLoaded",
|
||||
function def() {
|
||||
prepare("*");
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
define(
|
||||
"hpbLoaded",
|
||||
function def(e) {
|
||||
prepare(e.msg);
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
define(
|
||||
"engineReady",
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
/**
|
||||
* Dispatch error <name> with arguments <data>
|
||||
* @param {string} name The name of the event
|
||||
* @param {Object|undefined} data Data of the event
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function dispatch(name, data) {
|
||||
if (!data) {
|
||||
data = empty();
|
||||
}
|
||||
data.defaultPrevented = false;
|
||||
data.preventDefault = function preventDefault() {
|
||||
if (definedEvents.get(name).cancellable) {
|
||||
data.defaultPrevented = true;
|
||||
}
|
||||
};
|
||||
definedEvents.get(name).register.forEach(function call(currentHandler) {
|
||||
currentHandler(data);
|
||||
});
|
||||
if (!data.defaultPrevented && definedEvents.get(name).default) {
|
||||
definedEvents.get(name).default(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add EventListender <handler> to event <name>
|
||||
* @param {string} name The name of the event
|
||||
* @param {function} handler Function to register
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function addListener(name, handler) {
|
||||
if (definedEvents.has(name)) {
|
||||
definedEvents.get(name).register.push(handler);
|
||||
} else {
|
||||
H.events.dispatch(
|
||||
"error",
|
||||
{"msg": `unknown Event "${name}" discarded`}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
H.events = empty();
|
||||
H.events.dispatch = dispatch;
|
||||
H.events.define = define;
|
||||
H.events.addListener = addListener;
|
||||
}());
|
||||
|
||||
H.config = function config(userConfig) {
|
||||
const defaults = Object.create(null, {
|
||||
"compound": setProp("hyphen", 2),
|
||||
"exceptions": setProp(empty(), 2),
|
||||
"hyphen": setProp(String.fromCharCode(173), 2),
|
||||
"leftmin": setProp(0, 2),
|
||||
"loader": setProp("fs", 2),
|
||||
"minWordLength": setProp(6, 2),
|
||||
"normalize": setProp(false, 2),
|
||||
"orphanControl": setProp(1, 2),
|
||||
"paths": setProp(Object.create(null, {
|
||||
"maindir": setProp(`${__dirname}/`, 2),
|
||||
"patterndir": setProp(`${__dirname}/patterns/`, 2)
|
||||
}), 2),
|
||||
"require": setProp([], 2),
|
||||
"rightmin": setProp(0, 2),
|
||||
"sync": setProp(false, 2)
|
||||
});
|
||||
const settings = Object.create(defaults);
|
||||
Object.keys(userConfig).forEach(function each(key) {
|
||||
Object.defineProperty(
|
||||
settings,
|
||||
key,
|
||||
/* eslint-disable security/detect-object-injection */
|
||||
setProp(userConfig[key], 3)
|
||||
/* eslint-enable security/detect-object-injection */
|
||||
);
|
||||
});
|
||||
H.c = settings;
|
||||
if (H.c.loader === "https" || H.c.loader === "http") {
|
||||
/* eslint-disable global-require, security/detect-non-literal-require */
|
||||
loader = require(H.c.loader);
|
||||
/* eslint-enable global-require, security/detect-non-literal-require */
|
||||
}
|
||||
if (H.c.handleEvent) {
|
||||
Object.keys(H.c.handleEvent).forEach(function add(name) {
|
||||
/* eslint-disable security/detect-object-injection */
|
||||
H.events.addListener(name, H.c.handleEvent[name]);
|
||||
/* eslint-enable security/detect-object-injection */
|
||||
});
|
||||
}
|
||||
const result = new Map();
|
||||
if (H.c.require.length === 0) {
|
||||
H.events.dispatch(
|
||||
"error",
|
||||
{"msg": "No language has been required. Setup config according to documenation."}
|
||||
);
|
||||
}
|
||||
H.c.require.forEach(function each(lang) {
|
||||
loadHpb(lang);
|
||||
if (H.c.sync) {
|
||||
H.events.addListener("engineReady", function handler(e) {
|
||||
if (e.msg === lang) {
|
||||
result.set(lang, createTextHyphenator(lang));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const prom = new Promise(function pro(resolve, reject) {
|
||||
H.events.addListener("engineReady", function handler(e) {
|
||||
if (e.msg === lang) {
|
||||
resolve(createTextHyphenator(lang));
|
||||
}
|
||||
});
|
||||
H.events.addListener("error", function handler(e) {
|
||||
e.preventDefault();
|
||||
if (e.key === lang || e.key === "hyphenEngine") {
|
||||
reject(e.msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
result.set(lang, prom);
|
||||
}
|
||||
});
|
||||
loadWasm();
|
||||
return (result.size === 1)
|
||||
? result.get(H.c.require[0])
|
||||
: result;
|
||||
};
|
||||
|
||||
module.exports = H;
|
||||
Reference in New Issue
Block a user