Browse Source

Copy old files and rework look a bit

pull/1/head
Czarlie 7 months ago
parent
commit
5bfc16ff80
100 changed files with 3996 additions and 0 deletions
  1. BIN
      Bitter.woff2
  2. +60
    -0
      Hyphenopoly/.all-contributorsrc
  3. +141
    -0
      Hyphenopoly/.eslintrc
  4. +8
    -0
      Hyphenopoly/.travis.yml
  5. +216
    -0
      Hyphenopoly/CHANGELOG.md
  6. +981
    -0
      Hyphenopoly/Hyphenopoly.js
  7. +353
    -0
      Hyphenopoly/Hyphenopoly_Loader.js
  8. +21
    -0
      Hyphenopoly/LICENSE
  9. +182
    -0
      Hyphenopoly/README.md
  10. +3
    -0
      Hyphenopoly/docs/Branching-Model.md
  11. +95
    -0
      Hyphenopoly/docs/Coping-with-hyphenation-errors.md
  12. +33
    -0
      Hyphenopoly/docs/Download,-install-and-setup-for-deployment.md
  13. +115
    -0
      Hyphenopoly/docs/Events.md
  14. +7
    -0
      Hyphenopoly/docs/Gemfile
  15. +248
    -0
      Hyphenopoly/docs/Gemfile.lock
  16. +138
    -0
      Hyphenopoly/docs/Global-Hyphenopoly-Object.md
  17. +234
    -0
      Hyphenopoly/docs/Hyphenators.md
  18. +153
    -0
      Hyphenopoly/docs/Node-Module.md
  19. +505
    -0
      Hyphenopoly/docs/Setup.md
  20. +282
    -0
      Hyphenopoly/docs/Special-use-cases.md
  21. +57
    -0
      Hyphenopoly/docs/Usage-of-devDependencies.md
  22. +1
    -0
      Hyphenopoly/docs/_config.yml
  23. +1
    -0
      Hyphenopoly/docs/dist/index.html
  24. +9
    -0
      Hyphenopoly/docs/dist/js/hyphenopoly/Hyphenopoly.js
  25. BIN
      Hyphenopoly/docs/dist/js/hyphenopoly/patterns/de.wasm
  26. BIN
      Hyphenopoly/docs/dist/js/hyphenopoly/patterns/en-us.wasm
  27. BIN
      Hyphenopoly/docs/dist/js/hyphenopoly/patterns/es.wasm
  28. BIN
      Hyphenopoly/docs/dist/js/hyphenopoly/patterns/it.wasm
  29. +1
    -0
      Hyphenopoly/docs/dist/js/main.4ed9db5cdc68a8aa27cc.bundle.js
  30. +1
    -0
      Hyphenopoly/docs/dist/js/main.d9bab6d06a0682f5ff15.bundle.js
  31. +1
    -0
      Hyphenopoly/docs/dist/js/runtime.128aaed72608b13685d7.bundle.js
  32. +1
    -0
      Hyphenopoly/docs/dist/js/runtime.9aeb478c6c5b3465d2af.bundle.js
  33. +25
    -0
      Hyphenopoly/docs/dist/js/vendor_body.9097fa9c35779a1cbeca.bundle.js
  34. +10
    -0
      Hyphenopoly/docs/dist/js/vendor_head.09ed1a3cacffe89819d6.bundle.js
  35. +10
    -0
      Hyphenopoly/docs/dist/js/vendor_head.25990bd727f05ac4d643.bundle.js
  36. +34
    -0
      Hyphenopoly/docs/index.md
  37. +9
    -0
      Hyphenopoly/docs/min/Hyphenopoly.js
  38. +9
    -0
      Hyphenopoly/docs/min/Hyphenopoly_Loader.js
  39. +52
    -0
      Hyphenopoly/docs/min/example.html
  40. BIN
      Hyphenopoly/docs/min/patterns/af.wasm
  41. BIN
      Hyphenopoly/docs/min/patterns/as.wasm
  42. BIN
      Hyphenopoly/docs/min/patterns/be.wasm
  43. BIN
      Hyphenopoly/docs/min/patterns/bg.wasm
  44. BIN
      Hyphenopoly/docs/min/patterns/bn.wasm
  45. BIN
      Hyphenopoly/docs/min/patterns/ca.wasm
  46. BIN
      Hyphenopoly/docs/min/patterns/cop.wasm
  47. BIN
      Hyphenopoly/docs/min/patterns/cs.wasm
  48. BIN
      Hyphenopoly/docs/min/patterns/cy.wasm
  49. BIN
      Hyphenopoly/docs/min/patterns/da.wasm
  50. BIN
      Hyphenopoly/docs/min/patterns/de.wasm
  51. BIN
      Hyphenopoly/docs/min/patterns/el-monoton.wasm
  52. BIN
      Hyphenopoly/docs/min/patterns/el-polyton.wasm
  53. BIN
      Hyphenopoly/docs/min/patterns/en-gb.wasm
  54. BIN
      Hyphenopoly/docs/min/patterns/en-us.wasm
  55. BIN
      Hyphenopoly/docs/min/patterns/eo.wasm
  56. BIN
      Hyphenopoly/docs/min/patterns/es.wasm
  57. BIN
      Hyphenopoly/docs/min/patterns/et.wasm
  58. BIN
      Hyphenopoly/docs/min/patterns/eu.wasm
  59. BIN
      Hyphenopoly/docs/min/patterns/fi.wasm
  60. BIN
      Hyphenopoly/docs/min/patterns/fr.wasm
  61. BIN
      Hyphenopoly/docs/min/patterns/fur.wasm
  62. BIN
      Hyphenopoly/docs/min/patterns/ga.wasm
  63. BIN
      Hyphenopoly/docs/min/patterns/gl.wasm
  64. BIN
      Hyphenopoly/docs/min/patterns/grc.wasm
  65. BIN
      Hyphenopoly/docs/min/patterns/gu.wasm
  66. BIN
      Hyphenopoly/docs/min/patterns/hi.wasm
  67. BIN
      Hyphenopoly/docs/min/patterns/hr.wasm
  68. BIN
      Hyphenopoly/docs/min/patterns/hsb.wasm
  69. BIN
      Hyphenopoly/docs/min/patterns/hu.wasm
  70. BIN
      Hyphenopoly/docs/min/patterns/hy.wasm
  71. BIN
      Hyphenopoly/docs/min/patterns/ia.wasm
  72. BIN
      Hyphenopoly/docs/min/patterns/id.wasm
  73. BIN
      Hyphenopoly/docs/min/patterns/is.wasm
  74. BIN
      Hyphenopoly/docs/min/patterns/it.wasm
  75. BIN
      Hyphenopoly/docs/min/patterns/ka.wasm
  76. BIN
      Hyphenopoly/docs/min/patterns/kmr.wasm
  77. BIN
      Hyphenopoly/docs/min/patterns/kn.wasm
  78. BIN
      Hyphenopoly/docs/min/patterns/la.wasm
  79. BIN
      Hyphenopoly/docs/min/patterns/lt.wasm
  80. BIN
      Hyphenopoly/docs/min/patterns/lv.wasm
  81. BIN
      Hyphenopoly/docs/min/patterns/mk.wasm
  82. BIN
      Hyphenopoly/docs/min/patterns/ml.wasm
  83. BIN
      Hyphenopoly/docs/min/patterns/mn-cyrl.wasm
  84. BIN
      Hyphenopoly/docs/min/patterns/mr.wasm
  85. BIN
      Hyphenopoly/docs/min/patterns/mul-ethi.wasm
  86. BIN
      Hyphenopoly/docs/min/patterns/nb-no.wasm
  87. BIN
      Hyphenopoly/docs/min/patterns/nl.wasm
  88. BIN
      Hyphenopoly/docs/min/patterns/nn.wasm
  89. BIN
      Hyphenopoly/docs/min/patterns/oc.wasm
  90. BIN
      Hyphenopoly/docs/min/patterns/or.wasm
  91. BIN
      Hyphenopoly/docs/min/patterns/pa.wasm
  92. BIN
      Hyphenopoly/docs/min/patterns/pi.wasm
  93. BIN
      Hyphenopoly/docs/min/patterns/pl.wasm
  94. BIN
      Hyphenopoly/docs/min/patterns/pms.wasm
  95. BIN
      Hyphenopoly/docs/min/patterns/pt.wasm
  96. BIN
      Hyphenopoly/docs/min/patterns/rm.wasm
  97. BIN
      Hyphenopoly/docs/min/patterns/ro.wasm
  98. BIN
      Hyphenopoly/docs/min/patterns/ru.wasm
  99. BIN
      Hyphenopoly/docs/min/patterns/sh-cyrl.wasm
  100. BIN
      Hyphenopoly/docs/min/patterns/sh-latn.wasm

