diff --git a/CHANGELOG.md b/CHANGELOG.md index fb46006e..e173e09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,17 @@ # Version History +## Version 2.7.0 (Feb 01, 2019) +* implement sync mode for node module (issue #43) +* implement sync mode for hyphenopoly.module.js (issue #47) +* fixed issue with Hyphenopoly.config sometimes not resolving (issue #52) +* enable viewport dependent hyphenation (isse #53) +* get good grades in codacy.com + ## Version 2.6.1 (Jan 09, 2018) -* dontHyphenateClass is configurable (issue #48) -* fixed issue with StringDecoder in older node versions (issue #45) -* small refactorings for smaller codesize -* fixed a StateError in IE 11 +* dontHyphenateClass is configurable (issue #48) +* fixed issue with StringDecoder in older node versions (issue #45) +* small refactorings for smaller codesize +* fixed a StateError in IE 11 ## Version 2.6.0 (Dec 01, 2018) * improve hiding of elements while hyphenating (issue #40) diff --git a/Hyphenopoly.js b/Hyphenopoly.js index 0f026b8a..227d1c07 100644 --- a/Hyphenopoly.js +++ b/Hyphenopoly.js @@ -7,7 +7,7 @@ * http://mnater.github.io/Hyphenopoly/LICENSE */ -/* globals asmHyphenEngine */ +/* globals asmHyphenEngine, Hyphenopoly */ (function mainWrapper(w) { "use strict"; @@ -73,7 +73,9 @@ const r = empty(); const list = "abbr,acronym,audio,br,button,code,img,input,kbd,label,math,option,pre,samp,script,style,sub,sup,svg,textarea,var,video"; list.split(",").forEach(function add(value) { + /* eslint-disable security/detect-object-injection */ r[value] = true; + /* eslint-enable security/detect-object-injection */ }); return r; }()), 2), @@ -107,11 +109,13 @@ ); selectors.forEach(function copySelectors(sel) { const tmp = empty(); + /* eslint-disable security/detect-object-injection */ Object.keys(H.setup.selectors[sel]).forEach( function copySelectorSettings(k) { tmp[k] = setProp(H.setup.selectors[sel][k], 2); } ); + /* eslint-enable security/detect-object-injection */ Object.defineProperty( settings, sel, @@ -122,7 +126,9 @@ const tmp = empty(); Object.keys(H.setup.dontHyphenate).forEach( function copyTagNames(k) { + /* eslint-disable security/detect-object-injection */ tmp[k] = setProp(H.setup.dontHyphenate[k], 2); + /* eslint-enable security/detect-object-injection */ } ); Object.defineProperty( @@ -133,11 +139,13 @@ ) ); } else { + /* eslint-disable security/detect-object-injection */ Object.defineProperty( settings, key, setProp(H.setup[key], 3) ); + /* eslint-enable security/detect-object-injection */ } }); H.c = settings; @@ -153,7 +161,7 @@ * @returns {Object} elements-object */ function makeElementCollection() { - const list = empty(); + const list = new Map(); /* * Counter counts the elements to be hyphenated. @@ -173,10 +181,10 @@ "element": el, "selector": sel }; - if (!list[lang]) { - list[lang] = []; + if (!list.has(lang)) { + list.set(lang, []); } - list[lang].push(elo); + list.get(lang).push(elo); counter[0] += 1; return elo; } @@ -187,8 +195,8 @@ * @returns {undefined} */ function each(fn) { - Object.keys(list).forEach(function forEachElem(k) { - fn(k, list[k]); + list.forEach(function eachElement(val, key) { + fn(key, val); }); } @@ -209,7 +217,9 @@ el.addEventListener("copy", function oncopy(e) { e.preventDefault(); const selectedText = window.getSelection().toString(); + /* eslint-disable security/detect-non-literal-regexp */ e.clipboardData.setData("text/plain", selectedText.replace(new RegExp(SOFTHYPHEN, "g"), "")); + /* eslint-enable security/detect-non-literal-regexp */ }, true); } @@ -270,14 +280,31 @@ let s = "." + H.c.dontHyphenateClass; let k = null; for (k in C.dontHyphenate) { + /* eslint-disable security/detect-object-injection */ if (C.dontHyphenate[k]) { s += ", " + k; } + /* eslint-enable security/detect-object-injection */ } return s; }()); const matchingSelectors = C.selectors.join(", ") + ", " + dontHyphenateSelector; + /** + * Get Language of Element or of one of its ancestors. + * @param {Object} el The element to scan + * @param {string} pLang The language of the parent element + * @returns {string} the language + */ + function getElementLanguage(el, pLang) { + if (el.lang && typeof el.lang === "string") { + return el.lang.toLowerCase(); + } else if (pLang && pLang !== "") { + return pLang.toLowerCase(); + } + return getLang(el, true); + } + /** * Recursively walk all elements in el, lending lang and selName * add them to elements if necessary. @@ -288,15 +315,9 @@ * @returns {undefined} */ function processElements(el, pLang, sel, isChild) { - let eLang = null; isChild = isChild || false; - if (el.lang && typeof el.lang === "string") { - eLang = el.lang.toLowerCase(); - } else if (pLang && pLang !== "") { - eLang = pLang.toLowerCase(); - } else { - eLang = getLang(el, true); - } + const eLang = getElementLanguage(el, pLang); + /* eslint-disable security/detect-object-injection */ if (H.clientFeat.langs[eLang] === "H9Y") { elements.add(el, eLang, sel); if (!isChild && C.safeCopy) { @@ -305,7 +326,7 @@ } else if (!H.clientFeat.langs[eLang]) { H.events.dispatch("error", {"msg": "Element with '" + eLang + "' found, but '" + eLang + ".hpb' not loaded. Check language tags!"}); } - + /* eslint-enable security/detect-object-injection */ const cn = el.childNodes; Array.prototype.forEach.call(cn, function eachChildNode(n) { if (n.nodeType === 1 && @@ -323,7 +344,7 @@ H.elementsReady = true; } - const wordHyphenatorPool = empty(); + const wordHyphenatorPool = new Map(); /** * Factory for hyphenatorFunctions for a specific language and class @@ -333,10 +354,10 @@ * @returns {function} The hyphenate function */ function createWordHyphenator(lo, lang, sel) { + /* eslint-disable-next-line security/detect-object-injection */ const classSettings = C[sel]; const hyphen = classSettings.hyphen; - - lo.cache[sel] = empty(); + lo.cache.set(sel, new Map()); /** * HyphenateFunction for compound words @@ -346,19 +367,17 @@ function hyphenateCompound(word) { const zeroWidthSpace = String.fromCharCode(8203); let parts = null; - let i = 0; let wordHyphenator = null; let hw = word; if (classSettings.compound === "auto" || classSettings.compound === "all") { - parts = word.split("-"); wordHyphenator = createWordHyphenator(lo, lang, sel); - while (i < parts.length) { - if (parts[i].length >= classSettings.minWordLength) { - parts[i] = wordHyphenator(parts[i]); + parts = word.split("-").map(function h7eParts(p) { + if (p.length >= classSettings.minWordLength) { + return wordHyphenator(p); } - i += 1; - } + return p; + }); if (classSettings.compound === "auto") { hw = parts.join("-"); } else { @@ -376,32 +395,39 @@ * @returns {string} The hyphenated word */ function hyphenator(word) { - let hw = lo.cache[sel][word]; + let hw = lo.cache.get(sel).get(word); if (!hw) { - if (lo.exceptions[word]) { - hw = lo.exceptions[word].replace( + if (lo.exceptions.has(word)) { + hw = lo.exceptions.get(word).replace( /-/g, classSettings.hyphen ); } else if (word.indexOf("-") === -1) { - hw = lo.hyphenateFunction( - word, - hyphen, - classSettings.leftminPerLang[lang], - classSettings.rightminPerLang[lang] - ); + if (word.length > 61) { + H.events.dispatch("error", {"msg": "found word longer than 61 characters"}); + hw = word; + } else { + /* eslint-disable security/detect-object-injection */ + hw = lo.hyphenateFunction( + word, + hyphen, + classSettings.leftminPerLang[lang], + classSettings.rightminPerLang[lang] + ); + /* eslint-enable security/detect-object-injection */ + } } else { hw = hyphenateCompound(word); } - lo.cache[sel][word] = hw; + lo.cache.get(sel).set(word, hw); } return hw; } - wordHyphenatorPool[lang + "-" + sel] = hyphenator; + wordHyphenatorPool.set(lang + "-" + sel, hyphenator); return hyphenator; } - const orphanControllerPool = empty(); + const orphanControllerPool = new Map(); /** * Factory for function that handles orphans @@ -423,7 +449,9 @@ lastWord, trailingWhiteSpace ) { + /* eslint-disable security/detect-object-injection */ const classSettings = C[sel]; + /* eslint-enable security/detect-object-injection */ let h = classSettings.hyphen; if (".\\+*?[^]$(){}=!<>|:-".indexOf(classSettings.hyphen) !== -1) { h = "\\" + classSettings.hyphen; @@ -431,9 +459,11 @@ if (classSettings.orphanControl === 3 && leadingWhiteSpace === " ") { leadingWhiteSpace = String.fromCharCode(160); } + /* eslint-disable security/detect-non-literal-regexp */ return leadingWhiteSpace + lastWord.replace(new RegExp(h, "g"), "") + trailingWhiteSpace; + /* eslint-enable security/detect-non-literal-regexp */ } - orphanControllerPool[sel] = controlOrphans; + orphanControllerPool.set(sel, controlOrphans); return controlOrphans; } @@ -445,19 +475,21 @@ * @returns {string | null} hyphenated str according to setting of sel */ function hyphenate(lang, sel, entity) { - const lo = H.languages[lang]; + const lo = H.languages.get(lang); + /* eslint-disable security/detect-object-injection */ const classSettings = C[sel]; + /* eslint-enable security/detect-object-injection */ const minWordLength = classSettings.minWordLength; const normalize = C.normalize && Boolean(String.prototype.normalize); const poolKey = lang + "-" + sel; - const wordHyphenator = (wordHyphenatorPool[poolKey]) - ? wordHyphenatorPool[poolKey] + const wordHyphenator = (wordHyphenatorPool.has(poolKey)) + ? wordHyphenatorPool.get(poolKey) : createWordHyphenator(lo, lang, sel); - const orphanController = (orphanControllerPool[sel]) - ? orphanControllerPool[sel] + const orphanController = (orphanControllerPool.has(sel)) + ? orphanControllerPool.get(sel) : createOrphanController(sel); - const re = lo.genRegExps[sel]; + const re = lo.genRegExps.get(sel); /** * Hyphenate text according to setting in sel @@ -521,6 +553,18 @@ }; }; + H.unhyphenate = function unhyphenate() { + elements.each(function eachLang(lang, els) { + els.forEach(function eachElem(elo) { + const n = elo.element.firstChild; + const h = C[elo.selector].hyphen; + /* eslint-disable security/detect-non-literal-regexp */ + n.data = n.data.replace(new RegExp(h, "g"), ""); + /* eslint-enable security/detect-non-literal-regexp */ + }); + }); + }; + /** * Hyphenate all elements with a given language * @param {string} lang The language @@ -546,14 +590,29 @@ * @returns {Object} Map of exceptions */ function convertExceptions(exc) { - const r = empty(); + const r = new Map(); exc.split(", ").forEach(function eachExc(e) { const key = e.replace(/-/g, ""); - r[key] = e; + r.set(key, e); }); return r; } + /** + * Create lang Object + * @param {string} lang The language + * @returns {Object} The newly + */ + function createLangObj(lang) { + if (!H.languages) { + H.languages = new Map(); + } + if (!H.languages.has(lang)) { + H.languages.set(lang, empty()); + } + return H.languages.get(lang); + } + /** * Setup lo * @param {string} lang The language @@ -571,15 +630,10 @@ rightmin ) { alphabet = alphabet.replace(/-/g, ""); - if (!H.languages) { - H.languages = empty(); - } - if (!H.languages[lang]) { - H.languages[lang] = empty(); - } - const lo = H.languages[lang]; + const lo = createLangObj(lang); if (!lo.engineReady) { - lo.cache = empty(); + 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; @@ -591,14 +645,17 @@ lo.exceptions = convertExceptions(H.c.exceptions[lang]); delete H.c.exceptions[lang]; } else { - lo.exceptions = empty(); + lo.exceptions = new Map(); } - lo.genRegExps = empty(); + /* eslint-enable security/detect-object-injection */ + lo.genRegExps = new Map(); lo.leftmin = leftmin; lo.rightmin = rightmin; lo.hyphenateFunction = hyphenateFunction; C.selectors.forEach(function eachSelector(sel) { + /* eslint-disable security/detect-object-injection */ const classSettings = C[sel]; + /* eslint-enable security/detect-object-injection */ if (classSettings.leftminPerLang === 0) { Object.defineProperty( classSettings, @@ -613,6 +670,7 @@ setProp(empty(), 2) ); } + /* eslint-disable security/detect-object-injection */ if (classSettings.leftminPerLang[lang]) { classSettings.leftminPerLang[lang] = Math.max( lo.leftmin, @@ -637,6 +695,7 @@ classSettings.rightmin ); } + /* eslint-enable security/detect-object-injection */ /* * Find words with characters from `alphabet` and @@ -646,7 +705,9 @@ * that follow a character that is not in the `alphabet`. * Word delimiters are not taken in account. */ - lo.genRegExps[sel] = new RegExp("[\\w" + alphabet + String.fromCharCode(8204) + "-]{" + classSettings.minWordLength + ",}", "gi"); + /* eslint-disable security/detect-non-literal-regexp */ + lo.genRegExps.set(sel, new RegExp("[\\w" + alphabet + String.fromCharCode(8204) + "-]{" + classSettings.minWordLength + ",}", "gi")); + /* eslint-enable security/detect-non-literal-regexp */ }); lo.engineReady = true; } @@ -807,10 +868,6 @@ return function enclHyphenate(word, hyphenchar, leftmin, rightmin) { let i = 0; const wordLength = word.length; - if (wordLength > 61) { - H.events.dispatch("error", {"msg": "found word longer than 61 characters"}); - return word; - } leftmin = leftmin || defLeftmin; rightmin = rightmin || defRightmin; wordStore[0] = wordLength + 2; @@ -837,15 +894,16 @@ * @returns {undefined} */ function instantiateWasmEngine(lang) { - Promise.all([H.binaries[lang], H.binaries.hyphenEngine]).then( + Promise.all([H.binaries.get(lang), H.binaries.get("hyphenEngine")]).then( function onAll(binaries) { const hpbBuf = binaries[0]; const baseData = calculateBaseData(hpbBuf); const wasmModule = binaries[1]; + const specMem = H.specMems.get(lang); const wasmMemory = ( - H.specMems[lang].buffer.byteLength >= baseData.heapSize + specMem.buffer.byteLength >= baseData.heapSize ) - ? H.specMems[lang] + ? specMem : new WebAssembly.Memory({ "initial": baseData.heapSize / 65536, "maximum": 256 @@ -891,12 +949,11 @@ * @returns {undefined} */ function instantiateAsmEngine(lang) { - const hpbBuf = H.binaries[lang]; + const hpbBuf = H.binaries.get(lang); const baseData = calculateBaseData(hpbBuf); - const heapBuffer = ( - H.specMems[lang].byteLength >= baseData.heapSize - ) - ? H.specMems[lang] + const specMem = H.specMems.get(lang); + const heapBuffer = (specMem.byteLength >= baseData.heapSize) + ? specMem : new ArrayBuffer(baseData.heapSize); const ui8Heap = new Uint8Array(heapBuffer); const ui8Patterns = new Uint8Array(hpbBuf); @@ -966,8 +1023,8 @@ function onElementsReady() { elements.each(function eachElem(lang, values) { if (H.languages && - H.languages[lang] && - H.languages[lang].engineReady + H.languages.has(lang) && + H.languages.get(lang).engineReady ) { hyphenateLangElements(lang, values); } @@ -996,7 +1053,7 @@ "engineReady", function onEngineReady(e) { if (H.elementsReady) { - hyphenateLangElements(e.msg, elements.list[e.msg]); + hyphenateLangElements(e.msg, elements.list.get(e.msg)); } }, false diff --git a/Hyphenopoly_Loader.js b/Hyphenopoly_Loader.js index 0937e4bc..24128657 100644 --- a/Hyphenopoly_Loader.js +++ b/Hyphenopoly_Loader.js @@ -7,6 +7,8 @@ * http://mnater.github.io/Hyphenopoly/LICENSE */ +/* global Hyphenopoly */ + (function H9YL() { "use strict"; const d = document; @@ -31,7 +33,7 @@ Object.keys(obj).forEach(fn); } - (function config() { + (function configFeat() { // Set H.clientFeat (either from sessionStorage or empty) if (H.cacheFeatureTests && sessionStorage.getItem("Hyphenopoly_Loader")) { H.clientFeat = JSON.parse(sessionStorage.getItem("Hyphenopoly_Loader")); @@ -42,23 +44,31 @@ "wasm": null }; } + }()); + (function configPaths() { // Set defaults for paths and setup H.dfltPaths = Object.create({ "maindir": "../Hyphenopoly/", "patterndir": "../Hyphenopoly/patterns/" }); - if (H.paths && H.paths.patterndir) { - H.dfltPaths.patterndir = H.paths.patterndir; - } - if (H.paths && H.paths.maindir) { - H.dfltPaths.maindir = H.paths.maindir; + if (H.paths) { + if (H.paths.patterndir) { + H.dfltPaths.patterndir = H.paths.patterndir; + } + if (H.paths.maindir) { + H.dfltPaths.maindir = H.paths.maindir; + } } - + }()); + (function configSetup() { if (H.setup) { H.setup.selectors = H.setup.selectors || {".hyphenate": {}}; if (H.setup.classnames) { + // Convert classnames to selectors eachKey(H.setup.classnames, function cn2sel(cn) { + /* eslint-disable security/detect-object-injection */ H.setup.selectors["." + cn] = H.setup.classnames[cn]; + /* eslint-enable security/detect-object-injection */ }); H.setup.classnames = null; delete H.setup.classnames; @@ -72,14 +82,23 @@ "timeout": 1000 }; } - H.lcRequire = empty(); + }()); + (function configRequire() { + H.lcRequire = new Map(); eachKey(H.require, function copyRequire(k) { - H.lcRequire[k.toLowerCase()] = H.require[k]; + /* eslint-disable security/detect-object-injection */ + H.lcRequire.set(k.toLowerCase(), H.require[k]); + /* eslint-enable security/detect-object-injection */ }); if (H.fallbacks) { - H.lcFallbacks = empty(); + H.lcFallbacks = new Map(); eachKey(H.fallbacks, function copyFallbacks(k) { - H.lcFallbacks[k.toLowerCase()] = H.fallbacks[k].toLowerCase(); + /* eslint-disable security/detect-object-injection */ + H.lcFallbacks.set( + k.toLowerCase(), + H.fallbacks[k].toLowerCase() + ); + /* eslint-enable security/detect-object-injection */ }); } }()); @@ -117,7 +136,7 @@ (function setupEvents() { // Events known to the system - const definedEvents = empty(); + const definedEvents = new Map(); // Default events, execution deferred to Hyphenopoly.js const deferred = []; @@ -135,11 +154,11 @@ * @returns {undefined} */ function define(name, defFunc, cancellable) { - definedEvents[name] = { + definedEvents.set(name, { "cancellable": cancellable, "default": defFunc, "register": [] - }; + }); } define( @@ -204,19 +223,21 @@ function dispatch(name, data) { data = data || empty(); let defaultPrevented = false; - definedEvents[name].register.forEach(function call(currentHandler) { - data.preventDefault = function preventDefault() { - if (definedEvents[name].cancellable) { - defaultPrevented = true; - } - }; - currentHandler(data); - }); + definedEvents.get(name).register.forEach( + function call(currentHandler) { + data.preventDefault = function preventDefault() { + if (definedEvents.get(name).cancellable) { + defaultPrevented = true; + } + }; + currentHandler(data); + } + ); if ( !defaultPrevented && - definedEvents[name].default + definedEvents.get(name).default ) { - definedEvents[name].default(data); + definedEvents.get(name).default(data); } } @@ -228,8 +249,8 @@ * @returns {undefined} */ function addListener(name, handler, defer) { - if (definedEvents[name]) { - definedEvents[name].register.push(handler); + if (definedEvents.has(name)) { + definedEvents.get(name).register.push(handler); } else if (defer) { tempRegister.push({ "handler": handler, @@ -245,7 +266,9 @@ if (H.handleEvent) { eachKey(H.handleEvent, function add(name) { + /* eslint-disable security/detect-object-injection */ addListener(name, H.handleEvent[name], true); + /* eslint-enable security/detect-object-injection */ }); } @@ -315,7 +338,7 @@ } const scriptLoader = (function scriptLoader() { - const loadedScripts = empty(); + const loadedScripts = new Map(); /** * Load script by adding + + + + + +

