PageSourceSearch

https://www.miastomyszkow.pl/vendors/hyphenator/5.3.0/js/Hyphenator.js

js miastomyszkow.pl collected 2026-10-02 16:42:37 UTC 137,909 bytes, 3,517 lines download raw bytes

1/** @license Hyphenator 5.3.0 - client side hyphenation for webbrowsers
2 *  Copyright (C) 2017  Mathias Nater, Zürich (mathiasnater at gmail dot com)
3 *  https://github.com/mnater/Hyphenator
4 *
5 *  Released under the MIT license
6 *  http://mnater.github.io/Hyphenator/LICENSE.txt
7 */
8
9/*
10 * Comments are jsdoc3 formatted. See http://usejsdoc.org
11 * Use mergeAndPack.html to get rid of the comments and to reduce the file size of this script!
12 */
13
14/* The following comment is for JSLint: */
15/*jslint browser: true, multivar: true, eval: true*/
16/*global Hyphenator window*/
17
18/**
19 * @desc Provides all functionality to do hyphenation, except the patterns that are loaded externally
20 * @global
21 * @namespace Hyphenator
22 * @author Mathias Nater, <[email protected]>
23 * @version 5.3.0
24 * @example
25 * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
26 * &lt;script type = "text/javascript"&gt;
27 *   Hyphenator.run();
28 * &lt;/script&gt;
29 */
30var Hyphenator;
31Hyphenator = (function (window) {
32    "use strict";
33
34    /**
35     * @member Hyphenator~contextWindow
36     * @access private
37     * @desc
38     * contextWindow stores the window for the actual document to be hyphenated.
39     * If there are frames this will change.
40     * So use contextWindow instead of window!
41     */
42    var contextWindow = window;
43
44
45    /**
46     * @member {Object.<string, Hyphenator~supportedLangs~supportedLanguage>} Hyphenator~supportedLangs
47     * @desc
48     * A generated key-value object that stores supported languages and meta data.
49     * The key is the {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language and the value
50     * is an object of type {@link Hyphenator~supportedLangs~supportedLanguage}
51     * @namespace Hyphenator~supportedLangs
52     * @access private
53     * //Check if language lang is supported:
54     * if (supportedLangs.hasOwnProperty(lang))
55     */
56    var supportedLangs = (function () {
57        /**
58         * @typedef {Object} Hyphenator~supportedLangs~supportedLanguage
59         * @property {string} file - The name of the pattern file
60         * @property {number} script - The script type of the language (e.g. "latin" for english), this type is abbreviated by an id
61         * @property {string} prompt - The sentence prompted to the user, if Hyphenator.js doesn"t find a language hint
62         */
63
64        /**
65         * @lends Hyphenator~supportedLangs
66         */
67        var r = {},
68            /**
69             * @method Hyphenator~supportedLangs~o
70             * @desc
71             * Sets a value of Hyphenator~supportedLangs
72             * @access protected
73             * @param {string} code The {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language
74             * @param {string} file The name of the pattern file
75             * @param {Number} script A shortcut for a specific script: latin:0, cyrillic: 1, arabic: 2, armenian:3, bengali: 4, devangari: 5, greek: 6
76             * gujarati: 7, kannada: 8, lao: 9, malayalam: 10, oriya: 11, persian: 12, punjabi: 13, tamil: 14, telugu: 15, georgian: 16
77             * @param {string} prompt The sentence prompted to the user, if Hyphenator.js doesn"t find a language hint
78             */
79            o = function (code, file, script, prompt) {
80                r[code] = {"file": file, "script": script, "prompt": prompt};
81            };
82
83        o("be", "be.js", 1, "Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:");
84        o("ca", "ca.js", 0, "");
85        o("cs", "cs.js", 0, "Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:");
86        o("cu", "cu.js", 1, "Ꙗ҆зы́къ сегѡ̀ са́йта не мо́жетъ ѡ҆предѣле́нъ бы́ти. Прошꙋ́ тѧ ᲂу҆каза́ти ꙗ҆зы́къ:");
87        o("da", "da.js", 0, "Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:");
88        o("bn", "bn.js", 4, "");
89        o("de", "de.js", 0, "Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:");
90        o("el", "el-monoton.js", 6, "");
91        o("el-monoton", "el-monoton.js", 6, "");
92        o("el-polyton", "el-polyton.js", 6, "");
93        o("en", "en-us.js", 0, "The language of this website could not be determined automatically. Please indicate the main language:");
94        o("en-gb", "en-gb.js", 0, "The language of this website could not be determined automatically. Please indicate the main language:");
95        o("en-us", "en-us.js", 0, "The language of this website could not be determined automatically. Please indicate the main language:");
96        o("eo", "eo.js", 0, "La lingvo de ĉi tiu retpaĝo ne rekoneblas aŭtomate. Bonvolu indiki ĝian ĉeflingvon:");
97        o("es", "es.js", 0, "El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:");
98        o("et", "et.js", 0, "Veebilehe keele tuvastamine ebaõnnestus, palun valige kasutatud keel:");
99        o("fi", "fi.js", 0, "Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:");
100        o("fr", "fr.js", 0, "La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:");
101        o("ga", "ga.js", 0, "Níorbh fhéidir teanga an tsuímh a fháil go huathoibríoch. Cuir isteach príomhtheanga an tsuímh:");
102        o("grc", "grc.js", 6, "");
103        o("gu", "gu.js", 7, "");
104        o("hi", "hi.js", 5, "");
105        o("hu", "hu.js", 0, "A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:");
106        o("hy", "hy.js", 3, "Չհաջողվեց հայտնաբերել Õ¡Õ
106µÕ½ կայքի լեզուն։ Խնդրում ենք նշեք Õ°Õ«Õ´Õ¶Õ¡Õ¯Õ¡Õ¶ լեզուն՝");
107        o("it", "it.js", 0, "Lingua del sito sconosciuta. Indicare una lingua, per favore:");
108        o("ka", "ka.js", 16, "");
109        o("kn", "kn.js", 8, "ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:");
110        o("la", "la.js", 0, "");
111        o("lt", "lt.js", 0, "Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:");
112        o("lv", "lv.js", 0, "Šīs lapas valodu nevarēja noteikt automātiski. Lūdzu norādiet pamata valodu:");
113        o("ml", "ml.js", 10, "ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:");
114        o("nb", "nb-no.js", 0, "Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:");
115        o("no", "nb-no.js", 0, "Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:");
116        o("nb-no", "nb-no.js", 0, "Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:");
117        o("nl", "nl.js", 0, "De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:");
118        o("or", "or.js", 11, "");
119        o("pa", "pa.js", 13, "");
120        o("pl", "pl.js", 0, "Języka tej strony nie można ustalić automatycznie. Proszę wskazać język:");
121        o("pt", "pt.js", 0, "A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:");
122        o("ru", "ru.js", 1, "Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:");
123        o("sk", "sk.js", 0, "");
124        o("sl", "sl.js", 0, "Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:");
125        o("sr-cyrl", "sr-cyrl.js", 1, "Језик овог сајта није детектован аутоматски. Молим вас наведите језик:");
126        o("sr-latn", "sr-latn.js", 0, "Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:");
127        o("sv", "sv.js", 0, "Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:");
128        o("ta", "ta.js", 14, "");
129        o("te", "te.js", 15, "");
130        o("tr", "tr.js", 0, "Bu web sitesinin dili otomatik olarak tespit edilememiştir. Lütfen dökümanın dilini seçiniz%A0:");
131        o("uk", "uk.js", 1, "Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:");
132        o("ro", "ro.js", 0, "Limba acestui sit nu a putut fi determinată automat. Alege limba principală:");
133
134        return r;
135    }());
136
137
138
139    /**
140     * @member {Object} Hyphenator~locality
141     * @desc
142     * An object storing isBookmarklet, basePath and isLocal
143     * @access private
144     * @see {@link Hyphenator~loadPatterns}
145     */
146    var locality = (function getLocality() {
147        var r = {
148            isBookmarklet: false,
149            basePath: "//mnater.github.io/Hyphenator/",
150            isLocal: false
151        };
152        var fullPath;
153        function getBasePath(path) {
154            if (!path) {
155                return r.basePath;
156            }
157            return path.substring(0, path.lastIndexOf("/") + 1);
158        }
159        function findCurrentScript() {
160            var scripts = contextWindow.document.getElementsByTagName("script");
161            var num = scripts.length - 1;
162            var currScript;
163            var src;
164            while (num >= 0) {
165                currScript = scripts[num];
166                if ((currScript.src || currScript.hasAttribute("src")) && currScript.src.indexOf("Hyphenator") !== -1) {
167                    src = currScript.src;
168                    break;
169                }
170                num -= 1;
171            }
172            return src;
173        }
174        if (!!document.currentScript) {
175            fullPath = document.currentScript.src;
176        } else {
177            fullPath = findCurrentScript();
178        }
179        r.basePath = getBasePath(fullPath);
180        if (fullPath && fullPath.indexOf("bm=true") !== -1) {
181            r.isBookmarklet = true;
182        }
183        if (window.location.href.indexOf(r.basePath) !== -1) {
184            r.isLocal = true;
185        }
186        return r;
187    }());
188
189    /**
190     * @member {string} Hyphenator~basePath
191     * @desc
192     * A string storing the basepath from where Hyphenator.js was loaded.
193     * This is used to load the pattern files.
194     * The basepath is determined dynamically in getLocality by searching all script-tags for Hyphenator.js
195     * If the path cannot be determined {@link http://mnater.github.io/Hyphenator/} is used as fallback.
196     * @access private
197     * @see {@link Hyphenator~loadPatterns}
198     */
199    var basePath = locality.basePath;
200
201    /**
202     * @member {boolean} Hyphenator~isLocal
203     * @access private
204     * @desc
205     * This is computed by getLocality.
206     * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
207     * it"s loaded from an external source (i.e. directly from github)
208     */
209    var isLocal = locality.isLocal;
210
211    /**
212     * @member {boolean} Hyphenator~documentLoaded
213     * @access private
214     * @desc
215     * documentLoaded is true, when the DOM has been loaded. This is set by {@link Hyphenator~runWhenLoaded}
216     */
217    var documentLoaded = false;
218
219    /**
220     * @member {boolean} Hyphenator~persistentConfig
221     * @access private
222     * @desc
223     * if persistentConfig is set to true (defaults to false), config options and the state of the
224     * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven"t to be
225     * set for each page.
226     * @default false
227     * @see {@link Hyphenator.config}
228     */
229    var persistentConfig = false;
230
231    /**
232     * @member {boolean} Hyphenator~doFrames
233     * @access private
234     * @desc
235     * switch to control if frames/iframes should be hyphenated, too.
236     * defaults to false (frames are a bag of hurt!)
237     * @default false
238     * @see {@link Hyphenator.config}
239     */
240    var doFrames = false;
241
242    /**
243     * @member {Object.<string,boolean>} Hyphenator~dontHyphenate
244     * @desc
245     * A key-value object containing all html-tags whose content should not be hyphenated
246     * @access private
247     */
248    var dontHyphenate = {"video": true, "audio": true, "script": true, "code": true, "pre": true, "img": true, "br": true, "samp": true, "kbd": true, "var": true, "abbr": true, "acronym": true, "sub": true, "sup": true, "button": true, "option": true, "label": true, "textarea": true, "input": true, "math": true, "svg": true, "style": true};
249
250    /**
251     * @member {boolean} Hyphenator~enableCache
252     * @desc
253     * A variable to set if caching is enabled or not
254     * @default true
255     * @access private
256     * @see {@link Hyphenator.config}
257     */
258    var enableCache = true;
259
260    /**
261     * @member {string} Hyphenator~storageType
262     * @desc
263     * A variable to define what html5-DOM-Storage-Method is used ("none", "local" or "session")
264     * @default "local"
265     * @access private
266     * @see {@link Hyphenator.config}
267     */
268    var storageType = "local";
269
270    /**
271     * @member {Object|undefined} Hyphenator~storage
272     * @desc
273     * An alias to the storage defined in storageType. This is set by {@link Hyphenator~createStorage}.
274     * Set by {@link Hyphenator.run}
275     * @default null
276     * @access private
277     * @see {@link Hyphenator~createStorage}
278     */
279    var storage;
280
281    /**
282     * @member {boolean} Hyphenator~enableReducedPatternSet
283     * @desc
284     * A variable to set if storing the used patterns is set
285     * @default false
286     * @access private
287     * @see {@link Hyphenator.config}
288     * @see {@link Hyphenator.getRedPatternSet}
289     */
290    var enableReducedPatternSet = false;
291
292    /**
293     * @member {boolean} Hyphenator~enableRemoteLoading
294     * @desc
295     * A variable to set if pattern files should be loaded remotely or not
296     * @default true
297     * @access private
298     * @see {@link Hyphenator.config}
299     */
300    var enableRemoteLoading = true;
301
302    /**
303     * @member {boolean} Hyphenator~displayToggleBox
304     * @desc
305     * A variable to set if the togglebox should be displayed or not
306     * @default false
307     * @access private
308     * @see {@link Hyphenator.config}
309     */
310    var displayToggleBox = false;
311
312    /**
313     * @method Hyphenator~onError
314     * @desc
315     * A function that can be called upon an error.
316     * @see {@link Hyphenator.config}
317     * @access private
318     */
319    var onError = function (e) {
320        window.alert("Hyphenator.js says:\n\nAn Error occurred:\n" + e.message);
321    };
322
323    /**
324     * @method Hyphenator~onWarning
325     * @desc
326     * A function that can be called upon a warning.
327     * @see {@link Hyphenator.config}
328     * @access private
329     */
330    var onWarning = function (e) {
331        window.console.log(e.message);
332    };
333
334    /**
335     * @method Hyphenator~createElem
336     * @desc
337     * A function alias to document.createElementNS or document.createElement
338     * @access private
339     */
340    function createElem(tagname, context) {
341        context = context || contextWindow;
342        var el;
343        if (window.document.createElementNS) {
344            el = context.document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
345        } else if (window.document.createElement) {
346            el = context.document.createElement(tagname);
347        }
348        return el;
349    }
350    /**
351     * @method Hyphenator~forEachKey
352     * @desc
353     * Calls the function f on every property of o
354     * @access private
355     */
356    function forEachKey(o, f) {
357        var k;
358        if (Object.hasOwnProperty("keys")) {
359            Object.keys(o).forEach(f);
360        } else {
361            for (k in o) {
362                if (o.hasOwnProperty(k)) {
363                    f(k);
364                }
365            }
366        }
367    }
368
369    /**
370     * @member {boolean} Hyphenator~css3
371     * @desc
372     * A variable to set if css3 hyphenation should be used
373     * @default false
374     * @access private
375     * @see {@link Hyphenator.config}
376     */
377    var css3 = false;
378
379    /**
380     * @method Hyphenator~css3_gethsupport
381     * @desc
382     * This function returns a {@link Hyphenator~css3_hsupport} object for the current UA
383     * @type function
384     * @access private
385     * @see Hyphenator~css3_h9n
386     */
387    function css3_gethsupport() {
388        var support = false,
389            supportedBrowserLangs = {},
390            property = "",
391            checkLangSupport,
392            createLangSupportChecker = function (prefix) {
393                var testStrings = [
394                        //latin: 0
395                        "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
396                        //cyrillic: 1
397                        "абвгдеёжзийклмнопрстуфхцчшщъыьэюя",
398                        //arabic: 2
399                        "أبتثجحخدذرزسشصضطظعغفقكلمنهوي",
400                        //armenian: 3
401                        "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆ",
402                        //bengali: 4
403                        "ঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ",
404                        //devangari: 5
405                        "ँंःअआइईउऊऋऌएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसहऽािीुूृॄेैोौ्॒॑ॠॡॢॣ",
406                        //greek: 6
407                        "αβγδεζηθικλμνξοπρσςτυφχψω",
408                        //gujarati: 7
409                        "બહઅઆઇઈઉઊઋૠએઐઓઔાિીુૂૃૄૢૣેૈોૌકખગઘઙચછજઝઞટઠડઢણતથદધનપફસભમયરલળવશષ",
410                        //kannada: 8
411                        "ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡ",
412                        //lao: 9
413                        "ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮະັາິີຶືຸູົຼເແໂໃໄ່້໊໋ໜໝ",
414                        //malayalam: 10
415                        "ംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരറലളഴവശഷസഹാിീുൂൃെേൈൊോൌ്ൗൠൡൺൻർൽൾൿ",
416                        //oriya: 11
417                        "ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହାିୀୁୂୃେୈୋୌ୍ୗୠୡ",
418                        //persian: 12
419                        "أبتثجحخدذرزسشصضطظعغفقكلمنهوي",
420                        //punjabi: 13
421                        "ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹਾਿ
421ੀੁੂੇੈੋੌ੍ੰੱ",
422                        //tamil: 14
423                        "ஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஷஸஹாிீுூெேைொோௌ்ௗ",
424                        //telugu: 15
425                        "ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహాిీుూృౄెేైొోౌ్ౕౖౠౡ",
426                        //georgian: 16
427                        "აიერთხტუფბლდნვკწსგზმქყშჩცძჭჯოღპჟჰ"
428                    ],
429                    f = function (lang) {
430                        var shadow,
431                            computedHeight,
432                            bdy,
433                            r = false;
434
435                        //check if lang has already been tested
436                        if (supportedBrowserLangs.hasOwnProperty(lang)) {
437                            r = supportedBrowserLangs[lang];
438                        } else if (supportedLangs.hasOwnProperty(lang)) {
439                            //create and append shadow-test-element
440                            bdy = window.document.getElementsByTagName("body")[0];
441                            shadow = createElem("div", window);
442                            shadow.id = "Hyphenator_LanguageChecker";
443                            shadow.style.width = "5em";
444                            shadow.style.padding = "0";
445                            shadow.style.border = "none";
446                            shadow.style[prefix] = "auto";
447                            shadow.style.hyphens = "auto";
448                            shadow.style.fontSize = "12px";
449                            shadow.style.lineHeight = "12px";
450                            shadow.style.wordWrap = "normal";
451                            shadow.style.wordBreak = "normal";
452                            shadow.style.visibility = "hidden";
453                            shadow.lang = lang;
454                            shadow.style["-webkit-locale"] = "\"" + lang + "\"";
455                            shadow.innerHTML = testStrings[supportedLangs[lang].script];
456                            bdy.appendChild(shadow);
457                            //measure its height
458                            computedHeight = shadow.offsetHeight;
459                            //remove shadow element
460                            bdy.removeChild(shadow);
461                            r = !!(computedHeight > 12);
462                            supportedBrowserLangs[lang] = r;
463                        } else {
464                            r = false;
465                        }
466                        return r;
467                    };
468                return f;
469            },
470            s;
471
472        if (window.getComputedStyle) {
473            s = window.getComputedStyle(window.document.getElementsByTagName("body")[0], null);
474            if (s.hyphens !== undefined) {
475                support = true;
476                property = "hyphens";
477                checkLangSupport = createLangSupportChecker("hyphens");
478            } else if (s["-webkit-hyphens"] !== undefined) {
479                support = true;
480                property = "-webkit-hyphens";
481                checkLangSupport = createLangSupportChecker("-webkit-hyphens");
482            } else if (s.MozHyphens !== undefined) {
483                support = true;
484                property = "-moz-hyphens";
485                checkLangSupport = createLangSupportChecker("MozHyphens");
486            } else if (s["-ms-hyphens"] !== undefined) {
487                support = true;
488                property = "-ms-hyphens";
489                checkLangSupport = createLangSupportChecker("-ms-hyphens");
490            }
491        } //else we just return the initial values because ancient browsers don"t support css3 anyway
492
493
494        return {
495            support: support,
496            property: property,
497            supportedBrowserLangs: supportedBrowserLangs,
498            checkLangSupport: checkLangSupport
499        };
500    }
501
502    /**
503     * @typedef {Object} Hyphenator~css3_hsupport
504     * @property {boolean} support - if css3-hyphenation is supported
505     * @property {string} property - the css property name to access hyphen-settings (e.g. -webkit-hyphens)
506     * @property {Object.<string, boolean>} supportedBrowserLangs - an object caching tested languages
507     * @property {function} checkLangSupport - a method that checks if the browser supports a requested language
508     */
509
510    /**
511     * @member {Hyphenator~css3_h9n} Hyphenator~css3_h9n
512     * @desc
513     * A generated object containing information for CSS3-hyphenation support
514     * This is set by {@link Hyphenator~css3_gethsupport}
515     * @default undefined
516     * @access private
517     * @see {@link Hyphenator~css3_gethsupport}
518     * @example
519     * //Check if browser supports a language
520     * css3_h9n.checkLangSupport(&lt;lang&gt;)
521     */
522    var css3_h9n;
523
524    /**
525     * @member {string} Hyphenator~hyphenateClass
526     * @desc
527     * A string containing the css-class-name for the hyphenate class
528     * @default "hyphenate"
529     * @access private
530     * @example
531     * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
532     * @see {@link Hyphenator.config}
533     */
534    var hyphenateClass = "hyphenate";
535
536    /**
537     * @member {string} Hyphenator~urlHyphenateClass
538     * @desc
539     * A string containing the css-class-name for the urlhyphenate class
540     * @default "urlhyphenate"
541     * @access private
542     * @example
543     * &lt;p class = "urlhyphenate"&gt;Text&lt;/p&gt;
544     * @see {@link Hyphenator.config}
545     */
546    var urlHyphenateClass = "urlhyphenate";
547
548    /**
549     * @member {string} Hyphenator~classPrefix
550     * @desc
551     * A string containing a unique className prefix to be used
552     * whenever Hyphenator sets a CSS-class
553     * @access private
554     */
555    var classPrefix = "Hyphenator" + Math.round(Math.random() * 1000);
556
557    /**
558     * @member {string} Hyphenator~hideClass
559     * @desc
560     * The name of the class that hides elements
561     * @access private
562     */
563    var hideClass = classPrefix + "hide";
564
565    /**
566     * @member {RegExp} Hyphenator~hideClassRegExp
567     * @desc
568     * RegExp to remove hideClass from a list of classes
569     * @access private
570     */
571    var hideClassRegExp = new RegExp("\\s?\\b" + hideClass + "\\b", "g");
572
573    /**
574     * @member {string} Hyphenator~hideClass
575     * @desc
576     * The name of the class that unhides elements
577     * @access private
578     */
579    var unhideClass = classPrefix + "unhide";
580
581    /**
582     * @member {RegExp} Hyphenator~hideClassRegExp
583     * @desc
584     * RegExp to remove unhideClass from a list of classes
585     * @access private
586     */
587    var unhideClassRegExp = new RegExp("\\s?\\b" + unhideClass + "\\b", "g");
588
589    /**
590     * @member {string} Hyphenator~css3hyphenateClass
591     * @desc
592     * The name of the class that hyphenates elements with css3
593     * @access private
594     */
595    var css3hyphenateClass = classPrefix + "css3hyphenate";
596
597    /**
598     * @member {CSSEdit} Hyphenator~css3hyphenateClass
599     * @desc
600     * The var where CSSEdit class is stored
601     * @access private
602     */
603    var css3hyphenateClassHandle;
604
605    /**
606     * @member {string} Hyphenator~dontHyphenateClass
607     * @desc
608     * A string containing the css-class-name for elements that should not be hyphenated
609     * @default "donthyphenate"
610     * @access private
611     * @example
612     * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
613     * @see {@link Hyphenator.config}
614     */
615    var dontHyphenateClass = "donthyphenate";
616
617    /**
618     * @member {number} Hyphenator~min
619     * @desc
620     * A number wich indicates the minimal length of words to hyphenate.
621     * @default 6
622     * @access private
623     * @see {@link Hyphenator.config}
624     */
625    var min = 6;
626
627    /**
628     * @member {number} Hyphenator~leftmin
629     * @desc
630     * A number wich indicates the minimal length of characters before the first hyphenation.
631     * This value is only used if it is greater than the value in the pattern file.
632     * @default given by pattern file
633     * @access private
634     * @see {@link Hyphenator.config}
635     */
636    var leftmin = 0;
637
638    /**
639     * @member {number} Hyphenator~rightmin
640     * @desc
641     * A number wich indicates the minimal length of characters after the last hyphenation.
642     * This value is only used if it is greater than the value in the pattern file.
643     * @default given by pattern file
644     * @access private
645     * @see {@link Hyphenator.config}
646     */
647    var rightmin = 0;
648
649    /**
650     * @member {number} Hyphenator~rightmin
651     * @desc
652     * Control how compound words are hyphenated.
653     * "auto": factory-made -> fac-tory-made ("old" behaviour of Hyphenator.js)
654     * "all": factory-made -> fac-tory-[ZWSP]made ("made".length < minWordLength)
655     * "hyphen": factory-made -> factory-[ZWSP]made (Zero Width Space inserted after "-" to provide line breaking opportunity)
656     * @default "auto"
657     * @access private
658     * @see {@link Hyphenator.config}
659     */
660    var compound = "auto";
661
662    /**
663     * @member {number} Hyphenator~orphanControl
664     * @desc
665     * Control how the last words of a line are handled:
666     * level 1 (default): last word is hyphenated
667     * level 2: last word is not hyphenated
668     * level 3: last word is not hyphenated and last space is non breaking
669     * @default 1
670     * @access private
671     */
672    var orphanControl = 1;
673
674    /**
675     * @member {boolean} Hyphenator~isBookmarklet
676     * @desc
677     * This is computed by getLocality.
678     * True if Hyphanetor runs as bookmarklet.
679     * @access private
680     */
681    var isBookmarklet = locality.isBookmarklet;
682
683    /**
684     * @member {string|null} Hyphenator~mainLanguage
685     * @desc
686     * The general language of the document. In contrast to {@link Hyphenator~defaultLanguage},
687     * mainLanguage is defined by the client (i.e. by the html or by a prompt).
688     * @access private
689     * @see {@link Hyphenator~autoSetMainLanguage}
690     */
691    var mainLanguage = null;
692
693    /**
694     * @member {string|null} Hyphenator~defaultLanguage
695     * @desc
696     * The language defined by the developper. This language setting is defined by a config option.
697     * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can
698     * be found (i.e. just before the prompt).
699     * @access private
700     * @see {@link Hyphenator.config}
701     * @see {@link Hyphenator~autoSetMainLanguage}
702     */
703    var defaultLanguage = "";
704
705    /**
706     * @member {ElementCollection} Hyphenator~elements
707     * @desc
708     * A class representing all elements (of type Element) that have to be hyphenated. This var is filled by
709     * {@link Hyphenator~gatherDocumentInfos}
710     * @access private
711     */
712    var elements = (function () {
713        /**
714         * @constructor Hyphenator~elements~ElementCollection~Element
715         * @desc represents a DOM Element with additional information
716         * @access private
717         */
718        var makeElement = function (element) {
719                return {
720                    /**
721                     * @member {Object} Hyphenator~elements~ElementCollection~Element~element
722                     * @desc A DOM Element
723                     * @access protected
724                     */
725                    element: element,
726                    /**
727                     * @member {boolean} Hyphenator~elements~ElementCollection~Element~hyphenated
728                     * @desc Marks if the element has been hyphenated
729                     * @access protected
730                     */
731                    hyphenated: false,
732                    /**
733                     * @member {boolean} Hyphenator~elements~ElementCollection~Element~treated
734                     * @desc Marks if information of the element has been collected but not hyphenated (e.g. dohyphenation is off)
735                     * @access protected
736                     */
737                    treated: false
738                };
739            },
740            /**
741             * @constructor Hyphenator~elements~ElementCollection
742             * @desc A collection of Elements to be hyphenated
743             * @access protected
744             */
745            makeElementCollection = function () {
746                    /**
747                     * @member {number} Hyphenator~elements~ElementCollection~counters
748                     * @desc Array of [number of collected elements, number of hyphenated elements]
749                     * @access protected
750                     */
751                var counters = [0, 0],
752                    /**
753                     * @member {Object.<string, Array.<Element>>} Hyphenator~elements~ElementCollection~list
754                     * @desc The collection of elements, where the key is a language code and the value is an array of elements
755                     * @access protected
756                     */
757                    list = {},
758                    /**
759                     * @method Hyphenator~elements~ElementCollection.prototype~add
760                     * @augments Hyphenator~elements~ElementCollection
761                     * @access protected
762                     * @desc adds a DOM element to the collection
763                     * @param {Object} el - The DOM element
764                     * @param {string} lang - The language of the element
765                     */
766                    add = function (el, lang) {
767                        var elo = makeElement(el);
768                        if (!list.hasOwnProperty(lang)) {
769                            list[lang] = [];
770                        }
771                        list[lang].push(elo);
772                        counters[0] += 1;
773                        return elo;
774                    },
775                    /**
776                     * @callback Hyphenator~elements~ElementCollection.prototype~each~callback fn - The callback that is executed for each element
777                     * @param {string} [k] The key (i.e. language) of the collection
778                     * @param {Hyphenator~elements~ElementCollection~Element} element
779                     */
780
781                    /**
782                     * @method Hyphenator~elements~ElementCollection.prototype~each
783                     * @augments Hyphenator~elements~ElementCollection
784                     * @access protected
785                     * @desc takes each element of the collection as an argument of fn
786                     * @param {Hyphenator~elements~ElementCollection.prototype~each~callback} fn - A function that takes an element as an argument
787                     */
788                    each = function (fn) {
789                        forEachKey(list, function (k) {
790                            if (fn.length === 2) {
791                                fn(k, list[k]);
792                            } else {
793                                fn(list[k]);
794                            }
795                        });
796                    };
797
798                return {
799                    counters: counters,
800                    list: list,
801                    add: add,
802                    each: each
803                };
804
805            };
806        return makeElementCollection();
807    }());
808
809
810    /**
811     * @member {Object.<sting, string>} Hyphenator~exceptions
812     * @desc
813     * An object containing exceptions as comma separated strings for each language.
814     * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
815     * Exceptions can also be set by the user.
816     * @see {@link Hyphenator~prepareLanguagesObj}
817     * @access private
818     */
819    var exceptions = {};
820
821    /**
822     * @member {Object.<string, boolean>} Hyphenator~docLanguages
823     * @desc
824     * An object holding all languages used in the document. This is filled by
825     * {@link Hyphenator~gatherDocumentInfos}
826     * @access private
827     */
828    var docLanguages = {};
829
830    /**
831     * @member {string} Hyphenator~url
832     * @desc
833     * A string containing a insane RegularExpression to match URL"s
834     * @access private
835     */
836    var url = "(?:\\w*:\/\/)?(?:(?:\\w*:)?(?:\\w*)@)?(?:(?:(?:[\\d]{1,3}\\.){3}(?:[\\d]{1,3}))|(?:(?:www\\.|[a-zA-Z]\\.)?[a-zA-Z0-9\\-]+(?:\\.[a-z]{2,})+))(?::\\d*)?(?:\/[\\w#!:\\.?\\+=&%@!\\-]*)*";
837    //            protocoll     usr     pwd                    ip                             or                       host                        tld          port               path
838
839    /**
840     * @member {string} Hyphenator~mail
841     * @desc
842     * A string containing a RegularExpression to match mail-adresses
843     * @access private
844     */
845    var mail = "[\\w-\\.]+@[\\w\\.]+";
846
847    /**
848     * @member {string} Hyphenator~zeroWidthSpace
849     * @desc
850     * A string that holds a char.
851     * Depending on the browser, this is the zero with space or an empty string.
852     * zeroWidthSpace is used to break URLs
853     * @access private
854     */
855    var zeroWidthSpace = (function () {
856        var zws, ua = window.navigator.userAgent.toLowerCase();
857        zws = String.fromCharCode(8203); //Unicode zero width space
858        if (ua.indexOf("msie 6") !== -1) {
859            zws = ""; //IE6 doesn"t support zws
860        }
861        if (ua.indexOf("opera") !== -1 && ua.indexOf("version/10.00") !== -1) {
862            zws = ""; //opera 10 on XP doesn"t support zws
863        }
864        return zws;
865    }());
866
867    /**
868     * @method Hyphenator~onBeforeWordHyphenation
869     * @desc
870     * This method is called just before a word is hyphenated.
871     * It is called with two parameters: the word and its language.
872     * The method must return a string (aka the word).
873     * @see {@link Hyphenator.config}
874     * @access private
875     * @param {string} word
876     * @param {string} lang
877     * @return {string} The word that goes into hyphenation
878     */
879    var onBeforeWordHyphenation = function (word) {
880        return word;
881    };
882
883    /**
884     * @method Hyphenator~onAfterWordHyphenation
885     * @desc
886     * This method is called for each word after it is hyphenated.
887     * Takes the word as a first parameter and its language as a second parameter.
888     * Returns a string that will replace the word that has been hyphenated.
889     * @see {@link Hyphenator.config}
890     * @access private
891     * @param {string} word
892     * @param {string} lang
893     * @return {string} The word that goes into hyphenation
894     */
895    var onAfterWordHyphenation = function (word) {
896        return word;
897    };
898
899    /**
900     * @method Hyphenator~onHyphenationDone
901     * @desc
902     * A method to be called, when the last element has been hyphenated.
903     * If there are frames the method is called for each frame.
904     * Therefore the location.href of the contextWindow calling this method is given as a parameter
905     * @see {@link Hyphenator.config}
906     * @param {string} context
907     * @access private
908     */
909    var onHyphenationDone = function (context) {
910        return context;
911    };
912
913    /**
914     * @name Hyphenator~selectorFunction
915     * @desc
916     * A function set by the user that has to return a HTMLNodeList or array of Elements to be hyphenated.
917     * By default this is set to false so we can check if a selectorFunction is set…
918     * @see {@link Hyphenator.config}
919     * @see {@link Hyphenator~mySelectorFunction}
920     * @default false
921     * @type {function|boolean}
922     * @access private
923     */
924    var selectorFunction = false;
925
926    /**
927     * @name Hyphenator~flattenNodeList
928     * @desc
929     * Takes a nodeList and returns an array with all elements that are not contained by another element in the nodeList
930     * By using this function the elements returned by selectElements can be "flattened".
931     * @see {@link Hyphenator~selectElements}
932     * @param {nodeList} nl
933     * @return {Array} Array of "parent"-elements
934     * @access private
935     */
936    function flattenNodeList(nl) {
937        var parentElements = [],
938            i = 1,
939            j = 0,
940            isParent = true;
941
942        parentElements.push(nl[0]); //add the first item, since this is always an parent
943
944        while (i < nl.length) { //cycle through nodeList
945            while (j < parentElements.length) { //cycle through parentElements
946                if (parentElements[j].contains(nl[i])) {
947                    isParent = false;
948                    break;
949                }
950                j += 1;
951            }
952            if (isParent) {
953                parentElements.push(nl[i]);
954            }
955            isParent = true;
956            i += 1;
957        }
958
959        return parentElements;
960    }
961
962    /**
963     * @method Hyphenator~mySelectorFunction
964     * @desc
965     * A function that returns a HTMLNodeList or array of Elements to be hyphenated.
966     * By default it uses the classname ("hyphenate") to select the elements.
967     * @access private
968     */
969    function mySelectorFunction(hyphenateClass) {
970        var tmp,
971            el = [],
972            i = 0;
973        if (window.document.getElementsByClassName) {
974            el = contextWindow.document.getElementsByClassName(hyphenateClass);
975        } else if (window.document.querySelectorAll) {
976            el = contextWindow.document.querySelectorAll("." + hyphenateClass);
977        } else {
978            tmp = contextWindow.document.getElementsByTagName("*");
979            while (i < tmp.length) {
980                if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
981                    el.push(tmp[i]);
982                }
983                i += 1;
984            }
985        }
986        return el;
987    }
988
989    /**
990     * @method Hyphenator~selectElements
991     * @desc
992     * A function that uses either selectorFunction set by the user
993     * or the default mySelectorFunction.
994     * @access private
995     */
996    function selectElements() {
997        var elems;
998        if (selectorFunction) {
999            elems = selectorFunction();
1000        } else {
1001            elems = mySelectorFunction(hyphenateClass);
1002        }
1003        if (elems.length !== 0) {
1004            elems = flattenNodeList(elems);
1005        }
1006        return elems;
1007    }
1008
1009    /**
1010     * @member {string} Hyphenator~intermediateState
1011     * @desc
1012     * The visibility of elements while they are hyphenated:
1013     * "visible": unhyphenated text is visible and then redrawn when hyphenated.
1014     * "hidden": unhyphenated text is made invisible as soon as possible and made visible after hyphenation.
1015     * @default "hidden"
1016     * @see {@link Hyphenator.config}
1017     * @access private
1018     */
1019    var intermediateState = "hidden";
1020
1021    /**
1022     * @member {string} Hyphenator~unhide
1023     * @desc
1024     * How hidden elements unhide: either simultaneous (default: "wait") or progressively.
1025     * "wait" makes Hyphenator.js to wait until all elements are hyphenated (one redraw)
1026     * With "progressive" Hyphenator.js unhides elements as soon as they are hyphenated.
1027     * @see {@link Hyphenator.config}
1028     * @access private
1029     */
1030    var unhide = "wait";
1031
1032    /**
1033     * @member {Array.<Hyphenator~CSSEdit>} Hyphenator~CSSEditors
1034     * @desc A container array that holds CSSEdit classes
1035     * For each window object one CSSEdit class is inserted
1036     * @access private
1037     */
1038    var CSSEditors = [];
1039
1040    /**
1041     * @constructor Hyphenator~CSSEdit
1042     * @desc
1043     * This class handles access and editing of StyleSheets.
1044     * Thanks to this styles (e.g. hiding and unhiding elements upon hyphenation)
1045     * can be changed in one place instead for each element.
1046     * @access private
1047     */
1048    function makeCSSEdit(w) {
1049        w = w || window;
1050        var doc = w.document,
1051            /**
1052             * @member {Object} Hyphenator~CSSEdit~sheet
1053             * @desc
1054             * A StyleSheet, where Hyphenator can write to.
1055             * If no StyleSheet can be found, lets create one.
1056             * @access private
1057             */
1058            sheet = (function () {
1059                var i = 0,
1060                    l = doc.styleSheets.length,
1061                    s,
1062                    element,
1063                    r = false;
1064                while (i < l) {
1065                    s = doc.styleSheets[i];
1066                    try {
1067                        if (!!s.cssRules) {
1068                            r = s;
1069                            break;
1070                        }
1071                    } catch (ignore) {}
1072                    i += 1;
1073                }
1074                if (r === false) {
1075                    element = doc.createElement("style");
1076                    element.type = "text/css";
1077                    doc.getElementsByTagName("head")[0].appendChild(element);
1078                    r = doc.styleSheets[doc.styleSheets.length - 1];
1079                }
1080                return r;
1081            }()),
1082
1083            /**
1084             * @typedef {Object} Hyphenator~CSSEdit~changes
1085             * @property {Object} sheet - The StyleSheet where the change was made
1086             * @property {number} index - The index of the changed rule
1087             */
1088
1089            /**
1090             * @member {Array.<changes>} Hyphenator~CSSEdit~changes
1091             * @desc
1092             * Sets a CSS rule for a specified selector
1093             * @access private
1094             */
1095            changes = [],
1096
1097            /**
1098             * @typedef Hyphenator~CSSEdit~rule
1099             * @property {number} index - The index of the rule
1100             * @property {Object} rule - The style rule
1101             */
1102            /**
1103             * @method Hyphenator~CSSEdit~findRule
1104             * @desc
1105             * Searches the StyleSheets for a given selector and returns an object containing the rule.
1106             * If nothing can be found, false is returned.
1107             * @param {string} sel
1108             * @return {Hyphenator~CSSEdit~rule|false}
1109             * @access private
1110             */
1111            findRule = function (sel) {
1112                var s,
1113                    rule,
1114                    sheets = w.document.styleSheets,
1115                    rules,
1116                    i = 0,
1117                    j = 0,
1118                    r = false;
1119                while (i < sheets.length) {
1120                    s = sheets[i];
1121                    try { //FF has issues here with external CSS (s.o.p)
1122                        if (!!s.cssRules) {
1123                            rules = s.cssRules;
1124                        } else if (!!s.rules) {
1125                            // IE < 9
1126                            rules = s.rules;
1127                        }
1128                    } catch (ignore) {}
1129                    if (!!rules && !!rules.length) {
1130                        while (j < rules.length) {
1131                            rule = rules[j];
1132                            if (rule.selectorText === sel) {
1133                                r = {
1134                                    index: j,
1135                                    rule: rule
1136                                };
1137                            }
1138                            j += 1;
1139                        }
1140                    }
1141                    i += 1;
1142                }
1143                return r;
1144            },
1145            /**
1146             * @method Hyphenator~CSSEdit~addRule
1147             * @desc
1148             * Adds a rule to the {@link Hyphenator~CSSEdit~sheet}
1149             * @param {string} sel - The selector to be added
1150             * @param {string} rulesStr - The rules for the specified selector
1151             * @return {number} index of the new rule
1152             * @access private
1153             */
1154            addRule = function (sel, rulesStr) {
1155                var i, r;
1156                if (!!sheet.insertRule) {
1157                    if (!!sheet.cssRules) {
1158                        i = sheet.cssRules.length;
1159                    } else {
1160                        i = 0;
1161                    }
1162                    r = sheet.insertRule(sel + "{" + rulesStr + "}", i);
1163                } else if (!!sheet.addRule) {
1164                    // IE < 9
1165                    if (!!sheet.rules) {
1166                        i = sheet.rules.length;
1167                    } else {
1168                        i = 0;
1169                    }
1170                    sheet.addRule(sel, rulesStr, i);
1171                    r = i;
1172                }
1173                return r;
1174            },
1175            /**
1176             * @method Hyphenator~CSSEdit~removeRule
1177             * @desc
1178             * Removes a rule with the specified index from the specified sheet
1179             * @param {Object} sheet - The style sheet
1180             * @param {number} index - the index of the rule
1181             * @access private
1182             */
1183            removeRule = function (sheet, index) {
1184                if (sheet.deleteRule) {
1185                    sheet.deleteRule(index);
1186                } else {
1187                    // IE < 9
1188                    sheet.removeRule(index);
1189                }
1190            };
1191
1192        return {
1193            /**
1194             * @method Hyphenator~CSSEdit.setRule
1195             * @desc
1196             * Sets a CSS rule for a specified selector
1197             * @access public
1198             * @param {string} sel - Selector
1199             * @param {string} rulesString - CSS-Rules
1200             */
1201            setRule: function (sel, rulesString) {
1202                var i, existingRule, cssText;
1203                existingRule = findRule(sel);
1204                if (!!existingRule) {
1205                    if (!!existingRule.rule.cssText) {
1206                        cssText = existingRule.rule.cssText;
1207                    } else {
1208                        // IE < 9
1209                        cssText = existingRule.rule.style.cssText.toLowerCase();
1210                    }
1211                    if (cssText !== sel + " { " + rulesString + " }") {
1212                        //cssText of the found rule is not uniquely selector + rulesString,
1213                        if (cssText.indexOf(rulesString) !== -1) {
1214                            //maybe there are other rules or IE < 9
1215                            //clear existing def
1216                            existingRule.rule.style.visibility = "";
1217                        }
1218                        //add rule and register for later removal
1219                        i = addRule(sel, rulesString);
1220                        changes.push({sheet: sheet, index: i});
1221                    }
1222                } else {
1223                    i = addRule(sel, rulesString);
1224                    changes.push({sheet: sheet, index: i});
1225                }
1226            },
1227            /**
1228             * @method Hyphenator~CSSEdit.clearChanges
1229             * @desc
1230             * Removes all changes Hyphenator has made from the StyleSheets
1231             * @access public
1232             */
1233            clearChanges: function () {
1234                var change = changes.pop();
1235                while (!!change) {
1236                    removeRule(change.sheet, change.index);
1237                    change = changes.pop();
1238                }
1239            }
1240        };
1241    }
1242
1243    /**
1244     * @member {string} Hyphenator~hyphen
1245     * @desc
1246     * A string containing the character for in-word-hyphenation
1247     * @default the soft hyphen
1248     * @access private
1249     * @see {@link Hyphenator.config}
1250     */
1251    var hyphen = String.fromCharCode(173);
1252
1253    /**
1254     * @member {string} Hyphenator~urlhyphen
1255     * @desc
1256     * A string containing the character for url/mail-hyphenation
1257     * @default the zero width space
1258     * @access private
1259     * @see {@link Hyphenator.config}
1260     * @see {@link Hyphenator~zeroWidthSpace}
1261     */
1262    var urlhyphen = zeroWidthSpace;
1263
1264    /**
1265     * @method Hyphenator~hyphenateURL
1266     * @desc
1267     * Puts {@link Hyphenator~urlhyphen} (default: zero width space) after each no-alphanumeric char that my be in a URL.
1268     * @param {string} url to hyphenate
1269     * @returns string the hyphenated URL
1270     * @access public
1271     */
1272    function hyphenateURL(url) {
1273        var tmp = url.replace(/([:\/.?#&\-_,;!@]+)/gi, "$&" + urlhyphen),
1274            parts = tmp.split(urlhyphen),
1275            i = 0;
1276        while (i < parts.length) {
1277            if (parts[i].length > (2 * min)) {
1278                parts[i] = parts[i].replace(/(\w{3})(\w)/gi, "$1" + urlhyphen + "$2");
1279            }
1280            i += 1;
1281        }
1282        if (parts[parts.length - 1] === "") {
1283            parts.pop();
1284        }
1285        return parts.join(urlhyphen);
1286    }
1287
1288    /**
1289     * @member {boolean} Hyphenator~safeCopy
1290     * @desc
1291     * Defines wether work-around for copy issues is active or not
1292     * @default true
1293     * @access private
1294     * @see {@link Hyphenator.config}
1295     * @see {@link Hyphenator~registerOnCopy}
1296     */
1297    var safeCopy = true;
1298
1299    /**
1300     * @method Hyphenator~zeroTimeOut
1301     * @desc
1302     * defer execution of a function on the call stack
1303     * Analog to window.setTimeout(fn, 0) but without a clamped delay if postMessage is supported
1304     * @access private
1305     * @see {@link http://dbaron.org/log/20100309-faster-timeouts}
1306     */
1307    var zeroTimeOut = (function () {
1308        if (window.postMessage && window.addEventListener) {
1309            return (function () {
1310                var timeouts = [],
1311                    msg = "Hyphenator_zeroTimeOut_message",
1312                    setZeroTimeOut = function (fn) {
1313                        timeouts.push(fn);
1314                        window.postMessage(msg, "*");
1315                    },
1316                    handleMessage = function (event) {
1317                        if (event.source === window && event.data === msg) {
1318                            event.stopPropagation();
1319                            if (timeouts.length > 0) {
1320                                //var efn = timeouts.shift();
1321                                //efn();
1322                                timeouts.shift()();
1323                            }
1324                        }
1325                    };
1326                window.addEventListener("message", handleMessage, true);
1327                return setZeroTimeOut;
1328            }());
1329        }
1330        return function (fn) {
1331            window.setTimeout(fn, 0);
1332        };
1333    }());
1334
1335    /**
1336     * @member {Object} Hyphenator~hyphRunFor
1337     * @desc
1338     * stores location.href for documents where run() has been executed
1339     * to warn when Hyphenator.run() executed multiple times
1340     * @access private
1341     * @see {@link Hyphenator~runWhenLoaded}
1342     */
1343    var hyphRunFor = {};
1344
1345    /**
1346     * @method Hyphenator~removeHyphenationFromElement
1347     * @desc
1348     * Removes all hyphens from the element. If there are other elements, the function is
1349     * called recursively.
1350     * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
1351     * @param {Object} el The element where to remove hyphenation.
1352     * @access public
1353     */
1354    function removeHyphenationFromElement(el) {
1355        var h, u, i = 0, n;
1356        //escape hyphen
1357        if (".\\+*?[^]$(){}=!<>|:-".indexOf(hyphen) !== -1) {
1358            h = "\\" + hyphen;
1359        } else {
1360            h = hyphen;
1361        }
1362        //escape hyphen
1363        if (".\\+*?[^]$(){}=!<>|:-".indexOf(urlhyphen) !== -1) {
1364            u = "\\" + urlhyphen;
1365        } else {
1366            u = urlhyphen;
1367        }
1368        n = el.childNodes[i];
1369        while (!!n) {
1370            if (n.nodeType === 3) {
1371                n.data = n.data.replace(new RegExp(h, "g"), "");
1372                n.data = n.data.replace(new RegExp(u, "g"), "");
1373            } else if (n.nodeType === 1) {
1374                removeHyphenationFromElement(n);
1375            }
1376            i += 1;
1377            n = el.childNodes[i];
1378        }
1379    }
1380
1381    /**
1382     * @member {Object} Hyphenator~hyphRunFor
1383     * @desc
1384     * stores location.href for documents where run() has been executed
1385     * to warn when Hyphenator.run() executed multiple times
1386     * @access private
1387     * @see {@link Hyphenator~runWhenLoaded}
1388     */
1389    var copy = (function () {
1390        var factory = function () {
1391            var registeredElements = [];
1392            var oncopyHandler = function (e) {
1393                e = e || window.event;
1394                var shadow,
1395                    selection,
1396                    range,
1397                    rangeShadow,
1398                    restore,
1399                    target = e.target || e.srcElement,
1400                    currDoc = target.ownerDocument,
1401                    bdy = currDoc.getElementsByTagName("body")[0],
1402                    targetWindow = currDoc.defaultView || currDoc.parentWindow;
1403                if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
1404                    //Safari needs this
1405                    return;
1406                }
1407                //create a hidden shadow element
1408                shadow = currDoc.createElement("div");
1409                shadow.style.color = window.getComputedStyle
1410                    ? targetWindow.getComputedStyle(bdy, null).backgroundColor
1411                    : "#FFFFFF";
1412                shadow.style.fontSize = "0px";
1413                bdy.appendChild(shadow);
1414                if (!!window.getSelection) {
1415                    //FF3, Webkit, IE9
1416                    selection = targetWindow.getSelection();
1417                    range = selection.getRangeAt(0);
1418                    shadow.appendChild(range.cloneContents());
1419                    removeHyphenationFromElement(shadow);
1420                    selection.selectAllChildren(shadow);
1421                    restore = function () {
1422                        shadow.parentNode.removeChild(shadow);
1423                        selection.removeAllRanges(); //IE9 needs that
1424                        selection.addRange(range);
1425                    };
1426                } else {
1427                    // IE<9
1428                    selection = targetWindow.document.selection;
1429                    range = selection.createRange();
1430                    shadow.innerHTML = range.htmlText;
1431                    removeHyphenationFromElement(shadow);
1432                    rangeShadow = bdy.createTextRange();
1433                    rangeShadow.moveToElementText(shadow);
1434                    rangeShadow.select();
1435                    restore = function () {
1436                        shadow.parentNode.removeChild(shadow);
1437                        if (range.text !== "") {
1438                            range.select();
1439                        }
1440                    };
1441                }
1442                zeroTimeOut(restore);
1443            };
1444            var removeOnCopy = function () {
1445                var i = registeredElements.length - 1;
1446                while (i >= 0) {
1447                    if (window.removeEventListener) {
1448                        registeredElements[i].removeEventListener("copy", oncopyHandler, true);
1449                    } else {
1450                        registeredElements[i].detachEvent("oncopy", oncopyHandler);
1451                    }
1452                    i -= 1;
1453                }
1454            };
1455            var reactivateOnCopy = function () {
1456                var i = registeredElements.length - 1;
1457                while (i >= 0) {
1458                    if (window.addEventListener) {
1459                        registeredElements[i].addEventListener("copy", oncopyHandler, true);
1460                    } else {
1461                        registeredElements[i].attachEvent("oncopy", oncopyHandler);
1462                    }
1463                    i -= 1;
1464                }
1465            };
1466            var registerOnCopy = function (el) {
1467                registeredElements.push(el);
1468                if (window.addEventListener) {
1469                    el.addEventListener("copy", oncopyHandler, true);
1470                } else {
1471                    el.attachEvent("oncopy", oncopyHandler);
1472                }
1473            };
1474            return {
1475                oncopyHandler: oncopyHandler,
1476                removeOnCopy: removeOnCopy,
1477                registerOnCopy: registerOnCopy,
1478                reactivateOnCopy: reactivateOnCopy
1479            };
1480        };
1481        return (safeCopy
1482            ? factory()
1483            : false);
1484    }());
1485
1486    /**
1487     * @method Hyphenator~runWhenLoaded
1488     * @desc
1489     * A crossbrowser solution for the DOMContentLoaded-Event based on
1490     * <a href = "http://jquery.com/">jQuery</a>
1491     * I added some functionality: e.g. support for frames and iframes…
1492     * @param {Object} w the window-object
1493     * @param {function()} f the function to call when the document is ready
1494     * @access private
1495     */
1496    function runWhenLoaded(w, f) {
1497        var toplevel,
1498            add = window.document.addEventListener
1499                ? "addEventListener"
1500                : "attachEvent",
1501            rem = window.document.addEventListener
1502                ? "removeEventListener"
1503                : "detachEvent",
1504            pre = window.document.addEventListener
1505                ? ""
1506                : "on";
1507
1508        function init(context) {
1509            if (hyphRunFor[context.location.href]) {
1510                onWarning(new Error("Warning: multiple execution of Hyphenator.run() – This may slow down the script!"));
1511            }
1512            contextWindow = context || window;
1513            f();
1514            hyphRunFor[contextWindow.location.href] = true;
1515        }
1516
1517        function doScrollCheck() {
1518            try {
1519                // If IE is used, use the trick by Diego Perini
1520                // http://javascript.nwbox.com/IEContentLoaded/
1521                w.document.documentElement.doScroll("left");
1522            } catch (ignore) {
1523                window.setTimeout(doScrollCheck, 1);
1524                return;
1525            }
1526            //maybe modern IE fired DOMContentLoaded
1527            if (!hyphRunFor[w.location.href]) {
1528                documentLoaded = true;
1529                init(w);
1530            }
1531        }
1532
1533        function doOnEvent(e) {
1534            var i = 0,
1535                fl,
1536                haveAccess;
1537            if (!!e && e.type === "readystatechange" && w.document.readyState !== "interactive" && w.document.readyState !== "complete") {
1538                return;
1539            }
1540
1541            //DOM is ready/interactive, but frames may not be loaded yet!
1542            //cleanup events
1543            w.document[rem](pre + "DOMContentLoaded", doOnEvent, false);
1544            w.document[rem](pre + "readystatechange", doOnEvent, false);
1545
1546            //check frames
1547            fl = w.frames.length;
1548            if (fl === 0 || !doFrames) {
1549                //there are no frames!
1550                //cleanup events
1551                w[rem](pre + "load", doOnEvent, false);
1552                documentLoaded = true;
1553                init(w);
1554            } else if (doFrames && fl > 0) {
1555                //we have frames, so wait for onload and then initiate runWhenLoaded recursevly for each frame:
1556                if (!!e && e.type === "load") {
1557                    //cleanup events
1558                    w[rem](pre + "load", doOnEvent, false);
1559                    while (i < fl) {
1560                        haveAccess = undefined;
1561                        //try catch isn"t enough for webkit
1562                        try {
1563                            //opera throws only on document.toString-access
1564                            haveAccess = w.frames[i].document.toString();
1565                        } catch (ignore) {
1566                            haveAccess = undefined;
1567                        }
1568                        if (!!haveAccess) {
1569                            runWhenLoaded(w.frames[i], f);
1570                        }
1571                        i += 1;
1572                    }
1573                    init(w);
1574                }
1575            }
1576        }
1577
1578        if (documentLoaded || w.document.readyState === "complete") {
1579            //Hyphenator has run already (documentLoaded is true) or
1580            //it has been loaded after onLoad
1581            documentLoaded = true;
1582            doOnEvent({type: "load"});
1583        } else {
1584            //register events
1585            w.document[add](pre + "DOMContentLoaded", doOnEvent, false);
1586            w.document[add](pre + "readystatechange", doOnEvent, false);
1587            w[add](pre + "load", doOnEvent, false);
1588            toplevel = false;
1589            try {
1590                toplevel = !window.frameElement;
1591            } catch (ignore) {}
1592            if (toplevel && w.document.documentElement.doScroll) {
1593                doScrollCheck(); //calls init()
1594            }
1595        }
1596    }
1597
1598    /**
1599     * @method Hyphenator~getLang
1600     * @desc
1601     * Gets the language of an element. If no language is set, it may use the {@link Hyphenator~mainLanguage}.
1602     * @param {Object} el The first parameter is an DOM-Element-Object
1603     * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator~mainLanguage}
1604     * if there"s no language found for the element.
1605     * @return {string} The language of the element
1606     * @access private
1607     */
1608    function getLang(el, fallback) {
1609        try {
1610            return !!el.getAttribute("lang")
1611                ? el.getAttribute("lang").toLowerCase()
1612                : !!el.getAttribute("xml:lang")
1613                    ? el.getAttribute("xml:lang").toLowerCase()
1614                    : el.tagName.toLowerCase() !== "html"
1615                        ? getLang(el.parentNode, fallback)
1616                        : fallback
1617                            ? mainLanguage
1618                            : null;
1619        } catch (ignore) {
1620            return fallback
1621                ? mainLanguage
1622                : null;
1623        }
1624    }
1625
1626    /**
1627     * @method Hyphenator~autoSetMainLanguage
1628     * @desc
1629     * Retrieves the language of the document from the DOM and sets the lang attribute of the html-tag.
1630     * The function looks in the following places:
1631     * <ul>
1632     * <li>lang-attribute in the html-tag</li>
1633     * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
1634     * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
1635     * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
1636     * </li>
1637     * If nothing can be found a prompt using {@link Hyphenator~languageHint} and a prompt-string is displayed.
1638     * If the retrieved language is in the object {@link Hyphenator~supportedLangs} it is copied to {@link Hyphenator~mainLanguage}
1639     * @access private
1640     */
1641    function autoSetMainLanguage(w) {
1642        w = w || contextWindow;
1643        var el = w.document.getElementsByTagName("html")[0],
1644            m = w.document.getElementsByTagName("meta"),
1645            i = 0,
1646            getLangFromUser = function () {
1647                var text = "";
1648                var ul = "";
1649                var languageHint = (function () {
1650                    var r = "";
1651                    forEachKey(supportedLangs, function (k) {
1652                        r += k + ", ";
1653                    });
1654                    r = r.substring(0, r.length - 2);
1655                    return r;
1656                }());
1657                ul = window.navigator.language || window.navigator.userLanguage;
1658                ul = ul.substring(0, 2);
1659                if (!!supportedLangs[ul] && supportedLangs[ul].prompt !== "") {
1660                    text = supportedLangs[ul].prompt;
1661                } else {
1662                    text = supportedLangs.en.prompt;
1663                }
1664                text += " (ISO 639-1)\n\n" + languageHint;
1665                return window.prompt(window.unescape(text), ul).toLowerCase();
1666            };
1667        mainLanguage = getLang(el, false);
1668        if (!mainLanguage) {
1669            while (i < m.length) {
1670                //<meta http-equiv = "content-language" content="xy">
1671                if (!!m[i].getAttribute("http-equiv") && (m[i].getAttribute("http-equiv").toLowerCase() === "content-language")) {
1672                    mainLanguage = m[i].getAttribute("content").toLowerCase();
1673                }
1674                //<meta name = "DC.Language" content="xy">
1675                if (!!m[i].getAttribute("name") && (m[i].getAttribute("name").toLowerCase() === "dc.language")) {
1676                    mainLanguage = m[i].getAttribute("content").toLowerCase();
1677                }
1678                //<meta name = "language" content = "xy">
1679                if (!!m[i].getAttribute("name") && (m[i].getAttribute("name").toLowerCase() === "language")) {
1680                    mainLanguage = m[i].getAttribute("content").toLowerCase();
1681                }
1682                i += 1;
1683            }
1684        }
1685        //get lang for frame from enclosing document
1686        if (!mainLanguage && doFrames && (!!contextWindow.frameElement)) {
1687            autoSetMainLanguage(window.parent);
1688        }
1689        //fallback to defaultLang if set
1690        if (!mainLanguage && defaultLanguage !== "") {
1691            mainLanguage = defaultLanguage;
1692        }
1693        //ask user for lang
1694        if (!mainLanguage) {
1695            mainLanguage = getLangFromUser();
1696        }
1697        el.lang = mainLanguage;
1698    }
1699
1700    /**
1701     * @method Hyphenator~gatherDocumentInfos
1702     * @desc
1703     * This method runs through the DOM and executes the process()-function on:
1704     * - every node returned by the {@link Hyphenator~selectorFunction}.
1705     * @access private
1706     */
1707    function gatherDocumentInfos() {
1708        var elToProcess,
1709            urlhyphenEls,
1710            tmp,
1711            i = 0;
1712        /**
1713         * @method Hyphenator~gatherDocumentInfos
1714         * @desc
1715         * This method copies the element to the elements-variable, sets its visibility
1716         * to intermediateState, retrieves its language and recursivly descends the DOM-tree until
1717         * the child-Nodes aren"t of type 1
1718         * @param {Object} el a DOM element
1719         * @param {string} plang the language of the parent element
1720         * @param {boolean} isChild true, if the parent of el has been processed
1721         */
1722        function process(el, pLang, isChild) {
1723            var n,
1724                j = 0,
1725                hyphenate = true,
1726                eLang,
1727                useCSS3 = function () {
1728                    css3hyphenateClassHandle = makeCSSEdit(contextWindow);
1729                    css3hyphenateClassHandle.setRule("." + css3hyphenateClass, css3_h9n.property + ": auto;");
1730                    css3hyphenateClassHandle.setRule("." + dontHyphenateClass, css3_h9n.property + ": manual;");
1731                    if ((eLang !== pLang) && css3_h9n.property.indexOf("webkit") !== -1) {
1732                        css3hyphenateClassHandle.setRule("." + css3hyphenateClass, "-webkit-locale : " + eLang + ";");
1733                    }
1734                    el.className = el.className + " " + css3hyphenateClass;
1735                },
1736                useHyphenator = function () {
1737                    //quick fix for test111.html
1738                    //better: weight elements
1739                    if (isBookmarklet && eLang !== mainLanguage) {
1740                        return;
1741                    }
1742                    if (supportedLangs.hasOwnProperty(eLang)) {
1743                        docLanguages[eLang] = true;
1744                    } else {
1745                        if (supportedLangs.hasOwnProperty(eLang.split("-")[0])) { //try subtag
1746                            eLang = eLang.split("-")[0];
1747                            docLanguages[eLang] = true;
1748                        } else if (!isBookmarklet) {
1749                            hyphenate = false;
1750                            onError(new Error("Language \"" + eLang + "\" is not yet supported."));
1751                        }
1752                    }
1753                    if (hyphenate) {
1754                        if (intermediateState === "hidden") {
1755                            el.className = el.className + " " + hideClass;
1756                        }
1757                        elements.add(el, eLang);
1758                    }
1759                };
1760            isChild = isChild || false;
1761            if (el.lang && typeof el.lang === "string") {
1762                eLang = el.lang.toLowerCase(); //copy attribute-lang to internal eLang
1763            } else if (!!pLang && pLang !== "") {
1764                eLang = pLang.toLowerCase();
1765            } else {
1766                eLang = getLang(el, true);
1767            }
1768
1769            if (!isChild) {
1770                if (css3 && css3_h9n.support && !!css3_h9n.checkLangSupport(eLang)) {
1771                    useCSS3();
1772                } else {
1773                    if (safeCopy) {
1774                        copy.registerOnCopy(el);
1775                    }
1776                    useHyphenator();
1777                }
1778            } else {
1779                if (eLang !== pLang) {
1780                    if (css3 && css3_h9n.support && !!css3_h9n.checkLangSupport(eLang)) {
1781                        useCSS3();
1782                    } else {
1783                        useHyphenator();
1784                    }
1785                } else {
1786                    if (!css3 || !css3_h9n.support || !css3_h9n.checkLangSupport(eLang)) {
1787                        useHyphenator();
1788                    } // else do nothing
1789                }
1790            }
1791            n = el.childNodes[j];
1792            while (!!n) {
1793                if (
1794                    n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
1795                        n.className.indexOf(dontHyphenateClass) === -1 &&
1796                        n.className.indexOf(urlHyphenateClass) === -1 && !elToProcess[n]
1797                ) {
1798                    process(n, eLang, true);
1799                }
1800                j += 1;
1801                n = el.childNodes[j];
1802            }
1803        }
1804        function processUrlStyled(el) {
1805            var n, j = 0;
1806
1807            n = el.childNodes[j];
1808            while (!!n) {
1809                if (
1810                    n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
1811                        n.className.indexOf(dontHyphenateClass) === -1 &&
1812                        n.className.indexOf(hyphenateClass) === -1 && !urlhyphenEls[n]
1813                ) {
1814                    processUrlStyled(n);
1815                } else if (n.nodeType === 3) {
1816                    if (safeCopy) {
1817                        copy.registerOnCopy(n.parentNode);
1818                    }
1819                    //n.data = hyphenateURL(n.data);
1820                    elements.add(n.parentNode, "urlstyled");
1821                }
1822                j += 1;
1823                n = el.childNodes[j];
1824            }
1825        }
1826
1827        if (css3) {
1828            css3_h9n = css3_gethsupport();
1829        }
1830        if (isBookmarklet) {
1831            elToProcess = contextWindow.document.getElementsByTagName("body")[0];
1832            process(elToProcess, mainLanguage, false);
1833        } else {
1834            if (!css3 && intermediateState === "hidden") {
1835                CSSEditors.push(makeCSSEdit(contextWindow));
1836                CSSEditors[CSSEditors.length - 1].setRule("." + hyphenateClass, "visibility: hidden;");
1837                CSSEditors[CSSEditors.length - 1].setRule("." + hideClass, "visibility: hidden;");
1838                CSSEditors[CSSEditors.length - 1].setRule("." + unhideClass, "visibility: visible;");
1839            }
1840            elToProcess = selectElements();
1841            tmp = elToProcess[i];
1842            while (!!tmp) {
1843                process(tmp, "", false);
1844                i += 1;
1845                tmp = elToProcess[i];
1846            }
1847
1848            urlhyphenEls = mySelectorFunction(urlHyphenateClass);
1849            i = 0;
1850            tmp = urlhyphenEls[i];
1851            while (!!tmp) {
1852                processUrlStyled(tmp);
1853                i += 1;
1854                tmp = urlhyphenEls[i];
1855            }
1856        }
1857        if (elements.counters[0] === 0) {
1858            //nothing to hyphenate or all hyphenated by css3
1859            i = 0;
1860            while (i < CSSEditors.length) {
1861                CSSEditors[i].clearChanges();
1862                i += 1;
1863            }
1864            onHyphenationDone(contextWindow.location.href);
1865        }
1866    }
1867
1868    /**
1869     * @method Hyphenator~createCharMap
1870     * @desc
1871     * reads the charCodes from lo.characters and stores them in a bidi map:
1872     * charMap.int2code =  [0: 97, //a
1873     *                      1: 98, //b
1874     *                      2: 99] //c etc.
1875     * charMap.code2int = {"97": 0, //a
1876     *                     "98": 1, //b
1877     *                     "99": 2} //c etc.
1878     * @access private
1879     * @param {Object} language object
1880     */
1881    function makeCharMap() {
1882        var int2code = [],
1883            code2int = {},
1884            add = function (newValue) {
1885                if (!code2int[newValue]) {
1886                    int2code.push(newValue);
1887                    code2int[newValue] = int2code.length - 1;
1888                }
1889            };
1890        return {
1891            int2code: int2code,
1892            code2int: code2int,
1893            add: add
1894        };
1895    }
1896
1897    /**
1898     * @constructor Hyphenator~ValueStore
1899     * @desc Storage-Object for storing hyphenation points (aka values)
1900     * @access private
1901     */
1902    function makeValueStore(len) {
1903        var indexes = (function () {
1904                var arr;
1905                if (Object.prototype.hasOwnProperty.call(window, "Uint32Array")) { //IE<9 doesn"t have window.hasOwnProperty (host object)
1906                    arr = new window.Uint32Array(3);
1907                    arr[0] = 1; //start position of a value set
1908                    arr[1] = 1; //next index
1909                    arr[2] = 1; //last index with a significant value
1910                } else {
1911                    arr = [1, 1, 1];
1912                }
1913                return arr;
1914            }()),
1915            keys = (function () {
1916                var i, r;
1917                if (Object.prototype.hasOwnProperty.call(window, "Uint8Array")) { //IE<9 doesn"t have window.hasOwnProperty (host object)
1918                    return new window.Uint8Array(len);
1919                }
1920                r = [];
1921                r.length = len;
1922                i = r.length - 1;
1923                while (i >= 0) {
1924                    r[i] = 0;
1925                    i -= 1;
1926                }
1927                return r;
1928            }()),
1929            add = function (p) {
1930                keys[indexes[1]] = p;
1931                indexes[2] = indexes[1];
1932                indexes[1] += 1;
1933            },
1934            add0 = function () {
1935                //just do a step, since array is initialized with zeroe
1935s
1936                indexes[1] += 1;
1937            },
1938            finalize = function () {
1939                var start = indexes[0];
1940                keys[indexes[2] + 1] = 255; //mark end of pattern
1941                indexes[0] = indexes[2] + 2;
1942                indexes[1] = indexes[0];
1943                return start;
1944            };
1945        return {
1946            keys: keys,
1947            add: add,
1948            add0: add0,
1949            finalize: finalize
1950        };
1951    }
1952
1953    /**
1954     * @method Hyphenator~convertPatternsToArray
1955     * @desc
1956     * converts the patterns to a (typed, if possible) array as described by Liang:
1957     *
1958     * 1. Create the CharMap: an alphabet of used character codes mapped to an int (e.g. a: "97" -> 0)
1959     *    This map is bidirectional:
1960     *    charMap.code2int is an object with charCodes as keys and corresponging ints as values
1961     *    charMao.int2code is an array of charCodes at int indizes
1962     *    the length of charMao.int2code is equal the length of the alphabet
1963     *
1964     * 2. Create a ValueStore: (typed) array that holds "values", i.e. the digits extracted from the patterns
1965     *    The first value set starts at index 1 (since the trie is initialized with zeroes, starting at 0 would create errors)
1966     *    Each value set ends with a value of 255; trailing 0"s are not stored. So pattern values like e.g. "010200" will become […,0,1,0,2,255,…]
1967     *    The ValueStore-Object manages handling of indizes automatically. Use ValueStore.add(p) to add a running value.
1968     *    Use ValueStore.finalize() when the last value of a pattern is added. It will add the final 255, prepare the valueStore for new values
1969     *    and return the starting index of the pattern.
1970     *    To prevent doubles we could temporarly store the values in a object {value: startIndex} and only add new values,
1971     *    but this object deoptimizes very fast (new hidden map for each entry); here we gain speed and pay memory
1972     *
1973     * 3. Create and zero initialize a (typed) array to store the trie. The trie uses two slots for each entry/node:
1974     *    i: a link to another position in the array or -1 if the pattern ends here or more rows have to be added.
1975     *    i + 1: a link to a value in the ValueStore or 0 if there"s no value for the path to this node.
1976     *    Although the array is one-dimensional it can be described as an array of "rows",
1977     *    where each "row" is an array of length trieRowLength (see below).
1978     *    The first entry of this "row" represents the first character of the alphabet, the second a possible link to value store,
1979     *    the third represents the second character of the alphabet and so on…
1980     *
1981     * 4. Initialize trieRowLength (length of the alphabet * 2)
1982     *
1983     * 5. Now we apply extract to each pattern collection (patterns of the same length are collected and concatenated to one string)
1984     *    extract goes through these pattern collections char by char and adds them either to the ValueStore (if they are digits) or
1985     *    to the trie (adding more "rows" if necessary, i.e. if the last link pointed to -1).
1986     *    So the first "row" holds all starting characters, where the subsequent rows hold the characters that follow the
1987     *    character that link to this row. Therefor the array is dense at the beginning and very sparse at the end.
1988     *
1989     *
1990     * @access private
1991     * @param {Object} language object
1992     */
1993    function convertPatternsToArray(lo) {
1994        var trieNextEmptyRow = 0,
1995            i,
1996            charMapc2i,
1997            valueStore,
1998            indexedTrie,
1999            trieRowLength,
2000
2001            extract = function (patternSizeInt, patterns) {
2002                var charPos = 0,
2003                    charCode = 0,
2004                    mappedCharCode = 0,
2005                    rowStart = 0,
2006                    nextRowStart = 0,
2007                    prevWasDigit = false;
2008                while (charPos < patterns.length) {
2009                    charCode = patterns.charCodeAt(charPos);
2010                    if ((charPos + 1) % patternSizeInt !== 0) {
2011                        //more to come…
2012                        if (charCode <= 57 && charCode >= 49) {
2013                            //charCode is a digit
2014                            valueStore.add(charCode - 48);
2015                            prevWasDigit = true;
2016                        } else {
2017                            //charCode is alphabetical
2018                            if (!prevWasDigit) {
2019                                valueStore.add0();
2020                            }
2021                            prevWasDigit = false;
2022                            if (nextRowStart === -1) {
2023                                nextRowStart = trieNextEmptyRow + trieRowLength;
2024                                trieNextEmptyRow = nextRowStart;
2025                                indexedTrie[rowStart + mappedCharCode * 2] = nextRowStart;
2026                            }
2027                            mappedCharCode = charMapc2i[charCode];
2028                            rowStart = nextRowStart;
2029                            nextRowStart = indexedTrie[rowStart + mappedCharCode * 2];
2030                            if (nextRowStart === 0) {
2031                                indexedTrie[rowStart + mappedCharCode * 2] = -1;
2032                                nextRowStart = -1;
2033                            }
2034                        }
2035                    } else {
2036                        //last part of pattern
2037                        if (charCode <= 57 && charCode >= 49) {
2038                            //the last charCode is a digit
2039                            valueStore.add(charCode - 48);
2040                            indexedTrie[rowStart + mappedCharCode * 2 + 1] = valueStore.finalize();
2041                        } else {
2042                            //the last charCode is alphabetical
2043                            if (!prevWasDigit) {
2044                                valueStore.add0();
2045                            }
2046                            valueStore.add0();
2047                            if (nextRowStart === -1) {
2048                                nextRowStart = trieNextEmptyRow + trieRowLength;
2049                                trieNextEmptyRow = nextRowStart;
2050                                indexedTrie[rowStart + mappedCharCode * 2] = nextRowStart;
2051                            }
2052                            mappedCharCode = charMapc2i[charCode];
2053                            rowStart = nextRowStart;
2054                            if (indexedTrie[rowStart + mappedCharCode * 2] === 0) {
2055                                indexedTrie[rowStart + mappedCharCode * 2] = -1;
2056                            }
2057                            indexedTrie[rowStart + mappedCharCode * 2 + 1] = valueStore.finalize();
2058                        }
2059                        rowStart = 0;
2060                        nextRowStart = 0;
2061                        prevWasDigit = false;
2062                    }
2063                    charPos += 1;
2064                }
2065            };/*,
2066            prettyPrintIndexedTrie = function (rowLength) {
2067                var s = "0: ",
2068                    idx;
2069                for (idx = 0; idx < indexedTrie.length; idx += 1) {
2070                    s += indexedTrie[idx];
2071                    s += ",";
2072                    if ((idx + 1) % rowLength === 0) {
2073                        s += "\n" + (idx + 1) + ": ";
2074                    }
2075                }
2076                console.log(s);
2077            };*/
2078
2079        lo.charMap = makeCharMap();
2080        i = 0;
2081        while (i < lo.patternChars.length) {
2082            lo.charMap.add(lo.patternChars.charCodeAt(i));
2083            i += 1;
2084        }
2085        charMapc2i = lo.charMap.code2int;
2086
2087        valueStore = makeValueStore(lo.valueStoreLength);
2088        lo.valueStore = valueStore;
2089
2090        if (Object.prototype.hasOwnProperty.call(window, "Int32Array")) { //IE<9 doesn"t have window.hasOwnProperty (host object)
2091            lo.indexedTrie = new window.Int32Array(lo.patternArrayLength * 2);
2092        } else {
2093            lo.indexedTrie = [];
2094            lo.indexedTrie.length = lo.patternArrayLength * 2;
2095            i = lo.indexedTrie.length - 1;
2096            while (i >= 0) {
2097                lo.indexedTrie[i] = 0;
2098                i -= 1;
2099            }
2100        }
2101        indexedTrie = lo.indexedTrie;
2102        trieRowLength = lo.charMap.int2code.length * 2;
2103
2104        forEachKey(lo.patterns, function (i) {
2105            extract(parseInt(i, 10), lo.patterns[i]);
2106        });
2107        //prettyPrintIndexedTrie(lo.charMap.int2code.length * 2);
2108    }
2109
2110    /**
2111     * @method Hyphenator~recreatePattern
2112     * @desc
2113     * Recreates the pattern for the reducedPatternSet
2114     * @param {string} pattern The pattern (chars)
2115     * @param {string} nodePoints The nodePoints (integers)
2116     * @access private
2117     * @return {string} The pattern (chars and numbers)
2118     */
2119    function recreatePattern(pattern, nodePoints) {
2120        var r = [],
2121            c = pattern.split(""),
2122            i = 0;
2123        while (i <= c.length) {
2124            if (nodePoints[i] && nodePoints[i] !== 0) {
2125                r.push(nodePoints[i]);
2126            }
2127            if (c[i]) {
2128                r.push(c[i]);
2129            }
2130            i += 1;
2131        }
2132        return r.join("");
2133    }
2134
2135    /**
2136     * @method Hyphenator~convertExceptionsToObject
2137     * @desc
2138     * Converts a list of comma seprated exceptions to an object:
2139     * "Fortran,Hy-phen-a-tion" -> {"Fortran":"Fortran","Hyphenation":"Hy-phen-a-tion"}
2140     * @access private
2141     * @param {string} exc a comma separated string of exceptions (without spaces)
2142     * @return {Object.<string, string>}
2143     */
2144    function convertExceptionsToObject(exc) {
2145        var w = exc.split(", "),
2146            r = {},
2147            i = 0,
2148            l = w.length,
2149            key;
2150        while (i < l) {
2151            key = w[i].replace(/-/g, "");
2152            if (!r.hasOwnProperty(key)) {
2153                r[key] = w[i];
2154            }
2155            i += 1;
2156        }
2157        return r;
2158    }
2159
2160    /**
2161     * @method Hyphenator~loadPatterns
2162     * @desc
2163     * Checks if the requested file is available in the network.
2164     * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
2165     * If the given language is not in the {@link Hyphenator~supportedLangs}-Object it returns.
2166     * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object
2167     * has a same-origin-policy. This makes the Bookmarklet impossible.
2168     * @param {string} lang The language to load the patterns for
2169     * @access private
2170     * @see {@link Hyphenator~basePath}
2171     */
2172    function loadPatterns(lang, cb) {
2173        var location, xhr, head, script, done = false;
2174        function getXHRforIElt6() {
2175            try {
2176                //IE>=5
2177                xhr = new window.ActiveXObject("Msxml2.XMLHTTP");
2178            } catch (ignore) {
2179                xhr = null;
2180            }
2181        }
2182        function getXHRforIElt10() {
2183            try {
2184                //IE>=6
2185                xhr = new window.ActiveXObject("Microsoft.XMLHTTP");
2186            } catch (ignore) {
2187                getXHRforIElt6();
2188            }
2189        }
2190        if (supportedLangs.hasOwnProperty(lang) && !Hyphenator.languages[lang]) {
2191            location = basePath + "patterns/" + supportedLangs[lang].file;
2192        } else {
2193            return;
2194        }
2195        if (isLocal && !isBookmarklet) {
2196            //check if "location" is available:
2197            xhr = null;
2198            try {
2199                // Mozilla, Opera, Safari and Internet Explorer (ab v7)
2200                xhr = new window.XMLHttpRequest();
2201            } catch (ignore) {
2202                getXHRforIElt10();
2203            }
2204
2205            if (xhr) {
2206                xhr.open("HEAD", location, true);
2207                xhr.setRequestHeader("Cache-Control", "no-cache");
2208                xhr.onreadystatechange = function () {
2209                    if (xhr.readyState === 2) {
2210                        if (xhr.status >= 400) {
2211                            onError(new Error("Could not load\n" + location));
2212                            delete docLanguages[lang];
2213                            return;
2214                        }
2215                        xhr.abort();
2216                    }
2217                };
2218                xhr.send(null);
2219            }
2220        }
2221        if (createElem) {
2222            head = window.document.getElementsByTagName("head").item(0);
2223            script = createElem("script", window);
2224            script.src = location;
2225            script.type = "text/javascript";
2226            script.charset = "utf8";
2227            script.onreadystatechange = function () {
2228                if (!done && (!script.readyState || script.readyState === "loaded" || script.readyState === "complete")) {
2229                    done = true;
2230
2231                    cb();
2232
2233                    // Handle memory leak in IE
2234                    script.onreadystatechange = null;
2235                    script.onload = null;
2236                    if (head && script.parentNode) {
2237                        head.removeChild(script);
2238                    }
2239                }
2240            };
2241            script.onload = script.onreadystatechange;
2242            head.appendChild(script);
2243        }
2244    }
2245
2246    /**
2247     * @method Hyphenator~createWordRegExp
2248     * @desc
2249     * build a regexp string for finding a word of a given languate
2250     * @access private
2251     * @param {string} lang The language
2252     * @return {string}
2253     */
2254    function createWordRegExp(lang) {
2255        var lo = Hyphenator.languages[lang],
2256            wrd = "";
2257        if (String.prototype.normalize) {
2258            wrd = "[\\w" + lo.specialChars + lo.specialChars.normalize("NFD") + hyphen + String.fromCharCode(8204) + "-]{" + min + ",}(?!:\\/\\/)";
2259        } else {
2260            wrd = "[\\w" + lo.specialChars + hyphen + String.fromCharCode(8204) + "-]{" + min + ",}(?!:\\/\\/)";
2261        }
2262        return wrd;
2263    }
2264
2265    /**
2266     * @method Hyphenator~prepareLanguagesObj
2267     * @desc
2268     * Adds some feature to the language object:
2269     * - cache
2270     * - exceptions
2271     * Converts the patterns to a trie using {@link Hyphenator~convertPatterns}
2271
2272     * @access private
2273     * @param {string} lang The language of the language object
2274     */
2275    function prepareLanguagesObj(lang) {
2276        var lo = Hyphenator.languages[lang];
2277
2278        if (!lo.prepared) {
2279            if (enableCache) {
2280                lo.cache = {};
2281                //Export
2282                //lo["cache"] = lo.cache;
2283            }
2284            if (enableReducedPatternSet) {
2285                lo.redPatSet = {};
2286            }
2287            if (leftmin > lo.leftmin) {
2288                lo.leftmin = leftmin;
2289            }
2290            if (rightmin > lo.rightmin) {
2291                lo.rightmin = rightmin;
2292            }
2293            //add exceptions from the pattern file to the local "exceptions"-obj
2294            if (lo.hasOwnProperty("exceptions")) {
2295                Hyphenator.addExceptions(lang, lo.exceptions);
2296                delete lo.exceptions;
2297            }
2298            //copy global exceptions to the language specific exceptions
2299            if (exceptions.hasOwnProperty("global")) {
2300                if (exceptions.hasOwnProperty(lang)) {
2301                    exceptions[lang] += ", " + exceptions.global;
2302                } else {
2303                    exceptions[lang] = exceptions.global;
2304                }
2305            }
2306            //move exceptions from the the local "exceptions"-obj to the "language"-object
2307            if (exceptions.hasOwnProperty(lang)) {
2308                lo.exceptions = convertExceptionsToObject(exceptions[lang]);
2309                delete exceptions[lang];
2310            } else {
2311                lo.exceptions = {};
2312            }
2313            convertPatternsToArray(lo);
2314            lo.genRegExp = new RegExp("(" + createWordRegExp(lang) + ")|(" + url + ")|(" + mail + ")", "gi");
2315            lo.prepared = true;
2316        }
2317    }
2318
2319    /****
2320     * @method Hyphenator~prepare
2321     * @desc
2322     * This funtion prepares the Hyphenator~Object: If RemoteLoading is turned off, it assumes
2323     * that the patternfiles are loaded, all conversions are made and the callback is called.
2324     * If storage is active the object is retrieved there.
2325     * If RemoteLoading is on (default), it loads the pattern files and repeatedly checks Hyphenator.languages.
2326     * If a patternfile is loaded the patterns are stored in storage (if enabled),
2327     * converted to their object style and the lang-object extended.
2328     * Finally the callback is called.
2329     * @access private
2330     */
2331    function prepare(callback) {
2332        var tmp1;
2333
2334        function languagesLoaded() {
2335            forEachKey(docLanguages, function (l) {
2336                if (Hyphenator.languages.hasOwnProperty(l)) {
2337                    delete docLanguages[l];
2338                    if (!!storage) {
2339                        storage.setItem(l, window.JSON.stringify(Hyphenator.languages[l]));
2340                    }
2341                    prepareLanguagesObj(l);
2342                    callback(l);
2343                }
2344            });
2345        }
2346
2347        if (!enableRemoteLoading) {
2348            forEachKey(Hyphenator.languages, function (lang) {
2349                prepareLanguagesObj(lang);
2350            });
2351            callback("*");
2352            return;
2353        }
2354        callback("urlstyled");
2355        // get all languages that are used and preload the patterns
2356        forEachKey(docLanguages, function (lang) {
2357            if (!!storage && storage.test(lang)) {
2358                Hyphenator.languages[lang] = window.JSON.parse(storage.getItem(lang));
2359                prepareLanguagesObj(lang);
2360                if (exceptions.hasOwnProperty("global")) {
2361                    tmp1 = convertExceptionsToObject(exceptions.global);
2362                    forEachKey(tmp1, function (tmp2) {
2363                        Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
2364                    });
2365                }
2366                //Replace exceptions since they may have been changed:
2367                if (exceptions.hasOwnProperty(lang)) {
2368                    tmp1 = convertExceptionsToObject(exceptions[lang]);
2369                    forEachKey(tmp1, function (tmp2) {
2370                        Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
2371                    });
2372                    delete exceptions[lang];
2373                }
2374                //Replace genRegExp since it may have been changed:
2375                Hyphenator.languages[lang].genRegExp = new RegExp("(" + createWordRegExp(lang) + ")|(" + url + ")|(" + mail + ")", "gi");
2376                if (enableCache) {
2377                    if (!Hyphenator.languages[lang].cache) {
2378                        Hyphenator.languages[lang].cache = {};
2379                    }
2380                }
2381                delete docLanguages[lang];
2382                callback(lang);
2383            } else {
2384                loadPatterns(lang, languagesLoaded);
2385            }
2386        });
2387        //call languagesLoaded in case language has been loaded manually
2388        //and remoteLoading is on (onload won"t fire)
2389        languagesLoaded();
2390    }
2391
2392    /**
2393     * @method Hyphenator~toggleBox
2394     * @desc
2395     * Creates the toggleBox: a small button to turn off/on hyphenation on a page.
2396     * @see {@link Hyphenator.config}
2397     * @access private
2398     */
2399    var toggleBox = function () {
2400        var bdy,
2401            myTextNode,
2402            text = (Hyphenator.doHyphenation
2403                ? "Hy-phen-a-tion"
2404                : "Hyphenation"),
2405            myBox = contextWindow.document.getElementById("HyphenatorToggleBox");
2406        if (!!myBox) {
2407            myBox.firstChild.data = text;
2408        } else {
2409            bdy = contextWindow.document.getElementsByTagName("body")[0];
2410            myBox = createElem("div", contextWindow);
2411            myBox.setAttribute("id", "HyphenatorToggleBox");
2412            myBox.setAttribute("class", dontHyphenateClass);
2413            myTextNode = contextWindow.document.createTextNode(text);
2414            myBox.appendChild(myTextNode);
2415            myBox.onclick = Hyphenator.toggleHyphenation;
2416            myBox.style.position = "absolute";
2417            myBox.style.top = "0px";
2418            myBox.style.right = "0px";
2419            myBox.style.zIndex = "1000";
2420            myBox.style.margin = "0";
2421            myBox.style.backgroundColor = "#AAAAAA";
2422            myBox.style.color = "#FFFFFF";
2423            myBox.style.font = "6pt Arial";
2424            myBox.style.letterSpacing = "0.2em";
2425            myBox.style.padding = "3px";
2426            myBox.style.cursor = "pointer";
2427            myBox.style.WebkitBorderBottomLeftRadius = "4px";
2428            myBox.style.MozBorderRadiusBottomleft = "4px";
2429            myBox.style.borderBottomLeftRadius = "4px";
2430            bdy.appendChild(myBox);
2431        }
2432    };
2433
2434    /**
2435     * @method Hyphenator~doCharSubst
2436     * @desc
2437     * Replace chars in a word
2438     *
2439     * @param {Object} loCharSubst Map of substitutions ({"ä": "a", "ü": "u", …})
2440     * @param {string} w the word
2441     * @returns string The word with substituted characers
2442     * @access private
2443     */
2444    function doCharSubst(loCharSubst, w) {
2445        var r = w;
2446        forEachKey(loCharSubst, function (subst) {
2447            r = r.replace(new RegExp(subst, "g"), loCharSubst[subst]);
2448        });
2449        return r;
2450    }
2451
2452    /**
2453     * @member {Array} Hyphenator~wwAsMappedCharCodeStore
2454     * @desc
2455     * Array (typed if supported) container for charCodes
2456     * @access private
2457     * @see {@link Hyphenator~hyphenateWord}
2458     */
2459    var wwAsMappedCharCodeStore = (function () {
2460        if (Object.prototype.hasOwnProperty.call(window, "Int32Array")) {
2461            return new window.Int32Array(64);
2462        }
2463        return [];
2464    }());
2465
2466    /**
2467     * @member {Array} Hyphenator~wwhpStore
2468     * @desc
2469     * Array (typed if supported) container for hyphenation points
2470     * @access private
2471     * @see {@link Hyphenator~hyphenateWord}
2472     */
2473    var wwhpStore = (function () {
2474        var r;
2475        if (Object.prototype.hasOwnProperty.call(window, "Uint8Array")) {
2476            r = new window.Uint8Array(64);
2477        } else {
2478            r = [];
2479        }
2480        return r;
2481    }());
2482
2483    /**
2484     * @method Hyphenator~hyphenateCompound
2485     * @desc
2486     * Treats compound words accordingly to the "compound" setting
2487     *
2488     * @param {Object} lo A language object (containing the patterns)
2489     * @param {string} lang The language of the word
2490     * @param {string} word The word
2491     * @returns string The (hyphenated) compound word
2492     * @access private
2493     */
2494    function hyphenateCompound(lo, lang, word) {
2495        var hw, parts, i = 0;
2496        switch (compound) {
2497        case "auto":
2498            parts = word.split("-");
2499            while (i < parts.length) {
2500                if (parts[i].length >= min) {
2501                    parts[i] = hyphenateWord(lo, lang, parts[i]);
2502                }
2503                i += 1;
2504            }
2505            hw = parts.join("-");
2506            break;
2507        case "all":
2508            parts = word.split("-");
2509            while (i < parts.length) {
2510                if (parts[i].length >= min) {
2511                    parts[i] = hyphenateWord(lo, lang, parts[i]);
2512                }
2513                i += 1;
2514            }
2515            hw = parts.join("-" + zeroWidthSpace);
2516            break;
2517        case "hyphen":
2518            hw = word.replace("-", "-" + zeroWidthSpace);
2519            break;
2520        default:
2521            onError(new Error("Hyphenator.settings: compound setting \"" + compound + "\" not known."));
2522        }
2523        return hw;
2524    }
2525
2526    /**
2527     * @method Hyphenator~hyphenateWord
2528     * @desc
2529     * This function is the heart of Hyphenator.js. It returns a hyphenated word.
2530     *
2531     * If there"s already a {@link Hyphenator~hypen} in the word, the word is returned as it is.
2532     * If the word is in the exceptions list or in the cache, it is retrieved from it.
2533     * If there"s a "-" it calls Hyphenator~hyphenateCompound
2534     * The hyphenated word is returned and (if acivated) cached.
2535     * Both special Events onBeforeWordHyphenation and onAfterWordHyphenation are called for the word.
2536     * @param {Object} lo A language object (containing the patterns)
2537     * @param {string} lang The language of the word
2538     * @param {string} word The word
2539     * @returns string The hyphenated word
2540     * @access private
2541     */
2542    function hyphenateWord(lo, lang, word) {
2543        var pattern = "",
2544            ww,
2545            wwlen,
2546            wwhp = wwhpStore,
2547            pstart = 0,
2548            plen,
2549            hp,
2550            hpc,
2551            wordLength = word.length,
2552            hw = "",
2553            charMap = lo.charMap.code2int,
2554            charCode,
2555            mappedCharCode,
2556            row = 0,
2557            link = 0,
2558            value = 0,
2559            values,
2560            indexedTrie = lo.indexedTrie,
2561            valueStore = lo.valueStore.keys,
2562            wwAsMappedCharCode = wwAsMappedCharCodeStore;
2563        word = onBeforeWordHyphenation(word, lang);
2564        if (word === "") {
2565            hw = "";
2566        } else if (enableCache && lo.cache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
2567            hw = lo.cache[word];
2568        } else if (word.indexOf(hyphen) !== -1) {
2569            //word already contains shy; -> leave at it is!
2570            hw = word;
2571        } else if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
2572            hw = lo.exceptions[word].replace(/-/g, hyphen);
2573        } else if (word.indexOf("-") !== -1) {
2574            hw = hyphenateCompound(lo, lang, word);
2575        } else {
2576            ww = word.toLowerCase();
2577            if (String.prototype.normalize) {
2578                ww = ww.normalize("NFC");
2579            }
2580            if (lo.hasOwnProperty("charSubstitution")) {
2581                ww = doCharSubst(lo.charSubstitution, ww);
2582            }
2583            if (word.indexOf("'") !== -1) {
2584                ww = ww.replace(/'/g, "’"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
2585            }
2586            ww = "_" + ww + "_";
2587            wwlen = ww.length;
2588            //prepare wwhp and wwAsMappedCharCode
2589            while (pstart < wwlen) {
2590                wwhp[pstart] = 0;
2591                charCode = ww.charCodeAt(pstart);
2592                wwAsMappedCharCode[pstart] = charMap.hasOwnProperty(charCode)
2593                    ? charMap[charCode]
2594                    : -1;
2595                pstart += 1;
2596            }
2597            //get hyphenation points for all substrings
2598            pstart = 0;
2599            while (pstart < wwlen) {
2600                row = 0;
2601                pattern = "";
2602                plen = pstart;
2603                while (plen < wwlen) {
2604                    mappedCharCode = wwAsMappedCharCode[plen];
2605                    if (mappedCharCode === -1) {
2606                        break;
2607                    }
2608                    if (enableReducedPatternSet) {
2609                        pattern += ww.charAt(plen);
2610                    }
2611                    link = indexedTrie[row + mappedCharCode * 2];
2612                    value = indexedTrie[row + mappedCharCode * 2 + 1];
2613                    if (value > 0) {
2614                        hpc = 0;
2615                        hp = valueStore[value + hpc];
2616                        while (hp !== 255) {
2617                            if (hp > wwhp[pstart + hpc]) {
2618                                wwhp[pstart + hpc] = hp;
2619                            }
2620                            hpc += 1;
2621                            hp = valueStore[value + hpc];
2622                        }
2623                        if (enableReducedPatternSet) {
2624                            if (!lo.redPatSet) {
2625                                lo.redPatSet = {};
2626                            }
2627                            if (valueStore.subarray) {
2628                                values = valueStore.subarray(value, value + hpc);
2629                            } else {
2630                                values = valueStore.slice(value, value + hpc);
2631                            }
2632                            lo.redPatSet[pattern] = recreatePattern(pattern, values);
2633                        }
2634                    }
2635                    if (link > 0) {
2636                        row = link;
2637                    } else {
2638                        break;
2639                    }
2640                    plen += 1;
2641                }
2642                pstart += 1;
2643            }
2644            //create hyphenated word
2645            hp = 0;
2646            while (hp < wordLength) {
2647                if (hp >= lo.leftmin && hp <= (wordLength - lo.rightmin) && (wwhp[hp + 1] % 2) !== 0) {
2648                    hw += hyphen + word.charAt(hp);
2649                } else {
2650                    hw += word.charAt(hp);
2651                }
2652                hp += 1;
2653            }
2654        }
2655        hw = onAfterWordHyphenation(hw, lang);
2656        if (enableCache) { //put the word in the cache
2657            lo.cache[word] = hw;
2658        }
2659        return hw;
2660    }
2661
2662
2663    /**
2664     * @method Hyphenator~checkIfAllDone
2665     * @desc
2666     * Checks if all elements in {@link Hyphenator~elements} are hyphenated, unhides them and fires onHyphenationDone()
2667     * @access private
2668     */
2669    function checkIfAllDone() {
2670        var allDone = true,
2671            i = 0,
2672            doclist = {};
2673        elements.each(function (ellist) {
2674            var j = 0,
2675                l = ellist.length;
2676            while (j < l) {
2677                allDone = allDone && ellist[j].hyphenated;
2678                if (!doclist.hasOwnProperty(ellist[j].element.baseURI)) {
2679                    doclist[ellist[j].element.ownerDocument.location.href] = true;
2680                }
2681                doclist[ellist[j].element.ownerDocument.location.href] = doclist[ellist[j].element.ownerDocument.location.href] && ellist[j].hyphenated;
2682                j += 1;
2683            }
2684        });
2685        if (allDone) {
2686            if (intermediateState === "hidden" && unhide === "progressive") {
2687                elements.each(function (ellist) {
2688                    var j = 0,
2689                        l = ellist.length,
2690                        el;
2691                    while (j < l) {
2692                        el = ellist[j].element;
2693                        el.className = el.className.replace(unhideClassRegExp, "");
2694                        if (el.className === "") {
2695                            el.removeAttribute("class");
2696                        }
2697                        j += 1;
2698                    }
2699                });
2700            }
2701            while (i < CSSEditors.length) {
2702                CSSEditors[i].clearChanges();
2703                i += 1;
2704            }
2705            forEachKey(doclist, function (doc) {
2706                onHyphenationDone(doc);
2707            });
2708            if (!!storage && storage.deferred.length > 0) {
2709                i = 0;
2710                while (i < storage.deferred.length) {
2711                    storage.deferred[i].call();
2712                    i += 1;
2713                }
2714                storage.deferred = [];
2715            }
2716        }
2717    }
2718
2719    /**
2720     * @method Hyphenator~controlOrphans
2721     * @desc
2722     * removes orphans depending on the "orphanControl"-setting:
2723     * orphanControl === 1: do nothing
2724     * orphanControl === 2: prevent last word to be hyphenated
2725     * orphanControl === 3: prevent one word on a last line (inserts a nobreaking space)
2726     * @param {string} part - The sring where orphans have to be removed
2727     * @access private
2728     */
2729    function controlOrphans(ignore, leadingWhiteSpace, lastWord, trailingWhiteSpace) {
2730        var h = hyphen;
2731        //escape hyphen
2732        if (".\\+*?[^]$(){}=!<>|:-".indexOf(hyphen) !== -1) {
2733            h = "\\" + hyphen;
2734        } else {
2735            h = hyphen;
2736        }
2737        if (orphanControl === 3 && leadingWhiteSpace === " ") {
2738            leadingWhiteSpace = String.fromCharCode(160);
2739        }
2740        return leadingWhiteSpace + lastWord.replace(new RegExp(h + "|" + zeroWidthSpace, "g"), "") + trailingWhiteSpace;
2741    }
2742
2743    /**
2744     * @method Hyphenator~hyphenateElement
2745     * @desc
2746     * Takes the content of the given element and - if there"s text - replaces the words
2747     * by hyphenated words. If there"s another element, the function is called recursively.
2748     * When all words are hyphenated, the visibility of the element is set to "visible".
2749     * @param {string} lang - The language-code of the element
2750     * @param {Element} elo - The element to hyphenate {@link Hyphenator~elements~ElementCollection~Element}
2751     * @access private
2752     */
2753    function hyphenateElement(lang, elo) {
2754        var el = elo.element,
2755            hyphenate,
2756            n,
2757            i,
2758            lo;
2759        if (lang === "urlstyled" && Hyphenator.doHyphenation) {
2760            i = 0;
2761            n = el.childNodes[i];
2762            while (!!n) {
2763                if (
2764                    n.nodeType === 3 //type 3 = #text
2765                        && (/\S/).test(n.data)
2766                ) { //not just white space
2767                    n.data = hyphenateURL(n.data);
2768                }
2769                i += 1;
2770                n = el.childNodes[i];
2771            }
2772        } else if (Hyphenator.languages.hasOwnProperty(lang) && Hyphenator.doHyphenation) {
2773            lo = Hyphenator.languages[lang];
2774            hyphenate = function (match, word, url, mail) {
2775                var r;
2776                if (!!url || !!mail) {
2777                    r = hyphenateURL(match);
2778                } else {
2779                    r = hyphenateWord(lo, lang, word);
2780                }
2781                return r;
2782            };
2783            i = 0;
2784            n = el.childNodes[i];
2785            while (!!n) {
2786                if (
2787                    n.nodeType === 3 //type 3 = #text
2788                        && (/\S/).test(n.data) //not just white space
2789                        && n.data.length >= min
2790                ) { //longer then min
2791                    n.data = n.data.replace(lo.genRegExp, hyphenate);
2792                    if (orphanControl !== 1) {
2793                        //prevent last word from being hyphenated
2794                        n.data = n.data.replace(/(\u0020*)(\S+)(\s*)$/, controlOrphans);
2795                    }
2796                }
2797                i += 1;
2798                n = el.childNodes[i];
2799            }
2800        }
2801        if (intermediateState === "hidden" && unhide === "wait") {
2802            el.className = el.className.replace(hideClassRegExp, "");
2803            if (el.className === "") {
2804                el.removeAttribute("class");
2805            }
2806        }
2807        if (intermediateState === "hidden" && unhide === "progressive") {
2808            el.className = el.className.replace(hideClassRegExp, " " + unhideClass);
2809        }
2810        elo.hyphenated = true;
2811        elements.counters[1] += 1;
2812        if (elements.counters[0] <= elements.counters[1]) {
2813            checkIfAllDone();
2814        }
2815    }
2816
2817    /**
2818     * @method Hyphenator~hyphenateLanguageElements
2819     * @desc
2820     * Calls hyphenateElement() for all elements of the specified language.
2821     * If the language is "*" then all elements are hyphenated.
2822     * This is done with a setTimout
2823     * to prevent a "long running Script"-alert when hyphenating large pages.
2824     * Therefore a tricky bind()-function was necessary.
2825     * @param {string} lang The language of the elements to hyphenate
2826     * @access private
2827     */
2828
2829    function hyphenateLanguageElements(lang) {
2830        /*function bind(fun, arg1, arg2) {
2831            return function () {
2832                return fun(arg1, arg2);
2833            };
2834        }*/
2835        var i = 0,
2836            l;
2837        if (lang === "*") {
2838            elements.each(function (lang, ellist) {
2839                var j = 0,
2840                    le = ellist.length;
2841                while (j < le) {
2842                    //zeroTimeOut(bind(hyphenateElement, lang, ellist[j]));
2843                    hyphenateElement(lang, ellist[j]);
2844                    j += 1;
2845                }
2846            });
2847        } else {
2848            if (elements.list.hasOwnProperty(lang)) {
2849                l = elements.list[lang].length;
2850                while (i < l) {
2851                    //zeroTimeOut(bind(hyphenateElement, lang, elements.list[lang][i]));
2852                    hyphenateElement(lang, elements.list[lang][i]);
2853                    i += 1;
2854                }
2855            }
2856        }
2857    }
2858
2859    /**
2860     * @method Hyphenator~removeHyphenationFromDocument
2861     * @desc
2862     * Does what it says and unregisters the onCopyEvent from the elements
2863     * @access private
2864     */
2865    function removeHyphenationFromDocument() {
2866        elements.each(function (ellist) {
2867            var i = 0,
2868                l = ellist.length;
2869            while (i < l) {
2870                removeHyphenationFromElement(ellist[i].element);
2871                ellist[i].hyphenated = false;
2872                i += 1;
2873            }
2874        });
2875    }
2876
2877    /**
2878     * @method Hyphenator~createStorage
2879     * @desc
2880     * inits the private var {@link Hyphenator~storage) depending of the setting in {@link Hyphenator~storageType}
2881     * and the supported features of the system.
2882     * @access private
2883     */
2884    function createStorage() {
2885        var s;
2886        function makeStorage(s) {
2887            var store = s,
2888                prefix = "Hyphenator_" + Hyphenator.version + "_",
2889                deferred = [],
2890                test = function (name) {
2891                    var val = store.getItem(prefix + name);
2892                    return !!val;
2893                },
2894                getItem = function (name) {
2895                    return store.getItem(prefix + name);
2896                },
2897                setItem = function (name, value) {
2898                    try {
2899                        store.setItem(prefix + name, value);
2900                    } catch (e) {
2901                        onError(e);
2902                    }
2903                };
2904            return {
2905                deferred: deferred,
2906                test: test,
2907                getItem: getItem,
2908                setItem: setItem
2909            };
2910        }
2911        try {
2912            if (
2913                storageType !== "none" &&
2914                    window.JSON !== undefined &&
2915                    window.localStorage !== undefined &&
2916                    window.sessionStorage !== undefined &&
2917                    window.JSON.stringify !== undefined &&
2918                    window.JSON.parse !== undefined
2919            ) {
2920                switch (storageType) {
2921                case "session":
2922                    s = window.sessionStorage;
2923                    break;
2924                case "local":
2925                    s = window.localStorage;
2926                    break;
2927                default:
2928                    s = undefined;
2929                }
2930                //check for private mode
2931                s.setItem("storageTest", "1");
2932                s.removeItem("storageTest");
2933            }
2934        } catch (ignore) {
2935            //FF throws an error if DOM.storage.enabled is set to false
2936            s = undefined;
2937        }
2938        if (s) {
2939            storage = makeStorage(s);
2940        } else {
2941            storage = undefined;
2942        }
2943    }
2944
2945    /**
2946     * @method Hyphenator~storeConfiguration
2947     * @desc
2948     * Stores the current config-options in DOM-Storage
2949     * @access private
2950     */
2951    function storeConfiguration() {
2952        if (!storage) {
2953            return;
2954        }
2955        var settings = {
2956            "STORED": true,
2957            "classname": hyphenateClass,
2958            "urlclassname": urlHyphenateClass,
2959            "donthyphenateclassname": dontHyphenateClass,
2960            "minwordlength": min,
2961            "hyphenchar": hyphen,
2962            "urlhyphenchar": urlhyphen,
2963            "togglebox": toggleBox,
2964            "displaytogglebox": displayToggleBox,
2965            "remoteloading": enableRemoteLoading,
2966            "enablecache": enableCache,
2967            "enablereducedpatternset": enableReducedPatternSet,
2968            "onhyphenationdonecallback": onHyphenationDone,
2969            "onerrorhandler": onError,
2970            "onwarninghandler": onWarning,
2971            "intermediatestate": intermediateState,
2972            "selectorfunction": selectorFunction || mySelectorFunction,
2973            "safecopy": safeCopy,
2974            "doframes": doFrames,
2975            "storagetype": storageType,
2976            "orphancontrol": orphanControl,
2977            "dohyphenation": Hyphenator.doHyphenation,
2978            "persistentconfig": persistentConfig,
2979            "defaultlanguage": defaultLanguage,
2980            "useCSS3hyphenation": css3,
2981            "unhide": unhide,
2982            "onbeforewordhyphenation": onBeforeWordHyphenation,
2983            "onafterwordhyphenation": onAfterWordHyphenation,
2984            "leftmin": leftmin,
2985            "rightmin": rightmin,
2986            "compound": compound
2987        };
2988        storage.setItem("config", window.JSON.stringify(settings));
2989    }
2990
2991    /**
2992     * @method Hyphenator~restoreConfiguration
2993     * @desc
2994     * Retrieves config-options from DOM-Storage and does configuration accordingly
2995     * @access private
2996     */
2997    function restoreConfiguration() {
2998        var settings;
2999        if (storage.test("config")) {
3000            settings = window.JSON.parse(storage.getItem("config"));
3001            Hyphenator.config(settings);
3002        }
3003    }
3004
3005    /**EXPORTED VALUES**/
3006
3007    /**
3008     * @member {string} Hyphenator.version
3009     * @desc
3010     * String containing the actual version of Hyphenator.js
3011     * [major release].[minor releas].[bugfix release]
3012     * major release: new API, new Features, big changes
3013     * minor release: new languages, improvements
3014     * @access public
3015     */
3016    var version = '5.3.0';
3017
3018    /**
3019     * @member {boolean} Hyphenator.doHyphenation
3020     * @desc
3021     * If doHyphenation is set to false, hyphenateDocument() isn"t called.
3022     * All other actions are performed.
3023     * @default true
3024     */
3025    var doHyphenation = true;
3026
3027    /**
3028     * @typedef {Object} Hyphenator.languages.language
3029     * @property {Number} leftmin - The minimum of chars to remain on the old line
3030     * @property {Number} rightmin - The minimum of chars to go on the new line
3031     * @property {string} specialChars - Non-ASCII chars in the alphabet.
3032     * @property {Object.<number, string>} patterns - the patterns in a compressed format. The key is the length of the patterns in the value string.
3033     * @property {Object.<string, string>} charSubstitution - optional: a hash table with chars that are replaced during hyphenation
3034     * @property {string | Object.<string, string>} exceptions - optional: a csv string containing exceptions
3035     */
3036
3037    /**
3038     * @member {Object.<string, Hyphenator.languages.language>} Hyphenator.languages
3039     * @desc
3040     * Objects that holds key-value pairs, where key is the language and the value is the
3041     * language-object loaded from (and set by) the pattern file.
3042     * @namespace Hyphenator.languages
3043     * @access public
3044     */
3045    var languages = {};
3046
3047    /**
3048     * @method Hyphenator.config
3049     * @desc
3050     * The Hyphenator.config() function that takes an object as an argument. The object contains key-value-pairs
3051     * containig Hyphenator-settings.
3052     * @param {Hyphenator.config} obj
3053     * @access public
3054     * @example
3055     * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
3056     * &lt;script type = "text/javascript"&gt;
3057     *     Hyphenator.config({"minwordlength":4,"hyphenchar":"|"});
3058     *     Hyphenator.run();
3059     * &lt;/script&gt;
3060     */
3061    function config(obj) {
3062        var assert = function (name, type) {
3063            var r,
3064                t;
3065            t = typeof obj[name];
3066            if (t === type) {
3067                r = true;
3068            } else {
3069                onError(new Error("Config onError: " + name + " must be of type " + type));
3070                r = false;
3071            }
3072            return r;
3073        };
3074
3075        if (obj.hasOwnProperty("storagetype")) {
3076            if (assert("storagetype", "string")) {
3077                storageType = obj.storagetype;
3078            }
3079            if (!storage) {
3080                createStorage();
3081            }
3082        }
3083        if (!obj.hasOwnProperty("STORED") && storage && obj.hasOwnProperty("persistentconfig") && obj.persistentconfig === true) {
3084            restoreConfiguration();
3085        }
3086
3087        forEachKey(obj, function (key) {
3088            switch (key) {
3089            case "STORED":
3090                break;
3091            case "classname":
3092                if (assert("classname", "string")) {
3093                    hyphenateClass = obj[key];
3094                }
3095                break;
3096            case "urlclassname":
3097                if (assert("urlclassname", "string")) {
3098                    urlHyphenateClass = obj[key];
3099                }
3100                break;
3101            case "donthyphenateclassname":
3102                if (assert("donthyphenateclassname", "string")) {
3103                    dontHyphenateClass = obj[key];
3104                }
3105                break;
3106            case "minwordlength":
3107                if (assert("minwordlength", "number")) {
3108                    min = obj[key];
3109                }
3110                break;
3111            case "hyphenchar":
3112                if (assert("hyphenchar", "string")) {
3113                    if (obj.hyphenchar === "&shy;") {
3114                        obj.hyphenchar = String.fromCharCode(173);
3115                    }
3116                    hyphen = obj[key];
3117                }
3118                break;
3119            case "urlhyphenchar":
3120                if (obj.hasOwnProperty("urlhyphenchar")) {
3121                    if (assert("urlhyphenchar", "string")) {
3122                        urlhyphen = obj[key];
3123                    }
3124                }
3125                break;
3126            case "togglebox":
3127                if (assert("togglebox", "function")) {
3128                    toggleBox = obj[key];
3129                }
3130                break;
3131            case "displaytogglebox":
3132                if (assert("displaytogglebox", "boolean")) {
3133                    displayToggleBox = obj[key];
3134                }
3135                break;
3136            case "remoteloading":
3137                if (assert("remoteloading", "boolean")) {
3138                    enableRemoteLoading = obj[key];
3139                }
3140                break;
3141            case "enablecache":
3142                if (assert("enablecache", "boolean")) {
3143                    enableCache = obj[key];
3144                }
3145                break;
3146            case "enablereducedpatternset":
3147                if (assert("enablereducedpatternset", "boolean")) {
3148                    enableReducedPatternSet = obj[key];
3149                }
3150                break;
3151            case "onhyphenationdonecallback":
3152                if (assert("onhyphenationdonecallback", "function")) {
3153                    onHyphenationDone = obj[key];
3154                }
3155                break;
3156            case "onerrorhandler":
3157                if (assert("onerrorhandler", "function")) {
3158                    onError = obj[key];
3159                }
3160                break;
3161            case "onwarninghandler":
3162                if (assert("onwarninghandler", "function")) {
3163                    onWarning = obj[key];
3164                }
3165                break;
3166            case "intermediatestate":
3167                if (assert("intermediatestate", "string")) {
3168                    intermediateState = obj[key];
3169                }
3170                break;
3171            case "selectorfunction":
3172                if (assert("selectorfunction", "function")) {
3173                    selectorFunction = obj[key];
3174                }
3175                break;
3176            case "safecopy":
3177                if (assert("safecopy", "boolean")) {
3178                    safeCopy = obj[key];
3179                }
3180                break;
3181            case "doframes":
3182                if (assert("doframes", "boolean")) {
3183                    doFrames = obj[key];
3184                }
3185                break;
3186            case "storagetype":
3187                if (assert("storagetype", "string")) {
3188                    storageType = obj[key];
3189                }
3190                break;
3191            case "orphancontrol":
3192                if (assert("orphancontrol", "number")) {
3193                    orphanControl = obj[key];
3194                }
3195                break;
3196            case "dohyphenation":
3197                if (assert("dohyphenation", "boolean")) {
3198                    Hyphenator.doHyphenation = obj[key];
3199                }
3200                break;
3201            case "persistentconfig":
3202                if (assert("persistentconfig", "boolean")) {
3203                    persistentConfig = obj[key];
3204                }
3205                break;
3206            case "defaultlanguage":
3207                if (assert("defaultlanguage", "string")) {
3208                    defaultLanguage = obj[key];
3209                }
3210                break;
3211            case "useCSS3hyphenation":
3212                if (assert("useCSS3hyphenation", "boolean")) {
3213                    css3 = obj[key];
3214                }
3215                break;
3216            case "unhide":
3217                if (assert("unhide", "string")) {
3218                    unhide = obj[key];
3219                }
3220                break;
3221            case "onbeforewordhyphenation":
3222                if (assert("onbeforewordhyphenation", "function")) {
3223                    onBeforeWordHyphenation = obj[key];
3224                }
3225                break;
3226            case "onafterwordhyphenation":
3227                if (assert("onafterwordhyphenation", "function")) {
3228                    onAfterWordHyphenation = obj[key];
3229                }
3230                break;
3231            case "leftmin":
3232                if (assert("leftmin", "number")) {
3233                    leftmin = obj[key];
3234                }
3235                break;
3236            case "rightmin":
3237                if (assert("rightmin", "number")) {
3238                    rightmin = obj[key];
3239                }
3240                break;
3241            case "compound":
3242                if (assert("compound", "string")) {
3243                    compound = obj[key];
3244                }
3245                break;
3246            default:
3247                onError(new Error("Hyphenator.config: property " + key + " not known."));
3248            }
3249        });
3250        if (storage && persistentConfig) {
3251            storeConfiguration();
3252        }
3253    }
3254
3255    /**
3256     * @method Hyphenator.run
3257     * @desc
3258     * Bootstrap function that starts all hyphenation processes when called:
3259     * Tries to create storage if required and calls {@link Hyphenator~runWhenLoaded} on "window" handing over the callback "process"
3260     * @access public
3261     * @example
3262     * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
3263     * &lt;script type = "text/javascript"&gt;
3264     *   Hyphenator.run();
3265     * &lt;/script&gt;
3266     */
3267    function run() {
3268            /**
3269             *@callback Hyphenator.run~process process - The function is called when the DOM has loaded (or called for each frame)
3270             */
3271        var process = function () {
3272            try {
3273                if (contextWindow.document.getElementsByTagName("frameset").length > 0) {
3274                    return; //we are in a frameset
3275                }
3276                autoSetMainLanguage(undefined);
3277                gatherDocumentInfos();
3278                if (displayToggleBox) {
3279                    toggleBox();
3280                }
3281                prepare(hyphenateLanguageElements);
3282            } catch (e) {
3283                onError(e);
3284            }
3285        };
3286
3287        if (!storage) {
3288            createStorage();
3289        }
3290        runWhenLoaded(window, process);
3291    }
3292
3293    /**
3294     * @method Hyphenator.addExceptions
3295     * @desc
3296     * Adds the exceptions from the string to the appropriate language in the
3297     * {@link Hyphenator~languages}-object
3298     * @param {string} lang The language
3299     * @param {string} words A comma separated string of hyphenated words WITH spaces.
3300     * @access public
3301     * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
3302     * &lt;script type = "text/javascript"&gt;
3303     *   Hyphenator.addExceptions("de","ziem-lich, Wach-stube");
3304     *   Hyphenator.run();
3305     * &lt;/script&gt;
3306     */
3307    function addExceptions(lang, words) {
3308        if (lang === "") {
3309            lang = "global";
3310        }
3311        if (exceptions.hasOwnProperty(lang)) {
3312            exceptions[lang] += ", " + words;
3313        } else {
3314            exceptions[lang] = words;
3315        }
3316    }
3317
3318    /**
3319     * @method Hyphenator.hyphenate
3320     * @access public
3321     * @desc
3322     * Hyphenates the target. The language patterns must be loaded.
3323     * If the target is a string, the hyphenated string is returned,
3324     * if it"s an object, the values are hyphenated directly and undefined (aka nothing) is returned
3325     * @param {string|Object} target the target to be hyphenated
3326     * @param {string} lang the language of the target
3327     * @returns {string|undefined}
3328     * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
3329     * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
3330     * &lt;script type = "text/javascript"&gt;
3331     * var t = Hyphenator.hyphenate("Hyphenation", "en"); //Hy|phen|ation
3332     * &lt;/script&gt;
3333     */
3334    function hyphenate(target, lang) {
3335        var turnout, n, i, lo;
3336        lo = Hyphenator.languages[lang];
3337        if (Hyphenator.languages.hasOwnProperty(lang)) {
3338            if (!lo.prepared) {
3339                prepareLanguagesObj(lang);
3340            }
3341            turnout = function (match, word, url, mail) {
3342                var r;
3343                if (!!url || !!mail) {
3344                    r = hyphenateURL(match);
3345                } else {
3346                    r = hyphenateWord(lo, lang, word);
3347                }
3348                return r;
3349            };
3350            if (typeof target === "object" && !(typeof target === "string" || target.constructor === String)) {
3351                i = 0;
3352                n = target.childNodes[i];
3353                while (!!n) {
3354                    if (
3355                        n.nodeType === 3 //type 3 = #text
3356                            && (/\S/).test(n.data) //not just white space
3357                            && n.data.length >= min
3358                    ) { //longer then min
3359                        n.data = n.data.replace(lo.genRegExp, turnout);
3360                    } else if (n.nodeType === 1) {
3361                        if (n.lang !== "") {
3362                            Hyphenator.hyphenate(n, n.lang);
3363                        } else {
3364                            Hyphenator.hyphenate(n, lang);
3365                        }
3366                    }
3367                    i += 1;
3368                    n = target.childNodes[i];
3369                }
3370            } else if (typeof target === "string" || target.constructor === String) {
3371                return target.replace(lo.genRegExp, turnout);
3372            }
3373        } else {
3374            onError(new Error("Language \"" + lang + "\" is not loaded."));
3375        }
3376    }
3377
3378    /**
3379     * @method Hyphenator.getRedPatternSet
3380     * @desc
3381     * Returns the reduced pattern set: an object looking like: {"patk": pat}
3382     * @param {string} lang the language patterns are stored for
3383     * @returns {Object.<string, string>}
3384     * @access public
3385     */
3386    function getRedPatternSet(lang) {
3387        return Hyphenator.languages[lang].redPatSet;
3388    }
3389
3390    /**
3391     * @method Hyphenator.getConfigFromURI
3392     * @desc
3393     * reads and sets configurations from GET parameters in the URI
3394     * @access public
3395     */
3396    function getConfigFromURI() {
3397        var loc = null,
3398            re = {},
3399            jsArray = contextWindow.document.getElementsByTagName("script"),
3400            i = 0,
3401            j = 0,
3402            l = jsArray.length,
3403            s,
3404            gp,
3405            option;
3406        while (i < l) {
3407            if (!!jsArray[i].getAttribute("src")) {
3408                loc = jsArray[i].getAttribute("src");
3409            }
3410            if (loc && (loc.indexOf("Hyphenator.js?") !== -1)) {
3411                s = loc.indexOf("Hyphenator.js?");
3412                gp = loc.substring(s + 14).split("&");
3413                while (j < gp.length) {
3414                    option = gp[j].split("=");
3415                    if (option[0] !== "bm") {
3416                        if (option[1] === "true") {
3417                            option[1] = true;
3418                        } else if (option[1] === "false") {
3419                            option[1] = false;
3420                        } else if (isFinite(option[1])) {
3421                            option[1] = parseInt(option[1], 10);
3422                        }
3423                        if (
3424                            option[0] === "togglebox" ||
3425                                option[0] === "onhyphenationdonecallback" ||
3426                                option[0] === "onerrorhandler" ||
3427                                option[0] === "selectorfunction" ||
3428                                option[0] === "onbeforewordhyphenation" ||
3429                                option[0] === "onafterwordhyphenation"
3430                        ) {
3431                            option[1] = new Function("", option[1]);
3432                        }
3433                        re[option[0]] = option[1];
3434                    }
3435                    j += 1;
3436                }
3437                break;
3438            }
3439            i += 1;
3440        }
3441        return re;
3442    }
3443
3444    /**
3445     * @method Hyphenator.toggleHyphenation
3446     * @desc
3447     * Checks the current state of Hyphenator.doHyphenation and removes or does hyphenation.
3448     * @access public
3449     */
3450    function toggleHyphenation() {
3451        if (Hyphenator.doHyphenation) {
3452            if (!!css3hyphenateClassHandle) {
3453                css3hyphenateClassHandle.setRule("." + css3hyphenateClass, css3_h9n.property + ": none;");
3454            }
3455            removeHyphenationFromDocument();
3456            if (safeCopy) {
3457                copy.removeOnCopy();
3458            }
3459            Hyphenator.doHyphenation = false;
3460            storeConfiguration();
3461            if (displayToggleBox) {
3462                toggleBox();
3463            }
3464        } else {
3465            if (!!css3hyphenateClassHandle) {
3466                css3hyphenateClassHandle.setRule("." + css3hyphenateClass, css3_h9n.property + ": auto;");
3467            }
3468            Hyphenator.doHyphenation = true;
3469            hyphenateLanguageElements("*");
3470            if (safeCopy) {
3471                copy.reactivateOnCopy();
3472            }
3473            storeConfiguration();
3474            if (displayToggleBox) {
3475                toggleBox();
3476            }
3477        }
3478    }
3479
3480    return {
3481        version: version,
3482        doHyphenation: doHyphenation,
3483        languages: languages,
3484        config: config,
3485        run: run,
3486        addExceptions: addExceptions,
3487        hyphenate: hyphenate,
3488        getRedPatternSet: getRedPatternSet,
3489        isBookmarklet: isBookmarklet,
3490        getConfigFromURI: getConfigFromURI,
3491        toggleHyphenation: toggleHyphenation
3492    };
3493
3494}(window));
3495
3496//Export properties/methods (for google closure compiler)
3497/**** to be moved to external file
3498Hyphenator["languages"] = Hyphenator.languages;
3499Hyphenator["config"] = Hyphenator.config;
3500Hyphenator["run"] = Hyphenator.run;
3501Hyphenator["addExceptions"] = Hyphenator.addExceptions;
3502Hyphenator["hyphenate"] = Hyphenator.hyphenate;
3503Hyphenator["getRedPatternSet"] = Hyphenator.getRedPatternSet;
3504Hyphenator["isBookmarklet"] = Hyphenator.isBookmarklet;
3505Hyphenator["getConfigFromURI"] = Hyphenator.getConfigFromURI;
3506Hyphenator["toggleHyphenation"] = Hyphenator.toggleHyphenation;
3507window["Hyphenator"] = Hyphenator;
3508*/
3509
3510/*
3511 * call Hyphenator if it is a Bookmarklet
3512 */
3513if (Hyphenator.isBookmarklet) {
3514    Hyphenator.config({displaytogglebox: true, intermediatestate: "visible", storagetype: "local", doframes: true, useCSS3hyphenation: true});
3515    Hyphenator.config(Hyphenator.getConfigFromURI());
3516    Hyphenator.run();
3517}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.