PageSourceSearch

https://www.vitaepensiero.it/js/hyphenator.js

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

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.