BIN
Bitter.woff2 View File


+ 60
- 0
Hyphenopoly/.all-contributorsrc View File

@@ -0,0 +1,60 @@
{
"projectName": "Hyphenopoly",
"projectOwner": "mnater",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "eslint",
"contributors": [
{
"login": "tbroadley",
"name": "Thomas Broadley",
"avatar_url": "https://avatars0.githubusercontent.com/u/8731922?v=4",
"profile": "http://thomasbroadley.com",
"contributions": [
"doc"
]
},
{
"login": "pothos",
"name": "Kai Lüke",
"avatar_url": "https://avatars0.githubusercontent.com/u/1189130?v=4",
"profile": "https://kailueke.gitlab.io/",
"contributions": [
"code"
]
},
{
"login": "blankse",
"name": "Sebastian Blank",
"avatar_url": "https://avatars2.githubusercontent.com/u/998558?v=4",
"profile": "http://www.data-factory.net/",
"contributions": [
"example"
]
},
{
"login": "ReLater",
"name": "ReLater",
"avatar_url": "https://avatars2.githubusercontent.com/u/20780646?v=4",
"profile": "https://www.ghsvs.de",
"contributions": [
"maintenance"
]
},
{
"login": "julian-zatloukal",
"name": "julian-zatloukal",
"avatar_url": "https://avatars3.githubusercontent.com/u/58230917?v=4",
"profile": "https://github.com/julian-zatloukal",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 7
}

+ 141
- 0
Hyphenopoly/.eslintrc View File

@@ -0,0 +1,141 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 2020
},
"env": {
"browser": true,
"es6": true
},
"ignorePatterns": ["min/*.js"],
"plugins": [
"security"
],
"extends": [
"eslint:all",
"plugin:security/recommended"
],
"rules": {
"no-undef": "error",
"complexity": [
"error",
6
],
"require-unicode-regexp": 0,
"require-jsdoc": [
"error",
{
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": true,
"FunctionExpression": true
}
}
],
"no-template-curly-in-string": 2,
"prefer-template": 0,
"wrap-iife": 1,
"max-lines": 0,
"max-lines-per-function": 0,
"max-len": [
1,
{
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}
],
"max-statements": 0,
"space-before-function-paren": [
1,
{
"anonymous": "always",
"named": "never",
"asyncArrow": "never"
}
],
"padded-blocks": [
1,
"never"
],
"id-length": [
1,
{
"min": 1
}
],
"one-var": 0,
"func-names": [
1,
"as-needed"
],
"func-style": [
1,
"declaration",
{
"allowArrowFunctions": true
}
],
"arrow-body-style": ["error", "always"],
"object-shorthand": 1,
"prefer-arrow-callback": 2,
"no-param-reassign": 0,
"no-extra-parens": 0,
"array-element-newline": [
1,
{
"multiline": false,
"minItems": 6
}
],
"no-magic-numbers": 0,
"function-paren-newline": [
1,
"consistent"
],
"function-call-argument-newline": [
1,
"consistent"
],
"no-ternary": 0,
"no-nested-ternary": 0,
"prefer-destructuring": 0,
"max-params": [
1,
5
],
"lines-around-comment": [
1,
{
"allowBlockStart": true
}
],
"sort-keys": [
1,
"asc",
{
"caseSensitive": false
}
],
"prefer-named-capture-group": 0
},
"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
}

+ 8
- 0
Hyphenopoly/.travis.yml View File

@@ -0,0 +1,8 @@
language: node_js
cache: bundler
node_js:
- lts/*
os:
- "linux"
notifications:
email: false

+ 216
- 0
Hyphenopoly/CHANGELOG.md View File

@@ -0,0 +1,216 @@
# Version History

## Version 4.4.0 (2020-05-04)
### Changed
* convert objects to maps (no API changes)

### Fixed
* bumb devDependencies
* simplify webpack example

## Version 4.3.0 (2020-04-17)
### Fixed
* Hyphenators handle subtags with different lang consistently (issue #108)
* Better word matching RegEx (issue #109)
* Emit multiple errors (instead of just one) (issue #112)
* bumpDevDependencies

### Added
* API to define character substitutions: [https://mnater.github.io/Hyphenopoly/Setup.html#substitute](doc) (issue #109)

### Changed
* Words with foreign characters are not hyphenated anymore (issue #109)

## Version 4.2.1 (2020-03-31)
### Fixed
* Enhance documentation (serve minified hyphenopoly in [https://mnater.github.io/Hyphenopoly/min/](https://mnater.github.io/Hyphenopoly/min/) and move examples to examples directory) (issue #104)
* exclude .DS_Store from npm package
* bumpDevDependencies

## Version 4.2.0 (2020-03-27)
### Added
* Added support for Macedonian

### Changed
* RequestCredentials are now configurable. See [doc](https://mnater.github.io/Hyphenopoly/Setup.html#corscredentials) (issue #98)
* Hyphenators now hyphenate content of childNodes, too (#issue96)
* Update patterns for Spanish

### Fixed
* Don't try to hyphenate whitespace-only text nodes
* bump devDependencies

## 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 file sizes and thus better performance.

### Changed
* No fallback to asm.js
* hyphenEngine and patterns in one file per language
* Promise based events
* Usage of modern ES2016 features

## Version 3.4.0 (2019-12-26)
### Added
* Option to disable mixed cased words (issue #91)

### Fixed
* Refactor encloseHyphenateFunction and enclHyphenate
* bump devDependencies

## Version 3.3.0 (2019-10-14)
### Added
* Add configuration option `keepAlive` (issue #69)
* Add documentation for issue #89

### Fixed
* Fixed issue where babelized Loader didn't work in iOS9 (issue #88)
* bump dev Dependencies (removed some unused eslint directives)

## Version 3.2.1 (2019-08-29)
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 issue with Firefox 68, where feature detection in Hyphenopoly_Loader fails
* bump devDependencies

### Changed
* OnCopy-eventHandler now also includes content with type "text/html" (besides "text/plain") (issue #87)

## Version 3.1.2 (2019-07-24)
### Fixed
* Fixed "Critical dependency: the request of a dependency is an expression" in webpack (issue #70)
* bump devDependencies

## Version 3.1.1 (2019-06-28)
### Fixed
* bump devDependencies

### Changed
* 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

## Version 3.1.0 (2019-05-28)
### Fixed
* Correctly reject hyphenator promises (issue #77)
* performance: reduce file size of Hyphenopoly_Loader.js
* update devDependencies

### Added
* feature: get maindir and patterndir from currentScript

## Version 3.0.2 (2019-04-28)
### Security
* refactor(Loader): don't use innerHTML
### Fixed
* performance: slightly improved hyphenEngine
* bump devDependencies

## Version 3.0.1 (2019-04-04)
### Fixed
* Improve the way how `registerOnCopy` builds a closure. This prevents a memory leak.
* Removed "use strict" from RunKit example.
* Adapt the initial sizes of wasm-memory in some special cases

## Version 3.0.0 (2019-03-29)
### Changed
* BREAKING CHANGE: implement new hyphenation pattern binary (.hpb) format (issue #61)
* update patterns to new format and include left-/rightmin according to source
* update hyphenEngine.asm/.wasm to consume new pattern format
* implement .hpb-version-check in Hyphenopoly.js and hyphenopoly.module.js
* fix issue #65

### Added
* feat: tearDown event (issue #67)
* feat: loadError event (issue #59)
* feat: hyphenopoly.module is now browserifyable. Added new config option "loader"

### Removed
* BREAKING CHANGE: remove `Hyphenopoly.setup.classnames` – use [selectors](https://github.com/mnater/Hyphenopoly/wiki/Global-Hyphenopoly-Object#new-with-version-260-selectors) instead

## 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 resources (issue #58)

## 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 (issue #53)
* implement `Hyphenopoly.unhyphenate()` (needed for issue #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 refactoring for smaller code size
* fixed a StateError in IE 11

## Version 2.6.0 (Dec 01, 2018)
* improve hiding of elements while hyphenating (issue #40)
* fix several issues with lang-fallbacks (issue #41 and #44)
* new feature: use selectors instead of classnames (issue #42)
* updated german patterns (issue #45)

## Version 2.5.1 (Nov 04, 2018)
* remove "Church Slavonic" patterns (see #38)
* fix issue #39

## Version 2.5.0 (Oct 02, 2018)
* fix issues with very long word (#33 and #34)
* Hyphenopoly exposes [Hyphenators](https://github.com/mnater/Hyphenopoly/wiki/Hyphenators)
* fix issues with very long word (#33 and #34)
* 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 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 refactoring

## Version 2.2.0 (June 26, 2018)
* provide example.js for RunKit
* use tap instead of mocha
* [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 (May 27, 2018)
* Configure Travis-CI
* bug fixes

## 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)
* better error handling (f4bbaa7759eed24208e5cd7c744f1131262abb20, 1c7b0b67666b507d6f6b02eea38460562a5835e4)
* correct implementation of e.preventDefault (df988788db6fb7120fc0c8a1cff1c91aac5a3998)
* 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 (May 13, 2018)
Prevent browsers to force layout on feature test in some cases.

## Version 1.0.0 (May 12, 2018)
First release

+ 981
- 0
Hyphenopoly/Hyphenopoly.js View File

@@ -0,0 +1,981 @@
/**
* @license Hyphenopoly 4.4.0 - client side hyphenation for webbrowsers
* ©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, o) => {
"use strict";
const SOFTHYPHEN = "\u00AD";

/**
* 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
*/
const 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 */
};

