home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Calibre / calibre-0.8.18.msi / file_566 < prev    next >
Text File  |  2010-11-25  |  66KB  |  2,144 lines

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