PageSourceSearch

https://www.unicen.edu.ar/sites/all/themes/unicen/js/hyphenator/Hyphenator.js

js unicen.edu.ar collected 2026-10-02 09:52:01 UTC 118,147 bytes, 2,772 lines download raw bytes

1/** @license Hyphenator 4.2.0 - client side hyphenation for webbrowsers
2 *  Copyright (C) 2013  Mathias Nater, Zürich (mathias at mnn dot ch)
3 *  Project and Source hosted on http://code.google.com/p/hyphenator/
4 * 
5 *  This JavaScript code is free software: you can redistribute
6 *  it and/or modify it under the terms of the GNU Lesser
7 *  General Public License (GNU LGPL) as published by the Free Software
8 *  Foundation, either version 3 of the License, or (at your option)
9 *  any later version.  The code is distributed WITHOUT ANY WARRANTY;
10 *  without even the implied warranty of MERCHANTABILITY or FITNESS
11 *  FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
12 *
13 *  As additional permission under GNU GPL version 3 section 7, you
14 *  may distribute non-source (e.g., minimized or compacted) forms of
15 *  that code without the copy of the GNU GPL normally required by
16 *  section 4, provided you include this license notice and a URL
17 *  through which recipients can access the Corresponding Source.
18 *
19 * 
20 *  Hyphenator.js contains code from Bram Steins hypher.js-Project:
21 *  https://github.com/bramstein/Hypher
22 *  
23 *  Code from this project is marked in the source and belongs 
24 *  to the following license:
25 *  
26 *  Copyright (c) 2011, Bram Stein
27 *  All rights reserved.
28 *  
29 *  Redistribution and use in source and binary forms, with or without 
30 *  modification, are permitted provided that the following conditions 
31 *  are met:
32 *   
33 *   1. Redistributions of source code must retain the above copyright
34 *      notice, this list of conditions and the following disclaimer. 
35 *   2. Redistributions in binary form must reproduce the above copyright 
36 *      notice, this list of conditions and the following disclaimer in the 
37 *      documentation and/or other materials provided with the distribution. 
38 *   3. The name of the author may not be used to endorse or promote products 
39 *      derived from this software without specific prior written permission. 
40 *  
41 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED 
42 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
43 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
44 *  EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
45 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
46 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
47 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
48 *  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
49 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
50 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 *  
52 */
53
54/* 
55 *  Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
56 */
57
58/* The following comment is for JSLint: */
59/*global window */
60/*jslint browser: true */
61
62/**
63 * @constructor
64 * @description Provides all functionality to do hyphenation, except the patterns that are loaded
65 * externally.
66 * @author Mathias Nater, <a href = "mailto:[email protected]">[email protected]</a>
67 * @version 4.2.0
68 * @namespace Holds all methods and properties
69 * @example
70 * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
71 * &lt;script type = "text/javascript"&gt;
72 *   Hyphenator.run();
73 * &lt;/script&gt;
74 */
75var Hyphenator = (function (window) {
76    'use strict';
77
78    /**
79     * @name Hyphenator-contextWindow
80     * @private
81     * @description
82     * contextWindow stores the window for the document to be hyphenated.
83     * If there are frames this will change.
84     * So use contextWindow instead of window!
85     */
86    var contextWindow = window,
87
88        /**
89         * @name Hyphenator-supportedLangs
90         * @description
91         * A key-value object that stores supported languages and meta data.
92         * The key is the bcp47 code of the language and the value
93         * is an object containing following informations about the language:
94         * file: filename of the pattern file,
95         * script: script type of the language (e.g. 'latin' for english), this type is abbreviated by an id,
96         * prompt: the sentence prompted to the user, if Hyphenator.js doesn't find a language hint.
97         * @type {Object.<string>, Object>}
98         * @private
99         * @example
100         * Check if language lang is supported:
101         * if (supportedLangs.hasOwnProperty(lang))
102         */
103        supportedLangs = (function () {
104            var r = {},
105                o = function (code, file, script, prompt) {
106                    r[code] = {'file': file, 'script': script, 'prompt': prompt};
107                };
108
109            //latin:0, cyrillic: 1, arabic: 2, armenian:3, bengali: 4, devangari: 5, greek: 6
110            //gujarati: 7, kannada: 8, lao: 9, malayalam: 10, oriya: 11, persian: 12, punjabi: 13, tamil: 14, telugu: 15
111            //
112            //(language code, file name, script, prompt)
113            o('be', 'be.js', 1, 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:');
114            o('ca', 'ca.js', 0, '');
115            o('cs', 'cs.js', 0, 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:');
116            o('da', 'da.js', 0, 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:');
117            o('bn', 'bn.js', 4, '');
118            o('de', 'de.js', 0, 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:');
119            o('el', 'el-monoton.js', 6, '');
120            o('el-monoton', 'el-monoton.js', 6, '');
121            o('el-polyton', 'el-polyton.js', 6, '');
122            o('en', 'en-us.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:');
123            o('en-gb', 'en-gb.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:');
124            o('en-us', 'en-us.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:');
125            o('eo', 'eo.js', 0, 'La lingvo de ĉi tiu retpaĝo ne rekoneblas aŭtomate. Bonvolu indiki ĝian ĉeflingvon:');
126            o('es', 'es.js', 0, 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:');
127            o('et', 'et.js', 0, 'Veebilehe keele tuvastamine ebaõnnestus, palun valige kasutatud keel:');
128            o('fi', 'fi.js', 0, 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:');
129            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:');
130            o('grc', 'grc.js', 6, '');
131            o('gu', 'gu.js', 7, '');
132            o('hi', 'hi.js', 5, '');
133            o('hu', 'hu.js', 0, 'A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:');
134            o('hy', 'hy.js', 3, 'Չհաջողվեց հայտնաբերել այս կայքի լեզուն։ Խնդրում ենք նշեք հիմնական լեզուն՝');
135            o('it', 'it.js', 0, 'Lingua del sito sconosciuta. Indicare una lingua, per favore:');
136            o('kn', 'kn.js', 8, 'ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:');
137            o('la', 'la.js', 0, '');
138            o('lt', 'lt.js', 0, 'Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:');
139            o('lv', 'lv.js', 0, 'Šīs lapas valodu nevarēja noteikt automātiski. Lūdzu norādiet pamata valodu:');
140            o('ml', 'ml.js', 10, 'ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:');
141            o('nb', 'nb-no.js', 0, 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:');
142            o('no', 'nb-no.js', 0, 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:');
143            o('nb-no', 'nb-no.js', 0, 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:');
144            o('nl', 'nl.js', 0, 'De taal van deze website kan niet automatisch w
144orden bepaald. Geef de hoofdtaal op:');
145            o('or', 'or.js', 11, '');
146            o('pa', 'pa.js', 13, '');
147            o('pl', 'pl.js', 0, 'Języka tej strony nie można ustalić automatycznie. Proszę wskazać język:');
148            o('pt', 'pt.js', 0, 'A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:');
149            o('ru', 'ru.js', 1, 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:');
150            o('sk', 'sk.js', 0, '');
151            o('sl', 'sl.js', 0, 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:');
152            o('sr-latn', 'sr-latn.js', 0, 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:');
153            o('sv', 'sv.js', 0, 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:');
154            o('ta', 'ta.js', 14, '');
155            o('te', 'te.js', 15, '');
156            o('tr', 'tr.js', 0, 'Bu web sitesinin dili otomatik olarak tespit edilememiştir. Lütfen dökümanın dilini seçiniz%A0:');
157            o('uk', 'uk.js', 1, 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:');
158            o('ro', 'ro.js', 0, 'Limba acestui sit nu a putut fi determinată automat. Alege limba principală:');
159
160            return r;
161        }()),
162
163
164        /**
165         * @name Hyphenator-basePath
166         * @description
167         * A string storing the basepath from where Hyphenator.js was loaded.
168         * This is used to load the patternfiles.
169         * The basepath is determined dynamically by searching all script-tags for Hyphenator.js
170         * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback.
171         * @type {string}
172         * @private
173         * @see Hyphenator-loadPatterns
174         */
175        basePath = (function () {
176            var s = contextWindow.document.getElementsByTagName('script'), i = 0, p, src, t = s[i], r = '';
177            while (!!t) {
178                if (!!t.src) {
179                    src = t.src;
180                    p = src.indexOf('Hyphenator.js');
181                    if (p !== -1) {
182                        r = src.substring(0, p);
183                    }
184                }
185                i += 1;
186                t = s[i];
187            }
188            return !!r ? r : 'http://hyphenator.googlecode.com/svn/trunk/';
189        }()),
190
191        /**
192         * @name Hyphenator-isLocal
193         * @private
194         * @description
195         * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
196         * it's loaded from an external source (i.e. directly from google.code)
197         */
198        isLocal = (function () {
199            var re = false;
200            if (window.location.href.indexOf(basePath) !== -1) {
201                re = true;
202            }
203            return re;
204        }()),
205
206        /**
207         * @name Hyphenator-documentLoaded
208         * @private
209         * @description
210         * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
211         */
212        documentLoaded = false,
213
214        /**
215         * @name Hyphenator-persistentConfig
216         * @private
217         * @description
218         * if persistentConfig is set to true (defaults to false), config options and the state of the 
219         * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven't to be
220         * set for each page.
221         */
222        persistentConfig = false,
223
224        /**
225         * @name Hyphenator-doFrames
226         * @private
227         * @description
228         * switch to control if frames/iframes should be hyphenated, too
229         * defaults to false (frames are a bag of hurt!)
230         */
231        doFrames = false,
232
233        /**
234         * @name Hyphenator-dontHyphenate
235         * @description
236         * A key-value object containing all html-tags whose content should not be hyphenated
237         * @type {Object.<string,boolean>}
238         * @private
239         * @see Hyphenator-hyphenateElement
240         */
241        dontHyphenate = {'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},
242
243        /**
244         * @name Hyphenator-enableCache
245         * @description
246         * A variable to set if caching is enabled or not
247         * @type boolean
248         * @default true
249         * @private
250         * @see Hyphenator.config
251         * @see hyphenateWord
252         */
253        enableCache = true,
254
255        /**
256         * @name Hyphenator-storageType
257         * @description
258         * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
259         * @type {string}
260         * @default 'local'
261         * @private
262         * @see Hyphenator.config
263         */
264        storageType = 'local',
265
266        /**
267         * @name Hyphenator-storage
268         * @description
269         * An alias to the storage-Method defined in storageType.
270         * Set by Hyphenator.run()
271         * @type {Object|undefined}
272         * @default null
273         * @private
274         * @see Hyphenator.run
275         */
276        storage,
277
278        /**
279         * @name Hyphenator-enableReducedPatternSet
280         * @description
281         * A variable to set if storing the used patterns is set
282         * @type boolean
283         * @default false
284         * @private
285         * @see Hyphenator.config
286         * @see hyphenateWord
287         * @see Hyphenator.getRedPatternSet
288         */
289        enableReducedPatternSet = false,
290
291        /**
292         * @name Hyphenator-enableRemoteLoading
293         * @description
294         * A variable to set if pattern files should be loaded remotely or not
295         * @type boolean
296         * @default true
297         * @private
298         * @see Hyphenator.config
299         * @see Hyphenator-loadPatterns
300         */
301        enableRemoteLoading = true,
302
303        /**
304         * @name Hyphenator-displayToggleBox
305         * @description
306         * A variable to set if the togglebox should be displayed or not
307         * @type boolean
308         * @default false
309         * @private
310         * @see Hyphenator.config
311         * @see Hyphenator-toggleBox
312         */
313        displayToggleBox = false,
314
315        /**
316         * @name Hyphenator-onError
317         * @description
318         * A function that can be called upon an error.
319         * @see Hyphenator.config
320         * @type {function(Object)}
321         * @private
322         */
323        onError = function (e) {
324            window.alert("Hyphenator.js says:\n\nAn Error occurred:\n" + e.message);
325        },
326
327        /**
328         * @name Hyphenator-createElem
329         * @description
330         * A function alias to document.createElementNS or document.createElement
331         * @type {function(string, Object)}
332         * @private
333         */
334        createElem = function (tagname, context) {
335            context = context || contextWindow;
336            var el;
337            if (window.document.createElementNS) {
338                el = context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
339            } else if (window.document.createElement) {
340                el = context.document.createElement(tagname);
341            }
342            return el;
343        },
344
345        /**
346         * @name Hyphenator-css3
347         * @description
348         * A variable to set if css3 hyphenation should be used
349         * @type boolean
350         * @default false
351         * @private
352         * @see Hyphenator.config
353         */
354        css3 = false,
355
356        /**
357         * @name Hyphenator-css3_hsupport
358         * @description
359         * A generated object containing information for CSS3-hyphenation support
360         * {
361         *   support: boolean,
362         *   property: <the property name to access hyphen-settings>,
363         *   languages: <an object containing supported languages>
364         * }
365         * @type object
366         * @default undefined
367         * @private
368         * @see Hyphenator-css3_gethsupport
369         */
370        css3_h9n,
371
372        /**
373         * @name Hyphenator-css3_gethsupport
374         * @description
375         * This function sets Hyphenator-css3_h9n for the current UA
376         * @type function
377         * @private
378         * @see Hyphenator-css3_h9n
379         */
380        css3_gethsupport = function () {
381            var s,
382                createLangSupportChecker = function (prefix) {
383                    var testStrings = [
384                        //latin: 0
385                        'aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz',
386                        //cyrillic: 1
387                        'абвгдеёжзийклмнопрстуфхцчшщъыьэюя',
388                        //arabic: 2
389                        'أبتثجحخدذرزسشصضطظعغفقكلمنهوي',
390                        //armenian: 3
391                        'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆ',
392                        //bengali: 4
393                        'ঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ',
394                        //devangari: 5
395                        'ँंःअआइईउऊऋऌएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयरलळवशषसहऽािीुूृॄेैोौ्॒॑ॠॡॢॣ',
396                        //greek: 6
397                        'αβγδεζηθικλμνξοπρσςτυφχψω',
398                        //gujarati: 7
399                        'બહઅઆઇઈઉઊઋૠએઐઓઔાિીુૂૃૄૢૣેૈોૌકખગઘઙચછજઝઞટઠડઢણતથદધનપફસભમયરલળવશષ',
400                        //kannada: 8
401                        'ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡ',
402                        //lao: 9
403                        'ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮະັາິີຶືຸູົຼເແໂໃໄ່້໊໋ໜໝ',
404                        //malayalam: 10
405                        'ംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരറലളഴവശഷസഹാിീുൂൃെേൈൊോൌ്ൗൠൡൺൻർൽൾൿ',
406                        //oriya: 11
407                        'ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହାିୀୁୂୃେୈୋୌ୍ୗୠୡ',
408                        //persian: 12
409                        'أبتثجحخدذرزسشصضطظعغفقكلمنهوي',
410                        //punjabi: 13
411                        'ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹਾਿੀੁੂੇੈੋੌ੍ੰੱ',
412                        //tamil: 14
413                        'ஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஷஸஹாிீுூெேைொோௌ்ௗ',
414                        //telugu: 15
415                        'ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహాిీుూృౄెేైొోౌ్ౕౖౠౡ'
416                    ],
417                        f = function (lang) {
418                            var shadow,
419                                computedHeight,
420                                bdy = window.document.getElementsByTagName('body')[0],
421                                r = false;
422
423                            if (supportedLangs.hasOwnProperty(lang)) {
424                                //create and append shadow-test-element
425                                shadow = createElem('div', window);
426                                shadow.id = 'Hyphenator_LanguageChecker';
427                                shadow.style.width = '5em';
428                                shadow.style[prefix] = 'auto';
429                                shadow.style.hyphens = 'auto';
430                                shadow.style.fontSize = '12px';
431                                shadow.style.lineHeight = '12px';
432                                shadow.style.visibility = 'hidden';
433                                shadow.lang = lang;
434                                shadow.style['-webkit-locale'] = "'" + lang + "'";
435                                shadow.innerHTML = testStrings[supportedLangs[lang].script];
436                                bdy.appendChild(shadow);
437                                //measure its height
438                                computedHeight = shadow.offsetHeight;
439                                //remove shadow element
440                                bdy.removeChild(shadow);
441                                r = (computedHeight > 12) ? true : false;
442                            } else {
443                                r = false;
444                            }
445                            return r;
446                        };
447                    return f;
448                },
449                r = {
450                    support: false,
451                    property: '',
452                    checkLangSupport: function () {}
453                };
454
455            if (window.getComputedStyle) {
456                s = contextWindow.getComputedStyle(contextWindow.document.getElementsByTagName('body')[0], null);
457            } else {
458                //ancient Browsers don't support CSS3 anyway
459                css3_h9n = r;
460                return;
461            }
462
463            if (s['-webkit-hyphens'] !== undefined) {
464                r.support = true;
465                r.property = '-webkit-hyphens';
466                r.checkLangSupport = createLangSupportChecker('-webkit-hyphens');
467            } else if (s.MozHyphens !== undefined) {
468                r.support = true;
469                r.property = '-moz-hyphens';
470                r.checkLangSupport = createLangSupportChecker('MozHyphens');
471            } else if (s['-ms-hyphens'] !== undefined) {
472                r.support = true;
473                r.property = '-ms-hyphens';
474                r.checkLangSupport = createLangSupportChecker('-ms-hyphens');
475            }
476            css3_h9n = r;
477        },
478
479        /**
480         * @name Hyphenator-hyphenateClass
481         * @description
482         * A string containing the css-class-name for the hyphenate class
483         * @type {string}
484         * @default 'hyphenate'
485         * @private
486         * @example
487         * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
488         * @see Hyphenator.config
489         */
490        hyphenateClass = 'hyphenate',
491
492        /**
493         * @name Hyphenator-classPrefix
494         * @description
495         * A string containing a unique className prefix to be used
496         * whenever Hyphenator sets a CSS-class
497         * @type {string}
498         * @private
499         */
500        classPrefix = 'Hyphenator' + Math.round(Math.random() * 1000),
501
502        /**
503         * @name Hyphenator-hideClass
504         * @description
505         * The name of the class that hides elements
506         * @type {string}
507         * @private
508         */
509        hideClass = classPrefix + 'hide',
510
511        /**
512         * @name Hyphenator-hideClassRegExp
513         * @description
514         * RegExp to remove hideClass from a list of classes
515         * @type {RegExp}
516         * @private
517         */
518        hideClassRegExp = new RegExp("\\s?\\b" + hideClass + "\\b", "g"),
519
520        /**
521         * @name Hyphenator-hideClass
522         * @description
523         * The name of the class that unhides elements
524         * @type {string}
525         * @private
526         */
527        unhideClass = classPrefix + 'unhide',
528
529        /**
530         * @name Hyphenator-hideClassRegExp
531         * @description
532         * RegExp to remove unhideClass from a list of classes
533         * @type {RegExp}
534         * @private
535         */
536        unhideClassRegExp = new RegExp("\\s?\\b" + unhideClass + "\\b", "g"),
537
538        /**
539         * @name Hyphenator-css3hyphenateClass
540         * @description
541         * The name of the class that hyphenates elements with css3
542         * @type {string}
543         * @private
544         */
545        css3hyphenateClass = classPrefix + 'css3hyphenate',
546
547        /**
548         * @name Hyphenator-css3hyphenateClass
549         * @description
550         * The var where CSSEdit class is stored
551         * @type {Object}
552         * @private
553         */
554        css3hyphenateClassHandle,
555
556        /**
557         * @name Hyphenator-dontHyphenateClass
558         * @description
559         * A string containing the css-class-name for elements that should not be hyphenated
560         * @type {string}
561         * @default 'donthyphenate'
562         * @private
563         * @example
564         * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
565         * @see Hyphenator.config
566         */
567        dontHyphenateClass = 'donthyphenate',
568
569        /**
570         * @name Hyphenator-min
571         * @description
572         * A number wich indicates the minimal length of words to hyphenate.
573         * @type {number}
574         * @default 6
575         * @private
576         * @see Hyphenator.config
577         */
578        min = 6,
579
580        /**
581         * @name Hyphenator-orphanControl
582         * @description
583         * Control how the last words of a line are handled:
584         * level 1 (default): last word is hyphenated
585         * level 2: last word is not hyphenated
586         * level 3: last word is not hyphenated and last space is non breaking
587         * @type {number}
588         * @default 1
589         * @private
590         */
591        orphanControl = 1,
592
593        /**
594         * @name Hyphenator-isBookmarklet
595         * @description
596         * Indicates if Hyphanetor runs as bookmarklet or not.
597         * @type boolean
598         * @default false
599         * @private
600         */
601        isBookmarklet = (function () {
602            var loc = null,
603                re = false,
604                scripts = contextWindow.document.getElementsByTagName('script'),
605                i = 0,
606                l = scripts.length;
607            while (!re && i < l) {
608                loc = scripts[i].getAttribute('src');
609                if (!!loc && loc.indexOf('Hyphenator.js?bm=true') !== -1) {
610                    re = true;
611                }
612                i += 1;
613            }
614            return re;
615        }()),
616
617        /**
618         * @name Hyphenator-mainLanguage
619         * @description
620         * The general language of the document. In contrast to {@link Hyphenator-defaultLanguage},
621         * mainLanguage is defined by the client (i.e. by the html or by a prompt).
622         * @type {string|null}
623         * @private
624         * @see Hyphenator-autoSetMainLanguage
625         */
626        mainLanguage = null,
627
628        /**
629         * @name Hyphenator-defaultLanguage
630         * @description
631         * The language defined by the developper. This language setting is defined by a config option.
632         * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can
633         * be found (i.e. just before the prompt).
634         * @type {string|null}
635         * @private
636         * @see Hyphenator-autoSetMainLanguage
637         */
638        defaultLanguage = '',
639
640
641        /**
642         * @name Hyphenator-elements
643         * @description
644         * An object holding all elements that have to be hyphenated. This var is filled by
645         * {@link Hyphenator-gatherDocumentInfos}
646         * @type {Array}
647         * @private
648         */
649        elements = (function () {
650            var Element = function (element) {
651                this.element = element;
652                this.hyphenated = false;
653                this.treated = false; //collected but not hyphenated (dohyphenation is off)
654            },
655                ElementCollection = function () {
656                    this.count = 0;
657                    this.hyCount = 0;
658                    this.list = {};
659                };
660            ElementCollection.prototype = {
661                add: function (el, lang) {
662                    if (!this.list.hasOwnProperty(lang)) {
663                        this.list[lang] = [];
664                    }
665                    this.list[lang].push(new Element(el));
666                    this.count += 1;
667                },
668                each: function (fn) {
669                    var k;
670                    for (k in this.list) {
671                        if (this.list.hasOwnProperty(k)) {
672                            if (fn.length === 2) {
673                                fn(k, this.list[k]);
674                            } else {
675                                fn(this.list[k]);
676                            }
677                        }
678                    }
679                }
680            };
681            return new ElementCollection();
682        }()),
683
684
685        /**
686         * @name Hyphenator-exceptions
687         * @description
688         * An object containing exceptions as comma separated strings for each language.
689         * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
690         * @see Hyphenator-prepareLanguagesObj
691         * @type {Object}
692         * @private
693         */
694        exceptions = {},
695
696        /**
697         * @name Hyphenator-docLanguages
698         * @description
699         * An object holding all languages used in the document. This is filled by
700         * {@link Hyphenator-gatherDocumentInfos}
701         * @type {Object}
702         * @private
703         */
704        docLanguages = {},
705
706        /**
707         * @name Hyphenator-state
708         * @description
709         * A number that inidcates the current state of the script
710         * 0: not initialized
711         * 1: loading patterns
712         * 2: ready
713         * 3: hyphenation done
714         * 4: hyphenation removed
715         * @type {number}
716         * @private
717         */
718        state = 0,
719
720        /**
721         * @name Hyphenator-url
722         * @description
723         * A string containing a RegularExpression to match URL's
724         * @type {string}
725         * @private
726         */
727        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#!:\\.?\\+=&%@!\\-]*)*',
728        //      protocoll     usr     pwd                    ip               or                          host                 tld        port               path
729
730        /**
731         * @name Hyphenator-mail
732         * @description
733         * A string containing a RegularExpression to match mail-adresses
734         * @type {string}
735         * @private
736         */
737        mail = '[\\w-\\.]+@[\\w\\.]+',
738
739        /**
740         * @name Hyphenator-urlRE
741         * @description
742         * A RegularExpressions-Object for url- and mail adress matching
743         * @type {RegExp}
744         * @private
745         */
746        urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),
747
748        /**
749         * @name Hyphenator-zeroWidthSpace
750         * @description
751         * A string that holds a char.
752         * Depending on the browser, this is the zero with space or an empty string.
753         * zeroWidthSpace is used to break URLs
754         * @type {string}
755         * @private
756         */
757        zeroWidthSpace = (function () {
758            var zws, ua = window.navigator.userAgent.toLowerCase();
759            zws = String.fromCharCode(8203); //Unicode zero width space
760            if (ua.indexOf('msie 6') !== -1) {
761                zws = ''; //IE6 doesn't support zws
762            }
763            if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
764                zws = ''; //opera 10 on XP doesn't support zws
765            }
766            return zws;
767        }()),
768
769        /**
770         * @name Hyphenator-onBeforeWordHyphenation
771         * @description
772         * A method to be called for each word to be hyphenated before it is hyphenated.
773         * Takes the word as a first parameter and its language as a second parameter.
774         * Returns a string that will replace the word to be hyphenated.
775         * @see Hyphenator.config
776         * @type {function()}
777         * @private
778         */
779        onBeforeWordHyphenation = function (word) {
780            return word;
781        },
782
783        /**
784         * @name Hyphenator-onAfterWordHyphenation
785         * @description
786         * A method to be called for each word to be hyphenated after it is hyphenated.
787         * Takes the word as a first parameter and its language as a second parameter.
788         * Returns a string that will replace the word that has been hyphenated.
789         * @see Hyphenator.config
790         * @type {function()}
791         * @private
792         */
793        onAfterWordHyphenation = function (word) {
794            return word;
795        },
796
797        /**
798         * @name Hyphenator-onHyphenationDone
799         * @description
800         * A method to be called, when the last element has been hyphenated
801         * @see Hyphenator.config
802         * @type {function()}
803         * @private
804         */
805        onHyphenationDone = function () {},
806
807        /**
808         * @name Hyphenator-selectorFunction
809         * @description
810         * A function set by the user that has to return a HTMLNodeList or array of Elements to be hyphenated.
811         * By default this is set to false so we can check if a selectorFunction is set…
812         * @see Hyphenator.config
813         * @type {function()}
814         * @private
815         */
816        selectorFunction = false,
817
818        /**
819         * @name Hyphenator-mySelectorFunction
820         * @description
821         * A function that has to return a HTMLNodeList or array of Elements to be hyphenated.
822         * By default it uses the classname ('hyphenate') to select the elements.
823         * @type {function()}
824         * @private
825         */
826        mySelectorFunction = function (hyphenateClass) {
827            var tmp, el = [], i, l;
828            if (window.document.getElementsByClassName) {
829                el = contextWindow.document.getElementsByClassName(hyphenateClass);
830            } else if (window.document.querySelectorAll) {
831                el = contextWindow.document.querySelectorAll('.' + hyphenateClass);
832            } else {
833                tmp = contextWindow.document.getElementsByTagName('*');
834                l = tmp.length;
835                for (i = 0; i < l; i += 1) {
836                    if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
837                        el.push(tmp[i]);
838                    }
839                }
840            }
841            return el;
842        },
843
844        /**
845         * @name Hyphenator-selectElements
846         * @description
847         * A function that has to return a HTMLNodeList or array of Elements to be hyphenated.
848         * It uses either selectorFunction set by the user (and adds a unique class to each element)
849         * or the default mySelectorFunction.
850         * @type {function()}
851         * @private
852         */
853        selectElements = function () {
854            var elements;
855            if (selectorFunction) {
856                elements = selectorFunction();
857            } else {
858                elements = mySelectorFunction(hyphenateClass);
859            }
860
861            return elements;
862        },
863
864        /**
865         * @name Hyphenator-intermediateState
866         * @description
867         * The value of style.visibility of the text while it is hyphenated.
868         * @see Hyphenator.config
869         * @type {string}
870         * @private
871         */
872        intermediateState = 'hidden',
873
874        /**
875         * @name Hyphenator-unhide
876         * @description
877         * How hidden elements unhide: either simultaneous (default: 'wait') or progressively.
878         * 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw)
879         * With 'progressive' Hyphenator.js unhides elements as soon as they are hyphenated.
880         * @see Hyphenator.config
881         * @type {string}
882         * @private
883         */
884        unhide = 'wait',
885
886        /**
887         * @name Hyphenator-CSSEditors
888         * @description A container array that holds CSSEdit classes
889         * For each window object one CSSEdit class is inserted
890         * @see Hyphenator-CSSEdit
891         * @type {array}
892         * @private
893         */
894        CSSEditors = [],
895
896        /**
897         * @name Hyphenator-CSSEditors
898         * @description A custom class with two public methods: setRule() and clearChanges()
899         * Rather sets style for CSS-classes then for single elements
900         * This is used to hide/unhide elements when they are hyphenated.
901         * @see Hyphenator-gatherDocumentInfos
902         * @type {function ()}
903         * @private
904         */
905        CSSEdit = function (w) {
906            w = w || window;
907            var doc = w.document,
908                //find/create an accessible StyleSheet
909                sheet = (function () {
910                    var i,
911                        l = doc.styleSheets.length,
912                        sheet,
913                        element,
914                        r = false;
915                    for (i = 0; i < l; i += 1) {
916                        sheet = doc.styleSheets[i];
917                        try {
918                            if (!!sheet.cssRules) {
919                                r = sheet;
920                                break;
921                            }
922                        } catch (e) {}
923                    }
924                    if (r === false) {
925                        element = doc.createElement('style');
926                        element.type = 'text/css';
927                        doc.getElementsByTagName('head')[0].appendChild(element);
928                        r = doc.styleSheets[doc.styleSheets.length - 1];
929                    }
930                    return r;
931                }()),
932                changes = [],
933                findRule = function (sel) {
934                    var sheet, rule, sheets = window.document.styleSheets, rules, i, j, r = false;
935                    for (i = 0; i < sheets.length; i += 1) {
936                        sheet = sheets[i];
937                        try { //FF has issues here with external CSS (s.o.p)
938                            if (!!sheet.cssRules) {
939                                rules = sheet.cssRules;
940                            } else if (!!sheet.rules) {
941                                // IE < 9
942                                rules = sheet.rules;
943                            }
944                        } catch (e) {
945                            //do nothing
946                            //console.log(e);
947                        }
948                        if (!!rules && !!rules.length) {
949                            for (j = 0; j < rules.length; j += 1) {
950                                rule = rules[j];
951                                if (rule.selectorText === sel) {
952                                    r = {
953                                        index: j,
954                                        rule: rule
955                                    };
956                                }
957                            }
958                        }
959                    }
960                    return r;
961                },
962                addRule = function (sel, rulesStr) {
963                    var i, r;
964                    if (!!sheet.insertRule) {
965                        if (!!sheet.cssRules) {
966                            i = sheet.cssRules.length;
967                        } else {
968                            i = 0;
969                        }
970                        r = sheet.insertRule(sel + '{' + rulesStr + '}', i);
971                    } else if (!!sheet.addRule) {
972                        // IE < 9
973                        if (!!sheet.rules) {
974                            i = sheet.rules.length;
975                        } else {
976                            i = 0;
977                        }
978                        sheet.addRule(sel, rulesStr, i);
979                        r = i;
980                    }
981                    return r;
982                },
983                removeRule = function (sheet, index) {
984                    if (sheet.deleteRule) {
985                        sheet.deleteRule(index);
986                    } else {
987                        // IE < 9
988                        sheet.removeRule(index);
989                    }
990                };
991
992            return {
993                setRule: function (sel, rulesString) {
994                    var i, existingRule, cssText;
995                    existingRule = findRule(sel);
996                    if (!!existingRule) {
997                        if (!!existingRule.rule.cssText) {
998                            cssText = existingRule.rule.cssText;
999                        } else {
1000                            // IE < 9
1001                            cssText = existingRule.rule.style.cssText.toLowerCase();
1002                        }
1003                        if (cssText === '.' + hyphenateClass + ' { visibility: hidden; }') {
1004                            //browsers w/o IE < 9 and no additional style defs:
1005                            //add to [changes] for later removal
1006                            changes.push({sheet: existingRule.rule.parentStyleSheet, index: existingRule.index});
1007                        } else if (cssText.indexOf('visibility: hidden') !== -1) {
1008                            // IE < 9 or additional style defs:
1009                            // add new rule
1010                            i = addRule(sel, rulesString);
1011                            //add to [changes] for later removal
1012                            changes.push({sheet: sheet, index: i});
1013                            // clear existing def
1014                            existingRule.rule.style.visibility = '';
1015                        } else {
1016                            addRule(sel, rulesString);
1017                        }
1018                    } else {
1019                        i = addRule(sel, rulesString);
1020                        changes.push({sheet: sheet, index: i});
1021                    }
1022                },
1023                clearChanges: function () {
1024                    var change = changes.pop();
1025                    while (!!change) {
1026                        removeRule(change.sheet, change.index);
1027                        change = changes.pop();
1028                    }
1029                }
1030            };
1031        },
1032
1033        /**
1034         * @name Hyphenator-hyphen
1035         * @description
1036         * A string containing the character for in-word-hyphenation
1037         * @type {string}
1038         * @default the soft hyphen
1039         * @private
1040         * @see Hyphenator.config
1041         */
1042        hyphen = String.fromCharCode(173),
1043
1044        /**
1045         * @name Hyphenator-urlhyphen
1046         * @description
1047         * A string containing the character for url/mail-hyphenation
1048         * @type {string}
1049         * @default the zero width space
1050         * @private
1051         * @see Hyphenator.config
1052         * @see Hyphenator-zeroWidthSpace
1053         */
1054        urlhyphen = zeroWidthSpace,
1055
1056        /**
1057         * @name Hyphenator-safeCopy
1058         * @description
1059         * Defines wether work-around for copy issues is active or not
1060         * Not supported by Opera (no onCopy handler)
1061         * @type boolean
1062         * @default true
1063         * @private
1064         * @see Hyphenator.config
1065         * @see Hyphenator-registerOnCopy
1066         */
1067        safeCopy = true,
1068
1069        /*
1070         * runOnContentLoaded is based od jQuery.bindReady()
1071         * see
1072         * jQuery JavaScript Library v1.3.2
1073         * http://jquery.com/
1074         *
1075         * Copyright (c) 2009 John Resig
1076         * Dual licensed under the MIT and GPL licenses.
1077         * http://docs.jquery.com/License
1078         *
1079         * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
1080         * Revision: 6246
1081         */
1082        /**
1083         * @name Hyphenator-runOnContentLoaded
1084         * @description
1085         * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery
1086         * <a href = "http://jquery.com/</a>
1087         * I added some functionality: e.g. support for frames and iframes…
1088         * @param {Object} w the window-object
1089         * @param {function()} f the function to call onDOMContentLoaded
1090         * @private
1091         */
1092        runOnContentLoaded = function (w, f) {
1093            var
1094                toplevel, hyphRunForThis = {},
1095                add = window.document.addEventListener ? 'addEventListener' : 'attachEvent',
1096                rem = window.document.addEventListener ? 'removeEventListener' : 'detachEvent',
1097                pre = window.document.addEventListener ? '' : 'on',
1098
1099                init = function (context) {
1100                    contextWindow = context || window;
1101                    if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || !!contextWindow.frameElement)) {
1102                        documentLoaded = true;
1103                        f();
1104                        hyphRunForThis[contextWindow.location.href] = true;
1105                    }
1106                },
1107
1108                doScrollCheck = function () {
1109                    try {
1110                        // If IE is used, use the trick by Diego Perini
1111                        // http://javascript.nwbox.com/IEContentLoaded/
1112                        contextWindow.document.documentElement.doScroll("left");
1113                    } catch (error) {
1114                        window.setTimeout(doScrollCheck, 1);
1115                        return;
1116                    }
1117
1118                    // and execute any waiting functions
1119                    init(window);
1120                },
1121
1122                doOnLoad = function () {
1123                    var i, haveAccess, fl = window.frames.length;
1124                    if (doFrames && fl > 0) {
1125                        for (i = 0; i < fl; i += 1) {
1126                            haveAccess = undefined;
1127                            //try catch isn't enough for webkit
1128                            try {
1129                                //opera throws only on document.toString-access
1130                                haveAccess = window.frames[i].document.toString();
1131                            } catch (e) {
1132                                haveAccess = undefined;
1133                            }
1134                            if (!!haveAccess) {
1135                                if (window.frames[i].location.href !== 'about:blank') {
1136                                    init(window.frames[i]);
1137                                }
1138                            }
1139                        }
1140                        contextWindow = window;
1141                        f();
1142                        hyphRunForThis[window.location.href] = true;
1143                    } else {
1144                        init(window);
1145                    }
1146                },
1147
1148                // Cleanup functions for the document ready method
1149                DOMContentLoaded = function (e) {
1150                    if (e.type === 'readystatechange' && contextWindow.document.readyState !== 'complete') {
1151                        return;
1152                    }
1153                    contextWindow.document[rem](pre + e.type, DOMContentLoaded, false);
1154                    if (!doFrames && window.frames.length === 0) {
1155                        init(window);
1156                    } /* else {
1157                        //we are in a frameset, so do nothing but wait for onload to fire
1158                        
1159                    }*/
1160                };
1161
1162            if (documentLoaded && !hyphRunForThis[w.location.href]) {
1163                f();
1164                hyphRunForThis[w.location.href] = true;
1165                return;
1166            }
1167
1168            if (contextWindow.document.readyState === "complete" || contextWindow.document.readyState === "interactive") {
1169                //Running Hyphenator.js if it has been loaded later
1170                //Thanks to davenewtron http://code.google.com/p/hyphenator/issues/detail?id=158#c10
1171                window.setTimeout(doOnLoad, 1);
1172            } else {
1173                //registering events
1174                contextWindow.document[add](pre + "DOMContentLoaded", DOMContentLoaded, false);
1175                contextWindow.document[add](pre + 'readystatechange', DOMContentLoaded, false);
1176                window[add](pre + 'load', doOnLoad, false);
1177                toplevel = false;
1178                try {
1179                    toplevel = !window.frameElement;
1180                } catch (e) {}
1181                if (contextWindow.document.documentElement.doScroll && toplevel) {
1182                    doScrollCheck();
1183                }
1184            }
1185        },
1186
1187        /**
1188         * @name Hyphenator-getLang
1189         * @description
1190         * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}.
1191         * @param {Object} el The first parameter is an DOM-Element-Object
1192         * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage}
1193         * if there's no language found for the element.
1194         * @private
1195         */
1196        getLang = function (el, fallback) {
1197            try {
1198                return !!el.getAttribute('lang') ? el.getAttribute('lang').toLowerCase() :
1199                        !!el.getAttribute('xml:lang') ? el.getAttribute('xml:lang').toLowerCase() :
1200                                el.tagName.toLowerCase() !== 'html' ? getLang(el.parentNode, fallback) :
1201                                        fallback ? mainLanguage :
1202                                                null;
1203            } catch (e) {}
1204        },
1205
1206        /**
1207         * @name Hyphenator-autoSetMainLanguage
1208         * @description
1209         * Retrieves the language of the document from the DOM.
1210         * The function looks in the following places:
1211         * <ul>
1212         * <li>lang-attribute in the html-tag</li>
1213         * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
1214         * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
1215         * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
1216         * </li>
1217         * If nothing can be found a prompt using {@link Hyphenator-languageHint} and a prompt-string is displayed.
1218         * If the retrieved language is in the object {@link Hyphenator-supportedLangs} it is copied to {@link Hyphenator-mainLanguage}
1219         * @private
1220         */
1221        autoSetMainLanguage = function (w) {
1222            w = w || contextWindow;
1223            var el = w.document.getElementsByTagName('html')[0],
1224                m = w.document.getElementsByTagName('meta'),
1225                i,
1226                getLangFromUser = function () {
1227                    var mainLanguage,
1228                        text = '',
1229                        dH = 300,
1230                        dW = 450,
1231                        dX = Math.floor((w.outerWidth - dW) / 2) + window.screenX,
1232                        dY = Math.floor((w.outerHeight - dH) / 2) + window.screenY,
1233                        ul = '',
1234                        languageHint;
1235                    if (!!window.showModalDialog) {
1236                        mainLanguage = window.showModalDialog(basePath + 'modalLangDialog.html', supportedLangs, "dialogWidth: " + dW + "px; dialogHeight: " + dH + "px; dialogtop: " + dY + "; dialogleft: " + dX + "; center: on; resizable: off; scroll: off;");
1237                    } else {
1238                        languageHint = (function () {
1239                            var k, r = '';
1240                            for (k in supportedLangs) {
1241                                if (supportedLangs.hasOwnProperty(k)) {
1242                                    r += k + ', ';
1243                                }
1244                            }
1245                            r = r.substring(0, r.length - 2);
1246                            return r;
1247                        }());
1248                        ul = window.navigator.language || window.navigator.userLanguage;
1249                        ul = ul.substring(0, 2);
1250                        if (!!supportedLangs[ul] && supportedLangs[ul].prompt !== '') {
1251                            text = supportedLangs[ul].prompt;
1252                        } else {
1253                            text = supportedLangs.en.prompt;
1254                        }
1255                        text += ' (ISO 639-1)\n\n' + languageHint;
1256                        mainLanguage = window.prompt(window.unescape(text), ul).toLowerCase();
1257                    }
1258                    return mainLanguage;
1259                };
1260            mainLanguage = getLang(el, false);
1261            if (!mainLanguage) {
1262                for (i = 0; i < m.length; i += 1) {
1263                    //<meta http-equiv = "content-language" content="xy">
1264                    if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) {
1265                        mainLanguage = m[i].getAttribute('content').toLowerCase();
1266                    }
1267                    //<meta name = "DC.Language" content="xy">
1268                    if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) {
1269                        mainLanguage = m[i].getAttribute('content').toLowerCase();
1270                    }
1271                    //<meta name = "language" content = "xy">
1272                    if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) {
1273                        mainLanguage = m[i].getAttribute('content').toLowerCase();
1274                    }
1275                }
1276            }
1277            //get lang for frame from enclosing document
1278            if (!mainLanguage && doFrames && (!!contextWindow.frameElement)) {
1279                autoSetMainLanguage(window.parent);
1280            }
1281            //fallback to defaultLang if set
1282            if (!mainLanguage && defaultLanguage !== '') {
1283                mainLanguage = defaultLanguage;
1284            }
1285            //ask user for lang
1286            if (!mainLanguage) {
1287                mainLanguage = getLangFromUser();
1288            }
1289            el.lang = mainLanguage;
1290        },
1291
1292        /**
1293         * @name Hyphenator-gatherDocumentInfos
1294         * @description
1295         * This method runs through the DOM and executes the process()-function on:
1296         * - every node returned by the {@link Hyphenator-selectorFunction}.
1297         * The process()-function copies the element to the elements-variable, sets its visibility
1298         * to intermediateState, retrieves its language and recursivly descends the DOM-tree until
1299         * the child-Nodes aren't of type 1
1300         * @private
1301         */
1302        gatherDocumentInfos = function () {
1303            var elToProcess, tmp, i = 0,
1304                process = function (el, lang) {
1305                    var n, i = 0, hyphenate = true;
1306
1307                    if (el.lang && typeof (el.lang) === 'string') {
1308                        lang = el.lang.toLowerCase(); //copy attribute-lang to internal lang
1309                    } else if (lang) {
1310                        lang = lang.toLowerCase();
1311                    } else {
1312                        lang = getLang(el, true);
1313                    }
1314                    //if css3-hyphenation is supported: use it!
1315                    if (css3 && css3_h9n.support && !!css3_h9n.checkLangSupport(lang)) {
1316                        css3hyphenateClassHandle =  new CSSEdit(contextWindow);
1317                        css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': auto;');
1318                        css3hyphenateClassHandle.setRule('.' + dontHyphenateClass, css3_h9n.property + ': none;');
1319                        css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, '-webkit-locale : ' + lang + ';');
1320
1321                        el.className = el.className + ' ' + css3hyphenateClass;
1322                    } else {
1323                        if (supportedLangs.hasOwnProperty(lang)) {
1324                            docLanguages[lang] = true;
1325                        } else {
1326                            if (supportedLangs.hasOwnProperty(lang.split('-')[0])) { //try subtag
1327                                lang = lang.split('-')[0];
1328                                docLanguages[lang] = true;
1329                            } else if (!isBookmarklet) {
1330                                hyphenate = false;
1331                                onError(new Error('Language "' + lang + '" is not yet supported.'));
1332                            }
1333                        }
1334                        if (hyphenate) {
1335                            if (intermediateState === 'hidden') {
1336                                el.className = el.className + ' ' + hideClass;
1337                            }
1338                            elements.add(el, lang);
1339                        }
1340                    }
1341                    n = el.childNodes[i];
1342                    while (!!n) {
1343                        if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
1344                                n.className.indexOf(dontHyphenateClass) === -1 && !elToProcess[n]) {
1345                            process(n, lang);
1346                        }
1347                        i += 1;
1348                        n = el.childNodes[i];
1349                    }
1350                };
1351            if (css3) {
1352                css3_gethsupport();
1353            }
1354            if (isBookmarklet) {
1355                elToProcess = contextWindow.document.getElementsByTagName('body')[0];
1356                process(elToProcess, mainLanguage);
1357            } else {
1358                if (!css3 && intermediateState === 'hidden') {
1359                    CSSEditors.push(new CSSEdit(contextWindow));
1360                    CSSEditors[CSSEditors.length - 1].setRule('.' + hyphenateClass, 'visibility: hidden;');
1361                    CSSEditors[CSSEditors.length - 1].setRule('.' + hideClass, 'visibility: hidden;');
1362                    CSSEditors[CSSEditors.length - 1].setRule('.' + unhideClass, 'visibility: visible;');
1363                }
1364                elToProcess = selectElements();
1365                tmp = elToProcess[i];
1366                while (!!tmp) {
1367                    process(tmp, '');
1368                    i += 1;
1369                    tmp = elToProcess[i];
1370                }
1371            }
1372            if (elements.count === 0) {
1373                //nothing to hyphenate or all hyphenated by css3
1374                for (i = 0; i < CSSEditors.length; i += 1) {
1375                    CSSEditors[i].clearChanges();
1376                }
1377                state = 3;
1378                onHyphenationDone();
1379            }
1380        },
1381
1382        /**
1383         * @name Hyphenator-createTrie
1384         * @description
1385         * converts patterns of the given language in a trie
1386         * @private
1387         * @param {string} lang the language whose patterns shall be converted
1388         */
1389        convertPatterns = function (lang) {
1390            /** @license BSD licenced code
1391             * The following code is based on code from hypher.js and adapted for Hyphenator.js
1392             * Copyright (c) 2011, Bram Stein
1393             */
1394            var size = 0,
1395                tree = {
1396                    tpoints: []
1397                },
1398                patterns,
1399                pattern,
1400                i,
1401                j,
1402                k,
1403                patternObject = Hyphenator.languages[lang].patterns,
1404                c,
1405                chars,
1406                points,
1407                t,
1408                p,
1409                codePoint,
1410                test = 'in3se',
1411                rf,
1412                getPoints = (function () {
1413                    //IE<9 doesn't act like other browsers: doesn't preserve the separators
1414                    if (test.split(/\D/).length === 1) {
1415                        rf = function (pattern) {
1416                            pattern = pattern.replace(/\D/gi, ' ');
1417                            return pattern.split(' ');
1418                        };
1419                    } else {
1420                        rf = function (pattern) {
1421                            return pattern.split(/\D/);
1422                        };
1423                    }
1424                    return rf;
1425                }());
1426
1427            for (size in patternObject) {
1428                if (patternObject.hasOwnProperty(size)) {
1429                    patterns = patternObject[size].match(new RegExp('.{1,' + (+size) + '}', 'g'));
1430                    i = 0;
1431                    pattern = patterns[i];
1432                    while (!!pattern) {
1433                        chars = pattern.replace(/[\d]/g, '').split('');
1434                        points = getPoints(pattern);
1435                        t = tree;
1436
1437                        j = 0;
1438                        c = chars[j];
1439                        while (!!c) {
1440                            codePoint = c.charCodeAt(0);
1441
1442                            if (!t[codePoint]) {
1443                                t[codePoint] = {};
1444                            }
1445                            t = t[codePoint];
1446                            j += 1;
1447                            c = chars[j];
1448                        }
1449
1450                        t.tpoints = [];
1451                        for (k = 0; k < points.length; k += 1) {
1452                            p = points[k];
1453                            t.tpoints.push((p === "") ? 0 : p);
1454                        }
1455                        i += 1;
1456                        pattern = patterns[i];
1457                    }
1458                }
1459            }
1460            Hyphenator.languages[lang].patterns = tree;
1461            /**
1462             * end of BSD licenced code from hypher.js
1463             */
1464        },
1465
1466        /**
1467         * @name Hyphenator-recreatePattern
1468         * @description
1469         * Recreates the pattern for the reducedPatternSet
1470         * @private
1471         */
1472        recreatePattern = function (pattern, nodePoints) {
1473            var r = [], c = pattern.split(''), i;
1474            for (i = 0; i < nodePoints.length; i += 1) {
1475                if (nodePoints[i] !== 0) {
1476                    r.push(nodePoints[i]);
1477                }
1478                if (c[i]) {
1479                    r.push(c[i]);
1480                }
1481            }
1482            return r.join('');
1483        },
1484
1485        /**
1486         * @name Hyphenator-convertExceptionsToObject
1487         * @description
1488         * Converts a list of comma seprated exceptions to an object:
1489         * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
1490         * @private
1491         * @param {string} exc a comma separated string of exceptions (without spaces)
1492         */
1493        convertExceptionsToObject = function (exc) {
1494            var w = exc.split(', '),
1495                r = {},
1496                i,
1497                l,
1498                key;
1499            for (i = 0, l = w.length; i < l; i += 1) {
1500                key = w[i].replace(/-/g, '');
1501                if (!r.hasOwnProperty(key)) {
1502                    r[key] = w[i];
1503                }
1504            }
1505            return r;
1506        },
1507
1508        /**
1509         * @name Hyphenator-loadPatterns
1510         * @description
1511         * Checks if the requested file is available in the network.
1512         * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
1513         * If the given language is not in the {@link Hyphenator-supportedLangs}-Object it returns.
1514         * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object 
1515         * has a same-origin-policy. This makes the Bookmarklet impossible.
1516         * @param {string} lang The language to load the patterns for
1517         * @private
1518         * @see Hyphenator-basePath
1519         */
1520        loadPatterns = function (lang) {
1521            var url, xhr, head, script;
1522            if (supportedLangs.hasOwnProperty(lang) && !Hyphenator.languages[lang]) {
1523                url = basePath + 'patterns/' + supportedLangs[lang].file;
1524            } else {
1525                return;
1526            }
1527            if (isLocal && !isBookmarklet) {
1528                //check if 'url' is available:
1529                xhr = null;
1530                try {
1531                    // Mozilla, Opera, Safari and Internet Explorer (ab v7)
1532                    xhr = new window.XMLHttpRequest();
1533                } catch (e) {
1534                    try {
1535                        //IE>=6
1536                        xhr  = new window.ActiveXObject("Microsoft.XMLHTTP");
1537                    } catch (e2) {
1538                        try {
1539                            //IE>=5
1540                            xhr  = new window.ActiveXObject("Msxml2.XMLHTTP");
1541                        } catch (e3) {
1542                            xhr  = null;
1543                        }
1544                    }
1545                }
1546
1547                if (xhr) {
1548                    xhr.open('HEAD', url, true);
1549                    xhr.setRequestHeader('Cache-Control', 'no-cache');
1550                    xhr.onreadystatechange = function () {
1551                        if (xhr.readyState === 4) {
1552                            if (xhr.status === 404) {
1553                                onError(new Error('Could not load\n' + url));
1554                                delete docLanguages[lang];
1555                                return;
1556                            }
1557                        }
1558                    };
1559                    xhr.send(null);
1560                }
1561            }
1562            if (createElem) {
1563                head = window.document.getElementsByTagName('head').item(0);
1564                script = createElem('script', window);
1565                script.src = url;
1566                script.type = 'text/javascript';
1567                head.appendChild(script);
1568            }
1569        },
1570
1571        /**
1572         * @name Hyphenator-prepareLanguagesObj
1573         * @description
1574         * Adds a cache to each language and converts the exceptions-list to an object.
1575         * If storage is active the object is stored there.
1576         * @private
1577         * @param {string} lang the language ob the lang-obj
1578         */
1579        prepareLanguagesObj = function (lang) {
1580            var lo = Hyphenator.languages[lang], wrd;
1581            if (!lo.prepared) {
1582                if (enableCache) {
1583                    lo.cache = {};
1584                    //Export
1585                    //lo['cache'] = lo.cache;
1586                }
1587                if (enableReducedPatternSet) {
1588                    lo.redPatSet = {};
1589                }
1590                //add exceptions from the pattern file to the local 'exceptions'-obj
1591                if (lo.hasOwnProperty('exceptions')) {
1592                    Hyphenator.addExceptions(lang, lo.exceptions);
1593                    delete lo.exceptions;
1594                }
1595                //copy global exceptions to the language specific exceptions
1596                if (exceptions.hasOwnProperty('global')) {
1597                    if (exceptions.hasOwnProperty(lang)) {
1598                        exceptions[lang] += ', ' + exceptions.global;
1599                    } else {
1600                        exceptions[lang] = exceptions.global;
1601                    }
1602                }
1603                //move exceptions from the the local 'exceptions'-obj to the 'language'-object
1604                if (exceptions.hasOwnProperty(lang)) {
1605                    lo.exceptions = convertExceptionsToObject(exceptions[lang]);
1606                    delete exceptions[lang];
1607                } else {
1608                    lo.exceptions = {};
1609                }
1610                convertPatterns(lang);
1611                wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
1612                lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
1613                lo.prepared = true;
1614            }
1615            if (!!storage) {
1616                try {
1617                    storage.setItem(lang, window.JSON.stringify(lo));
1618                } catch (e) {
1619                    onError(e);
1620                }
1621            }
1622
1623        },
1624
1625        /**
1626         * @name Hyphenator-prepare
1627         * @description
1628         * This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
1629         * that the patternfiles are loaded, all conversions are made and the callback is called.
1630         * If storage is active the object is retrieved there.
1631         * If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
1632         * by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
1633         * converted to their object style and the lang-object extended.
1634         * Finally the callback is called.
1635         * @private
1636         */
1637        prepare = function (callback) {
1638            var lang, interval, tmp1, tmp2;
1639            if (!enableRemoteLoading) {
1640                for (lang in Hyphenator.languages) {
1641                    if (Hyphenator.languages.hasOwnProperty(lang)) {
1642                        prepareLanguagesObj(lang);
1643                    }
1644                }
1645                state = 2;
1646                callback('*');
1647                return;
1648            }
1649            // get all languages that are used and preload the patterns
1650            state = 1;
1651            for (lang in docLanguages) {
1652                if (docLanguages.hasOwnProperty(lang)) {
1653                    if (!!storage && storage.test(lang)) {
1654                        Hyphenator.languages[lang] = window.JSON.parse(storage.getItem(lang));
1655                        if (exceptions.hasOwnProperty('global')) {
1656                            tmp1 = convertExceptionsToObject(exceptions.global);
1657                            for (tmp2 in tmp1) {
1658                                if (tmp1.hasOwnProperty(tmp2)) {
1659                                    Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1660                                }
1661                            }
1662                        }
1663                        //Replace exceptions since they may have been changed:
1664                        if (exceptions.hasOwnProperty(lang)) {
1665                            tmp1 = convertExceptionsToObject(exceptions[lang]);
1666                            for (tmp2 in tmp1) {
1667                                if (tmp1.hasOwnProperty(tmp2)) {
1668                                    Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1669                                }
1670                            }
1671                            delete exceptions[lang];
1672                        }
1673                        //Replace genRegExp since it may have been changed:
1674                        tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
1675                        Hyphenator.languages[lang].genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + tmp1 + ')', 'gi');
1676
1677                        delete docLanguages[lang];
1678                        callback(lang);
1679                    } else {
1680                        loadPatterns(lang);
1681                    }
1682                }
1683            }
1684            // else async wait until patterns are loaded, then hyphenate
1685            interval = window.setInterval(function () {
1686                var finishedLoading = true, lang;
1687                for (lang in docLanguages) {
1688                    if (docLanguages.hasOwnProperty(lang)) {
1689                        finishedLoading = false;
1690                        if (!!Hyphenator.languages[lang]) {
1691                            delete docLanguages[lang];
1692                            //do conversion while other patterns are loading:
1693                            prepareLanguagesObj(lang);
1694                            callback(lang);
1695                        }
1696                    }
1697                }
1698                if (finishedLoading) {
1699                    window.clearInterval(interval);
1700                    state = 2;
1701                }
1702            }, 100);
1703        },
1704
1705        /**
1706         * @name Hyphenator-switchToggleBox
1707         * @description
1708         * Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
1709         * @see Hyphenator.config
1710         * @private
1711         */
1712        toggleBox = function () {
1713            var bdy, myTextNode,
1714                text = (Hyphenator.doHyphenation ? 'Hy-phen-a-tion' : 'Hyphenation'),
1715                myBox = contextWindow.document.getElementById('HyphenatorToggleBox');
1716            if (!!myBox) {
1717                myBox.firstChild.data = text;
1718            } else {
1719                bdy = contextWindow.document.getElementsByTagName('body')[0];
1720                myBox = createElem('div', contextWindow);
1721                myBox.setAttribute('id', 'HyphenatorToggleBox');
1722                myBox.setAttribute('class', dontHyphenateClass);
1723                myTextNode = contextWindow.document.createTextNode(text);
1724                myBox.appendChild(myTextNode);
1725                myBox.onclick =  Hyphenator.toggleHyphenation;
1726                myBox.style.position = 'absolute';
1727                myBox.style.top = '0px';
1728                myBox.style.right = '0px';
1729                myBox.style.margin = '0';
1730                myBox.style.backgroundColor = '#AAAAAA';
1731                myBox.style.color = '#FFFFFF';
1732                myBox.style.font = '6pt Arial';
1733                myBox.style.letterSpacing = '0.2em';
1734                myBox.style.padding = '3px';
1735                myBox.style.cursor = 'pointer';
1736                myBox.style.WebkitBorderBottomLeftRadius = '4px';
1737                myBox.style.MozBorderRadiusBottomleft = '4px';
1738                myBox.style.borderBottomLeftRadius = '4px';
1739                bdy.appendChild(myBox);
1740            }
1741        },
1742
1743
1744        /**
1745         * @name Hyphenator-hyphenateWord
1746         * @description
1747         * This function is the heart of Hyphenator.js. It returns a hyphenated word.
1748         *
1749         * If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
1750         * If the word is in the exceptions list or in the cache, it is retrieved from it.
1751         * If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
1752         * @param {string} lang The language of the word
1753         * @param {string} word The word
1754         * @returns string The hyphenated word
1755         * @public
1756         */
1757        hyphenateWord = function (lang, word) {
1758            var lo = Hyphenator.languages[lang], parts, l, subst,
1759                w, characters, origWord, originalCharacters, wordLength, i, j, k, node, points = [],
1760                characterPoints = [], nodePoints, nodePointsLength, m = Math.max, trie,
1761                result = [''], pattern, r;
1762            word = onBeforeWordHyphenation(word, lang);
1763            if (word === '') {
1764                r = '';
1765            } else if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
1766                r = lo.cache[word];
1767            } else if (word.indexOf(hyphen) !== -1) {
1768                //word already contains shy; -> leave at it is!
1769                r = word;
1770            } else if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
1771                r = lo.exceptions[word].replace(/-/g, hyphen);
1772            } else if (word.indexOf('-') !== -1) {
1773                //word contains '-' -> hyphenate the parts separated with '-'
1774                parts = word.split('-');
1775                for (i = 0, l = parts.length; i < l; i += 1) {
1776                    parts[i] = hyphenateWord(lang, parts[i]);
1777                }
1778                r = parts.join('-');
1779            } else {
1780                origWord = word;
1781                w = word = '_' + word + '_';
1782                if (!!lo.charSubstitution) {
1783                    for (subst in lo.charSubstitution) {
1784                        if (lo.charSubstitution.hasOwnProperty(subst)) {
1785                            w = w.replace(new RegExp(subst, 'g'), lo.charSubstitution[subst]);
1786                        }
1787                    }
1788                }
1789                if (origWord.indexOf("'") !== -1) {
1790                    w = w.replace("'", "’"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
1791                }
1792                /** @license BSD licenced code
1793                 * The following code is based on code from hypher.js
1794                 * Copyright (c) 2011, Bram Stein
1795                 */
1796                characters = w.toLowerCase().split('');
1797                originalCharacters = word.split('');
1798                wordLength = characters.length;
1799                trie = lo.patterns;
1800                for (i = 0; i < wordLength; i += 1) {
1801                    points[i] = 0;
1802                    characterPoints[i] = characters[i].charCodeAt(0);
1803                }
1804                for (i = 0; i < wordLength; i += 1) {
1805                    pattern = '';
1806                    node = trie;
1807                    for (j = i; j < wordLength; j += 1) {
1808                        node = node[characterPoints[j]];
1809                        if (node) {
1810                            if (enableReducedPatternSet) {
1811                                pattern += String.fromCharCode(characterPoints[j]);
1812                            }
1813                            nodePoints = node.tpoints;
1814                            if (nodePoints) {
1815                                if (enableReducedPatternSet) {
1816                                    if (!lo.redPatSet) {
1817                                        lo.redPatSet = {};
1818                                    }
1819                                    lo.redPatSet[pattern] = recreatePattern(pattern, nodePoints);
1820                                }
1821                                for (k = 0, nodePointsLength = nodePoints.length; k < nodePointsLength; k += 1) {
1822                                    points[i + k] = m(points[i + k], nodePoints[k]);
1823                                }
1824                            }
1825                        } else {
1826                            break;
1827                        }
1828                    }
1829                }
1830                for (i = 1; i < wordLength - 1; i += 1) {
1831                    if (i > lo.leftmin && i < (wordLength - lo.rightmin) && points[i] % 2) {
1832                        result.push(originalCharacters[i]);
1833                    } else {
1834                        result[result.length - 1] += originalCharacters[i];
1835                    }
1836                }
1837                r = result.join(hyphen);
1838                /**
1839                 * end of BSD licenced code from hypher.js
1840                 */
1841            }
1842            r = onAfterWordHyphenation(r, lang);
1843            if (enableCache) { //put the word in the cache
1844                lo.cache[origWord] = r;
1845            }
1846            return r;
1847        },
1848
1849        /**
1850         * @name Hyphenator-hyphenateURL
1851         * @description
1852         * Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
1853         * @param {string} url to hyphenate
1854         * @returns string the hyphenated URL
1855         * @public
1856         */
1857        hyphenateURL = function (url) {
1858            return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
1859        },
1860
1861        /**
1862         * @name Hyphenator-removeHyphenationFromElement
1863         * @description
1864         * Removes all hyphens from the element. If there are other elements, the function is
1865         * called recursively.
1866         * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
1867         * @param {Object} el The element where to remove hyphenation.
1868         * @public
1869         */
1870        removeHyphenationFromElement = function (el) {
1871            var h, i = 0, n;
1872            switch (hyphen) {
1873            case '|':
1874                h = '\\|';
1875                break;
1876            case '+':
1877                h = '\\+';
1878                break;
1879            case '*':
1880                h = '\\*';
1881                break;
1882            default:
1883                h = hyphen;
1884            }
1885            n = el.childNodes[i];
1886            while (!!n) {
1887                if (n.nodeType === 3) {
1888                    n.data = n.data.replace(new RegExp(h, 'g'), '');
1889                    n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
1890                } else if (n.nodeType === 1) {
1891                    removeHyphenationFromElement(n);
1892                }
1893                i += 1;
1894                n = el.childNodes[i];
1895            }
1896        },
1897
1898        /**
1899         * @name Hyphenator-oncopyHandler
1900         * @description
1901         * The function called by registerOnCopy
1902         * @private
1903         */
1904        oncopyHandler,
1905
1906        /**
1907         * @name Hyphenator-removeOnCopy
1908         * @description
1909         * Method to remove copy event handler from the given element
1910         * @param object a html object from witch we remove the event
1911         * @private
1912         */
1913        removeOnCopy = function (el) {
1914            var body = el.ownerDocument.getElementsByTagName('body')[0];
1915            if (!body) {
1916                return;
1917            }
1918            el = el || body;
1919            if (window.removeEventListener) {
1920                el.removeEventListener("copy", oncopyHandler, true);
1921            } else {
1922                el.detachEvent("oncopy", oncopyHandler);
1923            }
1924        },
1925
1926        /**
1927         * @name Hyphenator-registerOnCopy
1928         * @description
1929         * Huge work-around for browser-inconsistency when it comes to
1930         * copying of hyphenated text.
1931         * The idea behind this code has been provided by http://github.com/aristus/sweet-justice
1932         * sweet-justice is under BSD-License
1933         * @param object an HTML element where the copy event will be registered to
1934         * @private
1935         */
1936        registerOnCopy = function (el) {
1937            var body = el.ownerDocument.getElementsByTagName('body')[0],
1938                shadow,
1939                selection,
1940                range,
1941                rangeShadow,
1942                restore;
1943            oncopyHandler = function (e) {
1944                e = e || window.event;
1945                var target = e.target || e.srcElement,
1946                    currDoc = target.ownerDocument,
1947                    body = currDoc.getElementsByTagName('body')[0],
1948                    targetWindow = currDoc.defaultView || currDoc.parentWindow;
1949                if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
1950                    //Safari needs this
1951                    return;
1952                }
1953                //create a hidden shadow element
1954                shadow = currDoc.createElement('div');
1955                //Moving the element out of the screen doesn't work for IE9 (https://connect.microsoft.com/IE/feedback/details/663981/)
1956                //shadow.style.overflow = 'hidden';
1957                //shadow.style.position = 'absolute';
1958                //shadow.style.top = '-5000px';
1959                //shadow.style.height = '1px';
1960                //doing this instead:
1961                shadow.style.color = window.getComputedStyle ? targetWindow.getComputedStyle(body, null).backgroundColor : '#FFFFFF';
1962                shadow.style.fontSize = '0px';
1963                body.appendChild(shadow);
1964                if (!!window.getSelection) {
1965                    //FF3, Webkit, IE9
1966                    e.stopPropagation();
1967                    selection = targetWindow.getSelection();
1968                    range = selection.getRangeAt(0);
1969                    shadow.appendChild(range.cloneContents());
1970                    removeHyphenationFromElement(shadow);
1971                    selection.selectAllChildren(shadow);
1972                    restore = function () {
1973                        shadow.parentNode.removeChild(shadow);
1974                        selection.removeAllRanges(); //IE9 needs that
1975                        selection.addRange(range);
1976                    };
1977                } else {
1978                    // IE<9
1979                    e.cancelBubble = true;
1980                    selection = targetWindow.document.selection;
1981                    range = selection.createRange();
1982                    shadow.innerHTML = range.htmlText;
1983                    removeHyphenationFromElement(shadow);
1984                    rangeShadow = body.createTextRange();
1985                    rangeShadow.moveToElementText(shadow);
1986                    rangeShadow.select();
1987                    restore = function () {
1988                        shadow.parentNode.removeChild(shadow);
1989                        if (range.text !== "") {
1990                            range.select();
1991                        }
1992                    };
1993                }
1994                window.setTimeout(restore, 0);
1995            };
1996            if (!body) {
1997                return;
1998            }
1999            el = el || body;
2000            if (window.addEventListener) {
2001                el.addEventListener("copy", oncopyHandler, true);
2002            } else {
2003                el.attachEvent("oncopy", oncopyHandler);
2004            }
2005        },
2006
2007        /**
2008         * @name Hyphenator-checkIfAllDone
2009         * @description
2010         * Checks if all Elements are hyphenated, unhides them and fires onHyphenationDone()
2011         * @private
2012         */
2013        checkIfAllDone = function () {
2014            var allDone = true, i;
2015            elements.each(function (ellist) {
2016                var i, l = ellist.length;
2017                for (i = 0; i < l; i += 1) {
2018                    allDone = allDone && ellist[i].hyphenated;
2019                }
2020            });
2021            if (allDone) {
2022                if (intermediateState === 'hidden' && unhide === 'progressive') {
2023                    elements.each(function (ellist) {
2024                        var i, l = ellist.length, el;
2025                        for (i = 0; i < l; i += 1) {
2026                            el = ellist[i].element;
2027                            el.className = el.className.replace(unhideClassRegExp, '');
2028                            if (el.className === '') {
2029                                el.removeAttribute('class');
2030                            }
2031                        }
2032                    });
2033                }
2034                for (i = 0; i < CSSEditors.length; i += 1) {
2035                    CSSEditors[i].clearChanges();
2036                }
2037                state = 3;
2038                onHyphenationDone();
2039            }
2040        },
2041
2042
2043        /**
2044         * @name Hyphenator-hyphenateElement
2045         * @description
2046         * Takes the content of the given element and - if there's text - replaces the words
2047         * by hyphenated words. If there's another element, the function is called recursively.
2048         * When all words are hyphenated, the visibility of the element is set to 'visible'.
2049         * @param {Object} el The element to hyphenate
2050         * @private
2051         */
2052        hyphenateElement = function (lang, elo) {
2053            var el = elo.element,
2054                hyphenate,
2055                n,
2056                i,
2057                r,
2058                controlOrphans = function (part) {
2059                    var h, r;
2060                    switch (hyphen) {
2061                    case '|':
2062                        h = '\\|';
2063                        break;
2064                    case '+':
2065                        h = '\\+';
2066                        break;
2067                    case '*':
2068                        h = '\\*';
2069                        break;
2070                    default:
2071                        h = hyphen;
2072                    }
2073                    if (orphanControl >= 2) {
2074                        //remove hyphen points from last word
2075                        r = part.split(' ');
2076                        r[1] = r[1].replace(new RegExp(h, 'g'), '');
2077                        r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), '');
2078                        r = r.join(' ');
2079                    }
2080                    if (orphanControl === 3) {
2081                        //replace spaces by non breaking spaces
2082                        r = r.replace(/[ ]+/g, String.fromCharCode(160));
2083                    }
2084                    return r;
2085                };
2086            if (Hyphenator.languages.hasOwnProperty(lang)) {
2087                hyphenate = function (word) {
2088                    if (!Hyphenator.doHyphenation) {
2089                        r = word;
2090                    } else if (urlOrMailRE.test(word)) {
2091                        r = hyphenateURL(word);
2092                    } else {
2093                        r = hyphenateWord(lang, word);
2094                    }
2095                    return r;
2096                };
2097                if (safeCopy && (el.tagName.toLowerCase() !== 'body')) {
2098                    registerOnCopy(el);
2099                }
2100                i = 0;
2101                n = el.childNodes[i];
2102                while (!!n) {
2103                    if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
2104                        n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
2105                        if (orphanControl !== 1) {
2106                            n.data = n.data.replace(/[\S]+ [\S]+$/, controlOrphans);
2107                        }
2108                    }
2109                    i += 1;
2110                    n = el.childNodes[i];
2111                }
2112            }
2113            if (intermediateState === 'hidden' && unhide === 'wait') {
2114                el.className = el.className.replace(hideClassRegExp, '');
2115                if (el.className === '') {
2116                    el.removeAttribute('class');
2117                }
2118            }
2119            if (intermediateState === 'hidden' && unhide === 'progressive') {
2120                el.className = el.className.replace(hideClassRegExp, ' ' + unhideClass);
2121            }
2122            elo.hyphenated = true;
2123            elements.hyCount += 1;
2124            if (elements.count <= elements.hyCount) {
2125                checkIfAllDone();
2126            }
2127        },
2128
2129
2130        /**
2131         * @name Hyphenator-hyphenateLanguageElements
2132         * @description
2133         * Calls hyphenateElement() for all elements of the specified language.
2134         * If the language is '*' then all elements are hyphenated.
2135         * This is done with a setTimout
2136         * to prevent a "long running Script"-alert when hyphenating large pages.
2137         * Therefore a tricky bind()-function was necessary.
2138         * @private
2139         */
2140        hyphenateLanguageElements = function (lang) {
2141            function bind(fun, arg1, arg2) {
2142                return function () {
2143                    return fun(arg1, arg2);
2144                };
2145            }
2146            var i, l;
2147            if (lang === '*') {
2148                elements.each(function (lang, ellist) {
2149                    var i, l = ellist.length;
2150                    for (i = 0; i < l; i += 1) {
2151                        window.setTimeout(bind(hyphenateElement, lang, ellist[i]), 0);
2152                    }
2153                });
2154            } else {
2155                if (elements.list.hasOwnProperty(lang)) {
2156                    l = elements.list[lang].length;
2157                    for (i = 0; i < l; i += 1) {
2158                        window.setTimeout(bind(hyphenateElement, lang, elements.list[lang][i]), 0);
2159                    }
2160                }
2161            }
2162        },
2163
2164        /**
2165         * @name Hyphenator-removeHyphenationFromDocument
2166         * @description
2167         * Does what it says ;-)
2168         * @private
2169         */
2170        removeHyphenationFromDocument = function () {
2171            elements.each(function (ellist) {
2172                var i, l = ellist.length;
2173                for (i = 0; i < l; i += 1) {
2174                    removeHyphenationFromElement(ellist[i].element);
2175                    if (safeCopy) {
2176                        removeOnCopy(ellist[i].element);
2177                    }
2178                    ellist[i].hyphenated = false;
2179                }
2180            });
2181            state = 4;
2182        },
2183
2184        /**
2185         * @name Hyphenator-createStorage
2186         * @description
2187         * inits the private var storage depending of the setting in storageType
2188         * and the supported features of the system.
2189         * @private
2190         */
2191        createStorage = function () {
2192            var s;
2193            try {
2194                if (storageType !== 'none' &&
2195                        window.localStorage !== undefined &&
2196                        window.sessionStorage !== undefined &&
2197                        window.JSON.stringify !== undefined &&
2198                        window.JSON.parse !== undefined) {
2199                    switch (storageType) {
2200                    case 'session':
2201                        s = window.sessionStorage;
2202                        break;
2203                    case 'local':
2204                        s = window.localStorage;
2205                        break;
2206                    default:
2207                        s = undefined;
2208                        break;
2209                    }
2210                }
2211            } catch (f) {
2212                //FF throws an error if DOM.storage.enabled is set to false
2213            }
2214            if (s) {
2215                storage = {
2216                    prefix: 'Hyphenator_' + Hyphenator.version + '_',
2217                    store: s,
2218                    test: function (name) {
2219                        var val = this.store.getItem(this.prefix + name);
2220                        return (!!val) ? true : false;
2221                    },
2222                    getItem: function (name) {
2223                        return this.store.getItem(this.prefix + name);
2224                    },
2225                    setItem: function (name, value) {
2226                        this.store.setItem(this.prefix + name, value);
2227                    }
2228                };
2229            } else {
2230                storage = undefined;
2231            }
2232        },
2233
2234        /**
2235         * @name Hyphenator-storeConfiguration
2236         * @description
2237         * Stores the current config-options in DOM-Storage
2238         * @private
2239         */
2240        storeConfiguration = function () {
2241            if (!storage) {
2242                return;
2243            }
2244            var settings = {
2245                'STORED': true,
2246                'classname': hyphenateClass,
2247                'donthyphenateclassname': dontHyphenateClass,
2248                'minwordlength': min,
2249                'hyphenchar': hyphen,
2250                'urlhyphenchar': urlhyphen,
2251                'togglebox': toggleBox,
2252                'displaytogglebox': displayToggleBox,
2253                'remoteloading': enableRemoteLoading,
2254                'enablecache': enableCache,
2255                'onhyphenationdonecallback': onHyphenationDone,
2256                'onerrorhandler': onError,
2257                'intermediatestate': intermediateState,
2258                'selectorfunction': selectorFunction || mySelectorFunction,
2259                'safecopy': safeCopy,
2260                'doframes': doFrames,
2261                'storagetype': storageType,
2262                'orphancontrol': orphanControl,
2263                'dohyphenation': Hyphenator.doHyphenation,
2264                'persistentconfig': persistentConfig,
2265                'defaultlanguage': defaultLanguage,
2266                'useCSS3hyphenation': css3,
2267                'unhide': unhide,
2268                'onbeforewordhyphenation': onBeforeWordHyphenation,
2269                'onafterwordhyphenation': onAfterWordHyphenation
2270            };
2271            storage.setItem('config', window.JSON.stringify(settings));
2272        },
2273
2274        /**
2275         * @name Hyphenator-restoreConfiguration
2276         * @description
2277         * Retrieves config-options from DOM-Storage and does configuration accordingly
2278         * @private
2279         */
2280        restoreConfiguration = function () {
2281            var settings;
2282            if (storage.test('config')) {
2283                settings = window.JSON.parse(storage.getItem('config'));
2284                Hyphenator.config(settings);
2285            }
2286        };
2287
2288    return {
2289
2290        /**
2291         * @name Hyphenator.version
2292         * @memberOf Hyphenator
2293         * @description
2294         * String containing the actual version of Hyphenator.js
2295         * [major release].[minor releas].[bugfix release]
2296         * major release: new API, new Features, big changes
2297         * minor release: new languages, improvements
2298         * @public
2299         */
2300        version: '4.2.0',
2301
2302        /**
2303         * @name Hyphenator.doHyphenation
2304         * @description
2305         * If doHyphenation is set to false (defaults to true), hyphenateDocument() isn't called.
2306         * All other actions are performed.
2307         */
2308        doHyphenation: true,
2309
2310        /**
2311         * @name Hyphenator.languages
2312         * @memberOf Hyphenator
2313         * @description
2314         * Objects that holds key-value pairs, where key is the language and the value is the
2315         * language-object loaded from (and set by) the pattern file.
2316         * The language object holds the following members:
2317         * <table>
2318         * <tr><th>key</th><th>desc></th></tr>
2319         * <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
2320         * <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
2321         * <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
2322         * <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
2323         * <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
2324         * <tr><td>patterns</td><td>the patterns</td></tr>
2325         * </table>
2326         * And optionally (or after prepareLanguagesObj() has been called):
2327         * <table>
2328         * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
2329         * </table>
2330         * @public
2331         */
2332        languages: {},
2333
2334
2335        /**
2336         * @name Hyphenator.config
2337             * @description
2338         * Config function that takes an object as an argument. The object contains key-value-pairs
2339         * containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
2340         * @param {Object} obj <table>
2341         * <tr><th>key</th><th>values</th><th>default</th></tr>
2342         * <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
2343         * <tr><td>donthyphenateclassname</td><td>string</td><td>''</td></tr>
2344         * <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
2345         * <tr><td>
2345hyphenchar</td><td>string</td><td>'&amp;shy;'</td></tr>
2346         * <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
2347         * <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
2348         * <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
2349         * <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
2350         * <tr><td>enablecache</td><td>boolean</td><td>true</td></tr>
2351         * <tr><td>enablereducedpatternset</td><td>boolean</td><td>false</td></tr>
2352         * <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
2353         * <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
2354         * <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
2355         * <tr><td>selectorfunction</td><td>function</td><td>[…]</td></tr>
2356         * <tr><td>safecopy</td><td>boolean</td><td>true</td></tr>
2357         * <tr><td>doframes</td><td>boolean</td><td>false</td></tr>
2358         * <tr><td>storagetype</td><td>string</td><td>'none'</td></tr>
2359         * </table>
2360         * @public
2361         * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2362         * &lt;script type = "text/javascript"&gt;
2363         *     Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
2364         *     Hyphenator.run();
2365         * &lt;/script&gt;
2366         */
2367        config: function (obj) {
2368            var assert = function (name, type) {
2369                    var r, t;
2370                    t = typeof obj[name];
2371                    if (t === type) {
2372                        r = true;
2373                    } else {
2374                        onError(new Error('Config onError: ' + name + ' must be of type ' + type));
2375                        r = false;
2376                    }
2377                    return r;
2378                },
2379                key;
2380
2381            if (obj.hasOwnProperty('storagetype')) {
2382                if (assert('storagetype', 'string')) {
2383                    storageType = obj.storagetype;
2384                }
2385                if (!storage) {
2386                    createStorage();
2387                }
2388            }
2389            if (!obj.hasOwnProperty('STORED') && storage && obj.hasOwnProperty('persistentconfig') && obj.persistentconfig === true) {
2390                restoreConfiguration();
2391            }
2392
2393            for (key in obj) {
2394                if (obj.hasOwnProperty(key)) {
2395                    switch (key) {
2396                    case 'STORED':
2397                        break;
2398                    case 'classname':
2399                        if (assert('classname', 'string')) {
2400                            hyphenateClass = obj[key];
2401                        }
2402                        break;
2403                    case 'donthyphenateclassname':
2404                        if (assert('donthyphenateclassname', 'string')) {
2405                            dontHyphenateClass = obj[key];
2406                        }
2407                        break;
2408                    case 'minwordlength':
2409                        if (assert('minwordlength', 'number')) {
2410                            min = obj[key];
2411                        }
2412                        break;
2413                    case 'hyphenchar':
2414                        if (assert('hyphenchar', 'string')) {
2415                            if (obj.hyphenchar === '&shy;') {
2416                                obj.hyphenchar = String.fromCharCode(173);
2417                            }
2418                            hyphen = obj[key];
2419                        }
2420                        break;
2421                    case 'urlhyphenchar':
2422                        if (obj.hasOwnProperty('urlhyphenchar')) {
2423                            if (assert('urlhyphenchar', 'string')) {
2424                                urlhyphen = obj[key];
2425                            }
2426                        }
2427                        break;
2428                    case 'togglebox':
2429                        if (assert('togglebox', 'function')) {
2430                            toggleBox = obj[key];
2431                        }
2432                        break;
2433                    case 'displaytogglebox':
2434                        if (assert('displaytogglebox', 'boolean')) {
2435                            displayToggleBox = obj[key];
2436                        }
2437                        break;
2438                    case 'remoteloading':
2439                        if (assert('remoteloading', 'boolean')) {
2440                            enableRemoteLoading = obj[key];
2441                        }
2442                        break;
2443                    case 'enablecache':
2444                        if (assert('enablecache', 'boolean')) {
2445                            enableCache = obj[key];
2446                        }
2447                        break;
2448                    case 'enablereducedpatternset':
2449                        if (assert('enablereducedpatternset', 'boolean')) {
2450                            enableReducedPatternSet = obj[key];
2451                        }
2452                        break;
2453                    case 'onhyphenationdonecallback':
2454                        if (assert('onhyphenationdonecallback', 'function')) {
2455                            onHyphenationDone = obj[key];
2456                        }
2457                        break;
2458                    case 'onerrorhandler':
2459                        if (assert('onerrorhandler', 'function')) {
2460                            onError = obj[key];
2461                        }
2462                        break;
2463                    case 'intermediatestate':
2464                        if (assert('intermediatestate', 'string')) {
2465                            intermediateState = obj[key];
2466                        }
2467                        break;
2468                    case 'selectorfunction':
2469                        if (assert('selectorfunction', 'function')) {
2470                            selectorFunction = obj[key];
2471                        }
2472                        break;
2473                    case 'safecopy':
2474                        if (assert('safecopy', 'boolean')) {
2475                            safeCopy = obj[key];
2476                        }
2477                        break;
2478                    case 'doframes':
2479                        if (assert('doframes', 'boolean')) {
2480                            doFrames = obj[key];
2481                        }
2482                        break;
2483                    case 'storagetype':
2484                        if (assert('storagetype', 'string')) {
2485                            storageType = obj[key];
2486                        }
2487                        break;
2488                    case 'orphancontrol':
2489                        if (assert('orphancontrol', 'number')) {
2490                            orphanControl = obj[key];
2491                        }
2492                        break;
2493                    case 'dohyphenation':
2494                        if (assert('dohyphenation', 'boolean')) {
2495                            Hyphenator.doHyphenation = obj[key];
2496                        }
2497                        break;
2498                    case 'persistentconfig':
2499                        if (assert('persistentconfig', 'boolean')) {
2500                            persistentConfig = obj[key];
2501                        }
2502                        break;
2503                    case 'defaultlanguage':
2504                        if (assert('defaultlanguage', 'string')) {
2505                            defaultLanguage = obj[key];
2506                        }
2507                        break;
2508                    case 'useCSS3hyphenation':
2509                        if (assert('useCSS3hyphenation', 'boolean')) {
2510                            css3 = obj[key];
2511                        }
2512                        break;
2513                    case 'unhide':
2514                        if (assert('unhide', 'string')) {
2515                            unhide = obj[key];
2516                        }
2517                        break;
2518                    case 'onbeforewordhyphenation':
2519                        if (assert('onbeforewordhyphenation', 'function')) {
2520                            onBeforeWordHyphenation = obj[key];
2521                        }
2522                        break;
2523                    case 'onafterwordhyphenation':
2524                        if (assert('onafterwordhyphenation', 'function')) {
2525                            onAfterWordHyphenation = obj[key];
2526                        }
2527                        break;
2528                    default:
2529                        onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
2530                    }
2531                }
2532            }
2533            if (storage && persistentConfig) {
2534                storeConfiguration();
2535            }
2536        },
2537
2538        /**
2539         * @name Hyphenator.run
2540             * @description
2541         * Bootstrap function that starts all hyphenation processes when called.
2542         * @public
2543         * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2544         * &lt;script type = "text/javascript"&gt;
2545         *   Hyphenator.run();
2546         * &lt;/script&gt;
2547         */
2548        run: function () {
2549            var process = function () {
2550                try {
2551                    if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
2552                        return; //we are in a frameset
2553                    }
2554                    autoSetMainLanguage(undefined);
2555                    gatherDocumentInfos();
2556                    prepare(hyphenateLanguageElements);
2557                    if (displayToggleBox) {
2558                        toggleBox();
2559                    }
2560                } catch (e) {
2561                    onError(e);
2562                }
2563            }, i, haveAccess, fl = window.frames.length;
2564
2565            if (!storage) {
2566                createStorage();
2567            }
2568            if (!documentLoaded && !isBookmarklet) {
2569                runOnContentLoaded(window, process);
2570            }
2571            if (isBookmarklet || documentLoaded) {
2572                if (doFrames && fl > 0) {
2573                    for (i = 0; i < fl; i += 1) {
2574                        haveAccess = undefined;
2575                        //try catch isn't enough for webkit
2576                        try {
2577                            //opera throws only on document.toString-access
2578                            haveAccess = window.frames[i].document.toString();
2579                        } catch (e) {
2580                            haveAccess = undefined;
2581                        }
2582                        if (!!haveAccess) {
2583                            contextWindow = window.frames[i];
2584                            process();
2585                        }
2586                    }
2587                }
2588                contextWindow = window;
2589                process();
2590            }
2591        },
2592
2593        /**
2594         * @name Hyphenator.addExceptions
2595             * @description
2596         * Adds the exceptions from the string to the appropriate language in the 
2597         * {@link Hyphenator-languages}-object
2598         * @param {string} lang The language
2599         * @param {string} words A comma separated string of hyphenated words WITH spaces.
2600         * @public
2601         * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2602         * &lt;script type = "text/javascript"&gt;
2603         *   Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
2604         *   Hyphenator.run();
2605         * &lt;/script&gt;
2606         */
2607        addExceptions: function (lang, words) {
2608            if (lang === '') {
2609                lang = 'global';
2610            }
2611            if (exceptions.hasOwnProperty(lang)) {
2612                exceptions[lang] += ", " + words;
2613            } else {
2614                exceptions[lang] = words;
2615            }
2616        },
2617
2618        /**
2619         * @name Hyphenator.hyphenate
2620             * @public
2621         * @description
2622         * Hyphenates the target. The language patterns must be loaded.
2623         * If the target is a string, the hyphenated string is returned,
2624         * if it's an object, the values are hyphenated directly.
2625         * @param {string|Object} target the target to be hyphenated
2626         * @param {string} lang the language of the target
2627         * @returns string
2628         * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2629         * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
2630         * &lt;script type = "text/javascript"&gt;
2631         * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
2632         * &lt;/script&gt;
2633         */
2634        hyphenate: function (target, lang) {
2635            var hyphenate, n, i;
2636            if (Hyphenator.languages.hasOwnProperty(lang)) {
2637                if (!Hyphenator.languages[lang].prepared) {
2638                    prepareLanguagesObj(lang);
2639                }
2640                hyphenate = function (word) {
2641                    var r;
2642                    if (urlOrMailRE.test(word)) {
2643                        r = hyphenateURL(word);
2644                    } else {
2645                        r = hyphenateWord(lang, word);
2646                    }
2647                    return r;
2648                };
2649                if (typeof target === 'object' && !(typeof target === 'string' || target.constructor === String)) {
2650                    i = 0;
2651                    n = target.childNodes[i];
2652                    while (!!n) {
2653                        if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
2654                            n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
2655                        } else if (n.nodeType === 1) {
2656                            if (n.lang !== '') {
2657                                Hyphenator.hyphenate(n, n.lang);
2658                            } else {
2659                                Hyphenator.hyphenate(n, lang);
2660                            }
2661                        }
2662                        i += 1;
2663                        n = target.childNodes[i];
2664                    }
2665                } else if (typeof target === 'string' || target.constructor === String) {
2666                    return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
2667                }
2668            } else {
2669                onError(new Error('Language "' + lang + '" is not loaded.'));
2670            }
2671        },
2672
2673        /**
2674         * @name Hyphenator.getRedPatternSet
2675             * @description
2676         * Returns {@link Hyphenator-isBookmarklet}.
2677         * @param {string} lang the language patterns are stored for
2678         * @returns object {'patk': pat}
2679         * @public
2680         */
2681        getRedPatternSet: function (lang) {
2682            return Hyphenator.languages[lang].redPatSet;
2683        },
2684
2685        /**
2686         * @name Hyphenator.isBookmarklet
2687             * @description
2688         * Returns {@link Hyphenator-isBookmarklet}.
2689         * @returns boolean
2690         * @public
2691         */
2692        isBookmarklet: function () {
2693            return isBookmarklet;
2694        },
2695
2696        getConfigFromURI: function () {
2697            /*jslint evil: true*/
2698            var loc = null, re = {}, jsArray = contextWindow.document.getElementsByTagName('script'), i, j, l, s, gp, option;
2699            for (i = 0, l = jsArray.length; i < l; i += 1) {
2700                if (!!jsArray[i].getAttribute('src')) {
2701                    loc = jsArray[i].getAttribute('src');
2702                }
2703                if (loc && (loc.indexOf('Hyphenator.js?') !== -1)) {
2704                    s = loc.indexOf('Hyphenator.js?');
2705                    gp = loc.substring(s + 14).split('&');
2706                    for (j = 0; j < gp.length; j += 1) {
2707                        option = gp[j].split('=');
2708                        if (option[0] !== 'bm') {
2709                            if (option[1] === 'true') {
2710                                option[1] = true;
2711                            } else if (option[1] === 'false') {
2712                                option[1] = false;
2713                            } else if (isFinite(option[1])) {
2714                                option[1] = parseInt(option[1], 10);
2715                            }
2716                            if (option[0] === 'onhyphenationdonecallback') {
2717                                option[1] = new Function('', option[1]);
2718                            }
2719                            re[option[0]] = option[1];
2720                        }
2721                    }
2722                    break;
2723                }
2724            }
2725            return re;
2726        },
2727
2728        /**
2729         * @name Hyphenator.toggleHyphenation
2730             * @description
2731         * Checks the current state of the ToggleBox and removes or does hyphenation.
2732         * @public
2733         */
2734        toggleHyphenation: function () {
2735            if (Hyphenator.doHyphenation) {
2736                if (!!css3hyphenateClassHandle) {
2737                    css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': none;');
2738                }
2739                removeHyphenationFromDocument();
2740                Hyphenator.doHyphenation = false;
2741                storeConfiguration();
2742                toggleBox();
2743            } else {
2744                if (!!css3hyphenateClassHandle) {
2745                    css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': auto;');
2746                }
2747                hyphenateLanguageElements('*');
2748                Hyphenator.doHyphenation = true;
2749                storeConfiguration();
2750                toggleBox();
2751            }
2752        }
2753    };
2754}(window));
2755//Export properties/methods (for google closure compiler)
2756/* to be moved to external file
2757Hyphenator['languages'] = Hyphenator.languages;
2758Hyphenator['config'] = Hyphenator.config;
2759Hyphenator['run'] = Hyphenator.run;
2760Hyphenator['addExceptions'] = Hyphenator.addExceptions;
2761Hyphenator['hyphenate'] = Hyphenator.hyphenate;
2762Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
2763Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
2764Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
2765Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
2766window['Hyphenator'] = Hyphenator;
2767*/
2768if (Hyphenator.isBookmarklet()) {
2769    Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true, useCSS3hyphenation: true});
2770    Hyphenator.config(Hyphenator.getConfigFromURI());
2771    Hyphenator.run();
2772}

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.