home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_4163 < prev    next >
Encoding:
Text File  |  2009-07-14  |  47.4 KB  |  1,605 lines

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