Test 032

+

Make hyphenation viewport dependent (resize window!)

+
+
+

Silbentrennungsalgorithmus

+

Sil•ben•tren•nungs•al•go•rith•mus

+

Silbentrennungsalgorithmus

+ +

Silbentrennungsalgorithmus Donaudampfschiff

+

Sil•ben•tren•nungs•al•go•rith•mus Do•nau•dampf•schiff

+

Silbentrennungsalgorithmus Donaudampfschiff

+ +

Silbentrennungsalgorithmus Donaudampfschiff

+

Sil•ben•tren•nungs•al•go•rith•mus Donaudampfschiff

+

Silbentrennungsalgorithmus Donaudampfschiff

+ +

Silbentrennungsalgorithmus hyphenation

+

Sil•ben•tren•nungs•al•go•rith•mus hy•phen•ation

+

Silbentrennungsalgorithmus hyphenation

+
+
Test Ref
+ + + \ No newline at end of file diff --git a/testsuite/testdriver.js b/testsuite/testdriver.js index f6a8d8f1..5f1ccb71 100644 --- a/testsuite/testdriver.js +++ b/testsuite/testdriver.js @@ -36,7 +36,8 @@ {exec: true, path: "test28.html"}, {exec: true, path: "test29.html"}, {exec: true, path: "test30.html"}, - {exec: true, path: "test31.html"} + {exec: true, path: "test31.html"}, + {exec: true, path: "test32.html"} ]; var testframe = document.getElementById("testframe"); var currentTest = 1;