/**
* Event
*/
const event = ((H) => {
const knownEvents = new Map([
["afterElementHyphenation", []],
["beforeElementHyphenation", []],
["engineReady", []],
[
"error", [
(e) => {
if (e.runDefault) {
w.console.warn(e.msg);
}
}
]
],
["hyphenopolyEnd", []],
["hyphenopolyStart", []]
]);
if (H.handleEvent) {
const userEvents = new Map(o.entries(H.handleEvent));
knownEvents.forEach((eventFuncs, eventName) => {
if (userEvents.has(eventName)) {
eventFuncs.unshift(userEvents.get(eventName));
}
});
}
return {
"fire": ((eventName, eventData) => {
eventData.runDefault = true;
eventData.preventDefault = () => {
eventData.runDefault = false;
};
knownEvents.get(eventName).forEach((eventFn) => {
eventFn(eventData);
});
})
};
})(Hyphenopoly);

/**
* Register copy event on element
* @param {Object} el The element
* @returns {undefined}
*/
function registerOnCopy(el) {
el.addEventListener(
"copy",
(e) => {
e.preventDefault();
const sel = w.getSelection();
const docFrag = sel.getRangeAt(0).cloneContents();
const div = document.createElement("div");
div.appendChild(docFrag);
const selectedHTML = div.innerHTML;
const selectedText = sel.toString();
const re = RegExp(SOFTHYPHEN, "g");
e.clipboardData.setData("text/plain", selectedText.replace(re, ""));
e.clipboardData.setData("text/html", selectedHTML.replace(re, ""));
},
true
);
}

/**
* Convert settings from H.setup-Object to Map
* This is a IIFE to keep complexity low.
*/
((H) => {
/**
* Create a Map with a default Map behind the scenes. This mimics
* kind of a prototype chain of an object, but without the object-
* injection security risk.
*
* @param {Map} defaultsMap - A Map with default values
* @returns {Proxy} - A Proxy for the Map (dot-notation or get/set)
*/
function createMapWithDefaults(defaultsMap) {
const userMap = new Map();

/**
* The get-trap: get the value from userMap or else from defaults
* @param {Sring} key - The key to retrieve the value for
* @returns {*}
*/
function get(key) {
return (userMap.has(key))
? userMap.get(key)
: defaultsMap.get(key);
}

/**
* The set-trap: set the value to userMap and don't touch defaults
* @param {Sring} key - The key for the value
* @param {*} value - The value
* @returns {*}
*/
function set(key, value) {
userMap.set(key, value);
}
return new Proxy(defaultsMap, {
"get": (_target, prop) => {
if (prop === "set") {
return set;
}
if (prop === "get") {
return get;
}
return get(prop);
},
"ownKeys": () => {
return [
...new Set(
[...defaultsMap.keys(), ...userMap.keys()]
)
];
}
});
}

const settings = createMapWithDefaults(new Map([
["defaultLanguage", "en-us"],
[
"dontHyphenate", (() => {
const list = "abbr,acronym,audio,br,button,code,img,input,kbd,label,math,option,pre,samp,script,style,sub,sup,svg,textarea,var,video";
return createMapWithDefaults(
new Map(list.split(",").map((val) => {
return [val, true];
}))
);
})()
],
["dontHyphenateClass", "donthyphenate"],
["exceptions", new Map()],
["keepAlive", true],
["normalize", false],
["safeCopy", true],
["substitute", new Map()],
["timeout", 1000]
]));
o.entries(H.setup).forEach(([key, value]) => {
switch (key) {
case "selectors":
// Set settings.selectors to array of selectors
settings.set("selectors", o.keys(value));

/*
* For each selector add a property to settings with
* selector specific settings
*/
o.entries(value).forEach(([sel, selSettings]) => {
const selectorSettings = createMapWithDefaults(new Map([
["compound", "hyphen"],
["hyphen", SOFTHYPHEN],
["leftmin", 0],
["leftminPerLang", 0],
["minWordLength", 6],
["mixedCase", true],
["orphanControl", 1],
["rightmin", 0],
["rightminPerLang", 0]
]));
o.entries(selSettings).forEach(
([selSetting, setVal]) => {
if (typeof setVal === "object") {
selectorSettings.set(
selSetting,
new Map(o.entries(setVal))
);
} else {
selectorSettings.set(selSetting, setVal);
}
}
);
settings.set(sel, selectorSettings);
});
break;
case "dontHyphenate":
case "exceptions":
o.entries(value).forEach(([k, v]) => {
settings.get(key).set(k, v);
});
break;
case "substitute":
o.entries(value).forEach(([lang, subst]) => {
settings.substitute.set(
lang,
new Map(o.entries(subst))
);
});
break;
default:
settings.set(key, value);
}
});
H.c = settings;
})(Hyphenopoly);

