PageSourceSearch

https://iesvcentenario.org/plugins/content/admirorcolumnizer/admirorcolumnizer/scripts/Hyphenator.js

js iesvcentenario.org collected 2026-10-02 00:39:25 UTC 72,959 bytes, 2,390 lines download raw bytes

1/** @license Hyphenator 4.0.0 - client side hyphenation for webbrowsers
2 *  Copyright (C) 2011  Mathias Nater, Zürich (mathias at mnn dot ch)
3 *  Project and Source hosted on http://code.google.com/p/hyphenator/
4 * 
5 *  This JavaScript code is free software: you can redistribute
6 *  it and/or modify it under the terms of the GNU Lesser
7 *  General Public License (GNU LGPL) as published by the Free Software
8 *  Foundation, either version 3 of the License, or (at your option)
9 *  any later version.  The code is distributed WITHOUT ANY WARRANTY;
10 *  without even the implied warranty of MERCHANTABILITY or FITNESS
11 *  FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
12 *
13 *  As additional permission under GNU GPL version 3 section 7, you
14 *  may distribute non-source (e.g., minimized or compacted) forms of
15 *  that code without the copy of the GNU GPL normally required by
16 *  section 4, provided you include this license notice and a URL
17 *  through which recipients can access the Corresponding Source.
18 *
19 * 
20 *  Hyphenator.js contains code from Bram Steins hypher.js-Project:
21 *  https://github.com/bramstein/Hypher
22 *  
23 *  Code from this project is marked in the source and belongs 
24 *  to the following license:
25 *  
26 *  Copyright (c) 2011, Bram Stein
27 *  All rights reserved.
28 *  
29 *  Redistribution and use in source and binary forms, with or without 
30 *  modification, are permitted provided that the following conditions 
31 *  are met:
32 *   
33 *   1. Redistributions of source code must retain the above copyright
34 *      notice, this list of conditions and the following disclaimer. 
35 *   2. Redistributions in binary form must reproduce the above copyright 
36 *      notice, this list of conditions and the following disclaimer in the 
37 *      documentation and/or other materials provided with the distribution. 
38 *   3. The name of the author may not be used to endorse or promote products 
39 *      derived from this software without specific prior written permission. 
40 *  
41 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED 
42 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
43 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
44 *  EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
45 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
46 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
47 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
48 *  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
49 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
50 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 *  
52 */
53 
54/* 
55 *  Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
56 */
57 
58/* The following comment is for JSLint: */
59/*global window, ActiveXObject, unescape */
60/*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 */
61
62
63/**
64 * @constructor
65 * @description Provides all functionality to do hyphenation, except the patterns that are loaded
66 * externally.
67 * @author Mathias Nater, <a href = "mailto:[email protected]">[email protected]</a>
68 * @version X.Y.Z
69 * @namespace Holds all methods and properties
70 * @example
71 * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
72 * &lt;script type = "text/javascript"&gt;
73 *   Hyphenator.run();
74 * &lt;/script&gt;
75 */
76var Hyphenator = (function (window) {
77
78	var
79	/**
80	 * @name Hyphenator-supportedLang
81	 * @description
82	 * A key-value object that stores supported languages.
83	 * The key is the bcp47 code of the language and the value
84	 * is the (abbreviated) filename of the pattern file.
85	 * @type {Object.<string, string>}
86	 * @private
87	 * @example
88	 * Check if language lang is supported:
89	 * if (supportedLang.hasOwnProperty(lang))
90	 */
91	supportedLang = {
92		'sr-yu': 'sr-yu.js',
93		'be': 'be.js',
94		'ca': 'ca.js',
95		'cs': 'cs.js',
96		'da': 'da.js',
97		'bn': 'bn.js',
98		'de': 'de.js',
99		'el': 'el-monoton.js',
100		'el-monoton': 'el-monoton.js',
101		'el-polyton': 'el-polyton.js',
102		'en': 'en-us.js',
103		'en-gb': 'en-gb.js',
104		'en-us': 'en-us.js',
105		'es': 'es.js',
106		'fi': 'fi.js',
107		'fr': 'fr.js',
108		'grc': 'grc.js',
109		'gu': 'gu.js',
110		'hi': 'hi.js',
111		'hu': 'hu.js',
112		'hy': 'hy.js',
113		'it': 'it.js',
114		'kn': 'kn.js',
115		'la': 'la.js',
116		'lt': 'lt.js',
117		'lv': 'lv.js',
118		'ml': 'ml.js',
119		'nb': 'nb-no.js',
120		'no': 'nb-no.js',
121		'nb-no': 'nb-no.js',
122		'nl': 'nl.js',
123		'or': 'or.js',
124		'pa': 'pa.js',
125		'pl': 'pl.js',
126		'pt': 'pt.js',
127		'ru': 'ru.js',
128		'sk': 'sk.js',
129		'sl': 'sl.js',
130		'sv': 'sv.js',
131		'ta': 'ta.js',
132		'te': 'te.js',
133		'tr': 'tr.js',
134		'uk': 'uk.js'
135	},
136
137	/**
138	 * @name Hyphenator-languageHint
139	 * @description
140	 * An automatically generated string to be displayed in a prompt if the language can't be guessed.
141	 * The string is generated using the supportedLang-object.
142	 * @see Hyphenator-supportedLang
143	 * @type {string}
144	 * @private
145	 * @see Hyphenator-autoSetMainLanguage
146	 */
147
148	languageHint = (function () {
149		var k, r = '';
150		for (k in supportedLang) {
151			if (supportedLang.hasOwnProperty(k)) {
152				r += k + ', ';
153			}
154		}
155		r = r.substring(0, r.length - 2);
156		return r;
157	}()),
158	
159	/**
160	 * @name Hyphenator-prompterStrings
161	 * @description
162	 * A key-value object holding the strings to be displayed if the language can't be guessed
163	 * If you add hyphenation patterns change this string.
164	 * @type {Object.<string,string>}
165	 * @private
166	 * @see Hyphenator-autoSetMainLanguage
167	 */	
168	prompterStrings = {
169		'be': 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:',
170		'cs': 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:',
171		'da': 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:',
172		'de': 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:',
173		'en': 'The language of this website could not be determined automatically. Please indicate the main language:',
174		'es': 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:',
175		'fi': 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:',
176		'fr': 'La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:',
177		'hu': 'A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:',
178		'hy': 'Չհաջողվեց հայտնաբերել այս կայքի լեզուն։ Խնդրում ենք նշեք հիմնական լեզուն՝',
179		'it': 'Lingua del sito sconosciuta. Indicare una lingua, per favore:',
180		'kn': 'ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:',
181		'lt': 'Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:',
182		'lv': 'Šīs lapas valodu nevarēja noteikt automātiski. Lūdzu norādiet pamata valodu:',
183		'ml': 'ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:',
184		'nl': 'De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:',
185		'no': 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:',
186		'pt': 'A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:',
187		'ru': 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:',
188		'sl': 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:',
189		'sv': 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:',
190		'tr': 'Bu web sitesinin dili otomatik olarak tespit edilememiştir. Lütfen dökümanın dilini seçiniz%A0:',
191		'uk': 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:'
192	},
193	
194	/**
195	 * @name Hyphenator-basePath
196	 * @description
197	 * A string storing the basepath from where Hyphenator.js was loaded.
198	 * This is used to load the patternfiles.
199	 * The basepath is determined dynamically by searching all script-tags for Hyphenator.js
200	 * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback.
201	 * @type {string}
202	 * @private
203	 * @see Hyphenator-loadPatterns
204	 */
205	basePath = (function () {
206		var s = document.getElementsByTagName('script'), i = 0, p, src, t;
207		while (!!(t = s[i++])) {
208			if (!t.src) {
209				continue;
210			}
211			src = t.src;
212			p = src.indexOf('Hyphenator.js');
213			if (p !== -1) {
214				return src.substring(0, p);
215			}
216		}
217		return 'http://hyphenator.googlecode.com/svn/trunk/';
218	}()),
219
220	/**
221	 * @name Hyphenator-isLocal
222	 * @description
223	 * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
224	 * it's loaded from an external source (i.e. directly from google.code)
225	 */
226	isLocal = (function () {
227		var re = false;
228		if (window.location.href.indexOf(basePath) !== -1) {
229			re = true;
230		}
231		return re;
232	}()),
233	
234	/**
235	 * @name Hyphenator-documentLoaded
236	 * @description
237	 * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
238	 */
239	documentLoaded = false,
240	documentCount = 0,
241	
242	/**
243	 * @name Hyphenator-persistentConfig
244	 * @description
245	 * if persistentConfig is set to true (defaults to false), config options and the state of the 
246	 * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven't to be
247	 * set for each page.
248	 */	
249	persistentConfig = false,	
250
251	/**
252	 * @name Hyphenator-contextWindow
253	 * @description
254	 * contextWindow stores the window for the document to be hyphenated.
255	 * If there are frames this will change.
256	 * So use contextWindow instead of window!
257	 */
258	contextWindow = window,
259
260	/**
261	 * @name Hyphenator-doFrames
262	 * @description
263	 * switch to control if frames/iframes should be hyphenated, too
264	 * defaults to false (frames are a bag of hurt!)
265	 */
266	doFrames = false,
267	
268	/**
269	 * @name Hyphenator-dontHyphenate
270	 * @description
271	 * A key-value object containing all html-tags whose content should not be hyphenated
272	 * @type {Object.<string,boolean>}
273	 * @private
274	 * @see Hyphenator-hyphenateElement
275	 */
276	dontHyphenate = {'script': true, 'code': true, 'pre': true, 'img': true, 'br': true, 'samp': true, 'kbd': true, 'var': true, 'abbr': true, 'acronym': true, 'sub': true, 'sup': true, 'button': true, 'option': true, 'label': true, 'textarea': true, 'input': true, 'math': true, 'svg': true},
277
278	/**
279	 * @name Hyphenator-enableCache
280	 * @description
281	 * A variable to set if caching is enabled or not
282	 * @type boolean
283	 * @default true
284	 * @private
285	 * @see Hyphenator.config
286	 * @see hyphenateWord
287	 */
288	enableCache = true,
289
290	/**
291	 * @name Hyphenator-storageType
292	 * @description
293	 * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
294	 * @type {string}
295	 * @default 'none'
296	 * @private
297	 * @see Hyphenator.config
298	 */	
299	storageType = 'local',
300
301	/**
302	 * @name Hyphenator-storage
303	 * @description
304	 * An alias to the storage-Method defined in storageType.
305	 * Set by Hyphenator.run()
306	 * @type {Object|undefined}
307	 * @default null
308	 * @private
309	 * @see Hyphenator.run
310	 */	
311	storage,
312	
313	/**
314	 * @name Hyphenator-enableReducedPatternSet
315	 * @description
316	 * A variable to set if storing the used patterns is set
317	 * @type boolean
318	 * @default false
319	 * @private
320	 * @see Hyphenator.config
321	 * @see hyphenateWord
322	 * @see Hyphenator.getRedPatternSet
323	 */	
324	enableReducedPatternSet = false,
325	
326	/**
327	 * @name Hyphenator-enableRemoteLoading
328	 * @description
329	 * A variable to set if pattern files should be loaded remotely or not
330	 * @type boolean
331	 * @default true
332	 * @private
333	 * @see Hyphenator.config
334	 * @see Hyphenator-loadPatterns
335	 */
336	enableRemoteLoading = true,
337	
338	/**
339	 * @name Hyphenator-displayToggleBox
340	 * @description
341	 * A variable to set if the togglebox should be displayed or not
342	 * @type boolean
343	 * @default false
344	 * @private
345	 * @see Hyphenator.config
346	 * @see Hyphenator-toggleBox
347	 */
348	displayToggleBox = false,
349
350	/**
351	 * @name Hyphenator-css3
352	 * @description
353	 * A variable to set if css3 hyphenation should be used
354	 * @type boolean
355	 * @default false
356	 * @private
357	 * @see Hyphenator.config
358	 */
359	css3 = false,
360	/**
361	 * @name Hyphenator-css3_hsupport
362	 * @description
363	 * A generated object containing information for CSS3-hyphenation support
364	 * {
365	 *   support: boolean,
366	 *   property: <the property name to access hyphen-settings>,
367	 *   languages: <an object containing supported languages>
368	 * }
369	 * @type object
370	 * @default undefined
371	 * @private
372	 * @see Hyphenator-css3_gethsupport
373	 */
374	css3_h9n,
375	/**
376	 * @name Hyphenator-css3_gethsupport
377	 * @description
378	 * This function sets Hyphenator-css3_h9n for the current UA
379	 * @type function
380	 * @private
381	 * @see Hyphenator-css3_h9n
382	 */
383	css3_gethsupport = function () {
384		var s,
385		ua = navigator.userAgent,
386		r = {
387			support: false,
388			property: '',
389			languages: {}
390		};
391		if (window.getComputedStyle) {
392			s = window.getComputedStyle(window.document.getElementsByTagName('body')[0]);
393		} else {
394			//ancient Browser don't support CSS3 anyway
395			css3_h9n = r;
396			return;
397		}
398		if (ua.indexOf('Chrome') !== -1) {
399			//Chrome actually knows -webkit-hyphens but does no hyphenation
400			r.support = false;
401		} else if ((ua.indexOf('Safari') !== -1) && (s['-webkit-hyphens'] !== undefined)) {
402			r.support = true;
403			r.property = '-webkit-hyphens';
404			if (ua.indexOf('Mobile') !== -1) {
405				//iOS only hyphenates in systemlanguage
406				r.languages[navigator.language.split('-')[0]] = true;
407			} else {
408				//Desktop Safari only hyphenates some languages:
409				r.languages = {
410					de: true,
411					en: true,
412					es: true,
413					fr: true,
414					it: true,
415					nl: true,
416					ru: true,
417					zh: true
418				};
419			}
420		} else if ((ua.indexOf('Firefox') !== -1) && (s['MozHyphens'] !== undefined)) {
421			r.support = true;
422			r.property = 'MozHyphens';
423			r.languages = {
424				en: true
425			};
426		}
427		css3_h9n = r;
428	},
429	
430	/**
431	 * @name Hyphenator-hyphenateClass
432	 * @description
433	 * A string containing the css-class-name for the hyphenate class
434	 * @type {string}
435	 * @default 'hyphenate'
436	 * @private
437	 * @example
438	 * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
439	 * @see Hyphenator.config
440	 */
441	hyphenateClass = 'hyphenate',
442
443	/**
444	 * @name Hyphenator-dontHyphenateClass
445	 * @description
446	 * A string containing the css-class-name for elements that should not be hyphenated
447	 * @type {string}
448	 * @default 'donthyphenate'
449	 * @private
450	 * @example
451	 * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
452	 * @see Hyphenator.config
453	 */
454	dontHyphenateClass = 'donthyphenate',
455	
456	/**
457	 * @name Hyphenator-min
458	 * @description
459	 * A number wich indicates the minimal length of words to hyphenate.
460	 * @type {number}
461	 * @default 6
462	 * @private
463	 * @see Hyphenator.config
464	 */	
465	min = 6,
466	
467	/**
468	 * @name Hyphenator-orphanControl
469	 * @description
470	 * Control how the last words of a line are handled:
471	 * level 1 (default): last word is hyphenated
472	 * level 2: last word is not hyphenated
473	 * level 3: last word is not hyphenated and last space is non breaking
474	 * @type {number}
475	 * @default 1
476	 * @private
477	 */
478	orphanControl = 1,
479	
480	/**
481	 * @name Hyphenator-isBookmarklet
482	 * @description
483	 * Indicates if Hyphanetor runs as bookmarklet or not.
484	 * @type boolean
485	 * @default false
486	 * @private
487	 */	
488	isBookmarklet = (function () {
489		var loc = null, re = false, jsArray = document.getElementsByTagName('script'), i, l;
490		for (i = 0, l = jsArray.length; i < l; i++) {
491			if (!!jsArray[i].getAttribute('src')) {
492				loc = jsArray[i].getAttribute('src');
493			}
494			if (!loc) {
495				continue;
496			} else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
497				re = true;
498			}
499		}
500		return re;
501	}()),
502	
503	/**
504	 * @name Hyphenator-mainLanguage
505	 * @description
506	 * The general language of the document. In contrast to {@link Hyphenator-defaultLanguage},
507	 * mainLanguage is defined by the client (i.e. by the html or by a prompt).
508	 * @type {string|null}
509	 * @private
510	 * @see Hyphenator-autoSetMainLanguage
511	 */	
512	mainLanguage = null,
513
514	/**
515	 * @name Hyphenator-defaultLanguage
516	 * @description
517	 * The language defined by the developper. This language setting is defined by a config option.
518	 * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can
519	 * be found (i.e. just before the prompt).
520	 * @type {string|null}
521	 * @private
522	 * @see Hyphenator-autoSetMainLanguage
523	 */	
524	defaultLanguage = '',
525	
526
527	/**
528	 * @name Hyphenator-elements
529	 * @description
530	 * An array holding all elements that have to be hyphenated. This var is filled by
531	 * {@link Hyphenator-gatherDocumentInfos}
532	 * @type {Array}
533	 * @private
534	 */	
535	elements = (function () {
536		var Element = function (element, data) {
537			this.element = element;
538			this.hyphenated = false;
539			this.treated = false; //collected but not hyphenated (dohyphenation is off)
540			this.data = data;
541		},
542		ElementCollection = function () {
543			this.count = 0;
544			this.hyCount = 0;
545			this.list = {};
546		};
547		ElementCollection.prototype = {
548			add: function (el, lang, data) {
549				if (!this.list.hasOwnProperty(lang)) {
550					this.list[lang] = [];
551				}
552				this.list[lang].push(new Element(el, data));
553				this.count += 1;
554			},
555			each: function (fn) {
556				var k;
557				for (k in this.list) {
558					if (this.list.hasOwnProperty(k)) {
559						fn(k, this.list[k]);
560					}
561				}
562			}
563		};
564		return new ElementCollection();
565	}()),
566
567	
568	/**
569	 * @name Hyphenator-exceptions
570	 * @description
571	 * An object containing exceptions as comma separated strings for each language.
572	 * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
573	 * @see Hyphenator-prepareLanguagesObj
574	 * @type {Object}
575	 * @private
576	 */	
577	exceptions = {},
578	
579	/**
580	 * @name Hyphenator-docLanguages
581	 * @description
582	 * An object holding all languages used in the document. This is filled by
583	 * {@link Hyphenator-gatherDocumentInfos}
584	 * @type {Object}
585	 * @private
586	 */	
587	docLanguages = {},
588
589	/**
590	 * @name Hyphenator-state
591	 * @description
592	 * A number that inidcates the current state of the script
593	 * 0: not initialized
594	 * 1: loading patterns
595	 * 2: ready
596	 * 3: hyphenation done
597	 * 4: hyphenation removed
598	 * @type {number}
599	 * @private
600	 */	
601	state = 0,
602
603	/**
604	 * @name Hyphenator-url
605	 * @description
606	 * A string containing a RegularExpression to match URL's
607	 * @type {string}
608	 * @private
609	 */	
610	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#!:\\.?\\+=&%@!\\-]*)*',
611	//      protocoll     usr     pwd                    ip               or                          host                 tld        port               path
612	/**
613	 * @name Hyphenator-mail
614	 * @description
615	 * A string containing a RegularExpression to match mail-adresses
616	 * @type {string}
617	 * @private
618	 */	
619	mail = '[\\w-\\.]+@[\\w\\.]+',
620
621	/**
622	 * @name Hyphenator-urlRE
623	 * @description
624	 * A RegularExpressions-Object for url- and mail adress matching
625	 * @type {RegExp}
626	 * @private
627	 */		
628	urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),
629
630	/**
631	 * @name Hyphenator-zeroWidthSpace
632	 * @description
633	 * A string that holds a char.
634	 * Depending on the browser, this is the zero with space or an empty string.
635	 * zeroWidthSpace is used to break URLs
636	 * @type {string}
637	 * @private
638	 */		
639	zeroWidthSpace = (function () {
640		var zws, ua = navigator.userAgent.toLowerCase();
641		zws = String.fromCharCode(8203); //Unicode zero width space
642		if (ua.indexOf('msie 6') !== -1) {
643			zws = ''; //IE6 doesn't support zws
644		}
645		if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
646			zws = ''; //opera 10 on XP doesn't support zws
647		}
648		return zws;
649	}()),
650	
651	/**
652	 * @name Hyphenator-createElem
653	 * @description
654	 * A function alias to document.createElementNS or document.createElement
655	 * @type {function(string, Object)}
656	 * @private
657	 */		
658	createElem = function (tagname, context) {
659		context = context || contextWindow;
660		if (document.createElementNS) {
661			return context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
662		} else if (document.createElement) {
663			return context.document.createElement(tagname);
664		}
665	},
666	
667	/**
668	 * @name Hyphenator-onHyphenationDone
669	 * @description
670	 * A method to be called, when the last element has been hyphenated or the hyphenation has been
671	 * removed from the last element.
672	 * @see Hyphenator.config
673	 * @type {function()}
674	 * @private
675	 */		
676	onHyphenationDone = function () {},
677
678	/**
679	 * @name Hyphenator-onError
680	 * @description
681	 * A function that can be called upon an error.
682	 * @see Hyphenator.config
683	 * @type {function(Object)}
684	 * @private
685	 */		
686	onError = function (e) {
687		window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
688	},
689
690	/**
691	 * @name Hyphenator-selectorFunction
692	 * @description
693	 * A function that has to return a HTMLNodeList of Elements to be hyphenated.
694	 * By default it uses the classname ('hyphenate') to select the elements.
695	 * @see Hyphenator.config
696	 * @type {function()}
697	 * @private
698	 */		
699	selectorFunction = function () {
700		var tmp, el = [], i, l;
701		if (document.getElementsByClassName) {
702			el = contextWindow.document.getElementsByClassName(hyphenateClass);
703		} else {
704			tmp = contextWindow.document.getElementsByTagName('*');
705			l = tmp.length;
706			for (i = 0; i < l; i++)
707			{
708				if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
709					el.push(tmp[i]);
710				}
711			}
712		}
713		return el;
714	},
715
716	/**
717	 * @name Hyphenator-intermediateState
718	 * @description
719	 * The value of style.visibility of the text while it is hyphenated.
720	 * @see Hyphenator.config
721	 * @type {string}
722	 * @private
723	 */		
724	intermediateState = 'hidden',
725	
726	/**
727	 * @name Hyphenator-unhide
728	 * @description
729	 * How hidden elements unhide: either simultaneous (default: 'wait') or progressively.
730	 * 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw)
731	 * With 'progressiv' Hyphenator.js unhides elements as soon as they are hyphenated.
732	 * @see Hyphenator.config
733	 * @type {string}
734	 * @private
735	 */		
736	unhide = 'wait',
737	
738	/**
739	 * @name Hyphenator-hyphen
740	 * @description
741	 * A string containing the character for in-word-hyphenation
742	 * @type {string}
743	 * @default the soft hyphen
744	 * @private
745	 * @see Hyphenator.config
746	 */
747	hyphen = String.fromCharCode(173),
748	
749	/**
750	 * @name Hyphenator-urlhyphen
751	 * @description
752	 * A string containing the character for url/mail-hyphenation
753	 * @type {string}
754	 * @default the zero width space
755	 * @private
756	 * @see Hyphenator.config
757	 * @see Hyphenator-zeroWidthSpace
758	 */
759	urlhyphen = zeroWidthSpace,
760
761	/**
762	 * @name Hyphenator-safeCopy
763	 * @description
764	 * Defines wether work-around for copy issues is active or not
765	 * Not supported by Opera (no onCopy handler)
766	 * @type boolean
767	 * @default true
768	 * @private
769	 * @see Hyphenator.config
770	 * @see Hyphenator-registerOnCopy
771	 */
772	safeCopy = true,
773	
774		
775	/*
776	 * runOnContentLoaded is based od jQuery.bindReady()
777	 * see
778	 * jQuery JavaScript Library v1.3.2
779	 * http://jquery.com/
780	 *
781	 * Copyright (c) 2009 John Resig
782	 * Dual licensed under the MIT and GPL licenses.
783	 * http://docs.jquery.com/License
784	 *
785	 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
786	 * Revision: 6246
787	 */
788	/**
789	 * @name Hyphenator-runOnContentLoaded
790	 * @description
791	 * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery
792	 * <a href = "http://jquery.com/</a>
793	 * I added some functionality: e.g. support for frames and iframes…
794	 * @param {Object} w the window-object
795	 * @param {function()} f the function to call onDOMContentLoaded
796	 * @private
797	 */
798	runOnContentLoaded = function (w, f) {
799		var DOMContentLoaded = function () {}, toplevel, hyphRunForThis = {};
800		if (documentLoaded && !hyphRunForThis[w.location.href]) {
801			f();
802			hyphRunForThis[w.location.href] = true;
803			return;
804		}
805		function init(context) {
806			contextWindow = context || window;
807			if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || contextWindow != window.parent)) {
808				documentLoaded = true;
809				f();
810				hyphRunForThis[contextWindow.location.href] = true;
811			}
812		}
813		
814		function doScrollCheck() {
815			try {
816				// If IE is used, use the trick by Diego Perini
817				// http://javascript.nwbox.com/IEContentLoaded/
818				document.documentElement.doScroll("left");
819			} catch (error) {
820				setTimeout(doScrollCheck, 1);
821				return;
822			}
823		
824			// and execute any waiting functions
825			init(window);
826		}
827
828		function doOnLoad() {
829			var i, haveAccess, fl = window.frames.length;
830			if (doFrames && fl > 0) {
831				for (i = 0; i < fl; i++) {
832					haveAccess = undefined;
833					//try catch isn't enough for webkit
834					try {
835						//opera throws only on document.toString-access
836						haveAccess = window.frames[i].document.toString();
837					} catch (e) {
838						haveAccess = undefined;
839					}
840					if (!!haveAccess) {
841						init(window.frames[i]);
842					}
843				}
844				contextWindow = window;
845				f();
846				hyphRunForThis[window.location.href] = true;
847			} else {
848				init(window);
849			}
850		}
851		
852		// Cleanup functions for the document ready method
853		if (document.addEventListener) {
854			DOMContentLoaded = function () {
855				document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
856				if (doFrames && window.frames.length > 0) {
857					//we are in a frameset, so do nothing but wait for onload to fire
858					return;
859				} else {
860					init(window);
861				}
862			};
863		
864		} else if (document.attachEvent) {
865			DOMContentLoaded = function () {
866				// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
867				if (document.readyState === "complete") {
868					document.detachEvent("onreadystatechange", DOMContentLoaded);
869					if (doFrames && window.frames.length > 0) {
870						//we are in a frameset, so do nothing but wait for onload to fire
871						return;
872					} else {
873						init(window);
874					}
875				}
876			};
877		}
878
879		// Mozilla, Opera and webkit nightlies currently support this event
880		if (document.addEventListener) {
881			// Use the handy event callback
882			document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
883			
884			// A fallback to window.onload, that will always work
885			window.addEventListener("load", doOnLoad, false);
886
887		// If IE event model is used
888		} else if (document.attachEvent) {
889			// ensure firing before onload,
890			// maybe late but safe also for iframes
891			document.attachEvent("onreadystatechange", DOMContentLoaded);
892			
893			// A fallback to window.onload, that will always work
894			window.attachEvent("onload", doOnLoad);
895
896			// If IE and not a frame
897			// continually check to see if the document is ready
898			toplevel = false;
899			try {
900				toplevel = window.frameElement === null;
901			} catch (e) {}
902
903			if (document.documentElement.doScroll && toplevel) {
904				doScrollCheck();
905			}
906		}
907
908	},
909
910
911
912	/**
913	 * @name Hyphenator-getLang
914	 * @description
915	 * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}.
916	 * @param {Object} el The first parameter is an DOM-Element-Object
917	 * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage}
918	 * if there's no language found for the element.
919	 * @private
920	 */
921	getLang = function (el, fallback) {
922		if (!!el.getAttribute('lang')) {
923			return el.getAttribute('lang').toLowerCase();
924		}
925		// The following doesn't work in IE due to a bug when getAttribute('xml:lang') in a table
926		/*if (!!el.getAttribute('xml:lang')) {
927			return el.getAttribute('xml:lang').substring(0, 2);
928		}*/
929		//instead, we have to do this (thanks to borgzor):
930		try {
931			if (!!el.getAttribute('xml:lang')) {
932				return el.getAttribute('xml:lang').toLowerCase();
933			}
934		} catch (ex) {}
935		if (el.tagName !== 'HTML') {
936			return getLang(el.parentNode, true);
937		}
938		if (fallback) {
939			return mainLanguage;
940		}
941		return null;
942	},
943	
944	/**
945	 * @name Hyphenator-autoSetMainLanguage
946	 * @description
947	 * Retrieves the language of the document from the DOM.
948	 * The function looks in the following places:
949	 * <ul>
950	 * <li>lang-attribute in the html-tag</li>
951	 * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
952	 * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
953	 * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
954	 * </li>
955	 * If nothing can be found a prompt using {@link Hyphenator-languageHint} and {@link Hyphenator-prompterStrings} is displayed.
956	 * If the retrieved language is in the object {@link Hyphenator-supportedLang} it is copied to {@link Hyphenator-mainLanguage}
957	 * @private
958	 */		
959	autoSetMainLanguage = function (w) {
960		w = w || contextWindow;
961		var el = w.document.getElementsByTagName('html')[0],
962			m = w.document.getElementsByTagName('meta'),
963			i, text, e, ul;
964		mainLanguage = getLang(el, false);
965		if (!mainLanguage) {
966			for (i = 0; i < m.length; i++) {
967				//<meta http-equiv = "content-language" content="xy">	
968				if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) {
969					mainLanguage = m[i].getAttribute('content').toLowerCase();
970				}
971				//<meta name = "DC.Language" content="xy">
972				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) {
973					mainLanguage = m[i].getAttribute('content').toLowerCase();
974				}			
975				//<meta name = "language" content = "xy">
976				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) {
977					mainLanguage = m[i].getAttribute('content').toLowerCase();
978				}
979			}
980		}
981		//get lang for frame from enclosing document
982		if (!mainLanguage && doFrames && contextWindow != window.parent) {
983			autoSetMainLanguage(window.parent);
984		}
985		//fallback to defaultLang if set
986		if (!mainLanguage && defaultLanguage !== '') {
987			mainLanguage = defaultLanguage;
988		}
989		//ask user for lang
990		if (!mainLanguage) {
991			text = '';
992			ul = navigator.language ? navigator.language : navigator.userLanguage;
993			ul = ul.substring(0, 2);
994			if (prompterStrings.hasOwnProperty(ul)) {
995				text = prompterStrings[ul];
996			} else {
997				text = prompterStrings.en;
998			}
999			text += ' (ISO 639-1)\n\n' + languageHint;
1000			mainLanguage = window.prompt(unescape(text), ul).toLowerCase();
1001		}
1002		if (!supportedLang.hasOwnProperty(mainLanguage)) {
1003			if (supportedLang.hasOwnProperty(mainLanguage.split('-')[0])) { //try subtag
1004				mainLanguage = mainLanguage.split('-')[0];
1005			} else {
1006				e = new Error('The language "' + mainLanguage + '" is not yet supported.');
1007				throw e;
1008			}
1009		}
1010	},
1011    
1012	/**
1013	 * @name Hyphenator-gatherDocumentInfos
1014	 * @description
1015	 * This method runs through the DOM and executes the process()-function on:
1016	 * - every node returned by the {@link Hyphenator-selectorFunction}.
1017	 * The process()-function copies the element to the elements-variable, sets its visibility
1018	 * to intermediateState, retrieves its language and recursivly desce
1018nds the DOM-tree until
1019	 * the child-Nodes aren't of type 1
1020	 * @private
1021	 */		
1022	gatherDocumentInfos = function () {
1023		var elToProcess, tmp, i = 0,
1024		process = function (el, hide, lang) {
1025			var n, i = 0, hyphenatorSettings = {};
1026
1027			if (el.lang && typeof(el.lang) === 'string') {
1028				lang = el.lang.toLowerCase(); //copy attribute-lang to internal lang
1029			} else if (lang) {
1030				lang = lang.toLowerCase();
1031			} else {
1032				lang = getLang(el, true);
1033			}
1034			
1035			//if css3-hyphenation is supported: use it!
1036			if (css3 && css3_h9n.support && !!css3_h9n.languages[lang]) {
1037				el.style[css3_h9n.property] = "auto";
1038				el.style['-webkit-locale'] = "'" + lang + "'";
1039			} else {
1040				if (intermediateState === 'hidden') {
1041					if (!!el.getAttribute('style')) {
1042						hyphenatorSettings.hasOwnStyle = true;
1043					} else {
1044						hyphenatorSettings.hasOwnStyle = false;					
1045					}
1046					hyphenatorSettings.isHidden = true;
1047					el.style.visibility = 'hidden';
1048				}
1049				if (supportedLang[lang]) {
1050					docLanguages[lang] = true;
1051				} else {
1052					if (supportedLang.hasOwnProperty(lang.split('-')[0])) { //try subtag
1053						lang = lang.split('-')[0];
1054						hyphenatorSettings.language = lang;
1055					} else if (!isBookmarklet) {
1056						onError(new Error('Language ' + lang + ' is not yet supported.'));
1057					}
1058				}				
1059				elements.add(el, lang, hyphenatorSettings);
1060			}
1061			while (!!(n = el.childNodes[i++])) {
1062				if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
1063					n.className.indexOf(dontHyphenateClass) === -1 && !(n in elToProcess)) {
1064					process(n, false, lang);
1065				}
1066			}
1067		};
1068		if (css3) {
1069			css3_gethsupport();
1070		}
1071		if (isBookmarklet) {
1072			elToProcess = contextWindow.document.getElementsByTagName('body')[0];
1073			process(elToProcess, false, mainLanguage);
1074		} else {
1075			elToProcess = selectorFunction();
1076			while (!!(tmp = elToProcess[i++]))
1077			{
1078				process(tmp, true, '');
1079			}			
1080		}
1081		if (elements.count === 0) {
1082			//nothing to hyphenate or all hyphenated b css3
1083			state = 3;
1084			onHyphenationDone();
1085		}
1086	},
1087		 
1088	
1089	/**
1090	 * @name Hyphenator-createTrie
1091	 * @description
1092	 * converts patterns of the given language in a trie
1093	 * @private
1094	 * @param {string} lang the language whose patterns shall be converted
1095	 */		
1096	convertPatterns = function (lang) {
1097		/** @license BSD licenced code
1098		 * The following code is based on code from hypher.js and adapted for Hyphenator.js
1099		 * Copyright (c) 2011, Bram Stein
1100		 */
1101		var size = 0,
1102			tree = {
1103				tpoints: []
1104			},
1105			patterns, pattern, i, j, k,
1106			patternObject = Hyphenator.languages[lang].patterns,
1107			c, chars, points, t, p, codePoint,
1108			getPoints = (function () {
1109				//IE<9 doesn't act like other browsers
1110				if ('in3se'.split(/\D/).length === 1) {
1111					return function (pattern) {
1112						var chars = pattern.split(''), c, i, r = [],
1113						numb3rs = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}, lastWasNum = false;
1114						i = 0;
1115						while (!!(c = chars[i])) {
1116							if (numb3rs.hasOwnProperty(c)) {
1117								r.push(c);
1118								i += 2;
1119								lastWasNum = true;
1120							} else {
1121								r.push('');
1122								i += 1;
1123								lastWasNum = false;
1124							}
1125						}
1126						if (!lastWasNum) {
1127							r.push('');
1128						}
1129						return r;
1130					};
1131				} else {
1132					return function (pattern) {
1133						return pattern.split(/\D/);
1134					};
1135				}
1136			}());
1137	
1138		for (size in patternObject) {
1139			if (patternObject.hasOwnProperty(size)) {
1140				patterns = patternObject[size].match(new RegExp('.{1,' + (+size) + '}', 'g'));
1141				i = 0;
1142				while (!!(pattern = patterns[i++])) {
1143					chars = pattern.replace(/[\d]/g, '').split('');
1144					points = getPoints(pattern);
1145					t = tree;
1146
1147					j = 0;
1148					while (!!(c = chars[j++])) {
1149						codePoint = c.charCodeAt(0);
1150						
1151						if (!t[codePoint]) {
1152							t[codePoint] = {};
1153						}
1154						t = t[codePoint];
1155					}
1156
1157					t.tpoints = [];
1158					for (k = 0; k < points.length; k++) {
1159						p = points[k];
1160						t.tpoints.push((p == "") ? 0 : p);
1161					}
1162				}
1163			}
1164		}
1165		Hyphenator.languages[lang].patterns = tree;
1166		/**
1167		 * end of BSD licenced code from hypher.js
1168		 */
1169	},
1170
1171	recreatePattern = function (pattern, nodePoints) {
1172		var r = [], c = pattern.split(''), i;
1173		for (i = 0; i < nodePoints.length; i++) {
1174			if (nodePoints[i] !== 0) {
1175				r.push(nodePoints[i]);
1176			}
1177			if (c[i]) {
1178				r.push(c[i]);
1179			}
1180		}
1181		return r.join('');
1182	},
1183	
1184	/**
1185	 * @name Hyphenator-convertExceptionsToObject
1186	 * @description
1187	 * Converts a list of comma seprated exceptions to an object:
1188	 * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
1189	 * @private
1190	 * @param {string} exc a comma separated string of exceptions (without spaces)
1191	 */		
1192	convertExceptionsToObject = function (exc) {
1193		var w = exc.split(', '),
1194			r = {},
1195			i, l, key;
1196		for (i = 0, l = w.length; i < l; i++) {
1197			key = w[i].replace(/-/g, '');
1198			if (!r.hasOwnProperty(key)) {
1199				r[key] = w[i];
1200			}
1201		}
1202		return r;
1203	},
1204	
1205	/**
1206	 * @name Hyphenator-loadPatterns
1207	 * @description
1208	 * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
1209	 * If the given language is not in the {@link Hyphenator-supportedLang}-Object it returns.
1210	 * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object 
1211	 * has a same-origin-policy. This makes the isBookmarklet-functionality impossible.
1212	 * @param {string} lang The language to load the patterns for
1213	 * @private
1214	 * @see Hyphenator-basePath
1215	 */
1216	loadPatterns = function (lang) {
1217		var url, xhr, head, script;
1218		if (supportedLang[lang] && !Hyphenator.languages[lang]) {
1219	        url = basePath + 'patterns/' + supportedLang[lang];
1220		} else {
1221			return;
1222		}
1223		if (isLocal && !isBookmarklet) {
1224			//check if 'url' is available:
1225			xhr = null;
1226			if (typeof XMLHttpRequest !== 'undefined') {
1227				xhr = new XMLHttpRequest();
1228			}
1229			if (!xhr) {
1230				try {
1231					xhr  = new ActiveXObject("Msxml2.XMLHTTP");
1232				} catch (e) {
1233					xhr  = null;
1234				}
1235			}
1236			if (xhr) {
1237				xhr.open('HEAD', url, false);
1238				xhr.setRequestHeader('Cache-Control', 'no-cache');
1239				xhr.send(null);
1240				if (xhr.status === 404) {
1241					onError(new Error('Could not load\n' + url));
1242					delete docLanguages[lang];
1243					return;
1244				}
1245			}
1246		}
1247		if (createElem) {
1248			head = window.document.getElementsByTagName('head').item(0);
1249			script = createElem('script', window);
1250			script.src = url;
1251			script.type = 'text/javascript';
1252			head.appendChild(script);
1253		}
1254	},
1255	
1256	/**
1257	 * @name Hyphenator-prepareLanguagesObj
1258	 * @description
1259	 * Adds a cache to each language and converts the exceptions-list to an object.
1260	 * If storage is active the object is stored there.
1261	 * @private
1262	 * @param {string} lang the language ob the lang-obj
1263	 */		
1264	prepareLanguagesObj = function (lang) {
1265		var lo = Hyphenator.languages[lang], wrd;
1266		if (!lo.prepared) {	
1267			if (enableCache) {
1268				lo.cache = {};
1269				//Export
1270				lo['cache'] = lo.cache;
1271			}
1272			if (enableReducedPatternSet) {
1273				lo.redPatSet = {};
1274			}
1275			//add exceptions from the pattern file to the local 'exceptions'-obj
1276			if (lo.hasOwnProperty('exceptions')) {
1277				Hyphenator.addExceptions(lang, lo.exceptions);
1278				delete lo.exceptions;
1279			}
1280			//copy global exceptions to the language specific exceptions
1281			if (exceptions.hasOwnProperty('global')) {
1282				if (exceptions.hasOwnProperty(lang)) {
1283					exceptions[lang] += ', ' + exceptions.global;
1284				} else {
1285					exceptions[lang] = exceptions.global;
1286				}
1287			}
1288			//move exceptions from the the local 'exceptions'-obj to the 'language'-object
1289			if (exceptions.hasOwnProperty(lang)) {
1290				lo.exceptions = convertExceptionsToObject(exceptions[lang]);
1291				delete exceptions[lang];
1292			} else {
1293				lo.exceptions = {};
1294			}
1295			convertPatterns(lang);
1296			wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
1297			lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
1298			lo.prepared = true;
1299		}
1300		if (!!storage) {
1301			try {
1302				storage.setItem('Hyphenator_' + lang, window.JSON.stringify(lo));
1303			} catch (e) {
1304				//onError(e);
1305			}
1306		}
1307		
1308	},
1309	
1310	/**
1311	 * @name Hyphenator-prepare
1312	 * @description
1313	 * This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
1314	 * that the patternfiles are loaded, all conversions are made and the callback is called.
1315	 * If storage is active the object is retrieved there.
1316	 * If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
1317	 * by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
1318	 * converted to their object style and the lang-object extended.
1319	 * Finally the callback is called.
1320	 * @private
1321	 */
1322	prepare = function (callback) {
1323		var lang, interval, tmp1, tmp2;
1324		if (!enableRemoteLoading) {
1325			for (lang in Hyphenator.languages) {
1326				if (Hyphenator.languages.hasOwnProperty(lang)) {
1327					prepareLanguagesObj(lang);
1328				}
1329			}
1330			state = 2;
1331			callback('*');
1332			return;
1333		}
1334		// get all languages that are used and preload the patterns
1335		state = 1;
1336		for (lang in docLanguages) {
1337			if (docLanguages.hasOwnProperty(lang)) {
1338				if (!!storage && storage.getItem('Hyphenator_' + lang)) {
1339					Hyphenator.languages[lang] = window.JSON.parse(storage.getItem('Hyphenator_' + lang));
1340					if (exceptions.hasOwnProperty('global')) {
1341						tmp1 = convertExceptionsToObject(exceptions.global);
1342						for (tmp2 in tmp1) {
1343							if (tmp1.hasOwnProperty(tmp2)) {
1344								Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1345							}
1346						}
1347					}
1348					//Replace exceptions since they may have been changed:
1349					if (exceptions.hasOwnProperty(lang)) {
1350						tmp1 = convertExceptionsToObject(exceptions[lang]);
1351						for (tmp2 in tmp1) {
1352							if (tmp1.hasOwnProperty(tmp2)) {
1353								Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
1354							}
1355						}
1356						delete exceptions[lang];
1357					}
1358					//Replace genRegExp since it may have been changed:
1359					tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
1360					Hyphenator.languages[lang].genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + tmp1 + ')', 'gi');
1361					
1362					delete docLanguages[lang];
1363					callback(lang);
1364					continue;
1365				} else {
1366					loadPatterns(lang);
1367				}
1368			}
1369		}
1370		// else async wait until patterns are loaded, then hyphenate
1371		interval = window.setInterval(function () {
1372			var finishedLoading = true, lang;
1373			for (lang in docLanguages) {
1374				if (docLanguages.hasOwnProperty(lang)) {
1375					finishedLoading = false;
1376					if (!!Hyphenator.languages[lang]) {
1377						delete docLanguages[lang];
1378						//do conversion while other patterns are loading:
1379						prepareLanguagesObj(lang);
1380						callback(lang);
1381					}
1382				}
1383			}
1384			if (finishedLoading) {
1385				//console.log('callig callback for ' + contextWindow.location.href);
1386				window.clearInterval(interval);
1387				state = 2;
1388			}
1389		}, 100);
1390	},
1391
1392	/**
1393	 * @name Hyphenator-switchToggleBox
1394	 * @description
1395	 * Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
1396	 * @see Hyphenator.config
1397	 * @private
1398	 */		
1399	toggleBox = function () {
1400		var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute,
1401		text = (Hyphenator.doHyphenation ? 'Hy-phen-a-tion' : 'Hyphenation');
1402		if (!!(myBox = contextWindow.document.getElementById('HyphenatorToggleBox'))) {
1403			myBox.firstChild.data = text;
1404		} else {
1405			bdy = contextWindow.document.getElementsByTagName('body')[0];
1406			myBox = createElem('div', contextWindow);
1407			myIdAttribute = contextWindow.document.createAttribute('id');
1408			myIdAttribute.nodeValue = 'HyphenatorToggleBox';
1409			myClassAttribute = contextWindow.document.createAttribute('class');
1410			myClassAttribute.nodeValue = dontHyphenateClass;
1411			myTextNode = contextWindow.document.createTextNode(text);
1412			myBox.appendChild(myTextNode);
1413			myBox.setAttributeNode(myIdAttribute);
1414			myBox.setAttributeNode(myClassAttribute);
1415			myBox.onclick =  Hyphenator.toggleHyphenation;
1416			myBox.style.position = 'absolute';
1417			myBox.style.top = '0px';
1418			myBox.style.right = '0px';
1419			myBox.style.margin = '0';
1420			myBox.style.backgroundColor = '#AAAAAA';
1421			myBox.style.color = '#FFFFFF';
1422			myBox.style.font = '6pt Arial';
1423			myBox.style.letterSpacing = '0.2em';
1424			myBox.style.padding = '3px';
1425			myBox.style.cursor = 'pointer';
1426			myBox.style.WebkitBorderBottomLeftRadius = '4px';
1427			myBox.style.MozBorderRadiusBottomleft = '4px';
1428			bdy.appendChild(myBox);
1429		}
1430	},
1431
1432
1433	/**
1434	 * @name Hyphenator-hyphenateWord
1435	 * @description
1436	 * This function is the heart of Hyphenator.js. It returns a hyphenated word.
1437	 *
1438	 * If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
1439	 * If the word is in the exceptions list or in the cache, it is retrieved from it.
1440	 * If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
1441	 * @param {string} lang The language of the word
1442	 * @param {string} word The word
1443	 * @returns string The hyphenated word
1444	 * @public
1445	 */	
1446	hyphenateWord = function (lang, word) {
1447		var lo = Hyphenator.languages[lang], parts, l, subst,
1448			w, characters, originalCharacters, wordLength, i, j, k, node, points = [],
1449			characterPoints = [], nodePoints, nodePointsLength, m = Math.max, trie,
1450			result = [''], pattern;
1451		if (word === '') {
1452			return '';
1453		}
1454		if (word.indexOf(hyphen) !== -1) {
1455			//word already contains shy; -> leave at it is!
1456			return word;
1457		}
1458		if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
1459			return lo.cache[word];
1460		}
1461		if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
1462			return lo.exceptions[word].replace(/-/g, hyphen);
1463		}
1464		if (word.indexOf('-') !== -1) {
1465			//word contains '-' -> hyphenate the parts separated with '-'
1466			parts = word.split('-');
1467			for (i = 0, l = parts.length; i < l; i++) {
1468				parts[i] = hyphenateWord(lang, parts[i]);
1469			}
1470			return parts.join('-');
1471		}
1472		w = word = '_' + word + '_';
1473		if (!!lo.charSubstitution) {
1474			for (subst in lo.charSubstitution) {
1475				if (lo.charSubstitution.hasOwnProperty(subst)) {
1476					w = w.replace(new RegExp(subst, 'g'), lo.charSubstitution[subst]);
1477				}
1478			}
1479		}
1480		if (word.indexOf("'") !== -1) {
1481			w = w.replace("'", "’");
1481 //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
1482		}
1483		/** @license BSD licenced code
1484		 * The following code is based on code from hypher.js
1485		 * Copyright (c) 2011, Bram Stein
1486		 */
1487		characters = w.toLowerCase().split('');
1488		originalCharacters = word.split('');
1489		wordLength = characters.length;
1490		trie = lo.patterns;
1491		for (i = 0; i < wordLength; i += 1) {
1492			points[i] = 0;
1493			characterPoints[i] = characters[i].charCodeAt(0);
1494		}
1495		for (i = 0; i < wordLength; i += 1) {
1496			pattern = '';
1497			node = trie;
1498			for (j = i; j < wordLength; j += 1) {
1499				node = node[characterPoints[j]];
1500				if (node) {
1501					if (enableReducedPatternSet) {
1502						pattern += String.fromCharCode(characterPoints[j]);
1503					}
1504					nodePoints = node.tpoints;
1505					if (nodePoints) {
1506						if (enableReducedPatternSet) {
1507							if (!lo.redPatSet) {
1508								lo.redPatSet = {};
1509							}
1510							lo.redPatSet[pattern] = recreatePattern(pattern, nodePoints);
1511						}
1512						for (k = 0, nodePointsLength = nodePoints.length; k < nodePointsLength; k += 1) {
1513							points[i + k] = m(points[i + k], nodePoints[k]);
1514						}
1515					}
1516				} else {
1517					break;
1518				}
1519			}
1520		}
1521		for (i = 1; i < wordLength - 1; i += 1) {
1522			if (i > lo.leftmin && i < (wordLength - lo.rightmin) && points[i] % 2) {
1523				result.push(originalCharacters[i]);
1524			} else {
1525				result[result.length - 1] += originalCharacters[i];
1526			}
1527		}
1528		return result.join(hyphen);
1529		/**
1530		 * end of BSD licenced code from hypher.js
1531		 */
1532	},
1533		
1534	/**
1535	 * @name Hyphenator-hyphenateURL
1536	 * @description
1537	 * Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
1538	 * @param {string} url to hyphenate
1539	 * @returns string the hyphenated URL
1540	 * @public
1541	 */
1542	hyphenateURL = function (url) {
1543		return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
1544	},
1545
1546	/**
1547	 * @name Hyphenator-removeHyphenationFromElement
1548	 * @description
1549	 * Removes all hyphens from the element. If there are other elements, the function is
1550	 * called recursively.
1551	 * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
1552	 * @param {Object} el The element where to remove hyphenation.
1553	 * @public
1554	 */
1555	removeHyphenationFromElement = function (el) {
1556		var h, i = 0, n;
1557		switch (hyphen) {
1558		case '|':
1559			h = '\\|';
1560			break;
1561		case '+':
1562			h = '\\+';
1563			break;
1564		case '*':
1565			h = '\\*';
1566			break;
1567		default:
1568			h = hyphen;
1569		}
1570		while (!!(n = el.childNodes[i++])) {
1571			if (n.nodeType === 3) {
1572				n.data = n.data.replace(new RegExp(h, 'g'), '');
1573				n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
1574			} else if (n.nodeType === 1) {
1575				removeHyphenationFromElement(n);
1576			}
1577		}
1578	},
1579	
1580	
1581	/**
1582	 * @name Hyphenator-registerOnCopy
1583	 * @description
1584	 * Huge work-around for browser-inconsistency when it comes to
1585	 * copying of hyphenated text.
1586	 * The idea behind this code has been provided by http://github.com/aristus/sweet-justice
1587	 * sweet-justice is under BSD-License
1588	 * @private
1589	 */
1590	registerOnCopy = function (el) {
1591		var body = el.ownerDocument.getElementsByTagName('body')[0],
1592		shadow,
1593		selection,
1594		range,
1595		rangeShadow,
1596		restore,
1597		oncopyHandler = function (e) {
1598			e = e || window.event;
1599			var target = e.target || e.srcElement,
1600			currDoc = target.ownerDocument,
1601			body = currDoc.getElementsByTagName('body')[0],
1602			targetWindow = 'defaultView' in currDoc ? currDoc.defaultView : currDoc.parentWindow;
1603			if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
1604				//Safari needs this
1605				return;
1606			}
1607			//create a hidden shadow element
1608			shadow = currDoc.createElement('div');
1609			//Moving the element out of the screen doesn't work for IE9 (https://connect.microsoft.com/IE/feedback/details/663981/)
1610			//shadow.style.overflow = 'hidden';
1611			//shadow.style.position = 'absolute';
1612			//shadow.style.top = '-5000px';
1613			//shadow.style.height = '1px';
1614			//doing this instead:
1615			shadow.style.color = window.getComputedStyle ? targetWindow.getComputedStyle(body).backgroundColor : '#FFFFFF';
1616			shadow.style.fontSize = '0px';
1617			body.appendChild(shadow);
1618			if (!!window.getSelection) {
1619				//FF3, Webkit, IE9
1620				e.stopPropagation();
1621				selection = targetWindow.getSelection();
1622				range = selection.getRangeAt(0);
1623				shadow.appendChild(range.cloneContents());
1624				removeHyphenationFromElement(shadow);
1625				selection.selectAllChildren(shadow);
1626				restore = function () {
1627					shadow.parentNode.removeChild(shadow);
1628					selection.removeAllRanges(); //IE9 needs that
1629					selection.addRange(range);
1630				};
1631			} else {
1632				// IE<9
1633				e.cancelBubble = true;
1634				selection = targetWindow.document.selection;
1635				range = selection.createRange();
1636				shadow.innerHTML = range.htmlText;
1637				removeHyphenationFromElement(shadow);
1638				rangeShadow = body.createTextRange();
1639				rangeShadow.moveToElementText(shadow);
1640				rangeShadow.select();
1641				restore = function () {
1642					shadow.parentNode.removeChild(shadow);
1643					if (range.text !== "") {
1644						range.select();
1645					}
1646				};
1647			}
1648			window.setTimeout(restore, 0);
1649		};
1650		if (!body) {
1651			return;
1652		}
1653		el = el || body;
1654		if (window.addEventListener) {
1655			el.addEventListener("copy", oncopyHandler, true);
1656		} else {
1657			el.attachEvent("oncopy", oncopyHandler);
1658		}
1659	},
1660	
1661	/**
1662	 * @name Hyphenator-unhideElement
1663	 * @description
1664	 * Unhides an element and removes the visibility attr if set by hyphenator
1665	 * @param Object The Element object from ElementCollection
1666	 * @private
1667	 */	
1668	unhideElement = function (elo) {
1669		var el = elo.element,
1670		hyphenatorSettings = elo.data;
1671		el.style.visibility = 'visible';
1672		elo.data.isHidden = false;
1673		if (!hyphenatorSettings.hasOwnStyle) {
1674			el.setAttribute('style', ''); // without this, removeAttribute doesn't work in Safari (thanks to molily)
1675			el.removeAttribute('style');
1676		} else {
1677			if (el.style.removeProperty) {
1678				el.style.removeProperty('visibility');
1679			} else if (el.style.removeAttribute) { // IE
1680				el.style.removeAttribute('visibility');
1681			}  
1682		}
1683	},
1684
1685	/**
1686	 * @name Hyphenator-checkIfAllDone
1687	 * @description
1688	 * Checks if all Elements are hyphenated, unhides them and fires onHyphenationDone()
1689	 * @private
1690	 */		
1691	checkIfAllDone = function () {
1692		var allDone = true;
1693		elements.each(function (lang, list) {
1694			var i, l = list.length;
1695			for (i = 0; i < l; i++) {
1696				allDone = allDone && list[i].hyphenated;
1697				if (intermediateState === 'hidden' && unhide === 'wait') {
1698					unhideElement(list[i]);
1699				}
1700			}
1701		});
1702		if (allDone) {
1703			state = 3;
1704			onHyphenationDone();
1705		}
1706	},
1707
1708
1709	/**
1710	 * @name Hyphenator-hyphenateElement
1711	 * @description
1712	 * Takes the content of the given element and - if there's text - replaces the words
1713	 * by hyphenated words. If there's another element, the function is called recursively.
1714	 * When all words are hyphenated, the visibility of the element is set to 'visible'.
1715	 * @param {Object} el The element to hyphenate
1716	 * @private
1717	 */
1718	hyphenateElement = function (lang, elo) {
1719		var hyphenatorSettings = elo.data,
1720			el = elo.element,
1721			hyphenate, n, i,
1722			controlOrphans = function (part) {
1723				var h, r;
1724				switch (hyphen) {
1725				case '|':
1726					h = '\\|';
1727					break;
1728				case '+':
1729					h = '\\+';
1730					break;
1731				case '*':
1732					h = '\\*';
1733					break;
1734				default:
1735					h = hyphen;
1736				}
1737				if (orphanControl >= 2) {
1738					//remove hyphen points from last word
1739					r = part.split(' ');
1740					r[1] = r[1].replace(new RegExp(h, 'g'), '');
1741					r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), '');
1742					r = r.join(' ');
1743				}
1744				if (orphanControl === 3) {
1745					//replace spaces by non breaking spaces
1746					r = r.replace(/[ ]+/g, String.fromCharCode(160));
1747				}
1748				return r;
1749			};
1750		if (Hyphenator.languages.hasOwnProperty(lang)) {
1751			hyphenate = function (word) {
1752				if (!Hyphenator.doHyphenation) {
1753					return word;
1754				} else if (urlOrMailRE.test(word)) {
1755					return hyphenateURL(word);
1756				} else {
1757					return hyphenateWord(lang, word);
1758				}
1759			};
1760			if (safeCopy && (el.tagName.toLowerCase() !== 'body')) {
1761				registerOnCopy(el);
1762			}
1763			i = 0;
1764			while (!!(n = el.childNodes[i++])) {
1765				if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
1766					n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1767					if (orphanControl !== 1) {
1768						n.data = n.data.replace(/[\S]+ [\S]+$/, controlOrphans);
1769					}
1770				}
1771			}
1772		}
1773		if (hyphenatorSettings.isHidden && intermediateState === 'hidden' && unhide === 'progressive') {
1774			unhideElement(elo);
1775		}
1776		elo.hyphenated = true;
1777		elements.hyCount += 1;
1778		if (elements.count <= elements.hyCount) {
1779			checkIfAllDone();
1780		}
1781	},
1782	
1783
1784	/**
1785	 * @name Hyphenator-hyphenateLanguageElements
1786	 * @description
1787	 * Calls hyphenateElement() for all elements of the specified language.
1788	 * If the language is '*' then all elements are hyphenated.
1789	 * This is done with a setTimout
1790	 * to prevent a "long running Script"-alert when hyphenating large pages.
1791	 * Therefore a tricky bind()-function was necessary.
1792	 * @private
1793	 */
1794	hyphenateLanguageElements = function (lang) {
1795		function bind(fun, arg1, arg2) {
1796			return function () {
1797				return fun(arg1, arg2);
1798			};
1799		}
1800		var el, i, l;
1801		if (lang === '*') {
1802			elements.each(function (lang, langels) {
1803				var i, l = langels.length;
1804				for (i = 0; i < l; i++) {
1805					window.setTimeout(bind(hyphenateElement, lang, langels[i]), 0);
1806				}
1807			});
1808		} else {
1809			if (elements.list.hasOwnProperty(lang)) {
1810				l = elements.list[lang].length;
1811				for (i = 0; i < l; i++) {
1812					window.setTimeout(bind(hyphenateElement, lang, elements.list[lang][i]), 0);
1813				}
1814			}
1815		}
1816	},
1817	
1818	/**
1819	 * @name Hyphenator-removeHyphenationFromDocument
1820	 * @description
1821	 * Does what it says ;-)
1822	 * @private
1823	 */
1824	removeHyphenationFromDocument = function () {
1825		elements.each(function (lang, elo) {
1826			var i, l = elo.length, el;
1827			for (i = 0; i < l; i++) {
1828				removeHyphenationFromElement(elo[i].element);
1829				elo[i].hyphenated = false;
1830			}
1831		});
1832		state = 4;
1833	},
1834		
1835	/**
1836	 * @name Hyphenator-createStorage
1837	 * @description
1838	 * inits the private var storage depending of the setting in storageType
1839	 * and the supported features of the system.
1840	 * @private
1841	 */
1842	createStorage = function () {
1843		try {
1844			if (storageType !== 'none' &&
1845				typeof(window.localStorage) !== 'undefined' &&
1846				typeof(window.sessionStorage) !== 'undefined' &&
1847				typeof(window.JSON.stringify) !== 'undefined' &&
1848				typeof(window.JSON.parse) !== 'undefined') {
1849				switch (storageType) {
1850				case 'session':
1851					storage = window.sessionStorage;
1852					break;
1853				case 'local':
1854					storage = window.localStorage;
1855					break;
1856				default:
1857					storage = undefined;
1858					break;
1859				}
1860			}
1861		} catch (f) {
1862			//FF throws an error if DOM.storage.enabled is set to false
1863		}
1864	},
1865	
1866	/**
1867	 * @name Hyphenator-storeConfiguration
1868	 * @description
1869	 * Stores the current config-options in DOM-Storage
1870	 * @private
1871	 */
1872	storeConfiguration = function () {
1873		if (!storage) {
1874			return;
1875		}
1876		var settings = {
1877			'STORED': true,
1878			'classname': hyphenateClass,
1879			'donthyphenateclassname': dontHyphenateClass,
1880			'minwordlength': min,
1881			'hyphenchar': hyphen,
1882			'urlhyphenchar': urlhyphen,
1883			'togglebox': toggleBox,
1884			'displaytogglebox': displayToggleBox,
1885			'remoteloading': enableRemoteLoading,
1886			'enablecache': enableCache,
1887			'onhyphenationdonecallback': onHyphenationDone,
1888			'onerrorhandler': onError,
1889			'intermediatestate': intermediateState,
1890			'selectorfunction': selectorFunction,
1891			'safecopy': safeCopy,
1892			'doframes': doFrames,
1893			'storagetype': storageType,
1894			'orphancontrol': orphanControl,
1895			'dohyphenation': Hyphenator.doHyphenation,
1896			'persistentconfig': persistentConfig,
1897			'defaultlanguage': defaultLanguage
1898		};
1899		storage.setItem('Hyphenator_config', window.JSON.stringify(settings));
1900	},
1901	
1902	/**
1903	 * @name Hyphenator-restoreConfiguration
1904	 * @description
1905	 * Retrieves config-options from DOM-Storage and does configuration accordingly
1906	 * @private
1907	 */
1908	restoreConfiguration = function () {
1909		var settings;
1910		if (storage.getItem('Hyphenator_config')) {
1911			settings = window.JSON.parse(storage.getItem('Hyphenator_config'));
1912			Hyphenator.config(settings);
1913		}
1914	};
1915
1916	return {
1917		
1918		/**
1919		 * @name Hyphenator.version
1920		 * @memberOf Hyphenator
1921		 * @description
1922		 * String containing the actual version of Hyphenator.js
1923		 * [major release].[minor releas].[bugfix release]
1924		 * major release: new API, new Features, big changes
1925		 * minor release: new languages, improvements
1926		 * @public
1927         */		
1928		version: '4.0.0',
1929
1930		/**
1931		 * @name Hyphenator.doHyphenation
1932		 * @description
1933		 * If doHyphenation is set to false (defaults to true), hyphenateDocument() isn't called.
1934		 * All other actions are performed.
1935		 */		
1936		doHyphenation: true,
1937		
1938		/**
1939		 * @name Hyphenator.languages
1940		 * @memberOf Hyphenator
1941		 * @description
1942		 * Objects that holds key-value pairs, where key is the language and the value is the
1943		 * language-object loaded from (and set by) the pattern file.
1944		 * The language object holds the following members:
1945		 * <table>
1946		 * <tr><th>key</th><th>desc></th></tr>
1947		 * <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
1948		 * <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
1949		 * <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
1950		 * <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
1951		 * <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
1952		 * <tr><td>patterns</td><td>the patterns</td></tr>
1953		 * </table>
1954		 * And optionally (or after prepareLanguagesObj() has been called):
1955		 * <table>
1956		 * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
1957		 * </table>
1958		 * @public
1959         */		
1960		languages: {},
1961		
1962
1963		/**
1964		 * @name Hyphenator.config
1965			 * @description
1966		 * Config function that takes an object as an argument. The object contains key-value-pairs
1967		 * containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
1968		 * @param {Object} obj <table>
1969		 * <tr><th>key</th><th>values</th><th>default</th></tr>
1970		 * <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
1971		 * <tr><td>donthyphenateclassname</td><td>string</td><td>''</td></tr>
1972		 * <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
1973		 * <tr><td>
1973hyphenchar</td><td>string</td><td>'&amp;shy;'</td></tr>
1974		 * <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
1975		 * <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
1976		 * <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
1977		 * <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
1978		 * <tr><td>enablecache</td><td>boolean</td><td>true</td></tr>
1979		 * <tr><td>enablereducedpatternset</td><td>boolean</td><td>false</td></tr>
1980		 * <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
1981		 * <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
1982		 * <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
1983		 * <tr><td>selectorfunction</td><td>function</td><td>[…]</td></tr>
1984		 * <tr><td>safecopy</td><td>boolean</td><td>true</td></tr>
1985		 * <tr><td>doframes</td><td>boolean</td><td>false</td></tr>
1986		 * <tr><td>storagetype</td><td>string</td><td>'none'</td></tr>
1987		 * </table>
1988		 * @public
1989		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1990         * &lt;script type = "text/javascript"&gt;
1991         *     Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
1992         *     Hyphenator.run();
1993         * &lt;/script&gt;
1994         */
1995		config: function (obj) {
1996			var assert = function (name, type) {
1997					if (typeof obj[name] === type) {
1998						return true;
1999					} else {
2000						onError(new Error('Config onError: ' + name + ' must be of type ' + type));
2001						return false;
2002					}
2003				},
2004				key;
2005
2006			if (obj.hasOwnProperty('storagetype')) {
2007				if (assert('storagetype', 'string')) {
2008					storageType = obj.storagetype;
2009				}
2010				if (!storage) {
2011					createStorage();
2012				}			
2013			}
2014			if (!obj.hasOwnProperty('STORED') && storage && obj.hasOwnProperty('persistentconfig') && obj.persistentconfig === true) {
2015				restoreConfiguration();
2016			}
2017			
2018			for (key in obj) {
2019				if (obj.hasOwnProperty(key)) {
2020					switch (key) {
2021					case 'STORED':
2022						break;
2023					case 'classname':
2024						if (assert('classname', 'string')) {
2025							hyphenateClass = obj[key];
2026						}
2027						break;
2028					case 'donthyphenateclassname':
2029						if (assert('donthyphenateclassname', 'string')) {
2030							dontHyphenateClass = obj[key];
2031						}						
2032						break;
2033					case 'minwordlength':
2034						if (assert('minwordlength', 'number')) {
2035							min = obj[key];
2036						}
2037						break;
2038					case 'hyphenchar':
2039						if (assert('hyphenchar', 'string')) {
2040							if (obj.hyphenchar === '&shy;') {
2041								obj.hyphenchar = String.fromCharCode(173);
2042							}
2043							hyphen = obj[key];
2044						}
2045						break;
2046					case 'urlhyphenchar':
2047						if (obj.hasOwnProperty('urlhyphenchar')) {
2048							if (assert('urlhyphenchar', 'string')) {
2049								urlhyphen = obj[key];
2050							}
2051						}
2052						break;
2053					case 'togglebox':
2054						if (assert('togglebox', 'function')) {
2055							toggleBox = obj[key];
2056						}
2057						break;
2058					case 'displaytogglebox':
2059						if (assert('displaytogglebox', 'boolean')) {
2060							displayToggleBox = obj[key];
2061						}
2062						break;
2063					case 'remoteloading':
2064						if (assert('remoteloading', 'boolean')) {
2065							enableRemoteLoading = obj[key];
2066						}
2067						break;
2068					case 'enablecache':
2069						if (assert('enablecache', 'boolean')) {
2070							enableCache = obj[key];
2071						}
2072						break;
2073					case 'enablereducedpatternset':
2074						if (assert('enablereducedpatternset', 'boolean')) {
2075							enableReducedPatternSet = obj[key];
2076						}
2077						break;
2078					case 'onhyphenationdonecallback':
2079						if (assert('onhyphenationdonecallback', 'function')) {
2080							onHyphenationDone = obj[key];
2081						}
2082						break;
2083					case 'onerrorhandler':
2084						if (assert('onerrorhandler', 'function')) {
2085							onError = obj[key];
2086						}
2087						break;
2088					case 'intermediatestate':
2089						if (assert('intermediatestate', 'string')) {
2090							intermediateState = obj[key];
2091						}
2092						break;
2093					case 'selectorfunction':
2094						if (assert('selectorfunction', 'function')) {
2095							selectorFunction = obj[key];
2096						}
2097						break;
2098					case 'safecopy':
2099						if (assert('safecopy', 'boolean')) {
2100							safeCopy = obj[key];
2101						}
2102						break;
2103					case 'doframes':
2104						if (assert('doframes', 'boolean')) {
2105							doFrames = obj[key];
2106						}
2107						break;
2108					case 'storagetype':
2109						if (assert('storagetype', 'string')) {
2110							storageType = obj[key];
2111						}						
2112						break;
2113					case 'orphancontrol':
2114						if (assert('orphancontrol', 'number')) {
2115							orphanControl = obj[key];
2116						}
2117						break;
2118					case 'dohyphenation':
2119						if (assert('dohyphenation', 'boolean')) {
2120							Hyphenator.doHyphenation = obj[key];
2121						}
2122						break;
2123					case 'persistentconfig':
2124						if (assert('persistentconfig', 'boolean')) {
2125							persistentConfig = obj[key];
2126						}
2127						break;
2128					case 'defaultlanguage':
2129						if (assert('defaultlanguage', 'string')) {
2130							defaultLanguage = obj[key];
2131						}
2132						break;
2133					case 'useCSS3hyphenation':
2134						if (assert('useCSS3hyphenation', 'boolean')) {
2135							css3 = obj[key];
2136						}
2137						break;
2138					case 'unhide':
2139						if (assert('unhide', 'string')) {
2140							unhide = obj[key];
2141						}
2142						break;
2143					default:
2144						onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
2145					}
2146				}
2147			}
2148			if (storage && persistentConfig) {
2149				storeConfiguration();
2150			}
2151		},
2152
2153		/**
2154		 * @name Hyphenator.run
2155			 * @description
2156		 * Bootstrap function that starts all hyphenation processes when called.
2157		 * @public
2158		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2159         * &lt;script type = "text/javascript"&gt;
2160         *   Hyphenator.run();
2161         * &lt;/script&gt;
2162         */
2163		run: function () {
2164			documentCount = 0;
2165			var process = function () {
2166				try {
2167					if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
2168						return; //we are in a frameset
2169					}
2170					documentCount++;
2171					autoSetMainLanguage(undefined);
2172					gatherDocumentInfos();
2173					//console.log('preparing for ' + contextWindow.location.href);
2174					prepare(hyphenateLanguageElements);
2175					if (displayToggleBox) {
2176						toggleBox();
2177					}
2178				} catch (e) {
2179					onError(e);
2180				}
2181			}, i, haveAccess, fl = window.frames.length;
2182			
2183			if (!storage) {
2184				createStorage();
2185			}
2186			if (!documentLoaded && !isBookmarklet) {
2187				runOnContentLoaded(window, process);
2188			}
2189			if (isBookmarklet || documentLoaded) {
2190				if (doFrames && fl > 0) {
2191					for (i = 0; i < fl; i++) {
2192						haveAccess = undefined;
2193						//try catch isn't enough for webkit
2194						try {
2195							//opera throws only on document.toString-access
2196							haveAccess = window.frames[i].document.toString();
2197						} catch (e) {
2198							haveAccess = undefined;
2199						}
2200						if (!!haveAccess) {
2201							contextWindow = window.frames[i];
2202							process();
2203						}						
2204					}
2205				}
2206				contextWindow = window;
2207				process();
2208			}
2209		},
2210		
2211		/**
2212		 * @name Hyphenator.addExceptions
2213			 * @description
2214		 * Adds the exceptions from the string to the appropriate language in the 
2215		 * {@link Hyphenator-languages}-object
2216		 * @param {string} lang The language
2217		 * @param {string} words A comma separated string of hyphenated words WITH spaces.
2218		 * @public
2219		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2220         * &lt;script type = "text/javascript"&gt;
2221         *   Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
2222         *   Hyphenator.run();
2223         * &lt;/script&gt;
2224         */
2225		addExceptions: function (lang, words) {
2226			if (lang === '') {
2227				lang = 'global';
2228			}
2229			if (exceptions.hasOwnProperty(lang)) {
2230				exceptions[lang] += ", " + words;
2231			} else {
2232				exceptions[lang] = words;
2233			}
2234		},
2235		
2236		/**
2237		 * @name Hyphenator.hyphenate
2238			 * @public
2239		 * @description
2240		 * Hyphenates the target. The language patterns must be loaded.
2241		 * If the target is a string, the hyphenated string is returned,
2242		 * if it's an object, the values are hyphenated directly.
2243		 * @param {string|Object} target the target to be hyphenated
2244		 * @param {string} lang the language of the target
2245		 * @returns string
2246		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
2247		 * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
2248         * &lt;script type = "text/javascript"&gt;
2249		 * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
2250		 * &lt;/script&gt;
2251		 */
2252		hyphenate: function (target, lang) {
2253			var hyphenate, n, i;
2254			if (Hyphenator.languages.hasOwnProperty(lang)) {
2255				if (!Hyphenator.languages[lang].prepared) {
2256					prepareLanguagesObj(lang);
2257				}
2258				hyphenate = function (word) {
2259					if (urlOrMailRE.test(word)) {
2260						return hyphenateURL(word);
2261					} else {
2262						return hyphenateWord(lang, word);
2263					}
2264				};
2265				if (typeof target === 'string' || target.constructor === String) {
2266					return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
2267				} else if (typeof target === 'object') {
2268					i = 0;
2269					while (!!(n = target.childNodes[i++])) {
2270						if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
2271							n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
2272						} else if (n.nodeType === 1) {
2273							if (n.lang !== '') {
2274								Hyphenator.hyphenate(n, n.lang);
2275							} else {
2276								Hyphenator.hyphenate(n, lang);
2277							}
2278						}
2279					}
2280				}
2281			} else {
2282				onError(new Error('Language "' + lang + '" is not loaded.'));
2283			}
2284		},
2285		
2286		/**
2287		 * @name Hyphenator.getRedPatternSet
2288			 * @description
2289		 * Returns {@link Hyphenator-isBookmarklet}.
2290		 * @param {string} lang the language patterns are stored for
2291		 * @returns object {'patk': pat}
2292		 * @public
2293         */
2294		getRedPatternSet: function (lang) {
2295			return Hyphenator.languages[lang].redPatSet;
2296		},
2297		
2298		/**
2299		 * @name Hyphenator.isBookmarklet
2300			 * @description
2301		 * Returns {@link Hyphenator-isBookmarklet}.
2302		 * @returns boolean
2303		 * @public
2304         */
2305		isBookmarklet: function () {
2306			return isBookmarklet;
2307		},
2308
2309		getConfigFromURI: function () {
2310			var loc = null, re = {}, jsArray = document.getElementsByTagName('script'), i, j, l, s, gp, option;
2311			for (i = 0, l = jsArray.length; i < l; i++) {
2312				if (!!jsArray[i].getAttribute('src')) {
2313					loc = jsArray[i].getAttribute('src');
2314				}
2315				if (!loc) {
2316					continue;
2317				} else {
2318					s = loc.indexOf('Hyphenator.js?');
2319					if (s === -1) {
2320						continue;
2321					}
2322					gp = loc.substring(s + 14).split('&');
2323					for (j = 0; j < gp.length; j++) {
2324						option = gp[j].split('=');
2325						if (option[0] === 'bm') {
2326							continue;
2327						}
2328						if (option[1] === 'true') {
2329							re[option[0]] = true;
2330							continue;
2331						}
2332						if (option[1] === 'false') {
2333							re[option[0]] = false;
2334							continue;
2335						}
2336						if (isFinite(option[1])) {
2337							re[option[0]] = parseInt(option[1], 10);
2338							continue;
2339						}
2340						if (option[0] === 'onhyphenationdonecallback') {
2341							re[option[0]] = new Function('', option[1]);
2342							continue;
2343						}
2344						re[option[0]] = option[1];
2345					}
2346					break;
2347				}
2348			}
2349			return re;
2350		},
2351
2352		/**
2353		 * @name Hyphenator.toggleHyphenation
2354			 * @description
2355		 * Checks the current state of the ToggleBox and removes or does hyphenation.
2356		 * @public
2357         */
2358		toggleHyphenation: function () {
2359			if (Hyphenator.doHyphenation) {
2360				removeHyphenationFromDocument();
2361				Hyphenator.doHyphenation = false;
2362				storeConfiguration();
2363				toggleBox();
2364			} else {
2365				hyphenateLanguageElements('*');
2366				Hyphenator.doHyphenation = true;
2367				storeConfiguration();
2368				toggleBox();
2369			}
2370		}
2371	};
2372}(window));
2373
2374//Export properties/methods (for google closure compiler)
2375Hyphenator['languages'] = Hyphenator.languages;
2376Hyphenator['config'] = Hyphenator.config;
2377Hyphenator['run'] = Hyphenator.run;
2378Hyphenator['addExceptions'] = Hyphenator.addExceptions;
2379Hyphenator['hyphenate'] = Hyphenator.hyphenate;
2380Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
2381Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
2382Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
2383Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
2384window['Hyphenator'] = Hyphenator;
2385
2386if (Hyphenator.isBookmarklet()) {
2387	Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true});
2388	Hyphenator.config(Hyphenator.getConfigFromURI());
2389	Hyphenator.run();
2390}

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