Syncs hyphenator with upstream
[semece.git] / static / js / Hyphenator.js
blob4d00cf592bcdcb8a65f72afe795d99f1b3dd622e
1 /** @license Hyphenator 3.1.0 - client side hyphenation for webbrowsers
2  *  Copyright (C) 2010  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  */
20 /* 
21  *  Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
22  */
24 /* The following comment is for JSLint: */
25 /*global window, ActiveXObject, unescape */
26 /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, regexp: true, sub: true, newcap: true, immed: true, evil: true, eqeqeq: false */
29 /**
30  * @constructor
31  * @description Provides all functionality to do hyphenation, except the patterns that are loaded
32  * externally.
33  * @author Mathias Nater, <a href = "mailto:mathias@mnn.ch">mathias@mnn.ch</a>
34  * @version 3.1.0
35  * @namespace Holds all methods and properties
36  * @example
37  * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
38  * &lt;script type = "text/javascript"&gt;
39  *   Hyphenator.run();
40  * &lt;/script&gt;
41  */
42 var Hyphenator = (function (window) {
44         var
45         /**
46          * @name Hyphenator-supportedLang
47          * @description
48          * A key-value object that stores supported languages.
49          * The key is the bcp47 code of the language and the value
50          * is the (abbreviated) filename of the pattern file.
51          * @type {Object.<string, string>}
52          * @private
53          * @example
54          * Check if language lang is supported:
55          * if (supportedLang.hasOwnProperty(lang))
56          */
57         supportedLang = {
58                 'be': 'be.js',
59                 'cs': 'cs.js',
60                 'da': 'da.js',
61                 'bn': 'bn.js',
62                 'de': 'de.js',
63                 'el': 'el-monoton.js',
64                 'el-monoton': 'el-monoton.js',
65                 'el-polyton': 'el-polyton.js',
66                 'en': 'en-us.js',
67                 'en-gb': 'en-gb.js',
68                 'en-us': 'en-us.js',
69                 'es': 'es.js',
70                 'fi': 'fi.js',
71                 'fr': 'fr.js',
72                 'grc': 'grc.js',
73                 'gu': 'gu.js',
74                 'hi': 'hi.js',
75                 'hu': 'hu.js',
76                 'hy': 'hy.js',
77                 'it': 'it.js',
78                 'kn': 'kn.js',
79                 'la': 'la.js',
80                 'lt': 'lt.js',
81                 'ml': 'ml.js',
82                 'nl': 'nl.js',
83                 'or': 'or.js',
84                 'pa': 'pa.js',
85                 'pl': 'pl.js',
86                 'pt': 'pt.js',
87                 'ru': 'ru.js',
88                 'sl': 'sl.js',
89                 'sv': 'sv.js',
90                 'ta': 'ta.js',
91                 'te': 'te.js',
92                 'tr': 'tr.js',
93                 'uk': 'uk.js'
94         },
96         /**
97          * @name Hyphenator-languageHint
98          * @description
99          * An automatically generated string to be displayed in a prompt if the language can't be guessed.
100          * The string is generated using the supportedLang-object.
101          * @see Hyphenator-supportedLang
102          * @type {string}
103          * @private
104          * @see Hyphenator-autoSetMainLanguage
105          */
107         languageHint = (function () {
108                 var k, r = '';
109                 for (k in supportedLang) {
110                         if (supportedLang.hasOwnProperty(k)) {
111                                 r += k + ', ';
112                         }
113                 }
114                 r = r.substring(0, r.length - 2);
115                 return r;
116         }()),
117         
118         /**
119          * @name Hyphenator-prompterStrings
120          * @description
121          * A key-value object holding the strings to be displayed if the language can't be guessed
122          * If you add hyphenation patterns change this string.
123          * @type {Object.<string,string>}
124          * @private
125          * @see Hyphenator-autoSetMainLanguage
126          */     
127         prompterStrings = {
128                 'be': 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:',
129                 'cs': 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:',
130                 'da': 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:',
131                 'de': 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:',
132                 'en': 'The language of this website could not be determined automatically. Please indicate the main language:',
133                 'es': 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:',
134                 'fi': 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:',
135                 'fr': 'La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:',
136                 'hu': 'A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:',
137                 'hy': 'Չհաջողվեց հայտնաբերել այս կայքի լեզուն։ Խնդրում ենք նշեք հիմնական լեզուն՝',
138                 'it': 'Lingua del sito sconosciuta. Indicare una lingua, per favore:',
139                 'kn': 'ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:',
140                 'lt': 'Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:',
141                 'ml': 'ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:',
142                 'nl': 'De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:',
143                 'pt': 'A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:',
144                 'ru': 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:',
145                 'sl': 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:',
146                 'sv': 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:',
147                 'tr': 'Bu web sitesinin dilini otomatik olarak tespit edilememiştir. Lütfen ana dili gösterir:',
148                 'uk': 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:'
149         },
150         
151         /**
152          * @name Hyphenator-basePath
153          * @description
154          * A string storing the basepath from where Hyphenator.js was loaded.
155          * This is used to load the patternfiles.
156          * The basepath is determined dynamically by searching all script-tags for Hyphenator.js
157          * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback.
158          * @type {string}
159          * @private
160          * @see Hyphenator-loadPatterns
161          */
162         basePath = (function () {
163                 var s = document.getElementsByTagName('script'), i = 0, p, src, t;
164                 while (!!(t = s[i++])) {
165                         if (!t.src) {
166                                 continue;
167                         }
168                         src = t.src;
169                         p = src.indexOf('Hyphenator.js');
170                         if (p !== -1) {
171                                 return src.substring(0, p);
172                         }
173                 }
174                 return 'http://hyphenator.googlecode.com/svn/trunk/';
175         }()),
177         /**
178          * @name Hyphenator-isLocal
179          * @description
180          * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
181          * it's loaded from an external source (i.e. directly from google.code)
182          */
183         isLocal = (function () {
184                 var re = false;
185                 if (window.location.href.indexOf(basePath) !== -1) {
186                         re = true;
187                 }
188                 return re;
189         }()),
190         
191         /**
192          * @name Hyphenator-documentLoaded
193          * @description
194          * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
195          */
196         documentLoaded = false,
197         documentCount = 0,
198         
200         /**
201          * @name Hyphenator-contextWindow
202          * @description
203          * contextWindow stores the window for the document to be hyphenated.
204          * If there are frames this will change.
205          * So use contextWindow instead of window!
206          */
207         contextWindow = window,
209         /**
210          * @name Hyphenator-doFrames
211          * @description
212          * switch to control if frames/iframes should be hyphenated, too
213          * defaults to false (frames are a bag of hurt!)
214          */
215         doFrames = false,
216         
217         /**
218          * @name Hyphenator-dontHyphenate
219          * @description
220          * A key-value object containing all html-tags whose content should not be hyphenated
221          * @type {Object.<string,boolean>}
222          * @private
223          * @see Hyphenator-hyphenateElement
224          */
225         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},
227         /**
228          * @name Hyphenator-enableCache
229          * @description
230          * A variable to set if caching is enabled or not
231          * @type boolean
232          * @default true
233          * @private
234          * @see Hyphenator.config
235          * @see hyphenateWord
236          */
237         enableCache = true,
239         /**
240          * @name Hyphenator-storageType
241          * @description
242          * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
243          * @type {string}
244          * @default 'none'
245          * @private
246          * @see Hyphenator.config
247          */     
248         storageType = 'local',
250         /**
251          * @name Hyphenator-storage
252          * @description
253          * An alias to the storage-Method defined in storageType.
254          * Set by Hyphenator.run()
255          * @type {Object|undefined}
256          * @default null
257          * @private
258          * @see Hyphenator.run
259          */     
260         storage,
261         
262         /**
263          * @name Hyphenator-enableReducedPatternSet
264          * @description
265          * A variable to set if storing the used patterns is set
266          * @type boolean
267          * @default false
268          * @private
269          * @see Hyphenator.config
270          * @see hyphenateWord
271          * @see Hyphenator.getRedPatternSet
272          */     
273         enableReducedPatternSet = false,
274         
275         /**
276          * @name Hyphenator-enableRemoteLoading
277          * @description
278          * A variable to set if pattern files should be loaded remotely or not
279          * @type boolean
280          * @default true
281          * @private
282          * @see Hyphenator.config
283          * @see Hyphenator-loadPatterns
284          */
285         enableRemoteLoading = true,
286         
287         /**
288          * @name Hyphenator-displayToggleBox
289          * @description
290          * A variable to set if the togglebox should be displayed or not
291          * @type boolean
292          * @default false
293          * @private
294          * @see Hyphenator.config
295          * @see Hyphenator-toggleBox
296          */
297         displayToggleBox = false,
298         
299         /**
300          * @name Hyphenator-hyphenateClass
301          * @description
302          * A string containing the css-class-name for the hyphenate class
303          * @type {string}
304          * @default 'hyphenate'
305          * @private
306          * @example
307          * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
308          * @see Hyphenator.config
309          */
310         hyphenateClass = 'hyphenate',
312         /**
313          * @name Hyphenator-dontHyphenateClass
314          * @description
315          * A string containing the css-class-name for elements that should not be hyphenated
316          * @type {string}
317          * @default 'donthyphenate'
318          * @private
319          * @example
320          * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
321          * @see Hyphenator.config
322          */
323         dontHyphenateClass = 'donthyphenate',
324         
325         /**
326          * @name Hyphenator-min
327          * @description
328          * A number wich indicates the minimal length of words to hyphenate.
329          * @type {number}
330          * @default 6
331          * @private
332          * @see Hyphenator.config
333          */     
334         min = 6,
335         
336         /**
337          * @name Hyphenator-orphanControl
338          * @description
339          * Control how the last words of a line are handled:
340          * level 1 (default): last word is hyphenated
341          * level 2: last word is not hyphenated
342          * level 3: last word is not hyphenated and last space is non breaking
343          * @type {number}
344          * @default 1
345          * @private
346          */
347         orphanControl = 1,
348         
349         /**
350          * @name Hyphenator-isBookmarklet
351          * @description
352          * Indicates if Hyphanetor runs as bookmarklet or not.
353          * @type boolean
354          * @default false
355          * @private
356          */     
357         isBookmarklet = (function () {
358                 var loc = null, re = false, jsArray = document.getElementsByTagName('script'), i, l;
359                 for (i = 0, l = jsArray.length; i < l; i++) {
360                         if (!!jsArray[i].getAttribute('src')) {
361                                 loc = jsArray[i].getAttribute('src');
362                         }
363                         if (!loc) {
364                                 continue;
365                         } else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
366                                 re = true;
367                         }
368                 }
369                 return re;
370         }()),
371         
372         /**
373          * @name Hyphenator-mainLanguage
374          * @description
375          * The general language of the document
376          * @type {string|null}
377          * @private
378          * @see Hyphenator-autoSetMainLanguage
379          */     
380         mainLanguage = null,
382         /**
383          * @name Hyphenator-elements
384          * @description
385          * An array holding all elements that have to be hyphenated. This var is filled by
386          * {@link Hyphenator-gatherDocumentInfos}
387          * @type {Array}
388          * @private
389          */     
390         elements = [],
391         
392         /**
393          * @name Hyphenator-exceptions
394          * @description
395          * An object containing exceptions as comma separated strings for each language.
396          * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
397          * @see Hyphenator-prepareLanguagesObj
398          * @type {Object}
399          * @private
400          */     
401         exceptions = {},
403         /**
404          * @name Hyphenator-docLanguages
405          * @description
406          * An object holding all languages used in the document. This is filled by
407          * {@link Hyphenator-gatherDocumentInfos}
408          * @type {Object}
409          * @private
410          */     
411         docLanguages = {},
414         /**
415          * @name Hyphenator-state
416          * @description
417          * A number that inidcates the current state of the script
418          * 0: not initialized
419          * 1: loading patterns
420          * 2: ready
421          * 3: hyphenation done
422          * 4: hyphenation removed
423          * @type {number}
424          * @private
425          */     
426         state = 0,
428         /**
429          * @name Hyphenator-url
430          * @description
431          * A string containing a RegularExpression to match URL's
432          * @type {string}
433          * @private
434          */     
435         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#!:\\.?\\+=&%@!\\-]*)*',
436         //      protocoll     usr     pwd                    ip               or                          host                 tld        port               path
437         /**
438          * @name Hyphenator-mail
439          * @description
440          * A string containing a RegularExpression to match mail-adresses
441          * @type {string}
442          * @private
443          */     
444         mail = '[\\w-\\.]+@[\\w\\.]+',
446         /**
447          * @name Hyphenator-urlRE
448          * @description
449          * A RegularExpressions-Object for url- and mail adress matching
450          * @type {RegExp}
451          * @private
452          */             
453         urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),
455         /**
456          * @name Hyphenator-zeroWidthSpace
457          * @description
458          * A string that holds a char.
459          * Depending on the browser, this is the zero with space or an empty string.
460          * zeroWidthSpace is used to break URLs
461          * @type {string}
462          * @private
463          */             
464         zeroWidthSpace = (function () {
465                 var zws, ua = navigator.userAgent.toLowerCase();
466                 zws = String.fromCharCode(8203); //Unicode zero width space
467                 if (ua.indexOf('msie 6') !== -1) {
468                         zws = ''; //IE6 doesn't support zws
469                 }
470                 if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
471                         zws = ''; //opera 10 on XP doesn't support zws
472                 }
473                 return zws;
474         }()),
475         
476         /**
477          * @name Hyphenator-createElem
478          * @description
479          * A function alias to document.createElementNS or document.createElement
480          * @type {function(string, Object)}
481          * @private
482          */             
483         createElem = function (tagname, context) {
484                 context = context || contextWindow;
485                 if (document.createElementNS) {
486                         return context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
487                 } else if (document.createElement) {
488                         return context.document.createElement(tagname);
489                 }
490         },
491         
492         /**
493          * @name Hyphenator-onHyphenationDone
494          * @description
495          * A method to be called, when the last element has been hyphenated or the hyphenation has been
496          * removed from the last element.
497          * @see Hyphenator.config
498          * @type {function()}
499          * @private
500          */             
501         onHyphenationDone = function () {},
503         /**
504          * @name Hyphenator-onError
505          * @description
506          * A function that can be called upon an error.
507          * @see Hyphenator.config
508          * @type {function(Object)}
509          * @private
510          */             
511         onError = function (e) {
512                 window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
513         },
515         /**
516          * @name Hyphenator-selectorFunction
517          * @description
518          * A function that has to return a HTMLNodeList of Elements to be hyphenated.
519          * By default it uses the classname ('hyphenate') to select the elements.
520          * @see Hyphenator.config
521          * @type {function()}
522          * @private
523          */             
524         selectorFunction = function () {
525                 var tmp, el = [], i, l;
526                 if (document.getElementsByClassName) {
527                         el = contextWindow.document.getElementsByClassName(hyphenateClass);
528                 } else {
529                         tmp = contextWindow.document.getElementsByTagName('*');
530                         l = tmp.length;
531                         for (i = 0; i < l; i++)
532                         {
533                                 if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
534                                         el.push(tmp[i]);
535                                 }
536                         }
537                 }
538                 return el;
539         },
541         /**
542          * @name Hyphenator-intermediateState
543          * @description
544          * The value of style.visibility of the text while it is hyphenated.
545          * @see Hyphenator.config
546          * @type {string}
547          * @private
548          */             
549         intermediateState = 'hidden',
550         
551         /**
552          * @name Hyphenator-hyphen
553          * @description
554          * A string containing the character for in-word-hyphenation
555          * @type {string}
556          * @default the soft hyphen
557          * @private
558          * @see Hyphenator.config
559          */
560         hyphen = String.fromCharCode(173),
561         
562         /**
563          * @name Hyphenator-urlhyphen
564          * @description
565          * A string containing the character for url/mail-hyphenation
566          * @type {string}
567          * @default the zero width space
568          * @private
569          * @see Hyphenator.config
570          * @see Hyphenator-zeroWidthSpace
571          */
572         urlhyphen = zeroWidthSpace,
574         /**
575          * @name Hyphenator-safeCopy
576          * @description
577          * Defines wether work-around for copy issues is active or not
578          * Not supported by Opera (no onCopy handler)
579          * @type boolean
580          * @default true
581          * @private
582          * @see Hyphenator.config
583          * @see Hyphenator-registerOnCopy
584          */
585         safeCopy = true,
586         
587         /**
588          * @name Hyphenator-Expando
589          * @description
590          * This custom object stores data for elements: storing data directly in elements
591          * (DomElement.customData = foobar;) isn't a good idea. It would lead to conflicts
592          * in form elements, when the form has a child with name="foobar". Therefore, this
593          * solution follows the approach of jQuery: the data is stored in an object and
594          * referenced by a unique attribute of the element. The attribute has a name that 
595          * is built by the prefix "HyphenatorExpando_" and a random number, so if the very
596          * very rare case occurs, that there's already an attribute with the same name, a
597          * simple reload is enough to make it function.
598          * @private
599          */             
600         Expando = (function () {
601                 var container = {},
602                         name = "HyphenatorExpando_" + Math.random(),
603                         uuid = 0;
604                 return {
605                         getDataForElem : function (elem) {
606                                 return container[elem[name].id];
607                         },
608                         setDataForElem : function (elem, data) {
609                                 var id;
610                                 if (elem[name] && elem[name].id !== '') {
611                                         id = elem[name].id;
612                                 } else {
613                                         id = uuid++;
614                                         elem[name] = {'id': id}; //object needed, otherways it is reflected in HTML in IE
615                                 }
616                                 container[id] = data;
617                         },
618                         appendDataForElem : function (elem, data) {
619                                 var k;
620                                 for (k in data) {
621                                         if (data.hasOwnProperty(k)) {
622                                                 container[elem[name].id][k] = data[k];
623                                         }
624                                 }
625                         },
626                         delDataOfElem : function (elem) {
627                                 delete container[elem[name]];
628                         }
629                 };
630         }()),
631                 
632         /*
633          * runOnContentLoaded is based od jQuery.bindReady()
634          * see
635          * jQuery JavaScript Library v1.3.2
636          * http://jquery.com/
637          *
638          * Copyright (c) 2009 John Resig
639          * Dual licensed under the MIT and GPL licenses.
640          * http://docs.jquery.com/License
641          *
642          * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
643          * Revision: 6246
644          */
645         /**
646          * @name Hyphenator-runOnContentLoaded
647          * @description
648          * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery
649          * <a href = "http://jquery.com/</a>
650          * I added some functionality: e.g. support for frames and iframes…
651          * @param {Object} w the window-object
652          * @param {function()} f the function to call onDOMContentLoaded
653          * @private
654          */
655         runOnContentLoaded = function (w, f) {
656                 var DOMContentLoaded = function () {}, toplevel, hyphRunForThis = {};
657                 if (documentLoaded && !hyphRunForThis[w.location.href]) {
658                         f();
659                         hyphRunForThis[w.location.href] = true;
660                         return;
661                 }
662                 function init(context) {
663                         contextWindow = context || window;
664                         if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || contextWindow != window.parent)) {
665                                 documentLoaded = true;
666                                 f();
667                                 hyphRunForThis[contextWindow.location.href] = true;
668                         }
669                 }
670                 
671                 function doScrollCheck() {
672                         try {
673                                 // If IE is used, use the trick by Diego Perini
674                                 // http://javascript.nwbox.com/IEContentLoaded/
675                                 document.documentElement.doScroll("left");
676                         } catch (error) {
677                                 setTimeout(doScrollCheck, 1);
678                                 return;
679                         }
680                 
681                         // and execute any waiting functions
682                         init(window);
683                 }
685                 function doOnLoad() {
686                         var i, haveAccess, fl = window.frames.length;
687                         if (doFrames && fl > 0) {
688                                 for (i = 0; i < fl; i++) {
689                                         haveAccess = undefined;
690                                         //try catch isn't enough for webkit
691                                         try {
692                                                 //opera throws only on document.toString-access
693                                                 haveAccess = window.frames[i].document.toString();
694                                         } catch (e) {
695                                                 haveAccess = undefined;
696                                         }
697                                         if (!!haveAccess) {
698                                                 init(window.frames[i]);
699                                         }
700                                 }
701                                 contextWindow = window;
702                                 f();
703                                 hyphRunForThis[window.location.href] = true;
704                         } else {
705                                 init(window);
706                         }
707                 }
708                 
709                 // Cleanup functions for the document ready method
710                 if (document.addEventListener) {
711                         DOMContentLoaded = function () {
712                                 document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
713                                 if (doFrames && window.frames.length > 0) {
714                                         //we are in a frameset, so do nothing but wait for onload to fire
715                                         return;
716                                 } else {
717                                         init(window);
718                                 }
719                         };
720                 
721                 } else if (document.attachEvent) {
722                         DOMContentLoaded = function () {
723                                 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
724                                 if (document.readyState === "complete") {
725                                         document.detachEvent("onreadystatechange", DOMContentLoaded);
726                                         if (doFrames && window.frames.length > 0) {
727                                                 //we are in a frameset, so do nothing but wait for onload to fire
728                                                 return;
729                                         } else {
730                                                 init(window);
731                                         }
732                                 }
733                         };
734                 }
736                 // Mozilla, Opera and webkit nightlies currently support this event
737                 if (document.addEventListener) {
738                         // Use the handy event callback
739                         document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
740                         
741                         // A fallback to window.onload, that will always work
742                         window.addEventListener("load", doOnLoad, false);
744                 // If IE event model is used
745                 } else if (document.attachEvent) {
746                         // ensure firing before onload,
747                         // maybe late but safe also for iframes
748                         document.attachEvent("onreadystatechange", DOMContentLoaded);
749                         
750                         // A fallback to window.onload, that will always work
751                         window.attachEvent("onload", doOnLoad);
753                         // If IE and not a frame
754                         // continually check to see if the document is ready
755                         toplevel = false;
756                         try {
757                                 toplevel = window.frameElement === null;
758                         } catch (e) {}
760                         if (document.documentElement.doScroll && toplevel) {
761                                 doScrollCheck();
762                         }
763                 }
765         },
769         /**
770          * @name Hyphenator-getLang
771          * @description
772          * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}.
773          * @param {Object} el The first parameter is an DOM-Element-Object
774          * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage}
775          * if there's no language found for the element.
776          * @private
777          */
778         getLang = function (el, fallback) {
779                 if (!!el.getAttribute('lang')) {
780                         return el.getAttribute('lang').toLowerCase();
781                 }
782                 // The following doesn't work in IE due to a bug when getAttribute('xml:lang') in a table
783                 /*if (!!el.getAttribute('xml:lang')) {
784                         return el.getAttribute('xml:lang').substring(0, 2);
785                 }*/
786                 //instead, we have to do this (thanks to borgzor):
787                 try {
788                         if (!!el.getAttribute('xml:lang')) {
789                                 return el.getAttribute('xml:lang').toLowerCase();
790                         }
791                 } catch (ex) {}
792                 if (el.tagName !== 'HTML') {
793                         return getLang(el.parentNode, true);
794                 }
795                 if (fallback) {
796                         return mainLanguage;
797                 }
798                 return null;
799         },
800         
801         /**
802          * @name Hyphenator-autoSetMainLanguage
803          * @description
804          * Retrieves the language of the document from the DOM.
805          * The function looks in the following places:
806          * <ul>
807          * <li>lang-attribute in the html-tag</li>
808          * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
809          * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
810          * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
811          * </li>
812          * If nothing can be found a prompt using {@link Hyphenator-languageHint} and {@link Hyphenator-prompterStrings} is displayed.
813          * If the retrieved language is in the object {@link Hyphenator-supportedLang} it is copied to {@link Hyphenator-mainLanguage}
814          * @private
815          */             
816         autoSetMainLanguage = function (w) {
817                 w = w || contextWindow;
818                 var el = w.document.getElementsByTagName('html')[0],
819                         m = w.document.getElementsByTagName('meta'),
820                         i, text, e, ul;
821                 mainLanguage = getLang(el, false);
822                 if (!mainLanguage) {
823                         for (i = 0; i < m.length; i++) {
824                                 //<meta http-equiv = "content-language" content="xy">   
825                                 if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) {
826                                         mainLanguage = m[i].getAttribute('content').toLowerCase();
827                                 }
828                                 //<meta name = "DC.Language" content="xy">
829                                 if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) {
830                                         mainLanguage = m[i].getAttribute('content').toLowerCase();
831                                 }                       
832                                 //<meta name = "language" content = "xy">
833                                 if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) {
834                                         mainLanguage = m[i].getAttribute('content').toLowerCase();
835                                 }
836                         }
837                 }
838                 if (!mainLanguage && doFrames && contextWindow != window.parent) {
839                         autoSetMainLanguage(window.parent);
840                 }
841                 if (!mainLanguage) {
842                         text = '';
843                         ul = navigator.language ? navigator.language : navigator.userLanguage;
844                         ul = ul.substring(0, 2);
845                         if (prompterStrings.hasOwnProperty(ul)) {
846                                 text = prompterStrings[ul];
847                         } else {
848                                 text = prompterStrings.en;
849                         }
850                         text += ' (ISO 639-1)\n\n' + languageHint;
851                         mainLanguage = window.prompt(unescape(text), ul).toLowerCase();
852                 }
853                 if (!supportedLang.hasOwnProperty(mainLanguage)) {
854                         if (supportedLang.hasOwnProperty(mainLanguage.split('-')[0])) { //try subtag
855                                 mainLanguage = mainLanguage.split('-')[0];
856                         } else {
857                                 e = new Error('The language "' + mainLanguage + '" is not yet supported.');
858                                 throw e;
859                         }
860                 }
861         },
862     
863         /**
864          * @name Hyphenator-gatherDocumentInfos
865          * @description
866          * This method runs through the DOM and executes the process()-function on:
867          * - every node returned by the {@link Hyphenator-selectorFunction}.
868          * The process()-function copies the element to the elements-variable, sets its visibility
869          * to intermediateState, retrieves its language and recursivly descends the DOM-tree until
870          * the child-Nodes aren't of type 1
871          * @private
872          */             
873         gatherDocumentInfos = function () {
874                 var elToProcess, tmp, i = 0,
875                 process = function (el, hide, lang) {
876                         var n, i = 0, hyphenatorSettings = {};
877                         if (hide && intermediateState === 'hidden') {
878                                 if (!!el.getAttribute('style')) {
879                                         hyphenatorSettings.hasOwnStyle = true;
880                                 } else {
881                                         hyphenatorSettings.hasOwnStyle = false;                                 
882                                 }
883                                 hyphenatorSettings.isHidden = true;
884                                 el.style.visibility = 'hidden';
885                         }
886                         if (el.lang && typeof(el.lang) === 'string') {
887                                 hyphenatorSettings.language = el.lang.toLowerCase(); //copy attribute-lang to internal lang
888                         } else if (lang) {
889                                 hyphenatorSettings.language = lang.toLowerCase();
890                         } else {
891                                 hyphenatorSettings.language = getLang(el, true);
892                         }
893                         lang = hyphenatorSettings.language;
894                         if (supportedLang[lang]) {
895                                 docLanguages[lang] = true;
896                         } else {
897                                 if (supportedLang.hasOwnProperty(lang.split('-')[0])) { //try subtag
898                                         lang = lang.split('-')[0];
899                                         hyphenatorSettings.language = lang;
900                                 } else if (!isBookmarklet) {
901                                         onError(new Error('Language ' + lang + ' is not yet supported.'));
902                                 }
903                         }
904                         Expando.setDataForElem(el, hyphenatorSettings);
905                         elements.push(el);
906                         while (!!(n = el.childNodes[i++])) {
907                                 if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
908                                         n.className.indexOf(dontHyphenateClass) === -1 && !(n in elToProcess)) {
909                                         process(n, false, lang);
910                                 }
911                         }
912                 };
913                 if (isBookmarklet) {
914                         elToProcess = contextWindow.document.getElementsByTagName('body')[0];
915                         process(elToProcess, false, mainLanguage);
916                 } else {
917                         elToProcess = selectorFunction();
918                         while (!!(tmp = elToProcess[i++]))
919                         {
920                                 process(tmp, true, '');
921                         }                       
922                 }
923                 if (!Hyphenator.languages.hasOwnProperty(mainLanguage)) {
924                         docLanguages[mainLanguage] = true;
925                 } else if (!Hyphenator.languages[mainLanguage].prepared) {
926                         docLanguages[mainLanguage] = true;
927                 }
928                 if (elements.length > 0) {
929                         Expando.appendDataForElem(elements[elements.length - 1], {isLast : true});
930                 }
931         },
932                  
933         /**
934          * @name Hyphenator-convertPatterns
935          * @description
936          * Converts the patterns from string '_a6' to object '_a':'_a6'.
937          * The result is stored in the {@link Hyphenator-patterns}-object.
938          * @private
939          * @param {string} lang the language whose patterns shall be converted
940          */             
941         convertPatterns = function (lang) {
942                 var plen, anfang, ende, pats, pat, key, tmp = {};
943                 pats = Hyphenator.languages[lang].patterns;
944                 for (plen in pats) {
945                         if (pats.hasOwnProperty(plen)) {
946                                 plen = parseInt(plen, 10);
947                                 anfang = 0;
948                                 ende = plen;
949                                 while (!!(pat = pats[plen].substring(anfang, ende))) {
950                                         key = pat.replace(/\d/g, '');
951                                         tmp[key] = pat;
952                                         anfang = ende;
953                                         ende += plen;
954                                 }
955                         }
956                 }
957                 Hyphenator.languages[lang].patterns = tmp;
958                 Hyphenator.languages[lang].patternsConverted = true;
959         },
961         /**
962          * @name Hyphenator-convertExceptionsToObject
963          * @description
964          * Converts a list of comma seprated exceptions to an object:
965          * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
966          * @private
967          * @param {string} exc a comma separated string of exceptions (without spaces)
968          */             
969         convertExceptionsToObject = function (exc) {
970                 var w = exc.split(', '),
971                         r = {},
972                         i, l, key;
973                 for (i = 0, l = w.length; i < l; i++) {
974                         key = w[i].replace(/-/g, '');
975                         if (!r.hasOwnProperty(key)) {
976                                 r[key] = w[i];
977                         }
978                 }
979                 return r;
980         },
981         
982         /**
983          * @name Hyphenator-loadPatterns
984          * @description
985          * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
986          * If the given language is not in the {@link Hyphenator-supportedLang}-Object it returns.
987          * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object 
988          * has a same-origin-policy. This makes the isBookmarklet-functionality impossible.
989          * @param {string} lang The language to load the patterns for
990          * @private
991          * @see Hyphenator-basePath
992          */
993         loadPatterns = function (lang) {
994                 var url, xhr, head, script;
995                 if (supportedLang[lang] && !Hyphenator.languages[lang]) {
996                 url = basePath + 'patterns/' + supportedLang[lang];
997                 } else {
998                         return;
999                 }
1000                 if (isLocal && !isBookmarklet) {
1001                         //check if 'url' is available:
1002                         xhr = null;
1003                         if (typeof XMLHttpRequest !== 'undefined') {
1004                                 xhr = new XMLHttpRequest();
1005                         }
1006                         if (!xhr) {
1007                                 try {
1008                                         xhr  = new ActiveXObject("Msxml2.XMLHTTP");
1009                                 } catch (e) {
1010                                         xhr  = null;
1011                                 }
1012                         }
1013                         if (xhr) {
1014                                 xhr.open('HEAD', url, false);
1015                                 xhr.setRequestHeader('Cache-Control', 'no-cache');
1016                                 xhr.send(null);
1017                                 if (xhr.status === 404) {
1018                                         onError(new Error('Could not load\n' + url));
1019                                         delete docLanguages[lang];
1020                                         return;
1021                                 }
1022                         }
1023                 }
1024                 if (createElem) {
1025                         head = window.document.getElementsByTagName('head').item(0);
1026                         script = createElem('script', window);
1027                         script.src = url;
1028                         script.type = 'text/javascript';
1029                         head.appendChild(script);
1030                 }
1031         },
1032         
1033         /**
1034          * @name Hyphenator-prepareLanguagesObj
1035          * @description
1036          * Adds a cache to each language and converts the exceptions-list to an object.
1037          * If storage is active the object is stored there.
1038          * @private
1039          * @param {string} lang the language ob the lang-obj
1040          */             
1041         prepareLanguagesObj = function (lang) {
1042                 var lo = Hyphenator.languages[lang], wrd;
1043                 if (!lo.prepared) {     
1044                         if (enableCache) {
1045                                 lo.cache = {};
1046                                 //Export
1047                                 lo['cache'] = lo.cache;
1048                         }
1049                         if (enableReducedPatternSet) {
1050                                 lo.redPatSet = {};
1051                         }
1052                         //add exceptions from the pattern file to the local 'exceptions'-obj
1053                         if (lo.hasOwnProperty('exceptions')) {
1054                                 Hyphenator.addExceptions(lang, lo.exceptions);
1055                                 delete lo.exceptions;
1056                         }
1057                         //copy global exceptions to the language specific exceptions
1058                         if (exceptions.hasOwnProperty('global')) {
1059                                 if (exceptions.hasOwnProperty(lang)) {
1060                                         exceptions[lang] += ', ' + exceptions.global;
1061                                 } else {
1062                                         exceptions[lang] = exceptions.global;
1063                                 }
1064                         }
1065                         //move exceptions from the the local 'exceptions'-obj to the 'language'-object
1066                         if (exceptions.hasOwnProperty(lang)) {
1067                                 lo.exceptions = convertExceptionsToObject(exceptions[lang]);
1068                                 delete exceptions[lang];
1069                         } else {
1070                                 lo.exceptions = {};
1071                         }
1072                         convertPatterns(lang);
1073                         wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + '-]{' + min + ',}';
1074                         lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
1075                         lo.prepared = true;
1076                 }
1077                 if (storage) {
1078                         try {
1079                                 storage.setItem('Hyphenator_' + lang, window.JSON.stringify(lo));
1080                         } catch (e) {
1081                                 //onError(e);
1082                         }
1083                 }
1084                 
1085         },
1086         
1087         /**
1088          * @name Hyphenator-prepare
1089          * @description
1090          * This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
1091          * that the patternfiles are loaded, all conversions are made and the callback is called.
1092          * If storage is active the object is retrieved there.
1093          * If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
1094          * by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
1095          * converted to their object style and the lang-object extended.
1096          * Finally the callback is called.
1097          * @param {function()} callback to call, when all patterns are loaded
1098          * @private
1099          */
1100         prepare = function (callback) {
1101                 var lang, languagesToLoad = 0, interval, tmp1, tmp2;
1102                 if (!enableRemoteLoading) {
1103                         for (lang in Hyphenator.languages) {
1104                                 if (Hyphenator.languages.hasOwnProperty(lang)) {
1105                                         prepareLanguagesObj(lang);
1106                                 }
1107                         }
1108                         state = 2;
1109                         callback();
1110                         return;
1111                 }
1112                 // get all languages that are used and preload the patterns
1113                 state = 1;
1114                 for (lang in docLanguages) {
1115                         if (docLanguages.hasOwnProperty(lang)) {
1116                                 ++languagesToLoad;
1117                                 if (storage) {
1118                                         if (storage.getItem('Hyphenator_' + lang)) {
1119                                                 Hyphenator.languages[lang] = window.JSON.parse(storage.getItem('Hyphenator_' + lang));
1120                                                 if (exceptions.hasOwnProperty('global')) {
1121                                                         tmp1 = convertExceptionsToObject(exceptions.global);
1122                                                         for (tmp2 in tmp1) {
1123                                                                 if (tmp1.hasOwnProperty(tmp2)) {
1124                                                                         Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1125                                                                 }
1126                                                         }
1127                                                 }
1128                                                 //Replace exceptions since they may have been changed:
1129                                                 if (exceptions.hasOwnProperty(lang)) {
1130                                                         tmp1 = convertExceptionsToObject(exceptions[lang]);
1131                                                         for (tmp2 in tmp1) {
1132                                                                 if (tmp1.hasOwnProperty(tmp2)) {
1133                                                                         Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1134                                                                 }
1135                                                         }
1136                                                         delete exceptions[lang];
1137                                                 }
1138                                                 //Replace genRegExp since it may have been changed:
1139                                                 tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + '@' + String.fromCharCode(173) + '-]{' + min + ',}';
1140                                                 Hyphenator.languages[lang].genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + tmp1 + ')', 'gi');
1141                                                 
1142                                                 delete docLanguages[lang];
1143                                                 --languagesToLoad;
1144                                                 continue;
1145                                         }
1146                                 } 
1147                                 loadPatterns(lang);
1148                         }
1149                 }
1150                 if (languagesToLoad === 0) {
1151                         state = 2;
1152                         callback();
1153                         return;
1154                 }
1155                 // wait until they are loaded
1156                 interval = window.setInterval(function () {
1157                         var finishedLoading = false, lang;
1158                         for (lang in docLanguages) {
1159                                 if (docLanguages.hasOwnProperty(lang)) {
1160                                         if (!Hyphenator.languages[lang]) {
1161                                                 finishedLoading = false;
1162                                                 break;
1163                                         } else {
1164                                                 finishedLoading = true;
1165                                                 delete docLanguages[lang];
1166                                                 //do conversion while other patterns are loading:
1167                                                 prepareLanguagesObj(lang);
1168                                         }
1169                                 }
1170                         }
1171                         if (finishedLoading) {
1172                                 window.clearInterval(interval);
1173                                 state = 2;
1174                                 callback();
1175                         }
1176                 }, 100);
1177         },
1179         /**
1180          * @name Hyphenator-switchToggleBox
1181          * @description
1182          * Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
1183          * @param {boolean} s true when hyphenation is on, false when it's off
1184          * @see Hyphenator.config
1185          * @private
1186          */             
1187         toggleBox = function (s) {
1188                 var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute;
1189                 if (!!(myBox = contextWindow.document.getElementById('HyphenatorToggleBox'))) {
1190                         if (s) {
1191                                 myBox.firstChild.data = 'Hy-phe-na-ti-on';
1192                         } else {
1193                                 myBox.firstChild.data = 'Hyphenation';
1194                         }
1195                 } else {
1196                         bdy = contextWindow.document.getElementsByTagName('body')[0];
1197                         myBox = createElem('div', contextWindow);
1198                         myIdAttribute = contextWindow.document.createAttribute('id');
1199                         myIdAttribute.nodeValue = 'HyphenatorToggleBox';
1200                         myClassAttribute = contextWindow.document.createAttribute('class');
1201                         myClassAttribute.nodeValue = dontHyphenateClass;
1202                         myTextNode = contextWindow.document.createTextNode('Hy-phe-na-ti-on');
1203                         myBox.appendChild(myTextNode);
1204                         myBox.setAttributeNode(myIdAttribute);
1205                         myBox.setAttributeNode(myClassAttribute);
1206                         myBox.onclick =  Hyphenator.toggleHyphenation;
1207                         myBox.style.position = 'absolute';
1208                         myBox.style.top = '0px';
1209                         myBox.style.right = '0px';
1210                         myBox.style.margin = '0';
1211                         myBox.style.backgroundColor = '#AAAAAA';
1212                         myBox.style.color = '#FFFFFF';
1213                         myBox.style.font = '6pt Arial';
1214                         myBox.style.letterSpacing = '0.2em';
1215                         myBox.style.padding = '3px';
1216                         myBox.style.cursor = 'pointer';
1217                         myBox.style.WebkitBorderBottomLeftRadius = '4px';
1218                         myBox.style.MozBorderRadiusBottomleft = '4px';
1219                         bdy.appendChild(myBox);
1220                 }
1221         },
1223         /**
1224          * @name Hyphenator-hyphenateWord
1225          * @description
1226          * This function is the heart of Hyphenator.js. It returns a hyphenated word.
1227          *
1228          * If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
1229          * If the word is in the exceptions list or in the cache, it is retrieved from it.
1230          * If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
1231          * @param {string} lang The language of the word
1232          * @param {string} word The word
1233          * @returns string The hyphenated word
1234          * @public
1235          */     
1236         hyphenateWord = function (lang, word) {
1237                 var lo = Hyphenator.languages[lang],
1238                         parts, i, l, w, wl, s, hypos, p, maxwins, win, pat = false, patk, c, t, n, numb3rs, inserted, hyphenatedword, val;
1239                 if (word === '') {
1240                         return '';
1241                 }
1242                 if (word.indexOf(hyphen) !== -1) {
1243                         //word already contains shy; -> leave at it is!
1244                         return word;
1245                 }
1246                 if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
1247                         return lo.cache[word];
1248                 }
1249                 if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
1250                         return lo.exceptions[word].replace(/-/g, hyphen);
1251                 }
1252                 if (word.indexOf('-') !== -1) {
1253                         //word contains '-' -> hyphenate the parts separated with '-'
1254                         parts = word.split('-');
1255                         for (i = 0, l = parts.length; i < l; i++) {
1256                                 parts[i] = hyphenateWord(lang, parts[i]);
1257                         }
1258                         return parts.join('-');
1259                 }
1260                 //finally the core hyphenation algorithm
1261                 w = '_' + word + '_';
1262                 wl = w.length;
1263                 s = w.split('');
1264                 if (word.indexOf("'") !== -1) {
1265                         w = w.toLowerCase().replace("'", "’"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
1266                 } else {
1267                         w = w.toLowerCase();
1268                 }
1269                 hypos = [];
1270                 numb3rs = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}; //check for member is faster then isFinite()
1271                 n = wl - lo.shortestPattern;
1272                 for (p = 0; p <= n; p++) {
1273                         maxwins = Math.min((wl - p), lo.longestPattern);
1274                         for (win = lo.shortestPattern; win <= maxwins; win++) {
1275                                 if (lo.patterns.hasOwnProperty(patk = w.substring(p, p + win))) {
1276                                         pat = lo.patterns[patk];
1277                                         if (enableReducedPatternSet) {
1278                                                 lo.redPatSet[patk] = pat;
1279                                         }
1280                                         if (typeof pat === 'string') {
1281                                                 //convert from string 'a5b' to array [1,5] (pos,value)
1282                                                 t = 0;
1283                                                 val = [];
1284                                                 for (i = 0; i < pat.length; i++) {
1285                                                         if (!!(c = numb3rs[pat.charAt(i)])) {
1286                                                                 val.push(i - t, c);
1287                                                                 t++;                                                            
1288                                                         }
1289                                                 }
1290                                                 pat = lo.patterns[patk] = val;
1291                                         }
1292                                 } else {
1293                                         continue;
1294                                 }
1295                                 for (i = 0; i < pat.length; i++) {
1296                                         c = p - 1 + pat[i];
1297                                         if (!hypos[c] || hypos[c] < pat[i + 1]) {
1298                                                 hypos[c] = pat[i + 1];
1299                                         }
1300                                         i++;
1301                                 }
1302                         }
1303                 }
1304                 inserted = 0;
1305                 for (i = lo.leftmin; i <= (word.length - lo.rightmin); i++) {
1306                         if (!!(hypos[i] & 1)) {
1307                                 s.splice(i + inserted + 1, 0, hyphen);
1308                                 inserted++;
1309                         }
1310                 }
1311                 hyphenatedword = s.slice(1, -1).join('');
1312                 if (enableCache) {
1313                         lo.cache[word] = hyphenatedword;
1314                 }
1315                 return hyphenatedword;
1316         },
1317                 
1318         /**
1319          * @name Hyphenator-hyphenateURL
1320          * @description
1321          * Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
1322          * @param {string} url to hyphenate
1323          * @returns string the hyphenated URL
1324          * @public
1325          */
1326         hyphenateURL = function (url) {
1327                 return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
1328         },
1330         /**
1331          * @name Hyphenator-hyphenateElement
1332          * @description
1333          * Takes the content of the given element and - if there's text - replaces the words
1334          * by hyphenated words. If there's another element, the function is called recursively.
1335          * When all words are hyphenated, the visibility of the element is set to 'visible'.
1336          * @param {Object} el The element to hyphenate
1337          * @public
1338          */
1339         hyphenateElement = function (el) {
1340                 var hyphenatorSettings = Expando.getDataForElem(el),
1341                         lang = hyphenatorSettings.language, hyphenate, n, i,
1342                         controlOrphans = function (part) {
1343                                 var h, r;
1344                                 switch (hyphen) {
1345                                 case '|':
1346                                         h = '\\|';
1347                                         break;
1348                                 case '+':
1349                                         h = '\\+';
1350                                         break;
1351                                 case '*':
1352                                         h = '\\*';
1353                                         break;
1354                                 default:
1355                                         h = hyphen;
1356                                 }
1357                                 if (orphanControl >= 2) {
1358                                         //remove hyphen points from last word
1359                                         r = part.split(' ');
1360                                         r[1] = r[1].replace(new RegExp(h, 'g'), '');
1361                                         r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), '');
1362                                         r = r.join(' ');
1363                                 }
1364                                 if (orphanControl === 3) {
1365                                         //replace spaces by non breaking spaces
1366                                         r = r.replace(/[ ]+/g, String.fromCharCode(160));
1367                                 }
1368                                 return r;
1369                         };
1370                 if (Hyphenator.languages.hasOwnProperty(lang)) {
1371                         hyphenate = function (word) {
1372                                 if (urlOrMailRE.test(word)) {
1373                                         return hyphenateURL(word);
1374                                 } else {
1375                                         return hyphenateWord(lang, word);
1376                                 }
1377                         };
1378                         i = 0;
1379                         while (!!(n = el.childNodes[i++])) {
1380                                 if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
1381                                         n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1382                                         if (orphanControl !== 1) {
1383                                                 n.data = n.data.replace(/[\S]+ [\S]+$/, controlOrphans);
1384                                         }
1385                                 }
1386                         }
1387                 }
1388                 if (hyphenatorSettings.isHidden && intermediateState === 'hidden') {
1389                         el.style.visibility = 'visible';
1390                         if (!hyphenatorSettings.hasOwnStyle) {
1391                                 el.setAttribute('style', ''); // without this, removeAttribute doesn't work in Safari (thanks to molily)
1392                                 el.removeAttribute('style');
1393                         } else {
1394                                 if (el.style.removeProperty) {
1395                                         el.style.removeProperty('visibility');
1396                                 } else if (el.style.removeAttribute) { // IE
1397                                         el.style.removeAttribute('visibility');
1398                                 }  
1399                         }
1400                 }
1401                 if (hyphenatorSettings.isLast) {
1402                         state = 3;
1403                         documentCount--;
1404                         if (documentCount > (-1000) && documentCount <= 0) {
1405                                 documentCount = (-2000);
1406                                 onHyphenationDone();
1407                         }
1408                 }
1409         },
1410         
1411         /**
1412          * @name Hyphenator-removeHyphenationFromElement
1413          * @description
1414          * Removes all hyphens from the element. If there are other elements, the function is
1415          * called recursively.
1416          * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
1417          * @param {Object} el The element where to remove hyphenation.
1418          * @public
1419          */
1420         removeHyphenationFromElement = function (el) {
1421                 var h, i = 0, n;
1422                 switch (hyphen) {
1423                 case '|':
1424                         h = '\\|';
1425                         break;
1426                 case '+':
1427                         h = '\\+';
1428                         break;
1429                 case '*':
1430                         h = '\\*';
1431                         break;
1432                 default:
1433                         h = hyphen;
1434                 }
1435                 while (!!(n = el.childNodes[i++])) {
1436                         if (n.nodeType === 3) {
1437                                 n.data = n.data.replace(new RegExp(h, 'g'), '');
1438                                 n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
1439                         } else if (n.nodeType === 1) {
1440                                 removeHyphenationFromElement(n);
1441                         }
1442                 }
1443         },
1445         /**
1446          * @name Hyphenator-hyphenateDocument
1447          * @description
1448          * Calls hyphenateElement() for all members of elements. This is done with a setTimout
1449          * to prevent a "long running Script"-alert when hyphenating large pages.
1450          * Therefore a tricky bind()-function was necessary.
1451          * @public
1452          */
1453         hyphenateDocument = function () {
1454                 function bind(fun, arg) {
1455                         return function () {
1456                                 return fun(arg);
1457                         };
1458                 }
1459                 var i = 0, el;
1460                 while (!!(el = elements[i++])) {
1461                         if (el.ownerDocument.location.href === contextWindow.location.href) {
1462                                 window.setTimeout(bind(hyphenateElement, el), 0);
1463                         }
1464                 }
1465         },
1467         /**
1468          * @name Hyphenator-removeHyphenationFromDocument
1469          * @description
1470          * Does what it says ;-)
1471          * @public
1472          */
1473         removeHyphenationFromDocument = function () {
1474                 var i = 0, el;
1475                 while (!!(el = elements[i++])) {
1476                         removeHyphenationFromElement(el);
1477                 }
1478                 state = 4;
1479         },
1480         
1481         /**
1482          * @name Hyphenator-registerOnCopy
1483          * @description
1484          * Huge work-around for browser-inconsistency when it comes to
1485          * copying of hyphenated text.
1486          * The idea behind this code has been provided by http://github.com/aristus/sweet-justice
1487          * sweet-justice is under BSD-License
1488          * @private
1489          */
1490         registerOnCopy = function () {
1491                 var body = contextWindow.document.getElementsByTagName('body')[0],
1492                 shadow,
1493                 selection,
1494                 range,
1495                 rangeShadow,
1496                 restore,
1497                 oncopyHandler = function (e) {
1498                         e = e || window.event;
1499                         var target = e.target || e.srcElement,
1500                         currDoc = target.ownerDocument,
1501                         body = currDoc.getElementsByTagName('body')[0],
1502                         targetWindow = 'defaultView' in currDoc ? currDoc.defaultView : currDoc.parentWindow;
1503                         if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
1504                                 //Safari needs this
1505                                 return;
1506                         }
1507                         //create a hidden shadow element
1508                         shadow = currDoc.createElement('div');
1509                         shadow.style.overflow = 'hidden';
1510                         shadow.style.position = 'absolute';
1511                         shadow.style.top = '-5000px';
1512                         shadow.style.height = '1px';
1513                         body.appendChild(shadow);
1514                         if (window.getSelection) {
1515                                 //FF3, Webkit
1516                                 selection = targetWindow.getSelection();
1517                                 range = selection.getRangeAt(0);
1518                                 shadow.appendChild(range.cloneContents());
1519                                 removeHyphenationFromElement(shadow);
1520                                 selection.selectAllChildren(shadow);
1521                                 restore = function () {
1522                                         shadow.parentNode.removeChild(shadow);
1523                                         if (targetWindow.getSelection().setBaseAndExtent) {
1524                                                 selection.setBaseAndExtent(
1525                                                         range.startContainer,
1526                                                         range.startOffset,
1527                                                         range.endContainer,
1528                                                         range.endOffset
1529                                                 );
1530                                         }
1531                                 };
1532                         } else {
1533                                 // IE
1534                                 selection = targetWindow.document.selection;
1535                                 range = selection.createRange();
1536                                 shadow.innerHTML = range.htmlText;
1537                                 removeHyphenationFromElement(shadow);
1538                                 rangeShadow = body.createTextRange();
1539                                 rangeShadow.moveToElementText(shadow);
1540                                 rangeShadow.select();
1541                                 restore = function () {
1542                                         shadow.parentNode.removeChild(shadow);
1543                                         if (range.text !== "") {
1544                                                 range.select();
1545                                         }
1546                                 };
1547                         }
1548                         window.setTimeout(restore, 0);
1549                 };
1550                 if (!body) {
1551                         return;
1552                 }
1553                 if (window.addEventListener) {
1554                         body.addEventListener("copy", oncopyHandler, false);
1555                 } else {
1556                         body.attachEvent("oncopy", oncopyHandler);
1557                 }
1558         };
1560         return {
1561                 
1562                 /**
1563                  * @name Hyphenator.version
1564                  * @memberOf Hyphenator
1565                  * @description
1566                  * String containing the actual version of Hyphenator.js
1567                  * [major release].[minor releas].[bugfix release]
1568                  * major release: new API, new Features, big changes
1569                  * minor release: new languages, improvements
1570                  * @public
1571          */             
1572                 version: '3.1.0',
1573                 
1574                 /**
1575                  * @name Hyphenator.languages
1576                  * @memberOf Hyphenator
1577                  * @description
1578                  * Objects that holds key-value pairs, where key is the language and the value is the
1579                  * language-object loaded from (and set by) the pattern file.
1580                  * The language object holds the following members:
1581                  * <table>
1582                  * <tr><th>key</th><th>desc></th></tr>
1583                  * <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
1584                  * <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
1585                  * <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
1586                  * <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
1587                  * <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
1588                  * <tr><td>patterns</td><td>the patterns</td></tr>
1589                  * </table>
1590                  * And optionally (or after prepareLanguagesObj() has been called):
1591                  * <table>
1592                  * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
1593                  * </table>
1594                  * @public
1595          */             
1596                 languages: {},
1597                 
1599                 /**
1600                  * @name Hyphenator.config
1601                          * @description
1602                  * Config function that takes an object as an argument. The object contains key-value-pairs
1603                  * containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
1604                  * @param {Object} obj <table>
1605                  * <tr><th>key</th><th>values</th><th>default</th></tr>
1606                  * <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
1607                  * <tr><td>donthyphenateclassname</td><td>string</td><td>''</td></tr>
1608                  * <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
1609                  * <tr><td>hyphenchar</td><td>string</td><td>'&amp;shy;'</td></tr>
1610                  * <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
1611                  * <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
1612                  * <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
1613                  * <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
1614                  * <tr><td>enablecache</td><td>boolean</td><td>true</td></tr>
1615                  * <tr><td>enablereducedpatternset</td><td>boolean</td><td>false</td></tr>
1616                  * <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
1617                  * <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
1618                  * <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
1619                  * <tr><td>selectorfunction</td><td>function</td><td>[…]</td></tr>
1620                  * <tr><td>safecopy</td><td>boolean</td><td>true</td></tr>
1621                  * <tr><td>doframes</td><td>boolean</td><td>false</td></tr>
1622                  * <tr><td>storagetype</td><td>string</td><td>'none'</td></tr>
1623                  * </table>
1624                  * @public
1625                  * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1626          * &lt;script type = "text/javascript"&gt;
1627          *     Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
1628          *     Hyphenator.run();
1629          * &lt;/script&gt;
1630          */
1631                 config: function (obj) {
1632                         var assert = function (name, type) {
1633                                         if (typeof obj[name] === type) {
1634                                                 return true;
1635                                         } else {
1636                                                 onError(new Error('Config onError: ' + name + ' must be of type ' + type));
1637                                                 return false;
1638                                         }
1639                                 },
1640                                 key;
1641                         for (key in obj) {
1642                                 if (obj.hasOwnProperty(key)) {
1643                                         switch (key) {
1644                                         case 'classname':
1645                                                 if (assert('classname', 'string')) {
1646                                                         hyphenateClass = obj[key];
1647                                                 }
1648                                                 break;
1649                                         case 'donthyphenateclassname':
1650                                                 if (assert('donthyphenateclassname', 'string')) {
1651                                                         dontHyphenateClass = obj[key];
1652                                                 }                                               
1653                                                 break;
1654                                         case 'minwordlength':
1655                                                 if (assert('minwordlength', 'number')) {
1656                                                         min = obj[key];
1657                                                 }
1658                                                 break;
1659                                         case 'hyphenchar':
1660                                                 if (assert('hyphenchar', 'string')) {
1661                                                         if (obj.hyphenchar === '&shy;') {
1662                                                                 obj.hyphenchar = String.fromCharCode(173);
1663                                                         }
1664                                                         hyphen = obj[key];
1665                                                 }
1666                                                 break;
1667                                         case 'urlhyphenchar':
1668                                                 if (obj.hasOwnProperty('urlhyphenchar')) {
1669                                                         if (assert('urlhyphenchar', 'string')) {
1670                                                                 urlhyphen = obj[key];
1671                                                         }
1672                                                 }
1673                                                 break;
1674                                         case 'togglebox':
1675                                                 if (assert('togglebox', 'function')) {
1676                                                         toggleBox = obj[key];
1677                                                 }
1678                                                 break;
1679                                         case 'displaytogglebox':
1680                                                 if (assert('displaytogglebox', 'boolean')) {
1681                                                         displayToggleBox = obj[key];
1682                                                 }
1683                                                 break;
1684                                         case 'remoteloading':
1685                                                 if (assert('remoteloading', 'boolean')) {
1686                                                         enableRemoteLoading = obj[key];
1687                                                 }
1688                                                 break;
1689                                         case 'enablecache':
1690                                                 if (assert('enablecache', 'boolean')) {
1691                                                         enableCache = obj[key];
1692                                                 }
1693                                                 break;
1694                                         case 'enablereducedpatternset':
1695                                                 if (assert('enablereducedpatternset', 'boolean')) {
1696                                                         enableReducedPatternSet = obj[key];
1697                                                 }
1698                                                 break;
1699                                         case 'onhyphenationdonecallback':
1700                                                 if (assert('onhyphenationdonecallback', 'function')) {
1701                                                         onHyphenationDone = obj[key];
1702                                                 }
1703                                                 break;
1704                                         case 'onerrorhandler':
1705                                                 if (assert('onerrorhandler', 'function')) {
1706                                                         onError = obj[key];
1707                                                 }
1708                                                 break;
1709                                         case 'intermediatestate':
1710                                                 if (assert('intermediatestate', 'string')) {
1711                                                         intermediateState = obj[key];
1712                                                 }
1713                                                 break;
1714                                         case 'selectorfunction':
1715                                                 if (assert('selectorfunction', 'function')) {
1716                                                         selectorFunction = obj[key];
1717                                                 }
1718                                                 break;
1719                                         case 'safecopy':
1720                                                 if (assert('safecopy', 'boolean')) {
1721                                                         safeCopy = obj[key];
1722                                                 }
1723                                                 break;
1724                                         case 'doframes':
1725                                                 if (assert('doframes', 'boolean')) {
1726                                                         doFrames = obj[key];
1727                                                 }
1728                                                 break;
1729                                         case 'storagetype':
1730                                                 if (assert('storagetype', 'string')) {
1731                                                         storageType = obj[key];
1732                                                 }                                               
1733                                                 break;
1734                                         case 'orphancontrol':
1735                                                 if (assert('orphancontrol', 'number')) {
1736                                                         orphanControl = obj[key];
1737                                                 }
1738                                                 break;
1739                                         default:
1740                                                 onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
1741                                         }
1742                                 }
1743                         }
1744                 },
1746                 /**
1747                  * @name Hyphenator.run
1748                          * @description
1749                  * Bootstrap function that starts all hyphenation processes when called.
1750                  * @public
1751                  * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1752          * &lt;script type = "text/javascript"&gt;
1753          *   Hyphenator.run();
1754          * &lt;/script&gt;
1755          */
1756                 run: function () {
1757                         documentCount = 0;
1758                         var process = function () {
1759                                 try {
1760                                         if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
1761                                                 return; //we are in a frameset
1762                                         }
1763                                         documentCount++;
1764                                         autoSetMainLanguage(undefined);
1765                                         gatherDocumentInfos();
1766                                         prepare(hyphenateDocument);
1767                                         if (displayToggleBox) {
1768                                                 toggleBox(true);
1769                                         }
1770                                         if (safeCopy) {
1771                                                 registerOnCopy();
1772                                         }
1773                                 } catch (e) {
1774                                         onError(e);
1775                                 }
1776                         }, i, haveAccess, fl = window.frames.length;
1777                         try {
1778                                 if (storageType !== 'none' &&
1779                                         typeof(window.localStorage) !== 'undefined' &&
1780                                         typeof(window.sessionStorage) !== 'undefined' &&
1781                                         typeof(window.JSON.stringify) !== 'undefined' &&
1782                                         typeof(window.JSON.parse) !== 'undefined') {
1783                                         switch (storageType) {
1784                                         case 'session':
1785                                                 storage = window.sessionStorage;
1786                                                 break;
1787                                         case 'local':
1788                                                 storage = window.localStorage;
1789                                                 break;
1790                                         default:
1791                                                 storage = undefined;
1792                                                 break;
1793                                         }
1794                                 }
1795                         } catch (f) {
1796                                 //FF throws an error if DOM.storage.enabled is set to false
1797                         }
1798                         if (!documentLoaded && !isBookmarklet) {
1799                                 runOnContentLoaded(window, process);
1800                         }
1801                         if (isBookmarklet || documentLoaded) {
1802                                 if (doFrames && fl > 0) {
1803                                         for (i = 0; i < fl; i++) {
1804                                                 haveAccess = undefined;
1805                                                 //try catch isn't enough for webkit
1806                                                 try {
1807                                                         //opera throws only on document.toString-access
1808                                                         haveAccess = window.frames[i].document.toString();
1809                                                 } catch (e) {
1810                                                         haveAccess = undefined;
1811                                                 }
1812                                                 if (!!haveAccess) {
1813                                                         contextWindow = window.frames[i];
1814                                                         process();
1815                                                 }                                               
1816                                         }
1817                                 }
1818                                 contextWindow = window;
1819                                 process();
1820                         }
1821                 },
1822                 
1823                 /**
1824                  * @name Hyphenator.addExceptions
1825                          * @description
1826                  * Adds the exceptions from the string to the appropriate language in the 
1827                  * {@link Hyphenator-languages}-object
1828                  * @param {string} lang The language
1829                  * @param {string} words A comma separated string of hyphenated words WITH spaces.
1830                  * @public
1831                  * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1832          * &lt;script type = "text/javascript"&gt;
1833          *   Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
1834          *   Hyphenator.run();
1835          * &lt;/script&gt;
1836          */
1837                 addExceptions: function (lang, words) {
1838                         if (lang === '') {
1839                                 lang = 'global';
1840                         }
1841                         if (exceptions.hasOwnProperty(lang)) {
1842                                 exceptions[lang] += ", " + words;
1843                         } else {
1844                                 exceptions[lang] = words;
1845                         }
1846                 },
1847                 
1848                 /**
1849                  * @name Hyphenator.hyphenate
1850                          * @public
1851                  * @description
1852                  * Hyphenates the target. The language patterns must be loaded.
1853                  * If the target is a string, the hyphenated string is returned,
1854                  * if it's an object, the values are hyphenated directly.
1855                  * @param {string|Object} target the target to be hyphenated
1856                  * @param {string} lang the language of the target
1857                  * @returns string
1858                  * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1859                  * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
1860          * &lt;script type = "text/javascript"&gt;
1861                  * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
1862                  * &lt;/script&gt;
1863                  */
1864                 hyphenate: function (target, lang) {
1865                         var hyphenate, n, i;
1866                         if (Hyphenator.languages.hasOwnProperty(lang)) {
1867                                 if (!Hyphenator.languages[lang].prepared) {
1868                                         prepareLanguagesObj(lang);
1869                                 }
1870                                 hyphenate = function (word) {
1871                                         if (urlOrMailRE.test(word)) {
1872                                                 return hyphenateURL(word);
1873                                         } else {
1874                                                 return hyphenateWord(lang, word);
1875                                         }
1876                                 };
1877                                 if (typeof target === 'string' || target.constructor === String) {
1878                                         return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1879                                 } else if (typeof target === 'object') {
1880                                         i = 0;
1881                                         while (!!(n = target.childNodes[i++])) {
1882                                                 if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
1883                                                         n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1884                                                 } else if (n.nodeType === 1) {
1885                                                         if (n.lang !== '') {
1886                                                                 Hyphenator.hyphenate(n, n.lang);
1887                                                         } else {
1888                                                                 Hyphenator.hyphenate(n, lang);
1889                                                         }
1890                                                 }
1891                                         }
1892                                 }
1893                         } else {
1894                                 onError(new Error('Language "' + lang + '" is not loaded.'));
1895                         }
1896                 },
1897                 
1898                 /**
1899                  * @name Hyphenator.getRedPatternSet
1900                          * @description
1901                  * Returns {@link Hyphenator-isBookmarklet}.
1902                  * @param {string} lang the language patterns are stored for
1903                  * @returns object {'patk': pat}
1904                  * @public
1905          */
1906                 getRedPatternSet: function (lang) {
1907                         return Hyphenator.languages[lang].redPatSet;
1908                 },
1909                 
1910                 /**
1911                  * @name Hyphenator.isBookmarklet
1912                          * @description
1913                  * Returns {@link Hyphenator-isBookmarklet}.
1914                  * @returns boolean
1915                  * @public
1916          */
1917                 isBookmarklet: function () {
1918                         return isBookmarklet;
1919                 },
1921                 getConfigFromURI: function () {
1922                         var loc = null, re = {}, jsArray = document.getElementsByTagName('script'), i, j, l, s, gp, option;
1923                         for (i = 0, l = jsArray.length; i < l; i++) {
1924                                 if (!!jsArray[i].getAttribute('src')) {
1925                                         loc = jsArray[i].getAttribute('src');
1926                                 }
1927                                 if (!loc) {
1928                                         continue;
1929                                 } else {
1930                                         s = loc.indexOf('Hyphenator.js?');
1931                                         if (s === -1) {
1932                                                 continue;
1933                                         }
1934                                         gp = loc.substring(s + 14).split('&');
1935                                         for (j = 0; j < gp.length; j++) {
1936                                                 option = gp[j].split('=');
1937                                                 if (option[0] === 'bm') {
1938                                                         continue;
1939                                                 }
1940                                                 if (option[1] === 'true') {
1941                                                         re[option[0]] = true;
1942                                                         continue;
1943                                                 }
1944                                                 if (option[1] === 'false') {
1945                                                         re[option[0]] = false;
1946                                                         continue;
1947                                                 }
1948                                                 if (isFinite(option[1])) {
1949                                                         re[option[0]] = parseInt(option[1], 10);
1950                                                         continue;
1951                                                 }
1952                                                 if (option[0] === 'onhyphenationdonecallback') {
1953                                                         re[option[0]] = new Function('', option[1]);
1954                                                         continue;
1955                                                 }
1956                                                 re[option[0]] = option[1];
1957                                         }
1958                                         break;
1959                                 }
1960                         }
1961                         return re;
1962                 },
1964                 /**
1965                  * @name Hyphenator.toggleHyphenation
1966                          * @description
1967                  * Checks the current state of the ToggleBox and removes or does hyphenation.
1968                  * @public
1969          */
1970                 toggleHyphenation: function () {
1971                         switch (state) {
1972                         case 3:
1973                                 removeHyphenationFromDocument();
1974                                 toggleBox(false);
1975                                 break;
1976                         case 4:
1977                                 hyphenateDocument();
1978                                 toggleBox(true);
1979                                 break;
1980                         }
1981                 }
1982         };
1983 }(window));
1985 //Export properties/methods (for google closure compiler)
1986 Hyphenator['languages'] = Hyphenator.languages;
1987 Hyphenator['config'] = Hyphenator.config;
1988 Hyphenator['run'] = Hyphenator.run;
1989 Hyphenator['addExceptions'] = Hyphenator.addExceptions;
1990 Hyphenator['hyphenate'] = Hyphenator.hyphenate;
1991 Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
1992 Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
1993 Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
1994 Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
1995 window['Hyphenator'] = Hyphenator;
1997 if (Hyphenator.isBookmarklet()) {
1998         Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true});
1999         Hyphenator.config(Hyphenator.getConfigFromURI());
2000         Hyphenator.run();