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