((H) => {
const C = H.c;
let mainLanguage = null;

event.fire(
"hyphenopolyStart",
{
"msg": "hyphenopolyStart"
}
);

/**
* Factory for elements
* @returns {Object} elements-object
*/
function makeElementCollection() {
const list = new Map();

/*
* Counter counts the elements to be hyphenated.
* Needs to be an object (Pass by reference)
*/
const counter = [0];

/**
* Add element to elements
* @param {object} el The element
* @param {string} lang The language of the element
* @param {string} sel The selector of the element
* @returns {Object} An element-object
*/
function add(el, lang, sel) {
const elo = {
"element": el,
"selector": sel
};
if (!list.has(lang)) {
list.set(lang, []);
}
list.get(lang).push(elo);
counter[0] += 1;
return elo;
}

/**
* Removes elements from the list and updates the counter
* @param {string} lang - The lang of the elements to remove
*/
function rem(lang) {
let langCount = 0;
if (list.has(lang)) {
langCount = list.get(lang).length;
list.delete(lang);
counter[0] -= langCount;
if (counter[0] === 0) {
event.fire(
"hyphenopolyEnd",
{
"msg": "hyphenopolyEnd"
}
);
if (!C.keepAlive) {
window.Hyphenopoly = null;
}
}
}
}

/**
* Execute fn for each element
* @param {function} fn The function to execute
* @returns {undefined}
*/
function each(fn) {
list.forEach((val, key) => {
fn(key, val);
});
}

return {
add,
counter,
each,
list,
rem
};
}

/**
* Get language of element by searching its parents or fallback
* @param {Object} el The element
* @param {boolean} fallback Will falback to mainlanguage
* @returns {string|null} The language or null
*/
function getLang(el, fallback) {
try {
return (el.getAttribute("lang"))
? el.getAttribute("lang").toLowerCase()
: el.tagName.toLowerCase() === "html"
? fallback
? mainLanguage
: null
: getLang(el.parentNode, fallback);
} catch (ignore) {
return null;
}
}

/**
* Collect elements that have a selector defined in C.selectors
* and add them to elements.
* @returns {undefined}
*/
function collectElements(parent = null, selector = null) {
const elements = makeElementCollection();

const dontHyphenateSelector = (() => {
let s = "." + C.dontHyphenateClass;
o.getOwnPropertyNames(C.dontHyphenate).forEach((tag) => {
if (C.dontHyphenate.get(tag)) {
s += "," + tag;
}
});
return s;
})();
const matchingSelectors = C.selectors.join(",") + "," + dontHyphenateSelector;

/**
* 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.
* @param {Object} el The element to scan
* @param {string} pLang The language of the oarent element
* @param {string} sel The selector of the parent element
* @param {boolean} isChild If el is a child element
* @returns {undefined}
*/
function processElements(el, pLang, sel, isChild = false) {
const eLang = getElementLanguage(el, pLang);
const langDef = H.cf.get("langs").get(eLang);
if (langDef === "H9Y") {
elements.add(el, eLang, sel);
if (!isChild && C.safeCopy) {
registerOnCopy(el);
}
} else if (!langDef) {
event.fire(
"error",
{
"msg": `Element with '${eLang}' found, but '${eLang}.hpb' not loaded. Check language tags!`
}
);
}
el.childNodes.forEach((n) => {
if (n.nodeType === 1 && !n.matches(matchingSelectors)) {
processElements(n, eLang, sel, true);
}
});
}
if (parent === null) {
C.selectors.forEach((sel) => {
w.document.querySelectorAll(sel).forEach((n) => {
processElements(n, getLang(n, true), sel, false);
});
});
} else {
processElements(parent, getLang(parent, true), selector, true);
}
return elements;
}

const wordHyphenatorPool = new Map();

/**
* Factory for hyphenatorFunctions for a specific language and selector
* @param {Object} lo Language-Object
* @param {string} lang The language
* @param {string} sel The selector
* @returns {function} The hyphenate function
*/
function createWordHyphenator(lo, lang, sel) {
const poolKey = lang + "-" + sel;
if (wordHyphenatorPool.has(poolKey)) {
return wordHyphenatorPool.get(poolKey);
}

const selSettings = C.get(sel);
const hyphen = selSettings.hyphen;
lo.cache.set(sel, new Map());

/**
* HyphenateFunction for non-compound words
* @param {string} word The word
* @returns {string} The hyphenated word
*/
function hyphenateNormal(word) {
if (word.length > 61) {
event.fire(
"error",
{
"msg": "found word longer than 61 characters"
}
);
}
if (!lo.reNotAlphabet.test(word)) {
return lo.hyphenate(
word,
hyphen.charCodeAt(0),
selSettings.leftminPerLang.get(lang),
selSettings.rightminPerLang.get(lang)
);
}
return word;
}

/**
* HyphenateFunction for compound words
* @param {string} word The word
* @returns {string} The hyphenated compound word
*/
function hyphenateCompound(word) {
const zeroWidthSpace = "\u200B";
let parts = null;
let wordHyphenator = null;
if (selSettings.compound === "auto" ||
selSettings.compound === "all") {
wordHyphenator = createWordHyphenator(lo, lang, sel);
parts = word.split("-").map((p) => {
if (p.length >= selSettings.minWordLength) {
return wordHyphenator(p);
}
return p;
});
if (selSettings.compound === "auto") {
word = parts.join("-");
} else {
word = parts.join("-" + zeroWidthSpace);
}
} else {
word = word.replace("-", "-" + zeroWidthSpace);
}
return word;
}

/**
* Checks if a string is mixed case
* @param {string} s The string
* @returns {boolean} true if s is mixed case
*/
function isMixedCase(s) {
return Array.prototype.map.call(s, (c) => {
return (c === c.toLowerCase());
}).some((v, i, a) => {
return (v !== a[0]);
});
}

/**
* HyphenateFunction for words (compound or not)
* @param {string} word The word
* @returns {string} The hyphenated word
*/
function hyphenator(word) {
let hw = lo.cache.get(sel).get(word);
if (!hw) {
if (lo.exc.has(word)) {
hw = lo.exc.get(word).replace(
/-/g,
selSettings.hyphen
);
} else if (!selSettings.mixedCase && isMixedCase(word)) {
hw = word;
} else if (word.indexOf("-") === -1) {
hw = hyphenateNormal(word);
} else {
hw = hyphenateCompound(word);
}
lo.cache.get(sel).set(word, hw);
}
return hw;
}
wordHyphenatorPool.set(poolKey, hyphenator);
return hyphenator;
}

const orphanControllerPool = new Map();

/**
* Factory for function that handles orphans
* @param {string} sel The selector
* @returns {function} The function created
*/
function createOrphanController(sel) {
if (orphanControllerPool.has(sel)) {
return orphanControllerPool.get(sel);
}
const selSettings = C.get(sel);

/**
* Function template
* @param {string} ignore unused result of replace
* @param {string} leadingWhiteSpace The leading whiteSpace
* @param {string} lastWord The last word
* @param {string} trailingWhiteSpace The trailing whiteSpace
* @returns {string} Treated end of text
*/
function controlOrphans(
ignore,
leadingWhiteSpace,
lastWord,
trailingWhiteSpace
) {
if (selSettings.orphanControl === 3 && leadingWhiteSpace === " ") {
// \u00A0 = no-break space (nbsp)
leadingWhiteSpace = "\u00A0";
}
return leadingWhiteSpace + lastWord.replace(RegExp(selSettings.hyphen, "g"), "") + trailingWhiteSpace;
}
orphanControllerPool.set(sel, controlOrphans);
return controlOrphans;
}

/**
* Hyphenate an entitiy (text string or Element-Object)
* @param {string} lang - the language of the string
* @param {string} sel - the selectorName of settings
* @param {string} entity - the entity to be hyphenated
* @returns {string | null} hyphenated str according to setting of sel
*/
function hyphenate(lang, sel, entity) {
const lo = H.languages.get(lang);
const selSettings = C.get(sel);
const minWordLength = selSettings.minWordLength;

/*
* Transpiled RegExp of
* /[${alphabet}\p{Letter}-]{${minwordlength},}/gui
*/
const reWord = RegExp(
`[${lo.alphabet}a-z\u00DF-\u00F6\u00F8-\u00FE\u0101\u0103\u0105\u0107\u0109\u010D\u010F\u0111\u0113\u0117\u0119\u011B\u011D\u011F\u0123\u0125\u012B\u012F\u0131\u0135\u0137\u013C\u013E\u0142\u0144\u0146\u0148\u014D\u0151\u0153\u0155\u0159\u015B\u015D\u015F\u0161\u0165\u016B\u016D\u016F\u0171\u0173\u017A\u017C\u017E\u017F\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC\u0219\u021B\u02BC\u0390\u03AC-\u03CE\u03D0\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF\u03F2\u0430-\u044F\u0451-\u045C\u045E\u045F\u0491\u04AF\u04E9\u0561-\u0585\u0587\u0905-\u090C\u090F\u0910\u0913-\u0928\u092A-\u0930\u0932\u0933\u0935-\u0939\u093D\u0960\u0961\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A85-\u0A8B\u0A8F\u0A90\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B60\u0B61\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60\u0D61\u0D7A-\u0D7F\u0E01-\u0E2E\u0E30\u0E32\u0E33\u0E40-\u0E45\u10D0-\u10F0\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1E0D\u1E37\u1E41\u1E43\u1E45\u1E47\u1E6D\u1F00-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB2-\u1FB4\u1FB6\u1FB7\u1FC2-\u1FC4\u1FC6\u1FC7\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2-\u1FE7\u1FF2-\u1FF4\u1FF6\u1FF7\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CC9\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E-]{${minWordLength},}`, "gui"
);

/**
* Hyphenate text according to setting in sel
* @param {string} text - the strint to be hyphenated
* @returns {string} hyphenated string according to setting of sel
*/
function hyphenateText(text) {
if (C.normalize) {
text = text.normalize("NFC");
}
let tn = text.replace(
reWord,
createWordHyphenator(lo, lang, sel)
);
if (selSettings.orphanControl !== 1) {
tn = tn.replace(
/(\u0020*)(\S+)(\s*)$/,
createOrphanController(sel)
);
}
return tn;
}

/**
* Hyphenate element according to setting in sel
* @param {object} el - the HTMLElement to be hyphenated
* @returns {undefined}
*/
function hyphenateElement(el) {
event.fire(
"beforeElementHyphenation",
{
el,
lang
}
);
el.childNodes.forEach((n) => {
if (
n.nodeType === 3 &&
(/\S/).test(n.data) &&
n.data.length >= minWordLength
) {
n.data = hyphenateText(n.data);
}
});
H.res.get("els").counter[0] -= 1;
event.fire(
"afterElementHyphenation",
{
el,
lang
}
);
}
let r = null;
if (typeof entity === "string") {
r = hyphenateText(entity);
} else if (entity instanceof HTMLElement) {
hyphenateElement(entity);
}
return r;
}

/**
* Creates a language-specific string hyphenator
* @param {String} lang - The language this hyphenator hyphenates
*/
function createStringHyphenator(lang) {
return ((entity, sel = ".hyphenate") => {
if (typeof entity !== "string") {
event.fire(
"error",
{
"msg": "This use of hyphenators is deprecated. See https://mnater.github.io/Hyphenopoly/Hyphenators.html"
}
);
}
return hyphenate(lang, sel, entity);
});
}

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

H.unhyphenate = () => {
H.res.get("els").each((lang, els) => {
els.forEach((elo) => {
const n = elo.element.firstChild;
n.data = n.data.replace(RegExp(C[elo.selector].hyphen, "g"), "");
});
});
return Promise.resolve(H.res.get("els"));
};

/**
* Hyphenate all elements with a given language
* @param {string} lang The language
* @param {Array} elArr Array of elements
* @returns {undefined}
*/
function hyphenateLangElements(lang, elements) {
const elArr = elements.list.get(lang);
if (elArr) {
elArr.forEach((elo) => {
hyphenate(lang, elo.selector, elo.element);
});
} else {
event.fire(
"error",
{
"msg": `engine for language '${lang}' loaded, but no elements found.`
}
);
}
if (elements.counter[0] === 0) {
w.clearTimeout(H.timeOutHandler);
if (C.hide !== 0) {
H.hide(0, null);
}
event.fire(
"hyphenopolyEnd",
{
"msg": "hyphenopolyEnd"
}
);
if (!C.keepAlive) {
window.Hyphenopoly = null;
}
}
}

/**
* Convert the exceptions from user input to Map
* @param {string} lang – The language for which the Map is created
* @return {Map}
*/
function createExceptionMap(lang) {
let exc = "";
if (C.exceptions.has(lang)) {
exc = C.exceptions.get(lang);
}
if (C.exceptions.has("global")) {
if (exc === "") {
exc = C.exceptions.get("global");
} else {
exc += ", " + C.exceptions.get("global");
}
}
if (exc === "") {
return new Map();
}
return new Map(exc.split(", ").map((e) => {
return [e.replace(/-/g, ""), e];
}));
}

/**
* Setup lo
* @param {string} lang The language
* @param {function} hyphenateFunction The hyphenateFunction
* @param {string} alphabet List of used characters
* @param {number} leftmin leftmin
* @param {number} rightmin rightmin
* @returns {undefined}
*/
function prepareLanguagesObj(
lang,
hyphenateFunction,
alphabet,
patternLeftmin,
patternRightmin
) {
C.selectors.forEach((sel) => {
const selSettings = C.get(sel);
if (selSettings.leftminPerLang === 0) {
selSettings.set("leftminPerLang", new Map());
}
if (selSettings.rightminPerLang === 0) {
selSettings.set("rightminPerLang", new Map());
}
selSettings.leftminPerLang.set(lang, Math.max(
patternLeftmin,
selSettings.leftmin,
Number(selSettings.leftminPerLang.get(lang)) || 0
));

selSettings.rightminPerLang.set(lang, Math.max(
patternRightmin,
selSettings.rightmin,
Number(selSettings.rightminPerLang.get(lang)) || 0
));
});
if (!H.languages) {
H.languages = new Map();
}
alphabet = alphabet.replace(/-/g, "");
H.languages.set(lang, o.create(null, {
"alphabet": setProp(alphabet, 2),
"cache": setProp(new Map(), 2),
"exc": setProp(createExceptionMap(lang), 2),
"hyphenate": setProp(hyphenateFunction, 2),
"ready": setProp(true, 2),
"reNotAlphabet": setProp(RegExp(`[^${alphabet}]`, "gi"), 2)
}));
H.hy6ors.get(lang).resolve(createStringHyphenator(lang));
event.fire(
"engineReady",
{
lang
}
);
hyphenateLangElements(lang, H.res.get("els"));
}

const decode = (() => {
if (w.TextDecoder) {
const utf16ledecoder = new TextDecoder("utf-16le");
return ((ui16) => {
return utf16ledecoder.decode(ui16);
});
}
return ((ui16) => {
return String.fromCharCode.apply(null, ui16);
});
})();

/**
* Setup env for hyphenateFunction
* @param {Object} baseData baseData
* @param {function} hyphenateFunc hyphenateFunction
* @returns {function} hyphenateFunction with closured environment
*/
function encloseHyphenateFunction(baseData, hyphenateFunc) {
const wordStore = new Uint16Array(baseData.buf, baseData.wo, 64);
const hydWrdStore = new Uint16Array(baseData.buf, baseData.hw, 128);
wordStore[0] = 95;
return ((word, hyphencc, leftmin, rightmin) => {
let i = 0;
for (const c of word) {
i += 1;
// eslint-disable-next-line security/detect-object-injection
wordStore[i] = c.charCodeAt(0);
}
wordStore[i + 1] = 95;
wordStore[i + 2] = 0;
if (hyphenateFunc(leftmin, rightmin, hyphencc) === 1) {
word = decode(hydWrdStore.subarray(1, hydWrdStore[0] + 1));
}
return word;
});
}

/**
* Instantiate Wasm Engine
* @param {string} lang The language
* @returns {undefined}
*/
function instantiateWasmEngine(heProm, lang) {
const wa = window.WebAssembly;

/**
* Register character substitutions in the .wasm-hyphenEngine
* @param {number} alphalen - The length of the alphabet
* @param {object} exp - Export-object of the hyphenEngine
*/
function registerSubstitutions(alphalen, exp) {
if (C.substitute.has(lang)) {
const subst = C.substitute.get(lang);
subst.forEach((substituer, substituted) => {
const substitutedU = substituted.toUpperCase();
const substitutedUcc = (substitutedU === substituted)
? 0
: substitutedU.charCodeAt(0);
alphalen = exp.subst(
substituted.charCodeAt(0),
substitutedUcc,
substituer.charCodeAt(0)
);
});
}
return alphalen;
}

/**
* Instantiate the hyphenEngine
* @param {object} res - The fetched ressource
*/
function handleWasm(res) {
const exp = res.instance.exports;
let alphalen = exp.conv();
alphalen = registerSubstitutions(alphalen, exp);
const baseData = {
/* eslint-disable multiline-ternary */
"buf": exp.mem.buffer,
"hw": (wa.Global) ? exp.hwo.value : exp.hwo,
"lm": (wa.Global) ? exp.lmi.value : exp.lmi,
"rm": (wa.Global) ? exp.rmi.value : exp.rmi,
"wo": (wa.Global) ? exp.uwo.value : exp.uwo
/* eslint-enable multiline-ternary */
};
prepareLanguagesObj(
lang,
encloseHyphenateFunction(
baseData,
exp.hyphenate
),
decode(new Uint16Array(exp.mem.buffer, 1026, alphalen - 1)),
baseData.lm,
baseData.rm
);
}
heProm.w.then((response) => {
if (response.ok) {
let r2 = response;
if (heProm.c > 1) {
r2 = response.clone();
}
if (
wa.instantiateStreaming &&
(response.headers.get("Content-Type") === "application/wasm")
) {
wa.instantiateStreaming(r2).then(handleWasm);
} else {
r2.arrayBuffer().
then((ab) => {
window.WebAssembly.instantiate(ab).
then(handleWasm);
});
}
} else {
H.res.get("els").rem(lang);
H.hy6ors.get(lang).reject({
"msg": `File ${lang}.wasm can't be loaded from ${H.paths.patterndir}`
});
}
});
}

H.res.get("DOM").then(() => {
mainLanguage = getLang(w.document.documentElement, false);
if (!mainLanguage && C.defaultLanguage !== "") {
mainLanguage = C.defaultLanguage;
}
const elements = collectElements();
H.res.set("els", elements);
elements.each((lang) => {
if (H.languages &&
H.languages.has(lang) &&
H.languages.get(lang).ready
) {
hyphenateLangElements(lang, elements);
}
});
});

H.res.get("he").forEach((heProm, lang) => {
instantiateWasmEngine(heProm, lang);
});

Promise.all(
// Make sure all lang specific hyphenators and DOM are ready
[...H.hy6ors.entries()].
reduce((accumulator, value) => {
if (value[0] !== "HTML") {
return accumulator.concat(value[1]);
}
return accumulator;
}, []).
concat(H.res.get("DOM"))
).then(() => {
H.hy6ors.get("HTML").resolve(createDOMHyphenator());
}, (e) => {
event.fire(
"error",
e
);
});
})(Hyphenopoly);
})(window, Object);

