diff --git a/.eslintrc b/.eslintrc index e37b5086..5dd3e602 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,4 +1,5 @@ { + "root": true, "parserOptions": { "ecmaVersion": 2020 }, @@ -6,9 +7,7 @@ "browser": true, "es6": true }, - "globals": { - "Hyphenopoly": "readonly" - }, + "ignorePatterns": ["min/*.js"], "plugins": [ "security" ], @@ -78,8 +77,9 @@ "allowArrowFunctions": true } ], - "object-shorthand": 0, - "prefer-arrow-callback": 0, + "arrow-body-style": ["error", "always"], + "object-shorthand": 1, + "prefer-arrow-callback": 2, "no-param-reassign": 0, "no-extra-parens": 0, "array-element-newline": [ @@ -119,5 +119,22 @@ } ] }, + "overrides": [ + { + "files": ["**/*.ts"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "require-jsdoc": 0, + "no-bitwise": 0, + "complexity": 0 + } + } + ], "reportUnusedDisableDirectives": true } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 386b3ee6..2b57805b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js cache: bundler node_js: - - "10" + - lts/* +os: + - "linux" notifications: - email: false \ No newline at end of file + email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 45140d8d..925bc141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,21 @@ # Version History +## Version 4.1.0 (2020-02-19) +### Changed +* Hyphenopoly.unhyphenate now returns `elements`: [doc](https://mnater.github.io/Hyphenopoly/Global-Hyphenopoly-Object.html#unhyphenate) +* \w is no longer part of the regex that finds words -> only words with characters from the alphabet (defined by patterns in the wasm module) are hyphenated +* remove -moz- prefix when feature testing for native CSS hyphens support +* disallow some characters for `hyphen` [doc](https://mnater.github.io/Hyphenopoly/Setup.html#hyphen) + +### Fixed +* fix decode polyfill for Edge +* fix ability to set paths +* ensure wasm loads only once with fallbacks +* fix issue with hyphenation depending on media queries + ## Version 4.0.0 (2020-02-02) With this major update Hyphenopoly NO LONGER SUPPORTS InternetExplorer. -This step allows the usage of modern JavaScript features which leads to smaller filesizes and thus better performance. +This step allows the usage of modern JavaScript features which leads to smaller file sizes and thus better performance. ### Changed * No fallback to asm.js @@ -33,7 +46,7 @@ Bugfix release, because I had to unpublish from npm ## Version 3.2.0 (2019-08-29) ### Fixed * Fixed issue with SSL Certificates and FireFox 60.x ESR (issue #85) -* Fixed "a potential security vulnerability" in github-pages dependencies +* Fixed "a potential security vulnerability" in GitHub-pages dependencies * Fixed issue with Firefox 68, where feature detection in Hyphenopoly_Loader fails * bump devDependencies @@ -50,7 +63,7 @@ Bugfix release, because I had to unpublish from npm * bump devDependencies ### Changed -* doc: uild github page from docs folder +* doc: build GitHub page from docs folder * tools: move eslint config from package.json to .eslintrc * tools: remove manual replacement of mutable globals when compiling to wasm @@ -95,7 +108,7 @@ Bugfix release, because I had to unpublish from npm ## Version 2.8.0 (Feb 28, 2019) * the error event now accepts a `lvl` field ("info"/"warn"/"error") and logs accordingly (issue #56) * add list of supported languages in hyphenopoly.module.js (issue #57) -* improve loading of ressources (issue #58) +* improve loading of resources (issue #58) ## Version 2.7.0 (Feb 01, 2019) * implement sync mode for node module (issue #43) @@ -108,7 +121,7 @@ Bugfix release, because I had to unpublish from npm ## 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 +* small refactoring for smaller code size * fixed a StateError in IE 11 ## Version 2.6.0 (Dec 01, 2018) @@ -128,14 +141,14 @@ Bugfix release, because I had to unpublish from npm * hyphenopoly.module.js is now [easy to use with browserify](https://github.com/mnater/Hyphenopoly/wiki/browserify) ## Version 2.4.0 (Sept 01, 2018) -* Implement fallback mechanism for language subtags where no patterns are available (e.g. en-au -> en-gb) [#29](https://github.com/mnater/Hyphenopoly/issues/29) +* Implement fallback mechanism for language sub-tags where no patterns are available (e.g. en-au -> en-gb) [#29](https://github.com/mnater/Hyphenopoly/issues/29) * updated patterns for Thai [#25](https://github.com/hyphenation/tex-hyphen/pull/25) ## Version 2.3.0 (Juli 26, 2018) * Don't use template strings [#28](https://github.com/mnater/Hyphenopoly/issues/28) * run feature test for wasm support only if necessary * define node >=8.3.0 as requirement (for util.TextDecoder) -* small refactorings +* small refactoring ## Version 2.2.0 (June 26, 2018) * provide example.js for RunKit @@ -143,11 +156,11 @@ Bugfix release, because I had to unpublish from npm * [6f9e539](https://github.com/mnater/Hyphenopoly/commit/6f9e539a5dab2d1eff5bdeb0c7857c6fda9eb41e) * bugfix: [#24](https://github.com/mnater/Hyphenopoly/issues/24): [aeefe6e](https://github.com/mnater/Hyphenopoly/commit/aeefe6e3a59e8356abc99ca490acabf6c3374d7b) -## Version 2.1.0 (Mai 27, 2018) +## Version 2.1.0 (May 27, 2018) * Configure Travis-CI -* bugfixes +* bug fixes -## Version 2.0.0 (Mai 27, 2018) +## Version 2.0.0 (May 27, 2018) * Provide [node module](https://github.com/mnater/Hyphenopoly/wiki/Node-Module) * default file locations better reflect usual installations [#19](https://github.com/mnater/Hyphenopoly/issues/19) * Add ability to store results of feature tests (optional) [#22](https://github.com/mnater/Hyphenopoly/issues/22) @@ -156,8 +169,8 @@ Bugfix release, because I had to unpublish from npm * fix string normalization (a3229f730f79ccdd3054cbac257b2345f5c8e11a) * Better tooling: minify, eslint, testing (mocha), compiling [devDependencies](https://github.com/mnater/Hyphenopoly/wiki/Usage-of-devDependencies) -## Version 1.0.1 (Mai 13, 2018) +## Version 1.0.1 (May 13, 2018) Prevent browsers to force layout on feature test in some cases. -## Version 1.0.0 (Mai 12, 2018) +## Version 1.0.0 (May 12, 2018) First release diff --git a/Hyphenopoly.js b/Hyphenopoly.js index ffb282dc..90e4b8df 100644 --- a/Hyphenopoly.js +++ b/Hyphenopoly.js @@ -1,5 +1,5 @@ /** - * @license Hyphenopoly 4.0.0 - client side hyphenation for webbrowsers + * @license Hyphenopoly 4.1.0 - client side hyphenation for webbrowsers * ©2020 Mathias Nater, Güttingen (mathiasnater at gmail dot com) * https://github.com/mnater/Hyphenopoly * @@ -7,9 +7,10 @@ * http://mnater.github.io/Hyphenopoly/LICENSE */ +/* globals Hyphenopoly:readonly */ ((w) => { "use strict"; - const SOFTHYPHEN = String.fromCharCode(173); + const SOFTHYPHEN = "\u00AD"; /** * Create Object without standard Object-prototype @@ -41,7 +42,7 @@ * @param {number} props bitfield * @returns {Object} Property object */ - function setProp(val, props) { + const setProp = (val, props) => { /* eslint-disable no-bitwise, sort-keys */ return { "configurable": (props & 4) > 0, @@ -50,7 +51,7 @@ "value": val }; /* eslint-enable no-bitwise, sort-keys */ - } + }; /** * Register copy event on element @@ -68,10 +69,10 @@ div.appendChild(docFrag); const selectedHTML = div.innerHTML; const selectedText = sel.toString(); - /* eslint-disable security/detect-non-literal-regexp */ - e.clipboardData.setData("text/plain", selectedText.replace(new RegExp(SOFTHYPHEN, "g"), "")); - e.clipboardData.setData("text/html", selectedHTML.replace(new RegExp(SOFTHYPHEN, "g"), "")); - /* eslint-enable security/detect-non-literal-regexp */ + // eslint-disable-next-line security/detect-non-literal-regexp + const re = new RegExp(SOFTHYPHEN, "g"); + e.clipboardData.setData("text/plain", selectedText.replace(re, "")); + e.clipboardData.setData("text/html", selectedHTML.replace(re, "")); }, true ); @@ -171,17 +172,6 @@ ((H) => { H.events = new Map(); - /** - * Create eventPromise - * @returns {undefined} - */ - H.createEventPromise = (name, immediate) => { - const promise = H.defProm(); - H.events.set(name, promise); - if (immediate) { - promise.resolve(name); - } - }; /* eslint-disable array-element-newline */ const knownEvents = new Set([ "afterElementHyphenation", @@ -192,7 +182,7 @@ "hyphenopolyStart" ]); /* eslint-enable array-element-newline */ - H.createEventPromise("error"); + H.events.set("error", H.defProm()); H.events.get("error").then((e) => { e.runDefault = true; e.preventDefault = () => { @@ -203,7 +193,7 @@ eachKey(H.handleEvent, (name) => { if (knownEvents.has(name)) { if (!H.events.has(name)) { - H.createEventPromise(name); + H.events.set(name, H.defProm()); } H.events.get(name).then((v) => { // eslint-disable-next-line security/detect-object-injection @@ -211,7 +201,6 @@ }); } else if (name !== "tearDown" && name !== "polyfill") { H.events.get("error").resolve({ - "lvl": "warn", "msg": `unknown Event "${name}" discarded` }); } @@ -219,16 +208,7 @@ } H.events.get("error").then((e) => { if (e.runDefault) { - switch (e.lvl) { - case "info": - w.console.info(e.msg); - break; - case "warn": - w.console.warn(e.msg); - break; - default: - w.console.error(e.msg); - } + w.console.warn(e.msg); } }); })(Hyphenopoly); @@ -246,7 +226,7 @@ */ function reinitEventPromise(name) { H.events.delete(name); - H.createEventPromise(name); + H.events.set(name, H.defProm()); H.events.get(name).then((v) => { // eslint-disable-next-line security/detect-object-injection H.handleEvent[name](v); @@ -319,11 +299,11 @@ } return { - "add": add, - "counter": counter, - "each": each, - "list": list, - "rem": rem + add, + counter, + each, + list, + rem }; } @@ -416,7 +396,6 @@ } } else if (!H.cf.langs[eLang]) { H.events.get("error").resolve({ - "lvl": "warn", "msg": `Element with '${eLang}' found, but '${eLang}.hpb' not loaded. Check language tags!` }); } @@ -433,7 +412,7 @@ processElements(n, getLang(n, true), sel, false); }); }); - H.res.set("elements", Promise.resolve(elements)); + H.res.set("els", Promise.resolve(elements)); } const wordHyphenatorPool = new Map(); @@ -457,7 +436,7 @@ * @returns {string} The hyphenated compound word */ function hyphenateCompound(word) { - const zeroWidthSpace = String.fromCharCode(8203); + const zeroWidthSpace = "\u200B"; let parts = null; let wordHyphenator = null; if (selSettings.compound === "auto" || @@ -486,9 +465,9 @@ * @returns {boolean} true if s is mixed case */ function isMixedCase(s) { - return Array.prototype.map.call(s, function mapper(c) { + return Array.prototype.map.call(s, (c) => { return (c === c.toLowerCase()); - }).some(function checker(v, i, a) { + }).some((v, i, a) => { return (v !== a[0]); }); } @@ -501,25 +480,23 @@ */ function hyphenator(word) { let hw = lo.cache.get(sel).get(word); - if (!selSettings.mixedCase && isMixedCase(word)) { - hw = word; - } if (!hw) { - if (lo.exceptions.has(word)) { - hw = lo.exceptions.get(word).replace( + 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) { if (word.length > 61) { H.events.get("error").resolve({ - "lvl": "warn", "msg": "found word longer than 61 characters" }); hw = word; } else { /* eslint-disable security/detect-object-injection */ - hw = lo.hyphenateFunction( + hw = lo.hyphenate( word, hyphen.charCodeAt(0), selSettings.leftminPerLang[lang], @@ -564,15 +541,12 @@ /* eslint-disable security/detect-object-injection */ const selSettings = C[sel]; /* eslint-enable security/detect-object-injection */ - let h = selSettings.hyphen; - if (".\\+*?[^]$(){}=!<>|:-".indexOf(selSettings.hyphen) !== -1) { - h = "\\" + selSettings.hyphen; - } if (selSettings.orphanControl === 3 && leadingWhiteSpace === " ") { - leadingWhiteSpace = String.fromCharCode(160); + // \u00A0 = no-break space (nbsp) + leadingWhiteSpace = "\u00A0"; } /* eslint-disable security/detect-non-literal-regexp */ - return leadingWhiteSpace + lastWord.replace(new RegExp(h, "g"), "") + trailingWhiteSpace; + return leadingWhiteSpace + lastWord.replace(new RegExp(selSettings.hyphen, "g"), "") + trailingWhiteSpace; /* eslint-enable security/detect-non-literal-regexp */ } orphanControllerPool.set(sel, controlOrphans); @@ -592,8 +566,6 @@ const selSettings = C[sel]; /* eslint-enable security/detect-object-injection */ const minWordLength = selSettings.minWordLength; - const normalize = C.normalize && - Boolean(String.prototype.normalize); const poolKey = lang + "-" + sel; const wordHyphenator = (wordHyphenatorPool.has(poolKey)) ? wordHyphenatorPool.get(poolKey) @@ -601,7 +573,7 @@ const orphanController = (orphanControllerPool.has(sel)) ? orphanControllerPool.get(sel) : createOrphanController(sel); - const re = lo.genRegExps.get(sel); + const re = lo.re.get(sel); /** * Hyphenate text according to setting in sel @@ -610,7 +582,7 @@ */ function hyphenateText(text) { let tn = null; - if (normalize) { + if (C.normalize) { tn = text.normalize("NFC").replace(re, wordHyphenator); } else { tn = text.replace(re, wordHyphenator); @@ -633,8 +605,8 @@ function hyphenateElement(el) { if (H.events.has("beforeElementHyphenation")) { H.events.get("beforeElementHyphenation").resolve({ - "el": el, - "lang": lang + el, + lang }); reinitEventPromise("beforeElementHyphenation"); } @@ -646,13 +618,13 @@ n.data = hyphenateText(n.data); } }); - H.res.get("elements").then((elements) => { + H.res.get("els").then((elements) => { elements.counter[0] -= 1; }); if (H.events.has("afterElementHyphenation")) { H.events.get("afterElementHyphenation").resolve({ - "el": el, - "lang": lang + el, + lang }); reinitEventPromise("afterElementHyphenation"); } @@ -666,19 +638,23 @@ return r; } - H.createHyphenator = (lang) => ((entity, sel = ".hyphenate") => hyphenate(lang, sel, entity)); + H.createHyphenator = ((lang) => { + return ((entity, sel = ".hyphenate") => { + return hyphenate(lang, sel, entity); + }); + }); H.unhyphenate = () => { - H.res.get("elements").then((elements) => { + return H.res.get("els").then((elements) => { elements.each((lang, els) => { els.forEach((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"), ""); + n.data = n.data.replace(new RegExp(C[elo.selector].hyphen, "g"), ""); /* eslint-enable security/detect-non-literal-regexp */ }); }); + return elements; }); }; @@ -695,15 +671,14 @@ }); } else { H.events.get("error").resolve({ - "lvl": "warn", "msg": `engine for language '${lang}' loaded, but no elements found.` }); } - H.res.get("elements").then((elements) => { + H.res.get("els").then((elements) => { if (elements.counter[0] === 0) { w.clearTimeout(C.timeOutHandler); if (C.hide !== 0) { - H.hiding(1, null); + H.hide(0, null); } if (H.events.has("hyphenopolyEnd")) { H.events.get("hyphenopolyEnd").resolve("hyphenopolyEnd"); @@ -715,20 +690,6 @@ }); } - /** - * Convert exceptions to object - * @param {string} exc comma separated list of exceptions - * @returns {Object} Map of exceptions - */ - function convertExceptions(exc) { - const r = new Map(); - exc.split(", ").forEach((e) => { - const key = e.replace(/-/g, ""); - r.set(key, e); - }); - return r; - } - /** * Create lang Object * @param {string} lang The language @@ -762,7 +723,7 @@ ) { alphabet = alphabet.replace(/-/g, ""); const lo = createLangObj(lang); - if (!lo.engineReady) { + if (!lo.ready) { lo.cache = new Map(); /* eslint-disable security/detect-object-injection */ if (C.exceptions.global) { @@ -773,14 +734,16 @@ } } if (C.exceptions[lang]) { - lo.exceptions = convertExceptions(C.exceptions[lang]); + lo.exc = new Map(C.exceptions[lang].split(", ").map((e) => { + return [e.replace(/-/g, ""), e]; + })); delete C.exceptions[lang]; } else { - lo.exceptions = new Map(); + lo.exc = new Map(); } /* eslint-enable security/detect-object-injection */ - lo.genRegExps = new Map(); - lo.hyphenateFunction = hyphenateFunction; + lo.re = new Map(); + lo.hyphenate = hyphenateFunction; C.selectors.forEach((sel) => { /* eslint-disable security/detect-object-injection */ const selSettings = C[sel]; @@ -822,24 +785,31 @@ * Word delimiters are not taken in account. */ /* eslint-disable security/detect-non-literal-regexp */ - lo.genRegExps.set(sel, new RegExp(`[\\w${alphabet}${String.fromCharCode(8204)}-]{${selSettings.minWordLength},}`, "gi")); + lo.re.set(sel, new RegExp(`[${alphabet}\u200C-]{${selSettings.minWordLength},}`, "gi")); /* eslint-enable security/detect-non-literal-regexp */ }); - lo.engineReady = true; + lo.ready = true; // eslint-disable-next-line security/detect-object-injection H.hyphenators[lang].resolve(H.createHyphenator(lang)); } if (H.events.has("engineReady")) { H.events.get("engineReady").resolve(lang); } - Promise.all([lo, H.res.get("elements")]).then((v) => { + Promise.all([lo, H.res.get("els")]).then((v) => { hyphenateLangElements(lang, v[1].list.get(lang)); }); } const decode = (() => { - const utf16ledecoder = new TextDecoder("utf-16le"); - return ((ui16) => utf16ledecoder.decode(ui16)); + if (w.TextDecoder) { + const utf16ledecoder = new TextDecoder("utf-16le"); + return ((ui16) => { + return utf16ledecoder.decode(ui16); + }); + } + return ((ui16) => { + return String.fromCharCode.apply(null, ui16); + }); })(); /** @@ -854,12 +824,10 @@ wordStore[0] = 95; return ((word, hyphencc, leftmin, rightmin) => { let i = 0; - let cc = word.charCodeAt(i); - while (cc) { + for (const c of word) { i += 1; // eslint-disable-next-line security/detect-object-injection - wordStore[i] = cc; - cc = word.charCodeAt(i); + wordStore[i] = c.charCodeAt(0); } wordStore[i + 1] = 95; wordStore[i + 2] = 0; @@ -901,31 +869,35 @@ baseData.rm ); } - heProm.then((response) => { + heProm.w.then((response) => { if (response.ok) { + let r2 = response; + if (heProm.c > 1) { + r2 = response.clone(); + } if ( wa.instantiateStreaming && (response.headers.get("Content-Type") === "application/wasm") ) { - wa.instantiateStreaming(response).then(handleWasm); + wa.instantiateStreaming(r2).then(handleWasm); } else { - response.arrayBuffer().then((ab) => { - window.WebAssembly.instantiate(ab).then(handleWasm); - }); + r2.arrayBuffer(). + then((ab) => { + window.WebAssembly.instantiate(ab). + then(handleWasm); + }); } } else { - H.res.get("elements").then((elements) => { + H.res.get("els").then((elements) => { elements.rem(lang); }); /* eslint-disable security/detect-object-injection */ H.hyphenators[lang].catch((e) => { H.events.get("error").resolve({ - "lvl": "warn", "msg": e.msg }); }); H.hyphenators[lang].reject({ - "lvl": "warn", "msg": `1 File ${lang}.wasm can't be loaded from ${H.paths.patterndir}` }); /* eslint-enable security/detect-object-injection */ @@ -939,11 +911,11 @@ mainLanguage = C.defaultLanguage; } collectElements(); - H.res.get("elements").then((elements) => { + H.res.get("els").then((elements) => { elements.each((lang, values) => { if (H.languages && H.languages.has(lang) && - H.languages.get(lang).engineReady + H.languages.get(lang).ready ) { hyphenateLangElements(lang, values); } diff --git a/Hyphenopoly_Loader.js b/Hyphenopoly_Loader.js index ed66b7cb..bb11ff6c 100644 --- a/Hyphenopoly_Loader.js +++ b/Hyphenopoly_Loader.js @@ -1,24 +1,26 @@ /** - * @license Hyphenopoly_Loader 4.0.0 - client side hyphenation + * @license Hyphenopoly_Loader 4.1.0 - client side hyphenation * ©2020 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"; const store = sessionStorage; - const storeID = "Hyphenopoly_Loader"; + const scriptName = "Hyphenopoly_Loader.js"; const lcRequire = new Map(); /** * Create Object without standard Object-prototype * @returns {Object} empty object */ - const empty = () => o.create(null); + const empty = () => { + return o.create(null); + }; const shortcuts = { "ac": "appendChild", @@ -32,7 +34,9 @@ * @param {function} fn the function to execute * @returns {undefined} */ - const eachKey = (obj, fn) => o.keys(obj).forEach(fn); + const eachKey = (obj, fn) => { + return o.keys(obj).forEach(fn); + }; /** * Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously @@ -40,8 +44,8 @@ * This is in an iife to keep complexity low. */ (() => { - if (H.cacheFeatureTests && store.getItem(storeID)) { - H.cf = JSON.parse(store.getItem(storeID)); + if (H.cacheFeatureTests && store.getItem(scriptName)) { + H.cf = JSON.parse(store.getItem(scriptName)); } else { H.cf = { "langs": empty(), @@ -56,7 +60,7 @@ * These are iifes to keep complexity low. */ (() => { - const maindir = d.currentScript.src.replace(/Hyphenopoly_Loader.js/i, ""); + const maindir = d.currentScript.src.replace(scriptName, ""); const patterndir = maindir + "patterns/"; if (H.paths) { H.paths.maindir = H.paths.maindir || maindir; @@ -81,6 +85,7 @@ "timeout": 1000 }; } + // Change mode string to mode int H.setup.hide = (() => { switch (H.setup.hide) { @@ -135,8 +140,8 @@ }; /** - * Define function H.toggle. - * This function hides (state = 0) or unhides (state = 1) + * Define function H.hide. + * This function hides (state = 1) or unhides (state = 0) * the whole document (mode == 1) or * each selected element (mode == 2) or * text of each selected element (mode == 3) or @@ -144,9 +149,9 @@ * @param {integer} state - State * @param {integer} mode - Mode */ - H.hiding = (state, mode) => { + H.hide = (state, mode) => { const sid = "H9Y_Styles"; - if (state === 1) { + if (state === 0) { const stylesNode = d.getElementById(sid); if (stylesNode) { stylesNode.parentNode.removeChild(stylesNode); @@ -172,14 +177,13 @@ } }; - H.res = new Map(); - H.res.set("he", new Map()); + H.res = new Map([["he", new Map()], ["fw", new Map()]]); (() => { const tester = (() => { let fakeBody = null; const ha = "hyphens:auto"; - const css = `visibility:hidden;-moz-${ha};-webkit-${ha};-ms-${ha};${ha};width:48px;font-size:12px;line-height:12px;border:none;padding:0;word-wrap:normal`; + const css = `visibility:hidden;-webkit-${ha};-ms-${ha};${ha};width:48px;font-size:12px;line-height:12px;border:none;padding:0;word-wrap:normal`; return { @@ -237,13 +241,11 @@ * @param {Object} elm - the element * @returns {Boolean} result of the check */ - function checkCSSHyphensSupport(elm) { - const a = "auto"; - const h = elm.style.hyphens || - elm.style.webkitHyphens || - elm.style.msHyphens || - elm.style["-moz-hyphens"]; - return (h === a); + function checkCSSHyphensSupport(elmStyle) { + const h = elmStyle.hyphens || + elmStyle.webkitHyphens || + elmStyle.msHyphens; + return (h === "auto"); } /** @@ -257,10 +259,22 @@ H.cf.pf = true; // eslint-disable-next-line security/detect-object-injection H.cf.langs[lang] = "H9Y"; - H.res.get("he").set( - lang, - w.fetch(H.paths.patterndir + filename, {"credentials": "include"}) - ); + if (H.res.get("fw").has(filename)) { + H.res.get("he").get(H.res.get("fw").get(filename)).c += 1; + H.res.get("he").set( + lang, + H.res.get("he").get(H.res.get("fw").get(filename)) + ); + } else { + H.res.get("he").set( + lang, + { + "c": 1, + "w": w.fetch(H.paths.patterndir + filename, {"credentials": "include"}) + } + ); + H.res.get("fw").set(filename, lang); + } } /** @@ -297,7 +311,7 @@ if (testContainer !== null) { const nl = testContainer.querySelectorAll("div"); nl.forEach((n) => { - if (checkCSSHyphensSupport(n) && n.offsetHeight > 12) { + if (checkCSSHyphensSupport(n.style) && n.offsetHeight > 12) { H.cf.langs[n.lang] = "CSS"; } else { loadhyphenEngine(n.lang); @@ -321,32 +335,29 @@ } })); if (H.setup.hide === 1) { - H.hiding(0, 1); + H.hide(1, 1); } if (H.setup.hide !== 0) { H.setup.timeOutHandler = w.setTimeout(() => { - H.hiding(1, null); + H.hide(0, null); // eslint-disable-next-line no-console - console.error(`Hyphenopoly timed out after ${H.setup.timeout}ms`); + console.error(`${scriptName} timed out after ${H.setup.timeout}ms`); }, H.setup.timeout); } H.res.get("DOM").then(() => { if (H.setup.hide > 1) { - H.hiding(0, H.setup.hide); + H.hide(1, H.setup.hide); } }); // Load main script const script = d[shortcuts.ce]("script"); script.src = H.paths.maindir + "Hyphenopoly.js"; d.head[shortcuts.ac](script); - + H.hyphenators = empty(); eachKey(H.cf.langs, (lang) => { /* eslint-disable security/detect-object-injection */ if (H.cf.langs[lang] === "H9Y") { - H.hyphenators = H.hyphenators || empty(); - if (!H.hyphenators[lang]) { - H.hyphenators[lang] = H.defProm(); - } + H.hyphenators[lang] = H.defProm(); } /* eslint-enable security/detect-object-injection */ }); @@ -356,7 +367,7 @@ tearDown(); } if (H.cacheFeatureTests) { - store.setItem(storeID, JSON.stringify(H.cf)); + store.setItem(scriptName, JSON.stringify(H.cf)); } })(); })(window, document, Hyphenopoly, Object); diff --git a/README.md b/README.md index e228457f..6d11515b 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Hyphenator_Loader.js will feature test the client (aka browser, aka user agent) If you want to force the usage of Hyphenopoly.js for a language (e.g. for testing purposes) write `"FORCEHYPHENOPOLY"` instead of the long word. ### Second script block – load and run Hyphenopoly_Loader.js -Hyphenopoly_Loader.js tests if the browser supports CSS hyphenation for the language(s) given in `Hyphenopoly.require`. If one of the given languages isn't supported it automatically hides the documents contents and loads Hyphenopoly.js and the necessary Webassembly modules. Hyphenopoly.js – once loaded – will hyphenate the elements according to the settings and unhide the document when it's done. If something goes wrong and Hyphenopoly.js is unable to unhide the document Hyphenopoly_Loader.js has a timeout that kicks in after some time (defaults to 1000ms) and unhides the document and writes a message to the console. +Hyphenopoly_Loader.js tests if the browser supports CSS hyphenation for the language(s) given in `Hyphenopoly.require`. If one of the given languages isn't supported it automatically hides the documents contents and loads Hyphenopoly.js and the necessary WebAssembly modules. Hyphenopoly.js – once loaded – will hyphenate the elements according to the settings and unhide the document when it's done. If something goes wrong and Hyphenopoly.js is unable to unhide the document Hyphenopoly_Loader.js has a timeout that kicks in after some time (defaults to 1000ms) and unhides the document and writes a message to the console. If the browser supports all required languages the script deletes the `Hyphenopoly`-object and terminates without further ado. ### enable CSS-hyphenation @@ -122,7 +122,7 @@ hyphenate_de("Silbentrennung verbessert den Blocksatz."); ```` ## Support this project -[![paypal](https://www.paypal.com/en_US/i/btn/btn_donateCC_LG_global.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SYNZKB8Z2FRQY) +[![PayPal](https://www.paypal.com/en_US/i/btn/btn_donateCC_LG_global.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SYNZKB8Z2FRQY) @@ -134,7 +134,7 @@ The algorithm used for hyphenation was developed by Franklin M. Liang for TeX. I 1. Load a set of precomputed language specific patterns. The patterns are stored in a structure called a trie, which is very efficient for this task. 2. Collect all patterns that are a substring of the word to be hyphenated. 3. Combine the numerical values between characters: higher values overwrite lower values. -4. Odd values are hyphenation points (except if the hyphenation point is left from leftmin and right from rightmin), replace them with a soft hyphen and drop the other values. +4. Odd values are hyphenation points (except if the hyphenation point is left from `leftmin` and right from `rightmin`), replace them with a soft hyphen and drop the other values. 5. Repeat 2. - 4. for all words longer than minWordLength Example: @@ -154,6 +154,6 @@ h0y3p0h0e2n5a4t2i0o2n Hy-phen-ation ```` -The patterns are precomputed and available for many languages on CTAN. Hyphenopoly.js uses a proprietary binary format (including pattern licence, metadata and the patterns). Patterns are computed from a large list of hyphenated words by a program called patgen. They aim to find some hyphenation points – not all – because it's better to miss a hyphenation point then to have some false hyphenation points. Most patterns are really good but none is error free. +The patterns are precomputed and available for many languages on CTAN. Hyphenopoly.js uses a proprietary binary format (including pattern license, metadata and the patterns). Patterns are computed from a large list of hyphenated words by a program called `patgen`. They aim to find some hyphenation points – not all – because it's better to miss a hyphenation point then to have some false hyphenation points. Most patterns are really good but none is error free. These pattern vary in size. This is mostly due to the different linguistic characteristics of the languages. diff --git a/docs/Branching-Model.md b/docs/Branching-Model.md index 2aee819b..013df3e5 100644 --- a/docs/Branching-Model.md +++ b/docs/Branching-Model.md @@ -25,7 +25,7 @@ Merges to `master` must always cause a new release. The public `develop` branch reflects the state of the latest stable development state. The code here is tested but waiting for more changes to accumulate, before a new release is triggered. ### release -The `release` branch is a temporary preparation branch. It is always branched from the `develop` branch, when enough changes piled up. This is the last stage where version numbers are updated, final tests are made and changelog is finalised before it is merged to master. +The `release` branch is a temporary preparation branch. It is always branched from the `develop` branch, when enough changes piled up. This is the last stage where version numbers are updated, final tests are made and change-log is finalized before it is merged to master. `release`branches are named `release-x.y.z`. In most cases they are not pushed to GitHub. diff --git a/docs/Coping-with-hyphenation-errors.md b/docs/Coping-with-hyphenation-errors.md index aab0c662..25984233 100644 --- a/docs/Coping-with-hyphenation-errors.md +++ b/docs/Coping-with-hyphenation-errors.md @@ -19,12 +19,14 @@ Automatic hyphenation can not be error free! Because ... ```` * ... the patterns differ in quality. Some are very new and reflect the current state of the language others are older. Some are based on a very large list of words others are crafted by hand (which is not necessarily bad). +* ... patterns are made for a specific set of characters (the alphabet of the respective language). So e.g. +`en-us` patterns will not be able to correctly hyphenate a word that contains a character outside of the range `[a-z]` (Hyphenopoly doesn't even try to hyphenate in this case but rather leaves the word unchanged). + In any case automatic hyphenation needs proofreading and may need some intervention. ## Proofread Use a visible hyphen character to display all hyphen opportunities: ````javascript - ```` See also [Setup#hyphen](./Setup.md#hyphen) @@ -46,20 +47,19 @@ There are three levels of fixing possibilities: 3. [Improve patterns](#help-to-improve-the-patterns) ### Fix hyphenation in the text -Words containing a soft hyphen (\­) will not be hyphenated by Hyphenopoly.js. Therefor you can simply add soft hyphens manually to 'overwrite' automatic hyphenation. +Words containing a soft hyphen (&shy;) will not be hyphenated by Hyphenopoly.js. Therefor you can simply add soft hyphens manually to 'overwrite' automatic hyphenation. __pro:__ - easy to do __contra:__ -- needs to be repeated for every occurence of the word +- needs to be repeated for every occurrence of the word - has no effect in the long term (others will not benefit) ### Define hyphenation exceptions. Hyphenopoly.js has an [API for hyphenation exceptions](https://github.com/mnater/Hyphenopoly/wiki/Setup#exceptions): ````javascript - ```` In the example above Hyphenopoly.js will never hyphenate the word "desert" (exceptions are not case sensitive) and hyphenate the words "dictionary", "dictionaries" and "blogosphere" at the positions marked with a hyphen-minus. __pro:__ - one place for all exceptions -- exceptions apply to all occurences of the words +- exceptions apply to all occurrences of the words __contra:__ - needs carefulness @@ -92,4 +91,5 @@ __contra:__ - fixing patterns often takes a long time (not all patterns are actively maintained) - fixing the patterns often requires extended knowledge -As an intermediate step to improved patterns, patterns also have the ability to include exceptions. Follow this guide ([todo](todo)) to improve the patterns Hyphenopoly.js uses. +As an intermediate step to improved patterns, Hyphenopoly has the ability to include exceptions in the wasm-file. Feel free to [open an issue](https://github.com/mnater/Hyphenopoly/issues) if you have a good list +of exceptions for a language. diff --git a/docs/Download,-install-and-setup-for-deployment.md b/docs/Download,-install-and-setup-for-deployment.md index ce89fec2..4de73d20 100644 --- a/docs/Download,-install-and-setup-for-deployment.md +++ b/docs/Download,-install-and-setup-for-deployment.md @@ -3,7 +3,7 @@ ## General Notes ### Minifying -While the .wasm files are already compact by design, the .js files are not minified. I highly recommend to use one of the es6-savy JavaScript minifier tools. +While the .wasm files are already compact by design, the .js files are not minified. I highly recommend to use one of the es6-capable JavaScript minifier tools. If you [installed Hyphenopoly with npm](#using-npmjs) run `npm run minify` to create a directory called `min` that contains a full set of minified files (and the test suite) minified with `terser`. diff --git a/docs/Events.md b/docs/Events.md index 397b8fe4..3e5c7de7 100644 --- a/docs/Events.md +++ b/docs/Events.md @@ -26,7 +26,7 @@ const Hyphenopoly = { } ```` -Internally events in Hyphenopoly are implemented as Promises that fullfill with a certain value. +Internally events in Hyphenopoly are implemented as Promises that fulfill with a certain value. ## afterElementHyphenation-Event Fired after an element has been hyphenated. diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 15853b58..3191b88b 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,13 +1,14 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.11.1) - i18n (~> 0.7) + activesupport (6.0.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) + zeitwerk (~> 2.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) coffee-script-source execjs @@ -15,7 +16,7 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.5) + concurrent-ruby (1.1.6) dnsruby (1.61.3) addressable (~> 2.5) em-websocket (0.5.1) @@ -25,33 +26,32 @@ GEM ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (0.15.4) + faraday (1.0.0) multipart-post (>= 1.2, < 3) - ffi (1.11.1) + ffi (1.12.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (198) - activesupport (= 4.2.11.1) + github-pages (204) github-pages-health-check (= 1.16.1) jekyll (= 3.8.5) - jekyll-avatar (= 0.6.0) + jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) - jekyll-commonmark-ghpages (= 0.1.5) + jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.11.0) + jekyll-feed (= 0.13.0) jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.12.1) - jekyll-mentions (= 1.4.1) - jekyll-optional-front-matter (= 0.3.0) + jekyll-github-metadata (= 2.13.0) + jekyll-mentions (= 1.5.1) + jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.2.0) - jekyll-redirect-from (= 0.14.0) - jekyll-relative-links (= 0.6.0) - jekyll-remote-theme (= 0.3.1) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.15.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.1) jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.5.0) - jekyll-sitemap (= 1.2.0) - jekyll-swiss (= 0.4.0) + jekyll-seo-tag (= 2.6.1) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) @@ -61,19 +61,18 @@ GEM jekyll-theme-midnight (= 0.1.1) jekyll-theme-minimal (= 0.1.1) jekyll-theme-modernist (= 0.1.1) - jekyll-theme-primer (= 0.5.3) + jekyll-theme-primer (= 0.5.4) jekyll-theme-slate (= 0.1.1) jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) - jekyll-titles-from-headings (= 0.5.1) - jemoji (= 0.10.2) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.11.1) kramdown (= 1.17.0) - liquid (= 4.0.0) - listen (= 3.1.5) + liquid (= 4.0.3) mercenary (~> 0.3) - minima (= 2.5.0) - nokogiri (>= 1.8.5, < 2.0) - rouge (= 2.2.1) + minima (= 2.5.1) + nokogiri (>= 1.10.4, < 2.0) + rouge (= 3.13.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -81,7 +80,7 @@ GEM octokit (~> 4.0) public_suffix (~> 3.0) typhoeus (~> 1.3) - html-pipeline (2.12.0) + html-pipeline (2.12.3) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.6.0) @@ -100,49 +99,50 @@ GEM pathutil (~> 0.9) rouge (>= 1.7, < 4) safe_yaml (~> 1.0) - jekyll-avatar (0.6.0) - jekyll (~> 3.0) + jekyll-avatar (0.7.0) + jekyll (>= 3.0, < 5.0) jekyll-coffeescript (1.1.1) coffee-script (~> 2.2) coffee-script-source (~> 1.11.1) jekyll-commonmark (1.3.1) commonmarker (~> 0.14) jekyll (>= 3.7, < 5.0) - jekyll-commonmark-ghpages (0.1.5) + jekyll-commonmark-ghpages (0.1.6) commonmarker (~> 0.17.6) - jekyll-commonmark (~> 1) - rouge (~> 2) + jekyll-commonmark (~> 1.2) + rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.11.0) - jekyll (~> 3.3) + jekyll-feed (0.13.0) + jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) - jekyll-github-metadata (2.12.1) - jekyll (~> 3.4) + jekyll-github-metadata (2.13.0) + jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.4.1) + jekyll-mentions (1.5.1) html-pipeline (~> 2.3) - jekyll (~> 3.0) - jekyll-optional-front-matter (0.3.0) - jekyll (~> 3.0) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) jekyll-paginate (1.1.0) - jekyll-readme-index (0.2.0) - jekyll (~> 3.0) - jekyll-redirect-from (0.14.0) - jekyll (~> 3.3) - jekyll-relative-links (0.6.0) - jekyll (~> 3.3) - jekyll-remote-theme (0.3.1) - jekyll (~> 3.5) - rubyzip (>= 1.2.1, < 3.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.15.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.1) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + rubyzip (>= 1.3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) - jekyll-seo-tag (2.5.0) - jekyll (~> 3.3) - jekyll-sitemap (1.2.0) - jekyll (~> 3.3) - jekyll-swiss (0.4.0) + jekyll-seo-tag (2.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-swiss (1.0.0) jekyll-theme-architect (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) @@ -170,8 +170,8 @@ GEM jekyll-theme-modernist (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.5.3) - jekyll (~> 3.5) + jekyll-theme-primer (0.5.4) + jekyll (> 3.5, < 5.0) jekyll-github-metadata (~> 2.9) jekyll-seo-tag (~> 2.0) jekyll-theme-slate (0.1.1) @@ -183,43 +183,42 @@ GEM jekyll-theme-time-machine (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.1) - jekyll (~> 3.3) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.10.2) + jemoji (0.11.1) gemoji (~> 3.0) html-pipeline (~> 2.2) - jekyll (~> 3.0) + jekyll (>= 3.0, < 5.0) kramdown (1.17.0) - liquid (4.0.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) + liquid (4.0.3) + listen (3.2.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) mini_portile2 (2.4.0) - minima (2.5.0) - jekyll (~> 3.5) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.11.3) + minitest (5.14.0) multipart-post (2.1.1) - nokogiri (1.10.4) + nokogiri (1.10.8) mini_portile2 (~> 2.4.0) - octokit (4.14.0) + octokit (4.16.0) + faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) rb-fsevent (0.10.3) - rb-inotify (0.10.0) + rb-inotify (0.10.1) ffi (~> 1.0) - rouge (2.2.1) + rouge (3.13.0) ruby-enum (0.7.2) i18n - ruby_dep (1.5.0) - rubyzip (2.0.0) + rubyzip (2.2.0) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -234,9 +233,10 @@ GEM thread_safe (0.3.6) typhoeus (1.3.1) ethon (>= 0.9.0) - tzinfo (1.2.5) + tzinfo (1.2.6) thread_safe (~> 0.1) - unicode-display_width (1.6.0) + unicode-display_width (1.6.1) + zeitwerk (2.2.2) PLATFORMS ruby @@ -245,4 +245,4 @@ DEPENDENCIES github-pages BUNDLED WITH - 2.0.2 + 2.1.4 diff --git a/docs/Global-Hyphenopoly-Object.md b/docs/Global-Hyphenopoly-Object.md index 09a01ade..fd90f094 100644 --- a/docs/Global-Hyphenopoly-Object.md +++ b/docs/Global-Hyphenopoly-Object.md @@ -14,7 +14,7 @@ require: { } ```` Hyphenopoly_Loader.js feature tests the browser for CSS-hyphenation support of the required languages using the long word. -If the feature test indicates that the browser doesn't support CSS-hyphenation for at least one language, all necessary ressources will be loaded and Hyphenopoly.js gets executed. +If the feature test indicates that the browser doesn't support CSS-hyphenation for at least one language, all necessary resources will be loaded and Hyphenopoly.js gets executed. Use this to test support for every language used on the current page. If e.g. the language of the page is `lang="de-DE"` you must require `de-de` (case doesn't matter). For languages that aren't in the patterns directory a fallback must be defined (see below). @@ -66,9 +66,9 @@ results and retrieve these stored results for other pages in the same browsing s The test results are stored in sessionStorage to assure that the tests are rerun when the browser occasionally gets updated. -Because the law in some contries require a user opt-in or opt-out or whatever if you store +Because the law in some countries require a user opt-in or opt-out or whatever if you store data on the client, `cacheFeatureTests` is deactivated by default and has to be activated -explicitely by hand in the [Hyphenopoly global object](./Global-Hyphenopoly-Object.md): +explicitly by hand in the [Hyphenopoly global object](./Global-Hyphenopoly-Object.md): ````javascript const Hyphenopoly = { "require": {...}, @@ -82,7 +82,7 @@ By default Hyphenopoly.js hyphenates elements with the classname `.hyphenate` an #### selectors -With selectors elements can be selected very precicely without the need of adding classes to the HTML. The selectors-object is a list of key-value-pairs where the key is a selector and the value is an object of settings specific to the selected elements. +With selectors elements can be selected very precisely without the need of adding classes to the HTML. The selectors-object is a list of key-value-pairs where the key is a selector and the value is an object of settings specific to the selected elements. ````javascript setup: { @@ -107,6 +107,12 @@ See [Hyphenators](./Hyphenators.md) ### Unhyphenate To remove all hyphenation previously applied by Hyphenopoly call `Hyphenopoly.unhyphenate();`. +This method asynchronously returns the elements that have been unhyphenated in the data structure used internally. +````javascript +Hyphenopoly.unhyphenate().then((elements) => { + console.log(elements); +}); +```` ## Putting it all together A typical init could look like this: @@ -130,15 +136,3 @@ const Hyphenopoly = { ## Internal Fields If you `console.dir(Hyphenopoly)` you'll see lots of other data that is internally used by Hyphenopoly_Loader.js and Hyphenopoly.js but isn't meant to be changed by the user. - -## Reclaim memory -Hyphenopoly is quite hungry regarding to memory usage: for each language ~2MB of wasm memory are allocated. If you're done with Hyphenopoly you can set `window.Hyphenopoly = null` and leave it to the garbage collector to free the memory: - -````javascript -handleEvent: { - hyphenopolyEnd: function (e) { - window.Hyphenopoly = null; - } -} -```` -See [Events](./Events.md) for more details about the `hyphenopolyEnd`-event. diff --git a/docs/Hyphenators.md b/docs/Hyphenators.md index 525a22a1..1ee1fe07 100644 --- a/docs/Hyphenators.md +++ b/docs/Hyphenators.md @@ -1,7 +1,7 @@ # Hyphenators -While the main functionality of Hyphenopoly is to just hyphenate your HTML with no further ado, it is sometimes usefull to have a function at hand that hyphenates text. +While the main functionality of Hyphenopoly is to just hyphenate your HTML with no further ado, it is sometimes useful to have a function at hand that hyphenates text. -Possible usecases are: +Possible use cases are: * dynamically loaded text * hyphenating text provided by the user (e.g. in a preview window of a blogging software) * … @@ -70,7 +70,7 @@ In the example above we enforced Hyphenopoly_Loader.js to use Hyphenopoly.js for In the example a `string` is handed over to the `hyphenator` which returns a hyphenated string according to the settings for the `selector`. If no `selector` is defined it defaults to `".hyphenate"`. ## Use `Hyphenopoly.hyphenators` for DOM-Elements -When handing over a HTMLELEMENT instead of a string `hyphenators` directly hyphenate the contents of a HTMLElement and return nothing (`undefined`). +When handing over a `HTMLELEMENT` instead of a string, `hyphenators` directly hyphenate the contents of a HTMLElement and return nothing (`undefined`). ````html diff --git a/docs/Node-Module.md b/docs/Node-Module.md index 2ba976ac..52d94030 100644 --- a/docs/Node-Module.md +++ b/docs/Node-Module.md @@ -109,7 +109,7 @@ Defaults: The only option that Must be set is `require` which takes an array of language-tags. ### loader -By default hyphenopoly.module.js loads pattern files and hyphenEnginge by using nodes "fs"-module. +By default hyphenopoly.module.js loads pattern files and hyphenEngine by using nodes "fs"-module. This can be changed to the "https"-module by setting the `loader` to "https": ````javascript const hyphenator = hyphenopoly.config({ @@ -118,7 +118,7 @@ const hyphenator = hyphenopoly.config({ }); ```` -This is useful if the module is transformed to a script used in a webbrowser (e.g. by using [browserify](http://browserify.org)). +This is useful if the module is transformed to a script used in a web browser (e.g. by using [browserify](http://browserify.org)). ### other options For documentation about the other options see the `Hyphenopoly.js`-documentation: diff --git a/docs/Setup.md b/docs/Setup.md index 22ffa5dd..49e6a832 100644 --- a/docs/Setup.md +++ b/docs/Setup.md @@ -165,7 +165,7 @@ If the word does not contain a hyphen, it will not be hyphenated by Hyphenopoly. type: string ("all" | "element" | "text") default: "all" ```` -Define if and how elements are made unvisible while being hyphenated. +Define if and how elements are made invisible while being hyphenated. ````html ```` The pattern files work with _precomposed_ characters. So an `Å` (LATIN CAPITAL LETTER A WITH RING ABOVE) must not be composed of `A` (LATIN CAPITAL LETTER A) and ` ̊` (COMBINING RING ABOVE) to be recognizable by hyphenation-engine. -If the text contains _composed_ characters they must be normalised to _precomposed_ characters. If `normalize` is activated and the user agent supports `String.prototype.normalize()` this can happen automatically. +If the text contains _composed_ characters they must be normalized to _precomposed_ characters. If `normalize` is activated and the user agent supports `String.prototype.normalize()` this can happen automatically. Since this comes with a performance penalty it is deactivated by default and it's recommended to use _precomposed_ characters in HTML. ### safeCopy @@ -245,7 +245,7 @@ var Hyphenopoly = { }; ```` -To prevent soft hyphens from beeing copied to the clipboard, Hyphenopoly.js registers a `onCopy`-Event on hyphenated elements. When text is copied to the clipboard, this event fires and soft hyphens are removed. +To prevent soft hyphens from being copied to the clipboard, Hyphenopoly.js registers a `onCopy`-Event on hyphenated elements. When text is copied to the clipboard, this event fires and soft hyphens are removed. _It does NOT remove other `hyphen`-characters!_ This feature is on by default, but it's a hack – disable it if you don't like it. @@ -268,7 +268,7 @@ var Hyphenopoly = { }; ```` -To prevent a _Flash Of Unhyphenated Content (FOUHC)_ Hyphenopoly_Loader.js hides text to be hyphenated until hyphenation is done. If something goes wrong (e.g. a ressource didn't load correctly) this timeout saves us from an empty page. The timeout is cleared when hyphenation succeeds. +To prevent a _Flash Of Unhyphenated Content (FOUHC)_ Hyphenopoly_Loader.js hides text to be hyphenated until hyphenation is done. If something goes wrong (e.g. a resource didn't load correctly) this timeout saves us from an empty page. The timeout is cleared when hyphenation succeeds. If the timeout kicks in, the `onTimeOut`event is fired. See [hide](#hide) about different ways of hiding. @@ -327,7 +327,7 @@ var Hyphenopoly = { }; ```` -Can be set to something visible for testing and documentation. +Can be set to something visible for testing and documentation. Characters that have a special meaning in RegularExpressions (`.\+*?[^]$(){}=!<>|:-`) are not allowed. ### leftmin and rightmin ```` diff --git a/docs/Special-use-cases.md b/docs/Special-use-cases.md index fde3f285..bdd0ae99 100644 --- a/docs/Special-use-cases.md +++ b/docs/Special-use-cases.md @@ -1,7 +1,7 @@ # Special use cases and how-to's 1. [Browserify hyphenopoly.module.js](#browserify-hyphenopolymodulejs) -2. [Webpack](#webpack) +2. [WebPack](#webpack) 3. [Hyphenate depending on media queries](#hyphenate-depending-on-media-queries) 4. [Set .focus() while Hyphenopoly is running](#set-focus-while-hyphenopoly-is-running) @@ -64,7 +64,7 @@ This will generate the script-file `bundle.js`. Usage of a minifying tool (e.g. _Note:_ Make sure the directories referenced in `paths` are available. -## Webpack +## WebPack __Note: A webpacked hyphenopoly.module.js is by far larger then the Hyphenopoly_Loader.js and Hyphenopoly.js scripts which are optimized for usage in browsers.__ Like `browserify` `webpack` will not shim "fs". Thus we have to tell `webpack` to shim the "fs" module with an empty object and configure `hyphenopoly` to use the "http"-loader. @@ -94,7 +94,7 @@ const hyphenator = hyphenopoly.config({ ```` ## Hyphenate depending on media queries -In CSS hyphenation can be restricted to special media-queries. If hyphenation on a website must be dependent of e.g. the width of the window and only active on small screens, you'd do somethink like this: +In CSS hyphenation can be restricted to special media-queries. If hyphenation on a website must be dependent of e.g. the width of the window and only active on small screens, you'd do something like this: ````css @media (max-width: 600px) { .hyphenate { @@ -138,13 +138,15 @@ var Hyphenopoly = { + + + + +

Test 041

+

Control orphans

+
+
+

1: allow orphans

+

Hyphenation algorithm

+

Hy·phen·ation al·go·rithm

+

2: don’t hyphenate the last word of an element

+

Hyphenation algorithm

+

Hy·phen·ation algorithm

+

3: don’t hyphenate the last word of an element and nbsp

+

Hyphenation algorithm

+

Hy·phen·ation algorithm

+
+
Test Ref
+ + \ No newline at end of file diff --git a/testsuite/test5.html b/testsuite/test5.html index 4a9c4007..54729566 100644 --- a/testsuite/test5.html +++ b/testsuite/test5.html @@ -163,7 +163,7 @@

16: sl

Ti|po|gra|fi|ja je veda o ti|po|graf|skem obli|ko|va|nju. Pre|u|ču|je iz|de|la|vo črk in pisav ter nji|ho|vo upo|ra|bo v be|se|di|lu.

17: la

Typographia disciplina transcribendarum litterarum est, quae propositum formam scripturae usu formae litterarum ac gentis litterarum assequi temptat, ut litterae eius simul elegantes et accomodatae sint.

-

Ty|po|gra|phia dis|ci|pli|na tran|scri|ben|da|rum lit|te|ra|rum est, quae pro|po|si|tum for|mam scrip|tu|rae usu for|mae lit|te|ra|rum ac gen|tis lit|te|ra|rum as|se|qui temp|tat, ut lit|te|rae eius simul ele|gan|tes et ac|co|mo|da|tae sint.

+

Ty|po|gra|phia di|sci|pli|na tran|scri|ben|da|rum lit|te|ra|rum est, quae pro|po|si|tum for|mam scrip|tu|rae usu for|mae lit|te|ra|rum ac gen|tis lit|te|ra|rum as|se|qui temp|tat, ut lit|te|rae eius simul ele|gan|tes et ac|co|mo|da|tae sint.

18: no

Typografi er utforming og behandling av skrift, bokstaver og andre grafiske elementer i trykkerfaget og i grafisk design.

Ty|po|gra|fi er ut|for|ming og be|hand|ling av skrift, bok|sta|ver og andre gra|fis|ke ele|men|ter i tryk|ker|fa|get og i gra|fisk de|sign.

diff --git a/testsuite/test7.html b/testsuite/test7.html index b3a2c526..d5929c53 100644 --- a/testsuite/test7.html +++ b/testsuite/test7.html @@ -35,7 +35,7 @@ var i = 1; var test = ""; var ref = ""; - var result = false; + var result = true; while (i <= tests) { test = document.getElementById("test" + i).innerHTML; ref = document.getElementById("ref" + i).innerHTML; @@ -43,10 +43,10 @@ document.getElementById("result").innerHTML += "" + (function (i) { return (i < 10) ? "0" + i : i; }(i)) + " "; - result = result || true; + result = result && true; } else { document.getElementById("result").innerHTML += "" + i + " "; - result = result || false; + result = false; } i += 1; } diff --git a/testsuite/testdriver.js b/testsuite/testdriver.js index 66664b14..71470f13 100644 --- a/testsuite/testdriver.js +++ b/testsuite/testdriver.js @@ -44,7 +44,8 @@ {"exec": true, "path": "test37.html"}, {"exec": true, "path": "test38.html"}, {"exec": true, "path": "test39.html"}, - {"exec": true, "path": "test40.html"} + {"exec": true, "path": "test40.html"}, + {"exec": true, "path": "test41.html"} ]; var testframe = document.getElementById("testframe"); var currentTest = 1; @@ -90,7 +91,7 @@ if (tests[index]) { currentTest = index; if (tests[index].exec) { - window.setTimeout(function defer() { + window.setTimeout(() => { testframe.src = tests[index].path; }, 0); } else { @@ -105,7 +106,7 @@ window.addEventListener( "message", - function onMessage(e) { + (e) => { var msg = JSON.parse(e.data); addTestResult(tests[msg.index].path, msg.desc, msg.result); if (msg.result === "failed") { diff --git a/tools/createWasmForLang.sh b/tools/createWasmForLang.sh index 78bae135..238c92e4 100644 --- a/tools/createWasmForLang.sh +++ b/tools/createWasmForLang.sh @@ -25,7 +25,13 @@ node ./tools/create_imports.js ./lang/$LANG/src/$LANG.hpb > ./lang/$LANG/src/g.t echo '(D) copy TypeScript sources' cp ./src/hyphenEngine.ts ./lang/$LANG/src/hyphenEngine.ts cp ./src/mytransform.js ./lang/$LANG/src/mytransform.js -cp ./src/tsconfig.json ./lang/$LANG/src/tsconfig.json +#cp ./src/tsconfig.json ./lang/$LANG/src/tsconfig.json +echo '{ + "extends": "../../../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +}' > ./lang/$LANG/src/tsconfig.json echo "$LANG\c" > ./lang/$LANG/src/lang.txt echo '(E) compile WASM-Module' diff --git a/tools/minify.sh b/tools/minify.sh index 36962368..62cf553b 100644 --- a/tools/minify.sh +++ b/tools/minify.sh @@ -1,10 +1,19 @@ #!/bin/sh # -*- coding: utf-8 -*- +LOADEROSIZE=$(wc -c min/Hyphenopoly_Loader.js) +H9YOSIZE=$(wc -c min/Hyphenopoly.js) + +echo 'old sizes:' +echo $LOADEROSIZE +echo $H9YOSIZE + mkdir -p min terser Hyphenopoly_Loader.js -o min/Hyphenopoly_Loader.js --comments -c -m --warn terser Hyphenopoly.js -o min/Hyphenopoly.js --comments -c -m --warn + +echo 'new sizes:' wc -c min/Hyphenopoly_Loader.js wc -c min/Hyphenopoly.js diff --git a/tools/tex2hpb.js b/tools/tex2hpb.js index 2ea3b7ff..8cb16d9d 100644 --- a/tools/tex2hpb.js +++ b/tools/tex2hpb.js @@ -173,7 +173,7 @@ const logger = (function createLogger() { } return { - "log": log + log }; }()); @@ -260,7 +260,7 @@ function getPatternsFile() { let patternsfile = fs.readFileSync("./" + patternsFileName, "utf8"); patternsfile = patternsfile.trim(); // eslint-disable-next-line prefer-named-capture-group - patternsfile = patternsfile.replace(/(\d{2})\n/, function repl(ignore, p1) { + patternsfile = patternsfile.replace(/(\d{2})\n/, (ignore, p1) => { const digits = p1.split(""); leftmin = parseInt(digits[0], 10); rightmin = parseInt(digits[1], 10); @@ -302,7 +302,7 @@ function createTranslate(characters) { translateTable.push(0); translateTable[0] += 1; - lines.forEach(function eachLine(value) { + lines.forEach((value) => { translateTable[0] += 1; if (value.length === 2) { translateTable.push(value.charCodeAt(0)); @@ -372,10 +372,10 @@ function createTranslateLookUpTable(translate) { function createExceptionPatterns(exceptions) { const lines = exceptions.split("\n"); const ret = []; - lines.forEach(function eachLine(value, index) { + lines.forEach((value, index) => { if (value !== "") { // eslint-disable-next-line security/detect-object-injection - ret[index] = "_" + value.split("").map(function mapper(c) { + ret[index] = "_" + value.split("").map((c) => { if (c === "-") { return "11"; } @@ -403,8 +403,7 @@ function createPatterns(translate, patterns, exceptionsfile) { } const allPatterns = patterns.split(" "); const exceptions = []; - // eslint-disable-next-line complexity - const translatedPatterns = allPatterns.map(function mapper(pat) { + const translatedPatterns = allPatterns.map((pat) => { let i = 0; let cP1 = 0; let cP2 = 0; @@ -451,7 +450,7 @@ function createPatterns(translate, patterns, exceptionsfile) { const outPatterns = []; - Object.keys(groupedPatterns).forEach(function eachPatternLength(k) { + Object.keys(groupedPatterns).forEach((k) => { groupedPatterns[k].sort(); let l = 0; let j = 0; @@ -698,7 +697,7 @@ function main() { patterns, header.byteLength + paddedLicenseBuf + translate.byteLength ); - fs.writeFile(saveFileName + ".hpb", fileBufferui8, function cb(err) { + fs.writeFile(saveFileName + ".hpb", fileBufferui8, (err) => { if (err) { console.log(err); } else {