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/
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.
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.
21 * Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
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 */
31 * @description Provides all functionality to do hyphenation, except the patterns that are loaded
33 * @author Mathias Nater, <a href = "mailto:mathias@mnn.ch">mathias@mnn.ch</a>
35 * @namespace Holds all methods and properties
37 * <script src = "Hyphenator.js" type = "text/javascript"></script>
38 * <script type = "text/javascript">
42 var Hyphenator = (function (window) {
46 * @name Hyphenator-supportedLang
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>}
54 * Check if language lang is supported:
55 * if (supportedLang.hasOwnProperty(lang))
63 'el': 'el-monoton.js',
64 'el-monoton': 'el-monoton.js',
65 'el-polyton': 'el-polyton.js',
97 * @name Hyphenator-languageHint
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
104 * @see Hyphenator-autoSetMainLanguage
107 languageHint = (function () {
109 for (k in supportedLang) {
110 if (supportedLang.hasOwnProperty(k)) {
114 r = r.substring(0, r.length - 2);
119 * @name Hyphenator-prompterStrings
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>}
125 * @see Hyphenator-autoSetMainLanguage
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': 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:'
152 * @name Hyphenator-basePath
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.
160 * @see Hyphenator-loadPatterns
162 basePath = (function () {
163 var s = document.getElementsByTagName('script'), i = 0, p, src, t;
164 while (!!(t = s[i++])) {
169 p = src.indexOf('Hyphenator.js');
171 return src.substring(0, p);
174 return 'http://hyphenator.googlecode.com/svn/trunk/';
178 * @name Hyphenator-isLocal
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)
183 isLocal = (function () {
185 if (window.location.href.indexOf(basePath) !== -1) {
192 * @name Hyphenator-documentLoaded
194 * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
196 documentLoaded = false,
201 * @name Hyphenator-contextWindow
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!
207 contextWindow = window,
210 * @name Hyphenator-doFrames
212 * switch to control if frames/iframes should be hyphenated, too
213 * defaults to false (frames are a bag of hurt!)
218 * @name Hyphenator-dontHyphenate
220 * A key-value object containing all html-tags whose content should not be hyphenated
221 * @type {Object.<string,boolean>}
223 * @see Hyphenator-hyphenateElement
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},
228 * @name Hyphenator-enableCache
230 * A variable to set if caching is enabled or not
234 * @see Hyphenator.config
240 * @name Hyphenator-storageType
242 * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
246 * @see Hyphenator.config
248 storageType = 'local',
251 * @name Hyphenator-storage
253 * An alias to the storage-Method defined in storageType.
254 * Set by Hyphenator.run()
255 * @type {Object|undefined}
258 * @see Hyphenator.run
263 * @name Hyphenator-enableReducedPatternSet
265 * A variable to set if storing the used patterns is set
269 * @see Hyphenator.config
271 * @see Hyphenator.getRedPatternSet
273 enableReducedPatternSet = false,
276 * @name Hyphenator-enableRemoteLoading
278 * A variable to set if pattern files should be loaded remotely or not
282 * @see Hyphenator.config
283 * @see Hyphenator-loadPatterns
285 enableRemoteLoading = true,
288 * @name Hyphenator-displayToggleBox
290 * A variable to set if the togglebox should be displayed or not
294 * @see Hyphenator.config
295 * @see Hyphenator-toggleBox
297 displayToggleBox = false,
300 * @name Hyphenator-hyphenateClass
302 * A string containing the css-class-name for the hyphenate class
304 * @default 'hyphenate'
307 * <p class = "hyphenate">Text</p>
308 * @see Hyphenator.config
310 hyphenateClass = 'hyphenate',
313 * @name Hyphenator-dontHyphenateClass
315 * A string containing the css-class-name for elements that should not be hyphenated
317 * @default 'donthyphenate'
320 * <p class = "donthyphenate">Text</p>
321 * @see Hyphenator.config
323 dontHyphenateClass = 'donthyphenate',
326 * @name Hyphenator-min
328 * A number wich indicates the minimal length of words to hyphenate.
332 * @see Hyphenator.config
337 * @name Hyphenator-orphanControl
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
350 * @name Hyphenator-isBookmarklet
352 * Indicates if Hyphanetor runs as bookmarklet or not.
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');
365 } else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
373 * @name Hyphenator-mainLanguage
375 * The general language of the document
376 * @type {string|null}
378 * @see Hyphenator-autoSetMainLanguage
383 * @name Hyphenator-elements
385 * An array holding all elements that have to be hyphenated. This var is filled by
386 * {@link Hyphenator-gatherDocumentInfos}
393 * @name Hyphenator-exceptions
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
404 * @name Hyphenator-docLanguages
406 * An object holding all languages used in the document. This is filled by
407 * {@link Hyphenator-gatherDocumentInfos}
415 * @name Hyphenator-state
417 * A number that inidcates the current state of the script
419 * 1: loading patterns
421 * 3: hyphenation done
422 * 4: hyphenation removed
429 * @name Hyphenator-url
431 * A string containing a RegularExpression to match URL's
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
438 * @name Hyphenator-mail
440 * A string containing a RegularExpression to match mail-adresses
444 mail = '[\\w-\\.]+@[\\w\\.]+',
447 * @name Hyphenator-urlRE
449 * A RegularExpressions-Object for url- and mail adress matching
453 urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'),
456 * @name Hyphenator-zeroWidthSpace
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
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
470 if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
471 zws = ''; //opera 10 on XP doesn't support zws
477 * @name Hyphenator-createElem
479 * A function alias to document.createElementNS or document.createElement
480 * @type {function(string, Object)}
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);
493 * @name Hyphenator-onHyphenationDone
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
501 onHyphenationDone = function () {},
504 * @name Hyphenator-onError
506 * A function that can be called upon an error.
507 * @see Hyphenator.config
508 * @type {function(Object)}
511 onError = function (e) {
512 window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
516 * @name Hyphenator-selectorFunction
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
524 selectorFunction = function () {
525 var tmp, el = [], i, l;
526 if (document.getElementsByClassName) {
527 el = contextWindow.document.getElementsByClassName(hyphenateClass);
529 tmp = contextWindow.document.getElementsByTagName('*');
531 for (i = 0; i < l; i++)
533 if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
542 * @name Hyphenator-intermediateState
544 * The value of style.visibility of the text while it is hyphenated.
545 * @see Hyphenator.config
549 intermediateState = 'hidden',
552 * @name Hyphenator-hyphen
554 * A string containing the character for in-word-hyphenation
556 * @default the soft hyphen
558 * @see Hyphenator.config
560 hyphen = String.fromCharCode(173),
563 * @name Hyphenator-urlhyphen
565 * A string containing the character for url/mail-hyphenation
567 * @default the zero width space
569 * @see Hyphenator.config
570 * @see Hyphenator-zeroWidthSpace
572 urlhyphen = zeroWidthSpace,
575 * @name Hyphenator-safeCopy
577 * Defines wether work-around for copy issues is active or not
578 * Not supported by Opera (no onCopy handler)
582 * @see Hyphenator.config
583 * @see Hyphenator-registerOnCopy
588 * @name Hyphenator-Expando
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.
600 Expando = (function () {
602 name = "HyphenatorExpando_" + Math.random(),
605 getDataForElem : function (elem) {
606 return container[elem[name].id];
608 setDataForElem : function (elem, data) {
610 if (elem[name] && elem[name].id !== '') {
614 elem[name] = {'id': id}; //object needed, otherways it is reflected in HTML in IE
616 container[id] = data;
618 appendDataForElem : function (elem, data) {
621 if (data.hasOwnProperty(k)) {
622 container[elem[name].id][k] = data[k];
626 delDataOfElem : function (elem) {
627 delete container[elem[name]];
633 * runOnContentLoaded is based od jQuery.bindReady()
635 * jQuery JavaScript Library v1.3.2
638 * Copyright (c) 2009 John Resig
639 * Dual licensed under the MIT and GPL licenses.
640 * http://docs.jquery.com/License
642 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
646 * @name Hyphenator-runOnContentLoaded
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
655 runOnContentLoaded = function (w, f) {
656 var DOMContentLoaded = function () {}, toplevel, hyphRunForThis = {};
657 if (documentLoaded && !hyphRunForThis[w.location.href]) {
659 hyphRunForThis[w.location.href] = true;
662 function init(context) {
663 contextWindow = context || window;
664 if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || contextWindow != window.parent)) {
665 documentLoaded = true;
667 hyphRunForThis[contextWindow.location.href] = true;
671 function doScrollCheck() {
673 // If IE is used, use the trick by Diego Perini
674 // http://javascript.nwbox.com/IEContentLoaded/
675 document.documentElement.doScroll("left");
677 setTimeout(doScrollCheck, 1);
681 // and execute any waiting functions
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
692 //opera throws only on document.toString-access
693 haveAccess = window.frames[i].document.toString();
695 haveAccess = undefined;
698 init(window.frames[i]);
701 contextWindow = window;
703 hyphRunForThis[window.location.href] = true;
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
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
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);
741 // A fallback to window.onload, that will always work
742 window.addEventListener("load", doOnLoad, false);
744 // If IE event model is used
745 } else if (document.attachEvent) {
746 // ensure firing before onload,
747 // maybe late but safe also for iframes
748 document.attachEvent("onreadystatechange", DOMContentLoaded);
750 // A fallback to window.onload, that will always work
751 window.attachEvent("onload", doOnLoad);
753 // If IE and not a frame
754 // continually check to see if the document is ready
757 toplevel = window.frameElement === null;
760 if (document.documentElement.doScroll && toplevel) {
770 * @name Hyphenator-getLang
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.
778 getLang = function (el, fallback) {
779 if (!!el.getAttribute('lang')) {
780 return el.getAttribute('lang').toLowerCase();
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);
786 //instead, we have to do this (thanks to borgzor):
788 if (!!el.getAttribute('xml:lang')) {
789 return el.getAttribute('xml:lang').toLowerCase();
792 if (el.tagName !== 'HTML') {
793 return getLang(el.parentNode, true);
802 * @name Hyphenator-autoSetMainLanguage
804 * Retrieves the language of the document from the DOM.
805 * The function looks in the following places:
807 * <li>lang-attribute in the html-tag</li>
808 * <li><meta http-equiv = "content-language" content = "xy" /></li>
809 * <li><meta name = "DC.Language" content = "xy" /></li>
810 * <li><meta name = "language" content = "xy" /></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}
816 autoSetMainLanguage = function (w) {
817 w = w || contextWindow;
818 var el = w.document.getElementsByTagName('html')[0],
819 m = w.document.getElementsByTagName('meta'),
821 mainLanguage = getLang(el, false);
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();
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();
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();
838 if (!mainLanguage && doFrames && contextWindow != window.parent) {
839 autoSetMainLanguage(window.parent);
843 ul = navigator.language ? navigator.language : navigator.userLanguage;
844 ul = ul.substring(0, 2);
845 if (prompterStrings.hasOwnProperty(ul)) {
846 text = prompterStrings[ul];
848 text = prompterStrings.en;
850 text += ' (ISO 639-1)\n\n' + languageHint;
851 mainLanguage = window.prompt(unescape(text), ul).toLowerCase();
853 if (!supportedLang.hasOwnProperty(mainLanguage)) {
854 if (supportedLang.hasOwnProperty(mainLanguage.split('-')[0])) { //try subtag
855 mainLanguage = mainLanguage.split('-')[0];
857 e = new Error('The language "' + mainLanguage + '" is not yet supported.');
864 * @name Hyphenator-gatherDocumentInfos
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
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;
881 hyphenatorSettings.hasOwnStyle = false;
883 hyphenatorSettings.isHidden = true;
884 el.style.visibility = 'hidden';
886 if (el.lang && typeof(el.lang) === 'string') {
887 hyphenatorSettings.language = el.lang.toLowerCase(); //copy attribute-lang to internal lang
889 hyphenatorSettings.language = lang.toLowerCase();
891 hyphenatorSettings.language = getLang(el, true);
893 lang = hyphenatorSettings.language;
894 if (supportedLang[lang]) {
895 docLanguages[lang] = true;
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.'));
904 Expando.setDataForElem(el, hyphenatorSettings);
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);
914 elToProcess = contextWindow.document.getElementsByTagName('body')[0];
915 process(elToProcess, false, mainLanguage);
917 elToProcess = selectorFunction();
918 while (!!(tmp = elToProcess[i++]))
920 process(tmp, true, '');
923 if (!Hyphenator.languages.hasOwnProperty(mainLanguage)) {
924 docLanguages[mainLanguage] = true;
925 } else if (!Hyphenator.languages[mainLanguage].prepared) {
926 docLanguages[mainLanguage] = true;
928 if (elements.length > 0) {
929 Expando.appendDataForElem(elements[elements.length - 1], {isLast : true});
934 * @name Hyphenator-convertPatterns
936 * Converts the patterns from string '_a6' to object '_a':'_a6'.
937 * The result is stored in the {@link Hyphenator-patterns}-object.
939 * @param {string} lang the language whose patterns shall be converted
941 convertPatterns = function (lang) {
942 var plen, anfang, ende, pats, pat, key, tmp = {};
943 pats = Hyphenator.languages[lang].patterns;
945 if (pats.hasOwnProperty(plen)) {
946 plen = parseInt(plen, 10);
949 while (!!(pat = pats[plen].substring(anfang, ende))) {
950 key = pat.replace(/\d/g, '');
957 Hyphenator.languages[lang].patterns = tmp;
958 Hyphenator.languages[lang].patternsConverted = true;
962 * @name Hyphenator-convertExceptionsToObject
964 * Converts a list of comma seprated exceptions to an object:
965 * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
967 * @param {string} exc a comma separated string of exceptions (without spaces)
969 convertExceptionsToObject = function (exc) {
970 var w = exc.split(', '),
973 for (i = 0, l = w.length; i < l; i++) {
974 key = w[i].replace(/-/g, '');
975 if (!r.hasOwnProperty(key)) {
983 * @name Hyphenator-loadPatterns
985 * Adds a <script>-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
991 * @see Hyphenator-basePath
993 loadPatterns = function (lang) {
994 var url, xhr, head, script;
995 if (supportedLang[lang] && !Hyphenator.languages[lang]) {
996 url = basePath + 'patterns/' + supportedLang[lang];
1000 if (isLocal && !isBookmarklet) {
1001 //check if 'url' is available:
1003 if (typeof XMLHttpRequest !== 'undefined') {
1004 xhr = new XMLHttpRequest();
1008 xhr = new ActiveXObject("Msxml2.XMLHTTP");
1014 xhr.open('HEAD', url, false);
1015 xhr.setRequestHeader('Cache-Control', 'no-cache');
1017 if (xhr.status === 404) {
1018 onError(new Error('Could not load\n' + url));
1019 delete docLanguages[lang];
1025 head = window.document.getElementsByTagName('head').item(0);
1026 script = createElem('script', window);
1028 script.type = 'text/javascript';
1029 head.appendChild(script);
1034 * @name Hyphenator-prepareLanguagesObj
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.
1039 * @param {string} lang the language ob the lang-obj
1041 prepareLanguagesObj = function (lang) {
1042 var lo = Hyphenator.languages[lang], wrd;
1047 lo['cache'] = lo.cache;
1049 if (enableReducedPatternSet) {
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;
1057 //copy global exceptions to the language specific exceptions
1058 if (exceptions.hasOwnProperty('global')) {
1059 if (exceptions.hasOwnProperty(lang)) {
1060 exceptions[lang] += ', ' + exceptions.global;
1062 exceptions[lang] = exceptions.global;
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];
1072 convertPatterns(lang);
1073 wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + '-]{' + min + ',}';
1074 lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
1079 storage.setItem('Hyphenator_' + lang, window.JSON.stringify(lo));
1088 * @name Hyphenator-prepare
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
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);
1112 // get all languages that are used and preload the patterns
1114 for (lang in docLanguages) {
1115 if (docLanguages.hasOwnProperty(lang)) {
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];
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];
1136 delete exceptions[lang];
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');
1142 delete docLanguages[lang];
1150 if (languagesToLoad === 0) {
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;
1164 finishedLoading = true;
1165 delete docLanguages[lang];
1166 //do conversion while other patterns are loading:
1167 prepareLanguagesObj(lang);
1171 if (finishedLoading) {
1172 window.clearInterval(interval);
1180 * @name Hyphenator-switchToggleBox
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
1187 toggleBox = function (s) {
1188 var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute;
1189 if (!!(myBox = contextWindow.document.getElementById('HyphenatorToggleBox'))) {
1191 myBox.firstChild.data = 'Hy-phe-na-ti-on';
1193 myBox.firstChild.data = 'Hyphenation';
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);
1224 * @name Hyphenator-hyphenateWord
1226 * This function is the heart of Hyphenator.js. It returns a hyphenated word.
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
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;
1242 if (word.indexOf(hyphen) !== -1) {
1243 //word already contains shy; -> leave at it is!
1246 if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
1247 return lo.cache[word];
1249 if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
1250 return lo.exceptions[word].replace(/-/g, hyphen);
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]);
1258 return parts.join('-');
1260 //finally the core hyphenation algorithm
1261 w = '_' + word + '_';
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)
1267 w = w.toLowerCase();
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;
1280 if (typeof pat === 'string') {
1281 //convert from string 'a5b' to array [1,5] (pos,value)
1284 for (i = 0; i < pat.length; i++) {
1285 if (!!(c = numb3rs[pat.charAt(i)])) {
1290 pat = lo.patterns[patk] = val;
1295 for (i = 0; i < pat.length; i++) {
1297 if (!hypos[c] || hypos[c] < pat[i + 1]) {
1298 hypos[c] = pat[i + 1];
1305 for (i = lo.leftmin; i <= (word.length - lo.rightmin); i++) {
1306 if (!!(hypos[i] & 1)) {
1307 s.splice(i + inserted + 1, 0, hyphen);
1311 hyphenatedword = s.slice(1, -1).join('');
1313 lo.cache[word] = hyphenatedword;
1315 return hyphenatedword;
1319 * @name Hyphenator-hyphenateURL
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
1326 hyphenateURL = function (url) {
1327 return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
1331 * @name Hyphenator-hyphenateElement
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
1339 hyphenateElement = function (el) {
1340 var hyphenatorSettings = Expando.getDataForElem(el),
1341 lang = hyphenatorSettings.language, hyphenate, n, i,
1342 controlOrphans = function (part) {
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'), '');
1364 if (orphanControl === 3) {
1365 //replace spaces by non breaking spaces
1366 r = r.replace(/[ ]+/g, String.fromCharCode(160));
1370 if (Hyphenator.languages.hasOwnProperty(lang)) {
1371 hyphenate = function (word) {
1372 if (urlOrMailRE.test(word)) {
1373 return hyphenateURL(word);
1375 return hyphenateWord(lang, word);
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);
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');
1394 if (el.style.removeProperty) {
1395 el.style.removeProperty('visibility');
1396 } else if (el.style.removeAttribute) { // IE
1397 el.style.removeAttribute('visibility');
1401 if (hyphenatorSettings.isLast) {
1404 if (documentCount > (-1000) && documentCount <= 0) {
1405 documentCount = (-2000);
1406 onHyphenationDone();
1412 * @name Hyphenator-removeHyphenationFromElement
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.
1420 removeHyphenationFromElement = function (el) {
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);
1446 * @name Hyphenator-hyphenateDocument
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.
1453 hyphenateDocument = function () {
1454 function bind(fun, arg) {
1455 return function () {
1460 while (!!(el = elements[i++])) {
1461 if (el.ownerDocument.location.href === contextWindow.location.href) {
1462 window.setTimeout(bind(hyphenateElement, el), 0);
1468 * @name Hyphenator-removeHyphenationFromDocument
1470 * Does what it says ;-)
1473 removeHyphenationFromDocument = function () {
1475 while (!!(el = elements[i++])) {
1476 removeHyphenationFromElement(el);
1482 * @name Hyphenator-registerOnCopy
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
1490 registerOnCopy = function () {
1491 var body = contextWindow.document.getElementsByTagName('body')[0],
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()]) {
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) {
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,
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 !== "") {
1548 window.setTimeout(restore, 0);
1553 if (window.addEventListener) {
1554 body.addEventListener("copy", oncopyHandler, false);
1556 body.attachEvent("oncopy", oncopyHandler);
1563 * @name Hyphenator.version
1564 * @memberOf Hyphenator
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
1575 * @name Hyphenator.languages
1576 * @memberOf Hyphenator
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:
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>
1590 * And optionally (or after prepareLanguagesObj() has been called):
1592 * <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
1600 * @name Hyphenator.config
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>'&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>
1625 * @example <script src = "Hyphenator.js" type = "text/javascript"></script>
1626 * <script type = "text/javascript">
1627 * Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
1631 config: function (obj) {
1632 var assert = function (name, type) {
1633 if (typeof obj[name] === type) {
1636 onError(new Error('Config onError: ' + name + ' must be of type ' + type));
1642 if (obj.hasOwnProperty(key)) {
1645 if (assert('classname', 'string')) {
1646 hyphenateClass = obj[key];
1649 case 'donthyphenateclassname':
1650 if (assert('donthyphenateclassname', 'string')) {
1651 dontHyphenateClass = obj[key];
1654 case 'minwordlength':
1655 if (assert('minwordlength', 'number')) {
1660 if (assert('hyphenchar', 'string')) {
1661 if (obj.hyphenchar === '­') {
1662 obj.hyphenchar = String.fromCharCode(173);
1667 case 'urlhyphenchar':
1668 if (obj.hasOwnProperty('urlhyphenchar')) {
1669 if (assert('urlhyphenchar', 'string')) {
1670 urlhyphen = obj[key];
1675 if (assert('togglebox', 'function')) {
1676 toggleBox = obj[key];
1679 case 'displaytogglebox':
1680 if (assert('displaytogglebox', 'boolean')) {
1681 displayToggleBox = obj[key];
1684 case 'remoteloading':
1685 if (assert('remoteloading', 'boolean')) {
1686 enableRemoteLoading = obj[key];
1690 if (assert('enablecache', 'boolean')) {
1691 enableCache = obj[key];
1694 case 'enablereducedpatternset':
1695 if (assert('enablereducedpatternset', 'boolean')) {
1696 enableReducedPatternSet = obj[key];
1699 case 'onhyphenationdonecallback':
1700 if (assert('onhyphenationdonecallback', 'function')) {
1701 onHyphenationDone = obj[key];
1704 case 'onerrorhandler':
1705 if (assert('onerrorhandler', 'function')) {
1709 case 'intermediatestate':
1710 if (assert('intermediatestate', 'string')) {
1711 intermediateState = obj[key];
1714 case 'selectorfunction':
1715 if (assert('selectorfunction', 'function')) {
1716 selectorFunction = obj[key];
1720 if (assert('safecopy', 'boolean')) {
1721 safeCopy = obj[key];
1725 if (assert('doframes', 'boolean')) {
1726 doFrames = obj[key];
1730 if (assert('storagetype', 'string')) {
1731 storageType = obj[key];
1734 case 'orphancontrol':
1735 if (assert('orphancontrol', 'number')) {
1736 orphanControl = obj[key];
1740 onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
1747 * @name Hyphenator.run
1749 * Bootstrap function that starts all hyphenation processes when called.
1751 * @example <script src = "Hyphenator.js" type = "text/javascript"></script>
1752 * <script type = "text/javascript">
1758 var process = function () {
1760 if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
1761 return; //we are in a frameset
1764 autoSetMainLanguage(undefined);
1765 gatherDocumentInfos();
1766 prepare(hyphenateDocument);
1767 if (displayToggleBox) {
1776 }, i, haveAccess, fl = window.frames.length;
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) {
1785 storage = window.sessionStorage;
1788 storage = window.localStorage;
1791 storage = undefined;
1796 //FF throws an error if DOM.storage.enabled is set to false
1798 if (!documentLoaded && !isBookmarklet) {
1799 runOnContentLoaded(window, process);
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
1807 //opera throws only on document.toString-access
1808 haveAccess = window.frames[i].document.toString();
1810 haveAccess = undefined;
1813 contextWindow = window.frames[i];
1818 contextWindow = window;
1824 * @name Hyphenator.addExceptions
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.
1831 * @example <script src = "Hyphenator.js" type = "text/javascript"></script>
1832 * <script type = "text/javascript">
1833 * Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
1837 addExceptions: function (lang, words) {
1841 if (exceptions.hasOwnProperty(lang)) {
1842 exceptions[lang] += ", " + words;
1844 exceptions[lang] = words;
1849 * @name Hyphenator.hyphenate
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
1858 * @example <script src = "Hyphenator.js" type = "text/javascript"></script>
1859 * <script src = "patterns/en.js" type = "text/javascript"></script>
1860 * <script type = "text/javascript">
1861 * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
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);
1870 hyphenate = function (word) {
1871 if (urlOrMailRE.test(word)) {
1872 return hyphenateURL(word);
1874 return hyphenateWord(lang, word);
1877 if (typeof target === 'string' || target.constructor === String) {
1878 return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
1879 } else if (typeof target === 'object') {
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);
1888 Hyphenator.hyphenate(n, lang);
1894 onError(new Error('Language "' + lang + '" is not loaded.'));
1899 * @name Hyphenator.getRedPatternSet
1901 * Returns {@link Hyphenator-isBookmarklet}.
1902 * @param {string} lang the language patterns are stored for
1903 * @returns object {'patk': pat}
1906 getRedPatternSet: function (lang) {
1907 return Hyphenator.languages[lang].redPatSet;
1911 * @name Hyphenator.isBookmarklet
1913 * Returns {@link Hyphenator-isBookmarklet}.
1917 isBookmarklet: function () {
1918 return isBookmarklet;
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');
1930 s = loc.indexOf('Hyphenator.js?');
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') {
1940 if (option[1] === 'true') {
1941 re[option[0]] = true;
1944 if (option[1] === 'false') {
1945 re[option[0]] = false;
1948 if (isFinite(option[1])) {
1949 re[option[0]] = parseInt(option[1], 10);
1952 if (option[0] === 'onhyphenationdonecallback') {
1953 re[option[0]] = new Function('', option[1]);
1956 re[option[0]] = option[1];
1965 * @name Hyphenator.toggleHyphenation
1967 * Checks the current state of the ToggleBox and removes or does hyphenation.
1970 toggleHyphenation: function () {
1973 removeHyphenationFromDocument();
1977 hyphenateDocument();
1985 //Export properties/methods (for google closure compiler)
1986 Hyphenator['languages'] = Hyphenator.languages;
1987 Hyphenator['config'] = Hyphenator.config;
1988 Hyphenator['run'] = Hyphenator.run;
1989 Hyphenator['addExceptions'] = Hyphenator.addExceptions;
1990 Hyphenator['hyphenate'] = Hyphenator.hyphenate;
1991 Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
1992 Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
1993 Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
1994 Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
1995 window['Hyphenator'] = Hyphenator;
1997 if (Hyphenator.isBookmarklet()) {
1998 Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true});
1999 Hyphenator.config(Hyphenator.getConfigFromURI());