+ 353
- 0
Hyphenopoly/Hyphenopoly_Loader.js View File

@@ -0,0 +1,353 @@
/**
* @license Hyphenopoly_Loader 4.4.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 scriptName = "Hyphenopoly_Loader.js";
const lcRequire = new Map();

const shortcuts = {
"ac": "appendChild",
"ce": "createElement",
"ct": "createTextNode"
};

/**
* Shorthand for Object.keys(obj).forEach(function () {})
* @param {Object} obj the object to iterate
* @param {function} fn the function to execute
* @returns {undefined}
*/
const eachKey = (obj, fn) => {
return o.keys(obj).forEach(fn);
};

/**
* Set H.cf (Hyphenopoly.clientFeatures) either by reading out previously
* computed settings from sessionStorage or creating an template object.
* This is in an iife to keep complexity low.
*/
(() => {
if (H.cacheFeatureTests && store.getItem(scriptName)) {
H.cf = new Map(JSON.parse(store.getItem(scriptName)));
H.cf.set("langs", new Map(H.cf.get("langs")));
} else {
H.cf = new Map([["langs", new Map()], ["pf", false]]);
}
})();

/**
* Set H.paths and some H.setup fields to defaults or
* overwrite with user settings.
* These are iifes to keep complexity low.
*/
(() => {
const maindir = d.currentScript.src.replace(scriptName, "");
const patterndir = maindir + "patterns/";
if (H.paths) {
H.paths.maindir = H.paths.maindir || maindir;
H.paths.patterndir = H.paths.patterndir || patterndir;
} else {
H.paths = {
maindir,
patterndir
};
}
})();

