• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*!
2 *  Hyphenator 2.5.0 - client side hyphenation for webbrowsers
3 *  Copyright (C) 2010  Mathias Nater, Zürich (mathias at mnn dot ch)
4 *  Project and Source hosted on http://code.google.com/p/hyphenator/
5 *
6 *  This JavaScript code is free software: you can redistribute
7 *  it and/or modify it under the terms of the GNU Lesser
8 *  General Public License (GNU LGPL) as published by the Free Software
9 *  Foundation, either version 3 of the License, or (at your option)
10 *  any later version.  The code is distributed WITHOUT ANY WARRANTY;
11 *  without even the implied warranty of MERCHANTABILITY or FITNESS
12 *  FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
13 *
14 *  As additional permission under GNU GPL version 3 section 7, you
15 *  may distribute non-source (e.g., minimized or compacted) forms of
16 *  that code without the copy of the GNU GPL normally required by
17 *  section 4, provided you include this license notice and a URL
18 *  through which recipients can access the Corresponding Source.
19 */
20
21/*
22 *  Comments are jsdoctoolkit formatted. See jsdoctoolkit.org
23 */
24
25/* The following comment is for JSLint: */
26/*global window, ActiveXObject, unescape */
27/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, newcap: true, immed: true */
28
29/**
30 * @fileOverview
31 * A script that does hyphenation in (X)HTML files
32 * @author Mathias Nater, <a href = "mailto:mathias@mnn.ch">mathias@mnn.ch</a>
33 * @version 2.5.0
34  */
35
36/**
37 * @constructor
38 * @description Provides all functionality to do hyphenation, except the patterns that are loaded
39 * externally.
40 * @namespace Holds all methods and properties
41 * @example
42 * &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
43 * &lt;script type = "text/javascript"&gt;
44 *   Hyphenator.run();
45 * &lt;/script&gt;
46 */
47var Hyphenator = (function () {
48
49
50	/**
51	 * @name Hyphenator-languageHint
52	 * @fieldOf Hyphenator
53	 * @description
54	 * A string to be displayed in a prompt if the language can't be guessed.
55	 * If you add hyphenation patterns change this string.
56	 * Internally, this string is used to define languages that are supported by Hyphenator.
57	 * @see Hyphenator-supportedLang
58	 * @type string
59	 * @private
60	 * @see Hyphenator-autoSetMainLanguage
61	 */
62	var languageHint = 'en, am',
63
64	/**
65	 * @name Hyphenator-supportedLang
66	 * @fieldOf Hyphenator
67	 * @description
68	 * A generated key-value object that stores supported languages.
69	 * The languages are retrieved from {@link Hyphenator-languageHint}.
70	 * @type object
71	 * @private
72	 * @example
73	 * Check if language lang is supported:
74	 * if (supportedLang[lang])
75	 */
76	supportedLang = (function () {
77		var k, i = 0, a = languageHint.split(', '), r = {};
78		while (!!(k = a[i++])) {
79			r[k] = true;
80		}
81		return r;
82	}()),
83
84	/**
85	 * @name Hyphenator-prompterStrings
86	 * @fieldOf Hyphenator
87	 * @description
88	 * A key-value object holding the strings to be displayed if the language can't be guessed
89	 * If you add hyphenation patterns change this string.
90	 * @type object
91	 * @private
92	 * @see Hyphenator-autoSetMainLanguage
93	 */
94	prompterStrings = {
95		'en': 'The language of this website could not be determined automatically. Please indicate the main language:',
96		'am': 'x'
97	},
98
99	/**
100	 * @name Hyphenator-basePath
101	 * @fieldOf Hyphenator
102	 * @description
103	 * A string storing the basepath from where Hyphenator.js was loaded.
104	 * This is used to load the patternfiles.
105	 * The basepath is determined dynamically by searching all script-tags for Hyphenator.js
106	 * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback.
107	 * @type string
108	 * @private
109	 * @see Hyphenator-loadPatterns
110	 */
111	basePath = (function () {
112		var s = document.getElementsByTagName('script'), i = 0, p, src, t;
113		while (!!(t = s[i++])) {
114			if (!t.src) {
115				continue;
116			}
117			src = t.src;
118			p = src.indexOf('Hyphenator.js');
119			if (p !== -1) {
120				return src.substring(0, p);
121			}
122		}
123		return 'http://hyphenator.googlecode.com/svn/trunk/';
124	}()),
125
126	/**
127	 * @name Hyphenator-isLocal
128	 * @fieldOf Hyphenator
129	 * @description
130	 * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if
131	 * it's loaded from an external source (i.e. directly from google.code)
132	 */
133	isLocal = (function () {
134		var re = false;
135		if (window.location.href.indexOf(basePath) !== -1) {
136			re = true;
137		}
138		return re;
139	}()),
140
141	/**
142	 * @name Hyphenator-documentLoaded
143	 * @fieldOf Hyphenator
144	 * @description
145	 * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
146	 */
147	documentLoaded = false,
148
149	/**
150	 * @name Hyphenator-dontHyphenate
151	 * @fieldOf Hyphenator
152	 * @description
153	 * A key-value object containing all html-tags whose content should not be hyphenated
154	 * @type object
155	 * @private
156	 * @see Hyphenator-hyphenateElement
157	 */
158	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},
159
160	/**
161	 * @name Hyphenator-enableCache
162	 * @fieldOf Hyphenator
163	 * @description
164	 * A variable to set if caching is enabled or not
165	 * @type boolean
166	 * @default true
167	 * @private
168	 * @see Hyphenator.config
169	 * @see hyphenateWord
170	 */
171	enableCache = true,
172
173	/**
174	 * @name Hyphenator-enableReducedPatternSet
175	 * @fieldOf Hyphenator
176	 * @description
177	 * A variable to set if storing the used patterns is set
178	 * @type boolean
179	 * @default false
180	 * @private
181	 * @see Hyphenator.config
182	 * @see hyphenateWord
183	 * @see Hyphenator.getRedPatternSet
184	 */
185	enableReducedPatternSet = false,
186
187	/**
188	 * @name Hyphenator-enableRemoteLoading
189	 * @fieldOf Hyphenator
190	 * @description
191	 * A variable to set if pattern files should be loaded remotely or not
192	 * @type boolean
193	 * @default true
194	 * @private
195	 * @see Hyphenator.config
196	 * @see Hyphenator-loadPatterns
197	 */
198	enableRemoteLoading = true,
199
200	/**
201	 * @name Hyphenator-displayToggleBox
202	 * @fieldOf Hyphenator
203	 * @description
204	 * A variable to set if the togglebox should be displayed or not
205	 * @type boolean
206	 * @default false
207	 * @private
208	 * @see Hyphenator.config
209	 * @see Hyphenator-toggleBox
210	 */
211	displayToggleBox = false,
212
213	/**
214	 * @name Hyphenator-hyphenateClass
215	 * @fieldOf Hyphenator
216	 * @description
217	 * A string containing the css-class-name for the hyphenate class
218	 * @type string
219	 * @default 'hyphenate'
220	 * @private
221	 * @example
222	 * &lt;p class = "hyphenate"&gt;Text&lt;/p&gt;
223	 * @see Hyphenator.config
224	 */
225	hyphenateClass = 'hyphenate',
226
227	/**
228	 * @name Hyphenator-dontHyphenateClass
229	 * @fieldOf Hyphenator
230	 * @description
231	 * A string containing the css-class-name for elements that should not be hyphenated
232	 * @type string
233	 * @default 'donthyphenate'
234	 * @private
235	 * @example
236	 * &lt;p class = "donthyphenate"&gt;Text&lt;/p&gt;
237	 * @see Hyphenator.config
238	 */
239	dontHyphenateClass = 'donthyphenate',
240
241	/**
242	 * @name Hyphenator-min
243	 * @fieldOf Hyphenator
244	 * @description
245	 * A number wich indicates the minimal length of words to hyphenate.
246	 * @type number
247	 * @default 6
248	 * @private
249	 * @see Hyphenator.config
250	 */
251	min = 6,
252
253	/**
254	 * @name Hyphenator-isBookmarklet
255	 * @fieldOf Hyphenator
256	 * @description
257	 * Indicates if Hyphanetor runs as bookmarklet or not.
258	 * @type boolean
259	 * @default false
260	 * @private
261	 */
262	isBookmarklet = (function () {
263		var loc = null, re = false, jsArray = document.getElementsByTagName('script'), i, l;
264		for (i = 0, l = jsArray.length; i < l; i++) {
265			if (!!jsArray[i].getAttribute('src')) {
266				loc = jsArray[i].getAttribute('src');
267			}
268			if (!loc) {
269				continue;
270			} else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
271				re = true;
272			}
273		}
274		return re;
275	}()),
276
277	/**
278	 * @name Hyphenator-mainLanguage
279	 * @fieldOf Hyphenator
280	 * @description
281	 * The general language of the document
282	 * @type number
283	 * @private
284	 * @see Hyphenator-autoSetMainLanguage
285	 */
286	mainLanguage = null,
287
288	/**
289	 * @name Hyphenator-elements
290	 * @fieldOf Hyphenator
291	 * @description
292	 * An array holding all elements that have to be hyphenated. This var is filled by
293	 * {@link Hyphenator-gatherDocumentInfos}
294	 * @type array
295	 * @private
296	 */
297	elements = [],
298
299	/**
300	 * @name Hyphenator-exceptions
301	 * @fieldOf Hyphenator
302	 * @description
303	 * An object containing exceptions as comma separated strings for each language.
304	 * When the language-objects are loaded, their exceptions are processed, copied here and then deleted.
305	 * @see Hyphenator-prepareLanguagesObj
306	 * @type object
307	 * @private
308	 */
309	exceptions = {},
310
311	/**
312	 * @name Hyphenator-docLanguages
313	 * @fieldOf Hyphenator
314	 * @description
315	 * An object holding all languages used in the document. This is filled by
316	 * {@link Hyphenator-gatherDocumentInfos}
317	 * @type object
318	 * @private
319	 */
320	docLanguages = {},
321
322
323	/**
324	 * @name Hyphenator-state
325	 * @fieldOf Hyphenator
326	 * @description
327	 * A number that inidcates the current state of the script
328	 * 0: not initialized
329	 * 1: loading patterns
330	 * 2: ready
331	 * 3: hyphenation done
332	 * 4: hyphenation removed
333	 * @type number
334	 * @private
335	 */
336	state = 0,
337
338	/**
339	 * @name Hyphenator-url
340	 * @fieldOf Hyphenator
341	 * @description
342	 * A string containing a RegularExpression to match URL's
343	 * @type string
344	 * @private
345	 */
346	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#!:\\.?\\+=&%@!\\-]*)*',
347	//      protocoll     usr     pwd                    ip               or                          host                 tld        port               path
348	/**
349	 * @name Hyphenator-mail
350	 * @fieldOf Hyphenator
351	 * @description
352	 * A string containing a RegularExpression to match mail-adresses
353	 * @type string
354	 * @private
355	 */
356	mail = '[\\w-\\.]+@[\\w\\.]+',
357
358	/**
359	 * @name Hyphenator-urlRE
360	 * @fieldOf Hyphenator
361	 * @description
362	 * A RegularExpressions-Object for url- and mail adress matching
363	 * @type object
364	 * @private
365	 */
366	urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),
367
368	/**
369	 * @name Hyphenator-zeroWidthSpace
370	 * @fieldOf Hyphenator
371	 * @description
372	 * A string that holds a char.
373	 * Depending on the browser, this is the zero with space or an empty string.
374	 * zeroWidthSpace is used to break URLs
375	 * @type string
376	 * @private
377	 */
378	zeroWidthSpace = (function () {
379		var zws, ua = navigator.userAgent.toLowerCase();
380		zws = String.fromCharCode(8203); //Unicode zero width space
381		if (ua.indexOf('msie 6') !== -1) {
382			zws = ''; //IE6 doesn't support zws
383		}
384		if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
385			zws = ''; //opera 10 on XP doesn't support zws
386		}
387		return zws;
388		//return '[zws]';
389	}()),
390
391	/**
392	 * @name Hyphenator-createElem
393	 * @fieldOf Hyphenator
394	 * @description
395	 * A function alias to document.createElementNS or document.createElement
396	 * @type function
397	 * @private
398	 */
399	createElem = function (tagname) {
400		if (document.createElementNS) {
401			return document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
402		} else if (document.createElement) {
403			return document.createElement(tagname);
404		}
405	},
406
407	/**
408	 * @name Hyphenator-onHyphenationDone
409	 * @fieldOf Hyphenator
410	 * @description
411	 * A method to be called, when the last element has been hyphenated or the hyphenation has been
412	 * removed from the last element.
413	 * @see Hyphenator.config
414	 * @type function
415	 * @private
416	 */
417	onHyphenationDone = function () {},
418
419	/**
420	 * @name Hyphenator-onError
421	 * @fieldOf Hyphenator
422	 * @description
423	 * A function that can be called upon an error.
424	 * @see Hyphenator.config
425	 * @type function
426	 * @private
427	 */
428	onError = function (e) {
429		window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
430	},
431
432	/**
433	 * @name Hyphenator-selectorFunction
434	 * @fieldOf Hyphenator
435	 * @description
436	 * A function that has to return a HTMLNodeList of Elements to be hyphenated.
437	 * By default it uses the classname ('hyphenate') to select the elements.
438	 * @see Hyphenator.config
439	 * @type function
440	 * @private
441	 */
442	selectorFunction = function () {
443		var tmp, el = [], i, l;
444		if (document.getElementsByClassName) {
445			el = document.getElementsByClassName(hyphenateClass);
446		} else {
447			tmp = document.getElementsByTagName('*');
448			l = tmp.length;
449			for (i = 0; i < l; i++)
450			{
451				if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
452					el.push(tmp[i]);
453				}
454			}
455		}
456		return el;
457	},
458
459	/**
460	 * @name Hyphenator-intermediateState
461	 * @fieldOf Hyphenator
462	 * @description
463	 * The value of style.visibility of the text while it is hyphenated.
464	 * @see Hyphenator.config
465	 * @type string
466	 * @private
467	 */
468	intermediateState = 'hidden',
469
470	/**
471	 * @name Hyphenator-hyphen
472	 * @fieldOf Hyphenator
473	 * @description
474	 * A string containing the character for in-word-hyphenation
475	 * @type string
476	 * @default the soft hyphen
477	 * @private
478	 * @see Hyphenator.config
479	 */
480	hyphen = String.fromCharCode(173),
481
482	/**
483	 * @name Hyphenator-urlhyphen
484	 * @fieldOf Hyphenator
485	 * @description
486	 * A string containing the character for url/mail-hyphenation
487	 * @type string
488	 * @default the zero width space
489	 * @private
490	 * @see Hyphenator.config
491	 * @see Hyphenator-zeroWidthSpace
492	 */
493	urlhyphen = zeroWidthSpace,
494
495	/**
496	 * @name Hyphenator-Expando
497	 * @methodOf Hyphenator
498	 * @description
499	 * This custom object stores data for elements: storing data directly in elements
500	 * (DomElement.customData = foobar;) isn't a good idea. It would lead to conflicts
501	 * in form elements, when the form has a child with name="foobar". Therefore, this
502	 * solution follows the approach of jQuery: the data is stored in an object and
503	 * referenced by a unique attribute of the element. The attribute has a name that
504	 * is built by the prefix "HyphenatorExpando_" and a random number, so if the very
505	 * very rare case occurs, that there's already an attribute with the same name, a
506	 * simple reload is enough to make it function.
507	 * @private
508	 */
509	Expando = (function () {
510		var container = {},
511			name = "HyphenatorExpando_" + Math.random(),
512			uuid = 0;
513		return {
514			getDataForElem : function (elem) {
515				return container[elem[name]];
516			},
517			setDataForElem : function (elem, data) {
518				var id;
519				if (elem[name] && elem[name] !== '') {
520					id = elem[name];
521				} else {
522					id = uuid++;
523					elem[name] = id;
524				}
525				container[id] = data;
526			},
527			appendDataForElem : function (elem, data) {
528				var k;
529				for (k in data) {
530					if (data.hasOwnProperty(k)) {
531						container[elem[name]][k] = data[k];
532					}
533				}
534			},
535			delDataOfElem : function (elem) {
536				delete container[elem[name]];
537			}
538		};
539	}()),
540
541	/*
542	 * runOnContentLoaded is based od jQuery.bindReady()
543	 * see
544	 * jQuery JavaScript Library v1.3.2
545	 * http://jquery.com/
546	 *
547	 * Copyright (c) 2009 John Resig
548	 * Dual licensed under the MIT and GPL licenses.
549	 * http://docs.jquery.com/License
550	 *
551	 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
552	 * Revision: 6246
553	 */
554	/**
555	 * @name Hyphenator-runOnContentLoaded
556	 * @methodOf Hyphenator
557	 * @description
558	 * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery
559	 * <a href = "http://jquery.com/</a>
560	 * @param object the window-object
561	 * @param function-object the function to call onDOMContentLoaded
562	 * @private
563	 */
564	runOnContentLoaded = function (w, f) {
565		var DOMContentLoaded, toplevel;
566		if (documentLoaded) {
567			f();
568			return;
569		}
570		function init() {
571			if (!documentLoaded) {
572				documentLoaded = true;
573				f();
574			}
575		}
576
577		function doScrollCheck() {
578			try {
579				// If IE is used, use the trick by Diego Perini
580				// http://javascript.nwbox.com/IEContentLoaded/
581				document.documentElement.doScroll("left");
582			} catch (error) {
583				setTimeout(doScrollCheck, 1);
584				return;
585			}
586
587			// and execute any waiting functions
588			init();
589		}
590
591
592		// Cleanup functions for the document ready method
593		if (document.addEventListener) {
594			DOMContentLoaded = function () {
595				document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
596				init();
597			};
598
599		} else if (document.attachEvent) {
600			DOMContentLoaded = function () {
601				// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
602				if (document.readyState === "complete") {
603					document.detachEvent("onreadystatechange", DOMContentLoaded);
604					init();
605				}
606			};
607		}
608
609		// Mozilla, Opera and webkit nightlies currently support this event
610		if (document.addEventListener) {
611			// Use the handy event callback
612			document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
613
614			// A fallback to window.onload, that will always work
615			window.addEventListener("load", init, false);
616
617		// If IE event model is used
618		} else if (document.attachEvent) {
619			// ensure firing before onload,
620			// maybe late but safe also for iframes
621			document.attachEvent("onreadystatechange", DOMContentLoaded);
622
623			// A fallback to window.onload, that will always work
624			window.attachEvent("onload", init);
625
626			// If IE and not a frame
627			// continually check to see if the document is ready
628			toplevel = false;
629			try {
630				toplevel = window.frameElement === null;
631			} catch (e) {}
632
633			if (document.documentElement.doScroll && toplevel) {
634				doScrollCheck();
635			}
636		}
637
638	},
639
640
641
642	/**
643	 * @name Hyphenator-getLang
644	 * @methodOf Hyphenator
645	 * @description
646	 * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}.
647	 * @param object The first parameter is an DOM-Element-Object
648	 * @param boolean The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage}
649	 * if there's no language found for the element.
650	 * @private
651	 */
652	getLang = function (el, fallback) {
653		if (!!el.getAttribute('lang')) {
654			return el.getAttribute('lang').substring(0, 2).toLowerCase();
655		}
656		// The following doesn't work in IE due to a bug when getAttribute('xml:lang') in a table
657		/*if (!!el.getAttribute('xml:lang')) {
658			return el.getAttribute('xml:lang').substring(0, 2);
659		}*/
660		//instead, we have to do this (thanks to borgzor):
661		try {
662			if (!!el.getAttribute('xml:lang')) {
663				return el.getAttribute('xml:lang').substring(0, 2).toLowerCase();
664			}
665		} catch (ex) {}
666		if (el.tagName !== 'HTML') {
667			return getLang(el.parentNode, true);
668		}
669		if (fallback) {
670			return mainLanguage;
671		}
672		return null;
673	},
674
675	/**
676	 * @name Hyphenator-autoSetMainLanguage
677	 * @methodOf Hyphenator
678	 * @description
679	 * Retrieves the language of the document from the DOM.
680	 * The function looks in the following places:
681	 * <ul>
682	 * <li>lang-attribute in the html-tag</li>
683	 * <li>&lt;meta http-equiv = "content-language" content = "xy" /&gt;</li>
684	 * <li>&lt;meta name = "DC.Language" content = "xy" /&gt;</li>
685	 * <li>&lt;meta name = "language" content = "xy" /&gt;</li>
686	 * </li>
687	 * If nothing can be found a prompt using {@link Hyphenator-languageHint} and {@link Hyphenator-prompterStrings} is displayed.
688	 * If the retrieved language is in the object {@link Hyphenator-supportedLang} it is copied to {@link Hyphenator-mainLanguage}
689	 * @private
690	 */
691	autoSetMainLanguage = function () {
692		var el = document.getElementsByTagName('html')[0],
693			m = document.getElementsByTagName('meta'),
694			i, text, lang, e, ul;
695		mainLanguage = getLang(el);
696		if (!mainLanguage) {
697			for (i = 0; i < m.length; i++) {
698				//<meta http-equiv = "content-language" content="xy">
699				if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv') === 'content-language')) {
700					mainLanguage = m[i].getAttribute('content').substring(0, 2).toLowerCase();
701				}
702				//<meta name = "DC.Language" content="xy">
703				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name') === 'DC.language')) {
704					mainLanguage = m[i].getAttribute('content').substring(0, 2).toLowerCase();
705				}
706				//<meta name = "language" content = "xy">
707				if (!!m[i].getAttribute('name') && (m[i].getAttribute('name') === 'language')) {
708					mainLanguage = m[i].getAttribute('content').substring(0, 2).toLowerCase();
709				}
710			}
711		}
712		if (!mainLanguage) {
713			text = '';
714			ul = navigator.language ? navigator.language : navigator.userLanguage;
715			ul = ul.substring(0, 2);
716			if (prompterStrings.hasOwnProperty(ul)) {
717				text = prompterStrings[ul];
718			} else {
719				text = prompterStrings.en;
720			}
721			text += ' (ISO 639-1)\n\n' + languageHint;
722			lang = window.prompt(unescape(text), ul).toLowerCase();
723			if (supportedLang[lang]) {
724				mainLanguage = lang;
725			} else {
726				e = new Error('The language "' + lang + '" is not yet supported.');
727				throw e;
728			}
729		}
730	},
731
732	/**
733	 * @name Hyphenator-gatherDocumentInfos
734	 * @methodOf Hyphenator
735	 * @description
736	 * This method runs through the DOM and executes the process()-function on:
737	 * - every node returned by the {@link Hyphenator-selectorFunction}.
738	 * The process()-function copies the element to the elements-variable, sets its visibility
739	 * to intermediateState, retrieves its language and recursivly descends the DOM-tree until
740	 * the child-Nodes aren't of type 1
741	 * @private
742	 */
743	gatherDocumentInfos = function () {
744		var elToProcess, tmp, i = 0,
745		process = function (el, hide, lang) {
746			var n, i = 0, hyphenatorSettings = {};
747			if (hide && intermediateState === 'hidden') {
748				if (!!el.getAttribute('style')) {
749					hyphenatorSettings.hasOwnStyle = true;
750				} else {
751					hyphenatorSettings.hasOwnStyle = false;
752				}
753				hyphenatorSettings.isHidden = true;
754				el.style.visibility = 'hidden';
755			}
756			if (el.lang && typeof(el.lang) === 'string') {
757				hyphenatorSettings.language = el.lang.toLowerCase(); //copy attribute-lang to internal lang
758			} else if (lang) {
759				hyphenatorSettings.language = lang.toLowerCase();
760			} else {
761				hyphenatorSettings.language = getLang(el, true);
762			}
763			lang = hyphenatorSettings.language;
764			if (supportedLang[lang]) {
765				docLanguages[lang] = true;
766			} else {
767				if (!Hyphenator.isBookmarklet()) {
768					onError(new Error('Language ' + lang + ' is not yet supported.'));
769				}
770			}
771			Expando.setDataForElem(el, hyphenatorSettings);
772			elements.push(el);
773			while (!!(n = el.childNodes[i++])) {
774				if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
775					n.className.indexOf(dontHyphenateClass) === -1 && !(n in elToProcess)) {
776					process(n, false, lang);
777				}
778			}
779		};
780		if (Hyphenator.isBookmarklet()) {
781			elToProcess = document.getElementsByTagName('body')[0];
782			process(elToProcess, false, mainLanguage);
783		} else {
784			elToProcess = selectorFunction();
785			while (!!(tmp = elToProcess[i++]))
786			{
787				process(tmp, true);
788			}
789		}
790		if (!Hyphenator.languages.hasOwnProperty(mainLanguage)) {
791			docLanguages[mainLanguage] = true;
792		} else if (!Hyphenator.languages[mainLanguage].prepared) {
793			docLanguages[mainLanguage] = true;
794		}
795		if (elements.length > 0) {
796			Expando.appendDataForElem(elements[elements.length - 1], {isLast : true});
797		}
798	},
799
800	/**
801	 * @name Hyphenator-convertPatterns
802	 * @methodOf Hyphenator
803	 * @description
804	 * Converts the patterns from string '_a6' to object '_a':'_a6'.
805	 * The result is stored in the {@link Hyphenator-patterns}-object.
806	 * @private
807	 * @param string the language whose patterns shall be converted
808	 */
809	convertPatterns = function (lang) {
810		var plen, anfang, pats, pat, key, tmp = {};
811		pats = Hyphenator.languages[lang].patterns;
812		for (plen in pats) {
813			if (pats.hasOwnProperty(plen)) {
814				plen = parseInt(plen, 10);
815				anfang = 0;
816				while (!!(pat = pats[plen].substr(anfang, plen))) {
817					key = pat.replace(/\d/g, '');
818					tmp[key] = pat;
819					anfang += plen;
820				}
821			}
822		}
823		Hyphenator.languages[lang].patterns = tmp;
824		Hyphenator.languages[lang].patternsConverted = true;
825	},
826
827	/**
828	 * @name Hyphenator-convertExceptionsToObject
829	 * @methodOf Hyphenator
830	 * @description
831	 * Converts a list of comma seprated exceptions to an object:
832	 * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
833	 * @private
834	 * @param string a comma separated string of exceptions (without spaces)
835	 */
836	convertExceptionsToObject = function (exc) {
837		var w = exc.split(', '),
838			r = {},
839			i, l, key;
840		for (i = 0, l = w.length; i < l; i++) {
841			key = w[i].replace(/-/g, '');
842			if (!r.hasOwnProperty(key)) {
843				r[key] = w[i];
844			}
845		}
846		return r;
847	},
848
849	/**
850	 * @name Hyphenator-loadPatterns
851	 * @methodOf Hyphenator
852	 * @description
853	 * Adds a &lt;script&gt;-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
854	 * If the iven language is not in the {@link Hyphenator-supportedLang}-Object it returns.
855	 * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object
856	 * has a same-origin-policy. This makes the isBookmarklet-functionality impossible.
857	 * @param string The language to load the patterns for
858	 * @private
859	 * @see Hyphenator-basePath
860	 */
861	loadPatterns = function (lang) {
862		var url, xhr, head, script;
863		if (supportedLang[lang] && !Hyphenator.languages[lang]) {
864	        url = basePath + 'patterns/' + lang + '.js';
865		} else {
866			return;
867		}
868		if (isLocal && !isBookmarklet) {
869			//check if 'url' is available:
870			xhr = null;
871			if (typeof XMLHttpRequest !== 'undefined') {
872				xhr = new XMLHttpRequest();
873			}
874			if (!xhr) {
875				try {
876					xhr  = new ActiveXObject("Msxml2.XMLHTTP");
877				} catch (e) {
878					xhr  = null;
879				}
880			}
881			if (xhr) {
882				xhr.open('HEAD', url, false);
883				xhr.setRequestHeader('Cache-Control', 'no-cache');
884				xhr.send(null);
885				if (xhr.status === 404) {
886					onError(new Error('Could not load\n' + url));
887					delete docLanguages[lang];
888					return;
889				}
890			}
891		}
892		if (createElem) {
893			head = document.getElementsByTagName('head').item(0);
894			script = createElem('script');
895			script.src = url;
896			script.type = 'text/javascript';
897			head.appendChild(script);
898		}
899	},
900
901	/**
902	 * @name Hyphenator-prepareLanguagesObj
903	 * @methodOf Hyphenator
904	 * @description
905	 * Adds a cache to each language and converts the exceptions-list to an object.
906	 * @private
907	 * @param string the language ob the lang-obj
908	 */
909	prepareLanguagesObj = function (lang) {
910		var lo = Hyphenator.languages[lang], wrd;
911		if (!lo.prepared) {
912			if (enableCache) {
913				lo.cache = {};
914			}
915			if (enableReducedPatternSet) {
916				lo.redPatSet = {};
917			}
918			if (lo.hasOwnProperty('exceptions')) {
919				Hyphenator.addExceptions(lang, lo.exceptions);
920				delete lo.exceptions;
921			}
922			if (exceptions.hasOwnProperty('global')) {
923				if (exceptions.hasOwnProperty(lang)) {
924					exceptions[lang] += ', ' + exceptions.global;
925				} else {
926					exceptions[lang] = exceptions.global;
927				}
928			}
929			if (exceptions.hasOwnProperty(lang)) {
930				lo.exceptions = convertExceptionsToObject(exceptions[lang]);
931				delete exceptions[lang];
932			} else {
933				lo.exceptions = {};
934			}
935			convertPatterns(lang);
936			wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + '-]{' + min + ',}';
937			lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
938			lo.prepared = true;
939		}
940	},
941
942	/**
943	 * @name Hyphenator-prepare
944	 * @methodOf Hyphenator
945	 * @description
946	 * This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
947	 * that the patternfiles are loaded, all conversions are made and the callback is called.
948	 * If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
949	 * by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
950	 * converted to their object style and the lang-object extended.
951	 * Finally the callback is called.
952	 * @param function-object callback to call, when all patterns are loaded
953	 * @private
954	 */
955	prepare = function (callback) {
956		var lang, docLangEmpty = true, interval;
957		if (!enableRemoteLoading) {
958			for (lang in Hyphenator.languages) {
959				if (Hyphenator.languages.hasOwnProperty(lang)) {
960					prepareLanguagesObj(lang);
961				}
962			}
963			state = 2;
964			callback();
965			return;
966		}
967		// get all languages that are used and preload the patterns
968		state = 1;
969		for (lang in docLanguages) {
970			if (docLanguages.hasOwnProperty(lang)) {
971				loadPatterns(lang);
972				docLangEmpty = false;
973			}
974		}
975		if (docLangEmpty) {
976			state = 2;
977			callback();
978			return;
979		}
980		// wait until they are loaded
981		interval = window.setInterval(function () {
982			var finishedLoading = false, lang;
983			for (lang in docLanguages) {
984				if (docLanguages.hasOwnProperty(lang)) {
985					if (!Hyphenator.languages[lang]) {
986						finishedLoading = false;
987						break;
988					} else {
989						finishedLoading = true;
990						delete docLanguages[lang];
991						//do conversion while other patterns are loading:
992						prepareLanguagesObj(lang);
993					}
994				}
995			}
996			if (finishedLoading) {
997				window.clearInterval(interval);
998				state = 2;
999				callback();
1000			}
1001		}, 100);
1002	},
1003
1004	/**
1005	 * @name Hyphenator-switchToggleBox
1006	 * @methodOf Hyphenator
1007	 * @description
1008	 * Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
1009	 * @param boolean true when hyphenation is on, false when it's off
1010	 * @see Hyphenator.config
1011	 * @private
1012	 */
1013	toggleBox = function (s) {
1014		var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute;
1015		if (!!(myBox = document.getElementById('HyphenatorToggleBox'))) {
1016			if (s) {
1017				myBox.firstChild.data = 'Hy-phe-na-ti-on';
1018			} else {
1019				myBox.firstChild.data = 'Hyphenation';
1020			}
1021		} else {
1022			bdy = document.getElementsByTagName('body')[0];
1023			myBox = createElem('div');
1024			myIdAttribute = document.createAttribute('id');
1025			myIdAttribute.nodeValue = 'HyphenatorToggleBox';
1026			myClassAttribute = document.createAttribute('class');
1027			myClassAttribute.nodeValue = dontHyphenateClass;
1028			myTextNode = document.createTextNode('Hy-phe-na-ti-on');
1029			myBox.appendChild(myTextNode);
1030			myBox.setAttributeNode(myIdAttribute);
1031			myBox.setAttributeNode(myClassAttribute);
1032			myBox.onclick =  Hyphenator.toggleHyphenation;
1033			myBox.style.position = 'absolute';
1034			myBox.style.top = '0px';
1035			myBox.style.right = '0px';
1036			myBox.style.margin = '0';
1037			myBox.style.backgroundColor = '#AAAAAA';
1038			myBox.style.color = '#FFFFFF';
1039			myBox.style.font = '6pt Arial';
1040			myBox.style.letterSpacing = '0.2em';
1041			myBox.style.padding = '3px';
1042			myBox.style.cursor = 'pointer';
1043			myBox.style.WebkitBorderBottomLeftRadius = '4px';
1044			myBox.style.MozBorderRadiusBottomleft = '4px';
1045			bdy.appendChild(myBox);
1046		}
1047	},
1048
1049	/**
1050	 * @name Hyphenator-hyphenateWord
1051	 * @methodOf Hyphenator
1052	 * @description
1053	 * This function is the heart of Hyphenator.js. It returns a hyphenated word.
1054	 *
1055	 * If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
1056	 * If the word is in the exceptions list or in the cache, it is retrieved from it.
1057	 * If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
1058	 * @param string The language of the word
1059	 * @param string The word
1060	 * @returns string The hyphenated word
1061	 * @public
1062	 */
1063	hyphenateWord = function (lang, word) {
1064		var lo = Hyphenator.languages[lang],
1065			parts, i, l, w, wl, s, hypos, p, maxwins, win, pat = false, patk, patl, c, digits, z, numb3rs, n, inserted, hyphenatedword;
1066		if (word === '') {
1067			return '';
1068		}
1069		if (word.indexOf(hyphen) !== -1) {
1070			//word already contains shy; -> leave at it is!
1071			return word;
1072		}
1073		if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
1074			return lo.cache[word];
1075		}
1076		if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
1077			return lo.exceptions[word].replace(/-/g, hyphen);
1078		}
1079		if (word.indexOf('-') !== -1) {
1080			//word contains '-' -> hyphenate the parts separated with '-'
1081			parts = word.split('-');
1082			for (i = 0, l = parts.length; i < l; i++) {
1083				parts[i] = hyphenateWord(lang, parts[i]);
1084			}
1085			return parts.join('-');
1086		}
1087		//finally the core hyphenation algorithm
1088		w = '_' + word + '_';
1089		wl = w.length;
1090		s = w.split('');
1091		w = w.toLowerCase();
1092		hypos = [];
1093		numb3rs = {'0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true}; //check for member is faster then isFinite()
1094		n = wl - lo.shortestPattern;
1095		for (p = 0; p <= n; p++) {
1096			maxwins = Math.min((wl - p), lo.longestPattern);
1097			for (win = lo.shortestPattern; win <= maxwins; win++) {
1098				if (lo.patterns.hasOwnProperty(patk = w.substr(p, win))) {
1099					pat = lo.patterns[patk];
1100				} else {
1101					continue;
1102				}
1103				if (enableReducedPatternSet) {
1104					lo.redPatSet[patk] = pat;
1105				}
1106				digits = 1;
1107				patl = pat.length;
1108				for (i = 0; i < patl; i++) {
1109					c = pat.charAt(i);
1110					if (numb3rs[c]) {
1111						if (i === 0) {
1112							z = p - 1;
1113							if (!hypos[z] || hypos[z] < c) {
1114								hypos[z] = c;
1115							}
1116						} else {
1117							z = p + i - digits;
1118							if (!hypos[z] || hypos[z] < c) {
1119								hypos[z] = c;
1120							}
1121						}
1122						digits++;
1123					}
1124				}
1125			}
1126		}
1127		inserted = 0;
1128		for (i = lo.leftmin; i <= (word.length - lo.rightmin); i++) {
1129			if (!!(hypos[i] & 1)) {
1130				s.splice(i + inserted + 1, 0, hyphen);
1131				inserted++;
1132			}
1133		}
1134		hyphenatedword = s.slice(1, -1).join('');
1135		if (enableCache) {
1136			lo.cache[word] = hyphenatedword;
1137		}
1138		return hyphenatedword;
1139	},
1140
1141	/**
1142	 * @name Hyphenator-hyphenateURL
1143	 * @methodOf Hyphenator
1144	 * @description
1145	 * Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
1146	 * @param string URL to hyphenate
1147	 * @returns string the hyphenated URL
1148	 * @public
1149	 */
1150	hyphenateURL = function (url) {
1151		return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
1152	},
1153
1154	/**
1155	 * @name Hyphenator-hyphenateElement
1156	 * @methodOf Hyphenator
1157	 * @description
1158	 * Takes the content of the given element and - if there's text - replaces the words
1159	 * by hyphenated words. If there's another element, the function is called recursively.
1160	 * When all words are hyphenated, the visibility of the element is set to 'visible'.
1161	 * @param object The element to hyphenate
1162	 * @param string The language used in this element
1163	 * @public
1164	 */
1165	hyphenateElement = function (el) {
1166		var hyphenatorSettings = Expando.getDataForElem(el),
1167			lang = hyphenatorSettings.language, hyphenate, n, i;
1168		if (Hyphenator.languages.hasOwnProperty(lang)) {
1169			hyphenate = function (word) {
1170				if (urlOrMailRE.test(word)) {
1171					return hyphenateURL(word);
1172				} else {
1173					return hyphenateWord(lang, word);
1174				}
1175			};
1176			i = 0;
1177			while (!!(n = el.childNodes[i++])) {
1178				if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
1179					n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1180				}
1181			}
1182		}
1183		if (hyphenatorSettings.isHidden && intermediateState === 'hidden') {
1184			el.style.visibility = 'visible';
1185			if (!hyphenatorSettings.hasOwnStyle) {
1186				el.setAttribute('style', ''); // without this, removeAttribute doesn't work in Safari (thanks to molily)
1187				el.removeAttribute('style');
1188			} else {
1189				if (el.style.removeProperty) {
1190					el.style.removeProperty('visibility');
1191				} else if (el.style.removeAttribute) { // IE
1192					el.style.removeAttribute('visibility');
1193				}
1194			}
1195		}
1196		if (hyphenatorSettings.isLast) {
1197			state = 3;
1198			onHyphenationDone();
1199		}
1200	},
1201
1202	/**
1203	 * @name Hyphenator-removeHyphenationFromElement
1204	 * @methodOf Hyphenator
1205	 * @description
1206	 * Removes all hyphens from the element. If there are other elements, the function is
1207	 * called recursively.
1208	 * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
1209	 * @param object The element where to remove hyphenation.
1210	 * @public
1211	 */
1212	removeHyphenationFromElement = function (el) {
1213		var h, i = 0, n;
1214		switch (hyphen) {
1215		case '|':
1216			h = '\\|';
1217			break;
1218		case '+':
1219			h = '\\+';
1220			break;
1221		case '*':
1222			h = '\\*';
1223			break;
1224		default:
1225			h = hyphen;
1226		}
1227		while (!!(n = el.childNodes[i++])) {
1228			if (n.nodeType === 3) {
1229				n.data = n.data.replace(new RegExp(h, 'g'), '');
1230				n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
1231			} else if (n.nodeType === 1) {
1232				removeHyphenationFromElement(n);
1233			}
1234		}
1235	},
1236
1237	/**
1238	 * @name Hyphenator-hyphenateDocument
1239	 * @methodOf Hyphenator
1240	 * @description
1241	 * Calls hyphenateElement() for all members of elements. This is done with a setTimout
1242	 * to prevent a "long running Script"-alert when hyphenating large pages.
1243	 * Therefore a tricky bind()-function was necessary.
1244	 * @public
1245	 */
1246	hyphenateDocument = function () {
1247		function bind(fun, arg) {
1248			return function () {
1249				return fun(arg);
1250			};
1251		}
1252		var i = 0, el;
1253		while (!!(el = elements[i++])) {
1254			window.setTimeout(bind(hyphenateElement, el), 0);
1255
1256		}
1257	},
1258
1259	/**
1260	 * @name Hyphenator-removeHyphenationFromDocument
1261	 * @methodOf Hyphenator
1262	 * @description
1263	 * Does what it says ;-)
1264	 * @public
1265	 */
1266	removeHyphenationFromDocument = function () {
1267		var i = 0, el;
1268		while (!!(el = elements[i++])) {
1269			removeHyphenationFromElement(el);
1270		}
1271		state = 4;
1272	};
1273
1274	return {
1275
1276		/**
1277		 * @name Hyphenator.version
1278		 * @memberOf Hyphenator
1279		 * @description
1280		 * String containing the actual version of Hyphenator.js
1281		 * [major release].[minor releas].[bugfix release]
1282		 * major release: new API, new Features, big changes
1283		 * minor release: new languages, improvements
1284		 * @public
1285         */
1286		version: '2.5.0',
1287
1288		/**
1289		 * @name Hyphenator.languages
1290		 * @memberOf Hyphenator
1291		 * @description
1292		 * Objects that holds key-value pairs, where key is the language and the value is the
1293		 * language-object loaded from (and set by) the pattern file.
1294		 * The language object holds the following members:
1295		 * <table>
1296		 * <tr><th>key</th><th>desc></th></tr>
1297		 * <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
1298		 * <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
1299		 * <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
1300		 * <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
1301		 * <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
1302		 * <tr><td>patterns</td><td>the patterns</td></tr>
1303		 * </table>
1304		 * And optionally (or after prepareLanguagesObj() has been called):
1305		 * <table>
1306		 * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
1307		 * </table>
1308		 * @public
1309         */
1310		languages: {},
1311
1312
1313		/**
1314		 * @name Hyphenator.config
1315		 * @methodOf Hyphenator
1316		 * @description
1317		 * Config function that takes an object as an argument. The object contains key-value-pairs
1318		 * containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
1319		 * @param object <table>
1320		 * <tr><th>key</th><th>values</th><th>default</th></tr>
1321		 * <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
1322		 * <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
1323		 * <tr><td>hyphenchar</td><td>string</td><td>'&amp;shy;'</td></tr>
1324		 * <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
1325		 * <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
1326		 * <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
1327		 * <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
1328		 * <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
1329		 * <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
1330		 * <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
1331		 * </table>
1332		 * @public
1333		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1334         * &lt;script type = "text/javascript"&gt;
1335         *     Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
1336         *     Hyphenator.run();
1337         * &lt;/script&gt;
1338         */
1339		config: function (obj) {
1340			var assert = function (name, type) {
1341					if (typeof obj[name] === type) {
1342						return true;
1343					} else {
1344						onError(new Error('Config onError: ' + name + ' must be of type ' + type));
1345						return false;
1346					}
1347				},
1348				key;
1349			for (key in obj) {
1350				if (obj.hasOwnProperty(key)) {
1351					switch (key) {
1352					case 'classname':
1353						if (assert('classname', 'string')) {
1354							hyphenateClass = obj.classname;
1355						}
1356						break;
1357					case 'donthyphenateclassname':
1358						if (assert('donthyphenateclassname', 'string')) {
1359							dontHyphenateClass = obj.donthyphenateclassname;
1360						}
1361						break;
1362					case 'minwordlength':
1363						if (assert('minwordlength', 'number')) {
1364							min = obj.minwordlength;
1365						}
1366						break;
1367					case 'hyphenchar':
1368						if (assert('hyphenchar', 'string')) {
1369							if (obj.hyphenchar === '&shy;') {
1370								obj.hyphenchar = String.fromCharCode(173);
1371							}
1372							hyphen = obj.hyphenchar;
1373						}
1374						break;
1375					case 'urlhyphenchar':
1376						if (obj.hasOwnProperty('urlhyphenchar')) {
1377							if (assert('urlhyphenchar', 'string')) {
1378								urlhyphen = obj.urlhyphenchar;
1379							}
1380						}
1381						break;
1382					case 'togglebox':
1383						if (assert('togglebox', 'function')) {
1384							toggleBox = obj.togglebox;
1385						}
1386						break;
1387					case 'displaytogglebox':
1388						if (assert('displaytogglebox', 'boolean')) {
1389							displayToggleBox = obj.displaytogglebox;
1390						}
1391						break;
1392					case 'remoteloading':
1393						if (assert('remoteloading', 'boolean')) {
1394							enableRemoteLoading = obj.remoteloading;
1395						}
1396						break;
1397					case 'enablecache':
1398						if (assert('enablecache', 'boolean')) {
1399							enableCache = obj.enablecache;
1400						}
1401						break;
1402					case 'enablereducedpatternset':
1403						if (assert('enablereducedpatternset', 'boolean')) {
1404							enableReducedPatternSet = obj.enablereducedpatternset;
1405						}
1406						break;
1407					case 'onhyphenationdonecallback':
1408						if (assert('onhyphenationdonecallback', 'function')) {
1409							onHyphenationDone = obj.onhyphenationdonecallback;
1410						}
1411						break;
1412					case 'onerrorhandler':
1413						if (assert('onerrorhandler', 'function')) {
1414							onError = obj.onerrorhandler;
1415						}
1416						break;
1417					case 'intermediatestate':
1418						if (assert('intermediatestate', 'string')) {
1419							intermediateState = obj.intermediatestate;
1420						}
1421						break;
1422					case 'selectorfunction':
1423						if (assert('selectorfunction', 'function')) {
1424							selectorFunction = obj.selectorfunction;
1425						}
1426						break;
1427					default:
1428						onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
1429					}
1430				}
1431			}
1432		},
1433
1434		/**
1435		 * @name Hyphenator.run
1436		 * @methodOf Hyphenator
1437		 * @description
1438		 * Bootstrap function that starts all hyphenation processes when called.
1439		 * @public
1440		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1441         * &lt;script type = "text/javascript"&gt;
1442         *   Hyphenator.run();
1443         * &lt;/script&gt;
1444         */
1445		run: function () {
1446			var process = function () {
1447				try {
1448					autoSetMainLanguage();
1449					gatherDocumentInfos();
1450					prepare(hyphenateDocument);
1451					if (displayToggleBox) {
1452						toggleBox(true);
1453					}
1454				} catch (e) {
1455					onError(e);
1456				}
1457			};
1458			if (!documentLoaded) {
1459				runOnContentLoaded(window, process);
1460			}
1461			if (Hyphenator.isBookmarklet() || documentLoaded) {
1462				process();
1463			}
1464		},
1465
1466		/**
1467		 * @name Hyphenator.addExceptions
1468		 * @methodOf Hyphenator
1469		 * @description
1470		 * Adds the exceptions from the string to the appropriate language in the
1471		 * {@link Hyphenator-languages}-object
1472		 * @param string The language
1473		 * @param string A comma separated string of hyphenated words WITH spaces.
1474		 * @public
1475		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1476         * &lt;script type = "text/javascript"&gt;
1477         *   Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
1478         *   Hyphenator.run();
1479         * &lt;/script&gt;
1480         */
1481		addExceptions: function (lang, words) {
1482			if (lang === '') {
1483				lang = 'global';
1484			}
1485			if (exceptions.hasOwnProperty[lang]) {
1486				exceptions[lang] += ", " + words;
1487			} else {
1488				exceptions[lang] = words;
1489			}
1490		},
1491
1492		/**
1493		 * @name Hyphenator.hyphenate
1494		 * @methodOf Hyphenator
1495		 * @public
1496		 * @description
1497		 * Hyphenates the target. The language patterns must be loaded.
1498		 * If the target is a string, the hyphenated string is returned,
1499		 * if it's an object, the values are hyphenated directly.
1500		 * @param mixed the target to be hyphenated
1501		 * @param string the language of the target
1502		 * @returns string
1503		 * @example &lt;script src = "Hyphenator.js" type = "text/javascript"&gt;&lt;/script&gt;
1504		 * &lt;script src = "patterns/en.js" type = "text/javascript"&gt;&lt;/script&gt;
1505         * &lt;script type = "text/javascript"&gt;
1506		 * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
1507		 * &lt;/script&gt;
1508		 */
1509		hyphenate: function (target, lang) {
1510			var hyphenate, n, i;
1511			if (Hyphenator.languages.hasOwnProperty(lang)) {
1512				if (!Hyphenator.languages[lang].prepared) {
1513					prepareLanguagesObj(lang);
1514				}
1515				hyphenate = function (word) {
1516					if (urlOrMailRE.test(word)) {
1517						return hyphenateURL(word);
1518					} else {
1519						return hyphenateWord(lang, word);
1520					}
1521				};
1522				if (typeof target === 'string' || target.constructor === String) {
1523					return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1524				} else if (typeof target === 'object') {
1525					i = 0;
1526					while (!!(n = target.childNodes[i++])) {
1527						if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
1528							n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1529						} else if (n.nodeType === 1) {
1530							if (n.lang !== '') {
1531								lang = n.lang;
1532							}
1533							Hyphenator.hyphenate(n, lang);
1534						}
1535					}
1536				}
1537			} else {
1538				onError(new Error('Language "' + lang + '" is not loaded.'));
1539			}
1540		},
1541
1542		/**
1543		 * @name Hyphenator.getRedPatternSet
1544		 * @methodOf Hyphenator
1545		 * @description
1546		 * Returns {@link Hyphenator-isBookmarklet}.
1547		 * @param string the language patterns are stored for
1548		 * @returns object {'patk': pat}
1549		 * @public
1550         */
1551		getRedPatternSet: function (lang) {
1552			return Hyphenator.languages[lang].redPatSet;
1553		},
1554
1555		/**
1556		 * @name Hyphenator.isBookmarklet
1557		 * @methodOf Hyphenator
1558		 * @description
1559		 * Returns {@link Hyphenator-isBookmarklet}.
1560		 * @returns boolean
1561		 * @public
1562         */
1563		isBookmarklet: function () {
1564			return isBookmarklet;
1565		},
1566
1567
1568		/**
1569		 * @name Hyphenator.toggleHyphenation
1570		 * @methodOf Hyphenator
1571		 * @description
1572		 * Checks the current state of the ToggleBox and removes or does hyphenation.
1573		 * @public
1574         */
1575		toggleHyphenation: function () {
1576			switch (state) {
1577			case 3:
1578				removeHyphenationFromDocument();
1579				toggleBox(false);
1580				break;
1581			case 4:
1582				hyphenateDocument();
1583				toggleBox(true);
1584				break;
1585			}
1586		}
1587	};
1588}());
1589if (Hyphenator.isBookmarklet()) {
1590	Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible'});
1591	Hyphenator.run();
1592}