Skip to content

Commit

Permalink
Do not hyphenate subtag with different lang in hyphenators (#111)
Browse files Browse the repository at this point in the history
fix: don't hyphenate subtag with different lang

Problem:
language specific hyphenators try to hyphenate elements with
a different language set. This can cause issues if the engine for
that different language isn't ready, yet.

Solution:
Deprecate old use of hyphenators and emit warning when a
stringHyphenator gets an HTMLElement to hyphenate, but still
hyphenate the element (but not it's children).
Implement two types of hyphenators:
-    language specific (hyphenate strings)
-    polyglot (hyphenate HTMLElements)

Notes:
With this, the PR can go in a minor release. Major release is to early.
Remove this functionality in next major release.

Makes createHyphenator private.

Fixes #108, [skip travis-ci]
  • Loading branch information
mnater authored Apr 16, 2020
1 parent f154a36 commit 59c6289
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 38 deletions.
55 changes: 43 additions & 12 deletions Hyphenopoly.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,11 @@
H.events.delete(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);
/* eslint-disable security/detect-object-injection */
if (H.handleEvent[name]) {
H.handleEvent[name](v);
}
/* eslint-enable security/detect-object-injection */
});
}

Expand Down Expand Up @@ -650,20 +653,35 @@
return r;
}

H.createHyphenator = ((lang) => {
/**
* Creates a language-specific string hyphenator
* @param {String} lang - The language this hyphenator hyphenates
*/
function createStringHyphenator(lang) {
return ((entity, sel = ".hyphenate") => {
if (entity instanceof HTMLElement) {
const elements = collectElements(entity, sel);
elements.each((l, els) => {
els.forEach((elo) => {
hyphenate(l, elo.selector, elo.element);
});
if (typeof entity !== "string") {
H.events.get("error").resolve({
"msg": "This use of hyphenators is deprecated. See https://mnater.github.io/Hyphenopoly/Hyphenators.html"
});
return null;
}
return hyphenate(lang, sel, entity);
});
});
}

/**
* Creates a polyglot HTML hyphenator
*/
function createDOMHyphenator() {
return ((entity, sel = ".hyphenate") => {
const elements = collectElements(entity, sel);
elements.each((l, els) => {
els.forEach((elo) => {
hyphenate(l, elo.selector, elo.element);
});
});
return null;
});
}

H.unhyphenate = () => {
return H.res.get("els").then((elements) => {
Expand Down Expand Up @@ -798,7 +816,7 @@
});
lo.ready = true;
// eslint-disable-next-line security/detect-object-injection
H.hyphenators[lang].resolve(H.createHyphenator(lang));
H.hyphenators[lang].resolve(createStringHyphenator(lang));
}
if (H.events.has("engineReady")) {
H.events.get("engineReady").resolve(lang);
Expand Down Expand Up @@ -958,5 +976,18 @@
H.res.get("he").forEach((heProm, lang) => {
instantiateWasmEngine(heProm, lang);
});

Promise.all(
// Check all entries except "HTML"
Object.entries(H.hyphenators).
reduce((accumulator, value) => {
if (value[0] !== "HTML") {
return accumulator.concat(value[1]);
}
return accumulator;
}, [])
).then(() => {
H.hyphenators.HTML.resolve(createDOMHyphenator());
});
})(Hyphenopoly);
})(window);
1 change: 1 addition & 0 deletions Hyphenopoly_Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
}
/* eslint-enable security/detect-object-injection */
});
H.hyphenators.HTML = H.defProm();
(() => {
if (he && he.polyfill) {
he.polyfill();
Expand Down
40 changes: 27 additions & 13 deletions docs/Hyphenators.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ Possible use cases are:
* hyphenating text provided by the user (e.g. in a preview window of a blogging software)
*

For this use cases Hyphenopoly.js exposes `hyphenators` – language specific functions that hyphenates a string or a DOM-Object.
For this use cases Hyphenopoly.js exposes `hyphenators` – functions that hyphenate strings or DOM-Objects.

There are two types of `hyphenators`:
* language specific `hyphenators` that can only hyphenate `strings`
* a polyglot `HTML`-hyphenator that can hyphenate DOM-objects of type `HTMLElement`

## Create and access `Hyphenopoly.hyphenators`
`hyphenators` are language specific functions that hyphenate their input.

Hyphenopoly_Loader.js creates a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) for a `hyphenator` for each language it loads (i.e. languages the UA doesn't support or languages you "FORCEHYPHENOPOLY"-ied).
Additionally it creates a Promise for a `HTML`-hyphenator, that is able to hyphenate HTMLEntities of all loaded languages.

````html
<script>
Expand All @@ -24,16 +28,16 @@ Hyphenopoly_Loader.js creates a [Promise](https://developer.mozilla.org/en-US/do
</script>
<script src="./Hyphenopoly_Loader.js"></script>
<script>
console.log(Hyphenopoly.hyphenators); //{en-us: Promise}
console.log(Hyphenopoly.hyphenators); //{en-us: Promise, HTML: Promise}
</script>
````

In the example above we enforced Hyphenopoly_Loader.js to use Hyphenopoly.js for `en-us`. Since the UA seems to support CSS3-hyphens for German, `Hyphenopoly.hyphenators` only contain a Promise for a `en-us`-hyphenator.
In the example above we enforced Hyphenopoly_Loader.js to use Hyphenopoly.js for `en-us`. Since the UA seems to support CSS3-hyphens for German, `Hyphenopoly.hyphenators` only contain a Promise for a `en-us`-hyphenator and a Promise for the `HTML`-hyphenator.

## Use `Hyphenopoly.hyphenators` for Strings
## Use `Hyphenopoly.hyphenators[<lang>]` for Strings
`hyphenators` are Promises. They are resolved as soon as everything necessary is loaded and ready (or rejected when something goes wrong). `hyphenators` resolve to a language specific function (a `hyphenator`) that hyphenates its input according to the settings for selectors (default: `.hyphenate`):

`function hyphenator({string|DOM-Element}, [Optional: selector=".hyphenate"]) => {string|undefined}`
`function hyphenator({string}, [Optional: selector=".hyphenate"]) => {string|undefined}`

````html
<script>
Expand Down Expand Up @@ -69,8 +73,8 @@ 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 (including its children) and return nothing (`null`).
## Use `Hyphenopoly.hyphenators.HTML` for DOM-Elements
Objects of type `HTMLElement` can be hyphenated with the `HTML`-hyphenator (`Hyphenopoly.hyphenators.HTML`). The `HTML`-hyphenator hyphenates the handed over `HTMLElement` and all it's `childElements` if their language is one of the loaded languages directly and returns `null`.

````html
<html>
Expand All @@ -92,25 +96,35 @@ When handing over a `HTMLELEMENT` instead of a string, `hyphenators` directly hy
</script>
<script src="./Hyphenopoly_Loader.js"></script>
<script>
Hyphenopoly.hyphenators["en-us"].then((hyphenator_en) => {
Hyphenopoly.hyphenators["HTML"].then((hyphenator_en) => {
hyphenator_en(document.getElementById("hyphenateme"));
});
</script>
</head>
<body>
<div id="hyphenateme">Supercalifragilisticexpialidocious</div>
<div id="hyphenateme">
<span lang="en-us">hyphenation</span>
<span lang="de">Silbentrennung</span>
</div>
<!--becomes -->
<!--<div id="hyphenateme">Su•per•cal•ifrag•ilis•tic•ex•pi•ali•do•cious</div>-->
<!--
<div id="hyphenateme">
<span lang="en-us">hy•phen•ation</span>
<span lang="de">Silbentrennung</span>
</div>
-->
</body>
</html>
````

In the example above we assume that the browser supports hyphention for German. So the `HTML`-hyphenator only hyphenates Englisch elements.

## Further notes and compatibility
Instead of using `.then` on the Promises we could also use `async/await`:

````javascript
async function runHyphenator(id) {
(await Hyphenopoly.hyphenators["en-us"])(document.getElementById(id));
(await Hyphenopoly.hyphenators["HTML"])(document.getElementById(id));
}
runHyphenator("hyphenateme");
````
Expand Down Expand Up @@ -194,7 +208,7 @@ class Toggle extends React.Component {
const el = this.el;
//if hyphenation is handled by CSS, Hyphenopoly is undefined
if (window.Hyphenopoly) {
window.Hyphenopoly.hyphenators["en-us"].then(
window.Hyphenopoly.hyphenators["HTML"].then(
function (enHyphenator) {
enHyphenator(el);
}
Expand Down
29 changes: 16 additions & 13 deletions testsuite/test42.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
document.getElementById("test2").innerText = (await Hyphenopoly.hyphenators.de)(text);
assert();
}*/
function hyphenate_de(text) {
Hyphenopoly.hyphenators.de.then(function (dehyph) {
dehyph(document.getElementById("test2"), ".usePipe");
dehyph(document.getElementById("test3"), ".usePipe");
dehyph(document.getElementById("test4"), ".usePipe");
dehyph(document.getElementById("test5"), ".usePipe");
dehyph(document.getElementById("test6"), ".usePipe");
assertAll();
});
}
hyphenate_de("Silbentrennung verbessert den Blocksatz.");
Hyphenopoly.hyphenators.de.then(function (dehyphenator) {
dehyphenator(document.getElementById("test7"), ".usePipe");
});
Hyphenopoly.hyphenators.HTML.then(function (hyphenateHTML) {
hyphenateHTML(document.getElementById("test2"), ".usePipe");
hyphenateHTML(document.getElementById("test3"), ".usePipe");
hyphenateHTML(document.getElementById("test4"), ".usePipe");
hyphenateHTML(document.getElementById("test5"), ".usePipe");
hyphenateHTML(document.getElementById("test6"), ".usePipe");
assertAll();
});
}
}
};
Expand Down Expand Up @@ -101,8 +101,8 @@ <h1>Test 042</h1>
<p id="desc">Use hyphenators to hyphenate element tree.</p>
<div id="result">R: </div>
<h2>1: Hyphenate automatically</h2>
<p id="test1" class="test hyphenate" lang="de">Silbentrennungsalgorithmus</p>
<p id="ref1" class="ref" lang="de">Sil•ben•tren•nungs•al•go•rith•mus</p>
<p id="test1" class="test hyphenate" lang="de">Silbentrennungsalgorithmus <span lang="en-us">hyphenation</span></p>
<p id="ref1" class="ref" lang="de">Sil•ben•tren•nungs•al•go•rith•mus <span lang="en-us">hy•phen•ation</span></p>
<h2>2: Hyphenator with one child (lang tag)</h2>
<p id="test2" class="test" lang="de">Silbentrennung <span>verbessert</span> den Blocksatz.</p>
<p id="ref2" class="ref" lang="de">Sil|ben|tren|nung <span>ver|bes|sert</span> den Block|satz.</p>
Expand All @@ -126,6 +126,9 @@ <h2>5: Formatted HTML</h2>
<h2>6: Rehyphenate with hyphenator</h2>
<p id="test6" class="test hyphenate" lang="de">Computertypographie</p>
<p id="ref6" class="ref" lang="de">Com•pu•ter•ty•po•gra•phie</p>
<h2>7: use H.hyphenators.de on HTMLElement</h2>
<p id="test7" class="test hyphenate" lang="de">Computertypographie</p>
<p id="ref7" class="ref" lang="de">Com•pu•ter•ty•po•gra•phie</p>
<hr>
<div><span class="test">Test</span> <span class="ref">Ref</span></div>

Expand Down

0 comments on commit 59c6289

Please sign in to comment.