(() => {
if (H.setup) {
H.setup.CORScredentials = H.setup.CORScredentials || "include";
H.setup.hide = H.setup.hide || "all";
H.setup.selectors = H.setup.selectors || {".hyphenate": {}};
H.setup.timeout = H.setup.timeout || 1000;
} else {
H.setup = {
"CORScredentials": "include",
"hide": "all",
"selectors": {".hyphenate": {}},
"timeout": 1000
};
}

// Change mode string to mode int
H.setup.hide = (() => {
const tr = new Map([["all", 1], ["element", 2], ["text", 3]]);
return tr.get(H.setup.hide) || 0;
})();
})();

/**
* Copy required languages to local lcRequire (lowercaseRequire) and
* eventually fallbacks to local lcFallbacks (lowercaseFallbacks).
* This is in an iife to keep complexity low.
*/
(() => {
const require = new Map(o.entries(H.require));
const fallbacks = (H.fallbacks)
? new Map(o.entries(H.fallbacks))
: new Map();
require.forEach((longWord, lang) => {
const fn = fallbacks.get(lang) || lang;
lcRequire.set(lang.toLowerCase(), new Map(
[["fn", fn], ["wo", longWord]]
));
});
})();

/**
* Create deferred Promise
*
* Kudos to http://lea.verou.me/2016/12/resolve-promises-externally-with-
* this-one-weird-trick/
* @return {promise}
*/
H.defProm = () => {
let res = null;
let rej = null;
const promise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
});
promise.resolve = res;
promise.reject = rej;

return promise;
};

