PageSourceSearch

https://iviglobaleducation.com/wp-content/themes/bilnea/js/external/functions.design.hyphenator.js?ver=5.2.0

js iviglobaleducation.com collected 2026-10-04 05:33:36 UTC 135,093 bytes, 3,429 lines download raw bytes

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

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.