/**
* 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
* nothing (mode == 0)
* @param {integer} state - State
* @param {integer} mode - Mode
*/
H.hide = (state, mode) => {
const sid = "H9Y_Styles";
if (state === 0) {
const stylesNode = d.getElementById(sid);
if (stylesNode) {
stylesNode.remove();
}
} else {
const vis = "{visibility:hidden!important}";
const sc = d[shortcuts.ce]("style");
let myStyle = "";
sc.id = sid;
if (mode === 1) {
myStyle = "html" + vis;
} else {
eachKey(H.setup.selectors, (sel) => {
if (mode === 2) {
myStyle += sel + vis;
} else {
myStyle += sel + "{color:transparent!important}";
}
});
}
sc[shortcuts.ac](d[shortcuts.ct](myStyle));
d.head[shortcuts.ac](sc);
}
};

const tester = (() => {
let fakeBody = null;
const ha = "hyphens:auto";
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 {

/**
* Append fakeBody with tests to target (document)
* @param {Object} target Where to append fakeBody
* @returns {Object|null} The body element or null, if no tests
*/
"ap": () => {
if (fakeBody) {
d.documentElement[shortcuts.ac](fakeBody);
return fakeBody;
}
return null;
},

/**
* Remove fakeBody
* @returns {undefined}
*/
"cl": () => {
if (fakeBody) {
fakeBody.remove();
}
},

/**
* Create and append div with CSS-hyphenated word
* @param {string} lang Language
* @returns {undefined}
*/
"cr": (lang) => {
if (H.cf.get("langs").get(lang)) {
return;
}
fakeBody = fakeBody || d[shortcuts.ce]("body");
const testDiv = d[shortcuts.ce]("div");
testDiv.lang = lang;
testDiv.style.cssText = css;
testDiv[shortcuts.ac](
d[shortcuts.ct](
lcRequire.get(lang).get("wo").
toLowerCase()
)
);
fakeBody[shortcuts.ac](testDiv);
}
};
})();

/**
* Checks if hyphens (ev.prefixed) is set to auto for the element.
* @param {Object} elm - the element
* @returns {Boolean} result of the check
*/
function checkCSSHyphensSupport(elmStyle) {
const h = elmStyle.hyphens ||
elmStyle.webkitHyphens ||
elmStyle.msHyphens;
return (h === "auto");
}

H.res = new Map([["he", new Map()]]);
const fw = new Map();

/**
* Load hyphenEngines
*
* Make sure each .wasm is loaded exactly once, even for fallbacks
* fw: fetched wasm (maps filename to language)
* he: hyphenEngines (maps lang to wasm and counter)
* c (counter) is needed in Hyphenopoly.js to decide
* if wasm needs to be cloned
* @param {string} lang The language
* @returns {undefined}
*/
function loadhyphenEngine(lang) {
const filename = lcRequire.get(lang).get("fn") + ".wasm";
H.cf.set("pf", true);
H.cf.get("langs").set(lang, "H9Y");
if (fw.has(filename)) {
const hyphenEngineWrapper = H.res.get("he").get(fw.get(filename));
hyphenEngineWrapper.c += 1;
H.res.get("he").set(lang, hyphenEngineWrapper);
} else {
H.res.get("he").set(
lang,
{
"c": 1,
"w": w.fetch(H.paths.patterndir + filename, {"credentials": H.setup.CORScredentials})
}
);
fw.set(filename, lang);
}
}
lcRequire.forEach((value, lang) => {
if (value.get("wo") === "FORCEHYPHENOPOLY" || H.cf.get("langs").get(lang) === "H9Y") {
loadhyphenEngine(lang);
} else {
tester.cr(lang);
}
});
const testContainer = tester.ap();
if (testContainer) {
const nl = testContainer.querySelectorAll("div");
nl.forEach((n) => {
if (checkCSSHyphensSupport(n.style) && n.offsetHeight > 12) {
H.cf.get("langs").set(n.lang, "CSS");
} else {
loadhyphenEngine(n.lang);
}
});
tester.cl();
}
const he = H.handleEvent;
if (H.cf.get("pf")) {
H.res.set("DOM", new Promise((res) => {
if (d.readyState === "loading") {
d.addEventListener(
"DOMContentLoaded",
res,
{
"once": true,
"passive": true
}
);
} else {
res();
}
}));
if (H.setup.hide === 1) {
H.hide(1, 1);
}
if (H.setup.hide !== 0) {
H.timeOutHandler = w.setTimeout(() => {
H.hide(0, null);
// eslint-disable-next-line no-console
console.info(scriptName + " timed out.");
}, H.setup.timeout);
}
H.res.get("DOM").then(() => {
if (H.setup.hide > 1) {
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.hy6ors = new Map();
H.cf.get("langs").forEach((langDef, lang) => {
if (langDef === "H9Y") {
H.hy6ors.set(lang, H.defProm());
}
});
H.hy6ors.set("HTML", H.defProm());
H.hyphenators = new Proxy(H.hy6ors, {
"get": (target, key) => {
return target.get(key);
},
"set": () => {
// Inhibit setting of hyphenators
return true;
}
});
(() => {
if (he && he.polyfill) {
he.polyfill();
}
})();
} else {
(() => {
if (he && he.tearDown) {
he.tearDown();
}
w.Hyphenopoly = null;
})();
}
(() => {
if (H.cacheFeatureTests) {
const langs = [...H.cf.get("langs").entries()];
store.setItem(scriptName, JSON.stringify(
[["langs", langs], ["pf", H.cf.get("pf")]]
));
}
})();
})(window, document, Hyphenopoly, Object);

+ 21
- 0
Hyphenopoly/LICENSE View File

@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2020 Mathias Nater

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 182
- 0
Hyphenopoly/README.md View File

@@ -0,0 +1,182 @@
# Hyphenopoly.js
[![Build Status](https://travis-ci.org/mnater/Hyphenopoly.svg?branch=master)](https://travis-ci.org/mnater/Hyphenopoly) [![Coverage Status](https://coveralls.io/repos/github/mnater/Hyphenopoly/badge.svg?branch=master)](https://coveralls.io/github/mnater/Hyphenopoly?branch=master) [![dependencies Status](https://david-dm.org/mnater/Hyphenopoly/status.svg)](https://david-dm.org/mnater/Hyphenopoly) [![devDependencies Status](https://david-dm.org/mnater/Hyphenopoly/dev-status.svg)](https://david-dm.org/mnater/Hyphenopoly?type=dev) [![npms score](https://badges.npms.io/hyphenopoly.svg)](https://npms.io/search?q=hyphenopoly) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/de50e1ae70a64a47b0bd9b5449f89353)](https://www.codacy.com/app/mnater/Hyphenopoly?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=mnater/Hyphenopoly&amp;utm_campaign=Badge_Grade)

Hyphenopoly.js is a __JavaScript-polyfill for hyphenation in HTML__: it hyphenates text if the user agent does not support CSS-hyphenation at all or not for the required languages and it is a __Node.js-module__.

The package consists of the following parts:
- _Hyphenopoly_Loader.js_ (~11KB unpacked, ~2KB minified and compressed): feature-checks the client and loads other resources if necessary.
- _Hyphenopoly.js_ (~38KB unpacked, ~5KB minified and compressed): does the whole DOM-foo and wraps wasm.
- _wasm-Modules_ (sizes differ! e.g. en-us.wasm: ~24KB uncompressed, ~15KB compressed): core hyphenation functions and hyphenation patterns in a space saving binary format (including pattern license).
- _hyphenopoly.module.js_: the node module

## Usage (Browser)
Place all code for Hyphenopoly at the top of the header (immediately after the `<title>` tag) to ensure resources are loaded as early as possible.
You'll have to insert two script blocks. In the first block provide the initial configurations for Hyphenopoly_Loader as inline script. In the second block load Hyphenopoly_Loader.js as external script.
Also, don't forget to enable CSS hyphenation.

[Example](http://mnater.github.io/Hyphenopoly/min/example.html):
```html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Example 1</title>
<script>
var Hyphenopoly = {
require: {
"la": "honorificabilitudinitas",
"de": "Silbentrennungsalgorithmus",
"en-us": "Supercalifragilisticexpialidocious"
},
setup: {
selectors: {
".container": {}
}
}
};
</script>
<script src="./Hyphenopoly_Loader.js"></script>
<style type="text/css">
body {
width:60%;
margin-left:20%;
}
p {
text-align: justify;
margin: 0 2em 0 0;
}
.container {
display: flex;
hyphens: auto;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
}
</style>
</head>
<body>
<h1>Example 1</h1>
<div class="container">
<p lang="la">Qua de causa Helvetii quoque reliquos Gallos virtute praecedunt, quod fere cotidianis proeliis cum Germanis contendunt, cum aut suis finibus eos prohibent aut ipsi in eorum finibus bellum gerunt.</p>
<p lang="en-us">For which reason the Helvetii also surpass the rest of the Gauls in valor, as they contend with the Germans in almost daily battles, when they either repel them from their own territories, or themselves wage war on their frontiers.</p>
<p lang="de">Aus diesem Grund übertreffen auch die Helvetier die übrigen Gallier an Tapferkeit, weil sie sich in fast täglichen Gefechten mit den Germanen messen, wobei sie diese entweder von ihrem Gebiet fernhalten oder selbst in deren Gebiet kämpfen.</p>
</div>
</body>
</html>
```
Let's go through this example step by step:

### UTF-8
Make sure your page is encoded as utf-8.

### First script block – configurations
Hyphenopoly_Loader.js needs some information to run. This information is provided in a globally accessible Object called `Hyphenopoly`. Hyphenopoly_Loader.js and (if necessary) Hyphenopoly.js will add other methods and properties only to this object – there will be no other global variables/functions beyond this object.

#### require
The `Hyphenopoly` object must have exactly one property called `require` which itself is an object containing at least one nameValuePair where the name is a language code string (Some languages are region-specific. See the patterns directory for supported languages. E.g. just using `en` won't work, use either `en-us`or `en-gb`) and the value is a long word string in that language (preferably more than 12 characters long).

Hyphenator_Loader.js will feature test the client (aka browser, aka user agent) for CSS-hyphens support for the given languages with the given words respectively. In the example above it will test if the client supports CSS-hyphenation for latin, german and us-english.

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.
If the browser supports all required languages the script deletes the `Hyphenopoly`-object and terminates without further ado.

### enable CSS-hyphenation
Hyphenopoly by default hyphenates elements (and their children) with the classname `.hyphenate`. Don't forget to enable CSS-hyphenation for the classes eventually handled by Hyphenopoly.

## Usage (node)
[![Try hyphenopoly on RunKit](https://badge.runkitcdn.com/hyphenopoly.svg)](https://npm.runkit.com/hyphenopoly)

Install:
````shell
npm i hyphenopoly
````

````javascript
"use strict";

const hyphenopoly = require("hyphenopoly");

const hyphenator = hyphenopoly.config({
"require": ["de", "en-us"],
"hyphen": "•",
"exceptions": {
"en-us": "en-han-ces"
}
});

async function hyphenate_en(text) {
const hyphenateText = await hyphenator.get("en-us");
console.log(hyphenateText(text));
}

async function hyphenate_de(text) {
const hyphenateText = await hyphenator.get("de");
console.log(hyphenateText(text));
}

hyphenate_en("hyphenation enhances justification.");
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)

<a href="https://opencollective.com/hyphenopoly/donate" target="_blank">
<img src="https://opencollective.com/hyphenopoly/donate/button@2x.png?color=blue" width=300 />
</a>

## Automatic hyphenation
The algorithm used for hyphenation was developed by Franklin M. Liang for TeX. It works more or less like this:

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.
5. Repeat 2. - 4. for all words longer than minWordLength

Example:
````text
Hyphenation
h y p h e n a t i o n
h y3p h
h e2n
h e n a4
h e n5a t
1n a
n2a t
1t i o
2i o
o2n
h0y3p0h0e2n5a4t2i0o2n
Hy-phen-ation
````

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.

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://thomasbroadley.com"><img src="https://avatars0.githubusercontent.com/u/8731922?v=4" width="100px;" alt=""/><br /><sub><b>Thomas Broadley</b></sub></a><br /><a href="https://github.com/mnater/Hyphenopoly/commits?author=tbroadley" title="Documentation">📖</a></td>
<td align="center"><a href="https://kailueke.gitlab.io/"><img src="https://avatars0.githubusercontent.com/u/1189130?v=4" width="100px;" alt=""/><br /><sub><b>Kai Lüke</b></sub></a><br /><a href="https://github.com/mnater/Hyphenopoly/commits?author=pothos" title="Code">💻</a></td>
<td align="center"><a href="http://www.data-factory.net/"><img src="https://avatars2.githubusercontent.com/u/998558?v=4" width="100px;" alt=""/><br /><sub><b>Sebastian Blank</b></sub></a><br /><a href="#example-blankse" title="Examples">💡</a></td>
<td align="center"><a href="https://www.ghsvs.de"><img src="https://avatars2.githubusercontent.com/u/20780646?v=4" width="100px;" alt=""/><br /><sub><b>ReLater</b></sub></a><br /><a href="#maintenance-ReLater" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/julian-zatloukal"><img src="https://avatars3.githubusercontent.com/u/58230917?v=4" width="100px;" alt=""/><br /><sub><b>julian-zatloukal</b></sub></a><br /><a href="https://github.com/mnater/Hyphenopoly/commits?author=julian-zatloukal" title="Documentation">📖</a></td>
</tr>
</table>

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

+ 3
- 0
Hyphenopoly/docs/Branching-Model.md View File

@@ -0,0 +1,3 @@
# Branching Model

Hyphenopoly uses [https://guides.github.com/introduction/flow/](https://guides.github.com/introduction/flow/).

+ 95
- 0
Hyphenopoly/docs/Coping-with-hyphenation-errors.md View File

@@ -0,0 +1,95 @@
# Hyphenation errors
Automatic hyphenation can not be error free! Because ...
* ... [homographs](https://en.wikipedia.org/wiki/Homograph) may be hyphenated differently.
````
desert (arid land) -> des‧ert
desert (to leave) -> de‧sert
````
In some patterns this is solved by not hyphenating ambiguous words at all.
* ... [neologisms](https://en.wikipedia.org/wiki/Neologism) that are not in the list of words used for pattern creation may be hyphenated incorrectly.
````
blogosphere -> blog·o·sphere (Merriam webster)
// bl‧o‧gos‧phere (en-us patterns)
// blo‧go‧sphere (en-gb patterns)
````
* ... the rules for hyphenation may have changed or are not specified unambiguously.
````
dictionary -> dic‧tion‧ary (Wiktionary)
// dic·tio·nary (Merriam-Webster)
````
* ... 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
var Hyphenopoly = {
require: [...],
setup: {
selectors: {
".hyphenate": {
hyphen: "•"
}
}
}
};
````
See also [Setup#hyphen](./Setup.md#hyphen)

## Fix hyphenation
There are three levels of fixing possibilities:
1. [Directly in the text](#fix-hyphenation-in-the-text)
2. [Defining hyphenation exceptions for Hyphenopoly](#define-hyphenation-exceptions)
3. [Improve patterns](#help-to-improve-the-patterns)

### Fix hyphenation in the text
Words containing a soft hyphen (&amp;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:__