1/** @license Hyphenator 5.2.0(devel) - client side hyphenation for webbrowsers 2 * Copyright (C) 2015 Mathias Nater, Zürich (mathiasnater at gmail dot com) 3 * https://github.com/mnater/Hyphenator 4 * 5 * Released under the MIT license 6 * http://mnater.github.io/Hyphenator/LICENSE.txt 7 */ 8 9/* 10 * Comments are jsdoc3 formatted. See http://usejsdoc.org 11 * Use mergeAndPack.html to get rid of the comments and to reduce the file size of this script! 12 */ 13 14/* The following comment is for JSLint: */ 15/*jslint browser: true*/ 16/*global Hyphenator window*/ 17 18/** 19 * @desc Provides all functionality to do hyphenation, except the patterns that are loaded externally 20 * @global 21 * @namespace Hyphenator 22 * @author Mathias Nater, <[email protected]> 23 * @version 5.2.0(devel) 24 * @example 25 * <script src = "Hyphenator.js" type = "text/javascript"></script> 26 * <script type = "text/javascript"> 27 * Hyphenator.run(); 28 * </script> 29 */ 30var Hyphenator; 31Hyphenator = (function (window) { 32 'use strict'; 33 34 /** 35 * @member Hyphenator~contextWindow 36 * @access private 37 * @desc 38 * contextWindow stores the window for the actual document to be hyphenated. 39 * If there are frames this will change. 40 * So use contextWindow instead of window! 41 */ 42 var contextWindow = window; 43 44 45 /** 46 * @member {Object.<string, Hyphenator~supportedLangs~supportedLanguage>} Hyphenator~supportedLangs 47 * @desc 48 * A generated key-value object that stores supported languages and meta data. 49 * The key is the {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language and the value 50 * is an object of type {@link Hyphenator~supportedLangs~supportedLanguage} 51 * @namespace Hyphenator~supportedLangs 52 * @access private 53 * //Check if language lang is supported: 54 * if (supportedLangs.hasOwnProperty(lang)) 55 */ 56 var supportedLangs = (function () { 57 /** 58 * @typedef {Object} Hyphenator~supportedLangs~supportedLanguage 59 * @property {string} file - The name of the pattern file 60 * @property {number} script - The script type of the language (e.g. 'latin' for english), this type is abbreviated by an id 61 * @property {string} prompt - The sentence prompted to the user, if Hyphenator.js doesn't find a language hint 62 */ 63 64 /** 65 * @lends Hyphenator~supportedLangs 66 */ 67 var r = {}, 68 /** 69 * @method Hyphenator~supportedLangs~o 70 * @desc 71 * Sets a value of Hyphenator~supportedLangs 72 * @access protected 73 * @param {string} code The {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language 74 * @param {string} file The name of the pattern file 75 * @param {Number} script A shortcut for a specific script: latin:0, cyrillic: 1, arabic: 2, armenian:3, bengali: 4, devangari: 5, greek: 6 76 * gujarati: 7, kannada: 8, lao: 9, malayalam: 10, oriya: 11, persian: 12, punjabi: 13, tamil: 14, telugu: 15 77 * @param {string} prompt The sentence prompted to the user, if Hyphenator.js doesn't find a language hint 78 */ 79 o = function (code, file, script, prompt) { 80 r[code] = {'file': file, 'script': script, 'prompt': prompt}; 81 }; 82 83 o('be', 'be.js', 1, 'Ðова гÑÑага ÑайÑа не можа бÑÑÑ Ð²ÑзнаÑÐ°Ð½Ñ Ð°ÑÑамаÑÑÑна. ÐÐ°Ð»Ñ Ð»Ð°Ñка пакажÑÑе мовÑ:'); 84 o('ca', 'ca.js', 0, ''); 85 o('cs', 'cs.js', 0, 'Jazyk této internetové stránky nebyl automaticky rozpoznán. UrÄete prosÃm jejà jazyk:'); 86 o('da', 'da.js', 0, 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:'); 87 o('bn', 'bn.js', 4, ''); 88 o('de', 'de.js', 0, 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:'); 89 o('el', 'el-monoton.js', 6, ''); 90 o('el-monoton', 'el-monoton.js', 6, ''); 91 o('el-polyton', 'el-polyton.js', 6, ''); 92 o('en', 'en-us.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:'); 93 o('en-gb', 'en-gb.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:'); 94 o('en-us', 'en-us.js', 0, 'The language of this website could not be determined automatically. Please indicate the main language:'); 95 o('eo', 'eo.js', 0, 'La lingvo de Äi tiu retpaÄo ne rekoneblas aÅtomate. Bonvolu indiki Äian Äeflingvon:'); 96 o('es', 'es.js', 0, 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:'); 97 o('et', 'et.js', 0, 'Veebilehe keele tuvastamine ebaõnnestus, palun valige kasutatud keel:'); 98 o('fi', 'fi.js', 0, 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:'); 99 o('fr', 'fr.js', 0, 'La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:'); 100 o('ga', 'ga.js', 0, 'NÃorbh fhéidir teanga an tsuÃmh a fháil go huathoibrÃoch. Cuir isteach prÃomhtheanga an tsuÃmh:'); 101 o('grc', 'grc.js', 6, ''); 102 o('gu', 'gu.js', 7, ''); 103 o('hi', 'hi.js', 5, ''); 104 o('hu', 'hu.js', 0, 'A weboldal nyelvét nem sikerült automatikusan megállapÃtani. Kérem adja meg a nyelvet:'); 105 o('hy', 'hy.js', 3, 'ÕÕ°Õ¡Õ»Õ¸Õ²Õ¾Õ¥Ö Õ°Õ¡ÕµÕ¿Õ¶Õ¡Õ¢Õ¥ÖÕ¥Õ¬ Õ¡Õ
105µÕ½ Õ¯Õ¡ÕµÖÕ« Õ¬Õ¥Õ¦Õ¸ÖÕ¶Ö Ô½Õ¶Õ¤ÖÕ¸ÖÕ´ Õ¥Õ¶Ö Õ¶Õ·Õ¥Ö Õ°Õ«Õ´Õ¶Õ¡Õ¯Õ¡Õ¶ Õ¬Õ¥Õ¦Õ¸ÖÕ¶Õ'); 106 o('it', 'it.js', 0, 'Lingua del sito sconosciuta. Indicare una lingua, per favore:'); 107 o('kn', 'kn.js', 8, 'à²à²¾à²² ತಾಣದ à²à²¾à²·à³à²¯à²¨à³à²¨à³ ನಿರà³à²§à²°à²¿à²¸à²²à³ ಸಾಧà³à²¯à²µà²¾à²à³à²¤à³à²¤à²¿à²²à³à²². ದಯವಿà²à³à²à³ ಮà³à²à³à²¯ à²à²¾à²·à³à²¯à²¨à³à²¨à³ ಸà³à²à²¿à²¸à²¿:'); 108 o('la', 'la.js', 0, ''); 109 o('lt', 'lt.js', 0, 'Nepavyko automatiÅ¡kai nustatyti Å¡ios svetainÄs kalbos. PraÅ¡ome įvesti kalbÄ :'); 110 o('lv', 'lv.js', 0, 'Å Ä«s lapas valodu nevarÄja noteikt automÄtiski. LÅ«dzu norÄdiet pamata valodu:'); 111 o('ml', 'ml.js', 10, 'à´ à´µàµ%u0D2C%u0D4D%u200Cà´¸àµà´±àµà´±à´¿à´¨àµà´±àµ à´à´¾à´· à´à´£àµà´àµà´ªà´¿à´à´¿à´¯àµà´àµà´à´¾%u0D28%u0D4D%u200D à´à´´à´¿à´àµà´à´¿à´²àµà´². à´à´¾à´· à´à´¤à´¾à´£àµà´¨àµà´¨àµ തിരà´àµà´àµà´àµà´àµà´àµà´:'); 112 o('nb', 'nb-no.js', 0, 'Nettstedets sprÃ¥k kunne ikke finnes automatisk. Vennligst oppgi sprÃ¥k:'); 113 o('no', 'nb-no.js', 0, 'Nettstedets sprÃ¥k kunne ikke finnes automatisk. Vennligst oppgi sprÃ¥k:'); 114 o('nb-no', 'nb-no.js', 0, 'Nettstedets sprÃ¥k kunne ikke finnes automatisk. Vennligst oppgi sprÃ¥k:'); 115 o('nl', 'nl.js', 0, 'De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:'); 116 o('or', 'or.js', 11, ''); 117 o('pa', 'pa.js', 13, ''); 118 o('pl', 'pl.js', 0, 'JÄzyka tej strony nie można ustaliÄ automatycznie. ProszÄ wskazaÄ jÄzyk:'); 119 o('pt', 'pt.js', 0, 'A lÃngua deste site não pôde ser determinada automaticamente. Por favor indique a lÃngua principal:'); 120 o('ru', 'ru.js', 1, 'ЯзÑк ÑÑого ÑайÑа не Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¾Ð¿Ñеделен авÑомаÑиÑеÑки. ÐожалÑйÑÑа ÑкажиÑе ÑзÑк:'); 121 o('sk', 'sk.js', 0, ''); 122 o('sl', 'sl.js', 0, 'Jezika te spletne strani ni bilo mogoÄe samodejno doloÄiti. Prosim navedite jezik:'); 123 o('sr-cyrl', 'sr-cyrl.js', 1, 'Ðезик овог ÑаÑÑа ниÑе деÑекÑован аÑÑомаÑÑки. Ðолим Ð²Ð°Ñ Ð½Ð°Ð²ÐµÐ´Ð¸Ñе Ñезик:'); 124 o('sr-latn', 'sr-latn.js', 0, 'Jezika te spletne strani ni bilo mogoÄe samodejno doloÄiti. Prosim navedite jezik:'); 125 o('sv', 'sv.js', 0, 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:'); 126 o('ta', 'ta.js', 14, ''); 127 o('te', 'te.js', 15, ''); 128 o('tr', 'tr.js', 0, 'Bu web sitesinin dili otomatik olarak tespit edilememiÅtir. Lütfen dökümanın dilini seçiniz%A0:'); 129 o('uk', 'uk.js', 1, 'Ðова ÑÑого веб-ÑайÑÑ Ð½Ðµ може бÑÑи визнаÑена авÑомаÑиÑно. ÐÑÐ´Ñ Ð»Ð°Ñка, вкажÑÑÑ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ Ð¼Ð¾Ð²Ñ:'); 130 o('ro', 'ro.js', 0, 'Limba acestui sit nu a putut fi determinatÄ automat. Alege limba principalÄ:'); 131 132 return r; 133 }()); 134 135 136 /** 137 * @member {string} Hyphenator~basePath 138 * @desc 139 * A string storing the basepath from where Hyphenator.js was loaded. 140 * This is used to load the pattern files. 141 * The basepath is determined dynamically by searching all script-tags for Hyphenator.js 142 * If the path cannot be determined {@link http://mnater.github.io/Hyphenator/} is used as fallback. 143 * @access private 144 * @see {@link Hyphenator~loadPatterns} 145 */ 146 var basePath = (function () { 147 var s = contextWindow.document.getElementsByTagName('script'), i = 0, p, src, t = s[i], r = ''; 148 while (!!t) { 149 if (!!t.src) { 150 src = t.src; 151 p = src.indexOf('Hyphenator.js'); 152 if (p !== -1) { 153 r = src.substring(0, p); 154 } 155 } 156 i += 1; 157 t = s[i]; 158 } 159 return !!r 160 ? r 161 : '//mnater.github.io/Hyphenator/'; 162 }()); 163 164 /** 165 * @member {boolean} Hyphenator~isLocal 166 * @access private
167 * @desc 168 * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if 169 * it's loaded from an external source (i.e. directly from github) 170 */ 171 var isLocal = (function () { 172 var re = false; 173 if (window.location.href.indexOf(basePath) !== -1) { 174 re = true; 175 } 176 return re; 177 }()); 178 179 /** 180 * @member {boolean} Hyphenator~documentLoaded 181 * @access private 182 * @desc 183 * documentLoaded is true, when the DOM has been loaded. This is set by {@link Hyphenator~runWhenLoaded} 184 */ 185 var documentLoaded = false; 186 187 /** 188 * @member {boolean} Hyphenator~persistentConfig 189 * @access private 190 * @desc 191 * if persistentConfig is set to true (defaults to false), config options and the state of the 192 * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven't to be 193 * set for each page. 194 * @default false 195 * @see {@link Hyphenator.config} 196 */ 197 var persistentConfig = false; 198 199 /** 200 * @member {boolean} Hyphenator~doFrames 201 * @access private 202 * @desc 203 * switch to control if frames/iframes should be hyphenated, too. 204 * defaults to false (frames are a bag of hurt!) 205 * @default false 206 * @see {@link Hyphenator.config} 207 */ 208 var doFrames = false; 209 210 /** 211 * @member {Object.<string,boolean>} Hyphenator~dontHyphenate 212 * @desc 213 * A key-value object containing all html-tags whose content should not be hyphenated 214 * @access private 215 */ 216 var dontHyphenate = {'video': true, 'audio': true, '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, 'math': true, 'svg': true, 'style': true}; 217 218 /** 219 * @member {boolean} Hyphenator~enableCache 220 * @desc 221 * A variable to set if caching is enabled or not 222 * @default true 223 * @access private 224 * @see {@link Hyphenator.config} 225 */ 226 var enableCache = true; 227 228 /** 229 * @member {string} Hyphenator~storageType 230 * @desc 231 * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session') 232 * @default 'local' 233 * @access private 234 * @see {@link Hyphenator.config} 235 */ 236 var storageType = 'local'; 237 238 /** 239 * @member {Object|undefined} Hyphenator~storage 240 * @desc 241 * An alias to the storage defined in storageType. This is set by {@link Hyphenator~createStorage}. 242 * Set by {@link Hyphenator.run} 243 * @default null 244 * @access private 245 * @see {@link Hyphenator~createStorage} 246 */ 247 var storage; 248 249 /** 250 * @member {boolean} Hyphenator~enableReducedPatternSet 251 * @desc 252 * A variable to set if storing the used patterns is set 253 * @default false 254 * @access private 255 * @see {@link Hyphenator.config} 256 * @see {@link Hyphenator.getRedPatternSet} 257 */ 258 var enableReducedPatternSet = false; 259 260 /** 261 * @member {boolean} Hyphenator~enableRemoteLoading 262 * @desc 263 * A variable to set if pattern files should be loaded remotely or not 264 * @default true 265 * @access private 266 * @see {@link Hyphenator.config} 267 */ 268 var enableRemoteLoading = true; 269 270 /** 271 * @member {boolean} Hyphenator~displayToggleBox 272 * @desc 273 * A variable to set if the togglebox should be displayed or not 274 * @default false 275 * @access private 276 * @see {@link Hyphenator.config} 277 */ 278 var displayToggleBox = false; 279 280 /** 281 * @method Hyphenator~onError 282 * @desc 283 * A function that can be called upon an error. 284 * @see {@link Hyphenator.config} 285 * @access private 286 */ 287 var onError = function (e) { 288 window.alert("Hyphenator.js says:\n\nAn Error occurred:\n" + e.message); 289 }; 290 291 /** 292 * @method Hyphenator~onWarning 293 * @desc 294 * A function that can be called upon a warning. 295 * @see {@link Hyphenator.config} 296 * @access private 297 */ 298 var onWarning = function (e) { 299 window.console.log(e.message); 300 }; 301 302 /** 303 * @method Hyphenator~createElem
304 * @desc 305 * A function alias to document.createElementNS or document.createElement 306 * @access private 307 */ 308 function createElem(tagname, context) { 309 context = context || contextWindow; 310 var el; 311 if (window.document.createElementNS) { 312 el = context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname); 313 } else if (window.document.createElement) { 314 el = context.document.createElement(tagname); 315 } 316 return el; 317 } 318 /** 319 * @method Hyphenator~forEachKey 320 * @desc 321 * Calls the function f on every property of o 322 * @access private 323 */ 324 function forEachKey(o, f) { 325 var k; 326 if (Object.hasOwnProperty("keys")) { 327 Object.keys(o).forEach(function (v) { 328 f(v); 329 }); 330 } else { 331 for (k in o) { 332 if (o.hasOwnProperty(k)) { 333 f(k); 334 } 335 } 336 } 337 } 338 339 /** 340 * @member {boolean} Hyphenator~css3 341 * @desc 342 * A variable to set if css3 hyphenation should be used 343 * @default false 344 * @access private 345 * @see {@link Hyphenator.config} 346 */ 347 var css3 = false; 348 349 /** 350 * @method Hyphenator~css3_gethsupport 351 * @desc 352 * This function returns a {@link Hyphenator~css3_hsupport} object for the current UA 353 * @type function 354 * @access private 355 * @see Hyphenator~css3_h9n 356 */ 357 function css3_gethsupport() { 358 var support = false, 359 supportedBrowserLangs = {}, 360 property = '', 361 checkLangSupport, 362 createLangSupportChecker = function (prefix) { 363 var testStrings = [ 364 //latin: 0 365 'aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz', 366 //cyrillic: 1 367 'абвгдеÑжзийклмнопÑÑÑÑÑÑ ÑÑÑÑÑÑÑÑÑÑ', 368 //arabic: 2 369 'Ø£Ø¨ØªØ«Ø¬ØØ®Ø¯Ø°Ø±Ø²Ø³Ø´ØµØ¶Ø·Ø¸Ø¹ØºÙÙÙÙÙ ÙÙÙÙ', 370 //armenian: 3 371 'Õ¡Õ¢Õ£Õ¤Õ¥Õ¦Õ§Õ¨Õ©ÕªÕ«Õ¬ÕÕ®Õ¯Õ°Õ±Õ²Õ³Õ´ÕµÕ¶Õ·Õ¸Õ¹ÕºÕ»Õ¼Õ½Õ¾Õ¿ÖÖÖÖÖÖ Ö', 372 //bengali: 4 373 'à¦à¦à¦à¦ à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦à¦ ডঢণতথদধনপফবà¦à¦®à¦¯à¦°à¦²à¦¶à¦·à¦¸à¦¹à¦¼à¦½à¦¾à¦¿à§à§à§à§à§à§à§à§à§à§à§à§à§à§à§à§ ৡৢৣ', 374 //devangari: 5 375 'à¤à¤à¤à¤ à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤à¤ डढणतथदधनपफबà¤à¤®à¤¯à¤°à¤²à¤³à¤µà¤¶à¤·à¤¸à¤¹à¤½à¤¾à¤¿à¥à¥à¥à¥à¥à¥à¥à¥à¥à¥à¥à¥à¥ ॡॢॣ', 376 //greek: 6 377 'αβγδεζηθικλμνξοÏÏÏÏÏÏ ÏÏÏÏ', 378 //gujarati: 7 379 'બહઠàªàªàªàªàªàªà« àªàªàªàªàª¾àª¿à«à«à«à«à«à«¢à«£à«à«à«à«àªàªàªàªàªàªàªàªàªàªàªàª ડઢણતથદધનપફસàªàª®àª¯àª°àª²àª³àªµàª¶àª·', 380 //kannada: 8 381 'à²à²à² à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à²à² ಡಢಣತಥದಧನಪಫಬà²à²®à²¯à²°à²±à²²à²³à²µà²¶à²·à²¸à²¹à²½à²¾à²¿à³à³à³à³à³à³à³à³à³à³à³à³à³à³à³à³ ೡ', 382 //lao: 9 383 'àºàºàºàºàºàºàºàºàºàºàºàºàºàºàºàºàºàºàº¡àº¢àº£àº¥àº§àºªàº«àºàº®àº°àº±àº²àº´àºµàº¶àº·àº¸àº¹àº»àº¼à»à»à»à»à»à»à»à»à»à»à»', 384 //malayalam: 10 385 'à´à´à´ à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´à´ ഡഢണതഥദധനപഫബà´à´®à´¯à´°à´±à´²à´³à´´à´µà´¶à´·à´¸à´¹à´¾à´¿àµàµàµàµàµàµàµàµàµàµàµàµàµ ൡൺൻർൽൾൿ', 386 //oriya: 11 387 'à¬à¬à¬à¬ à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬à¬ ଡଢଣତଥଦଧନପଫବà¬à¬®à¬¯à¬°à¬²à¬³à¬µà¬¶à¬·à¬¸à¬¹à¬¾à¬¿ààààààààààà à¡', 388 //persian: 12 389 'Ø£Ø¨ØªØ«Ø¬ØØ®Ø¯Ø°Ø±Ø²Ø³Ø´ØµØ¶Ø·Ø¸Ø¹ØºÙÙÙÙÙ ÙÙÙÙ', 390 //punjabi: 13 391 'à¨à¨à¨à¨ à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨à¨ ਡਢਣਤਥਦਧਨਪਫਬà¨à¨®à¨¯à¨°à¨²à¨³à¨µà¨¶à¨¸à¨¹à¨¾à¨¿
391à©à©à©à©à©à©à©à©à©°à©±', 392 //tamil: 14 393 'à®à® à®à®à®à®à®à®à®à®à®à®à®à®à®à®à®à®à®à®£à®¤à®¨à®©à®ªà®®à®¯à®°à®±à®²à®³à®´à®µà®·à®¸à®¹à®¾à®¿à¯à¯à¯à¯à¯à¯à¯à¯à¯à¯à¯', 394 //telugu: 15 395 'à°à°à°à° à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à°à° డఢణతథదధనపఫబà°à°®à°¯à°°à°±à°²à°³à°µà°¶à°·à°¸à°¹à°¾à°¿à±à±à±à±à±à±à±à±à±à±à±à±à±à±à± ౡ' 396 ], 397 f = function (lang) { 398 var shadow, 399 computedHeight, 400 bdy, 401 r = false; 402 403 //check if lang has already been tested 404 if (supportedBrowserLangs.hasOwnProperty(lang)) { 405 r = supportedBrowserLangs[lang]; 406 } else if (supportedLangs.hasOwnProperty(lang)) { 407 //create and append shadow-test-element 408 bdy = window.document.getElementsByTagName('body')[0]; 409 shadow = createElem('div', window); 410 shadow.id = 'Hyphenator_LanguageChecker'; 411 shadow.style.width = '5em'; 412 shadow.style.padding = '0'; 413 shadow.style.border = 'none'; 414 shadow.style[prefix] = 'auto'; 415 shadow.style.hyphens = 'auto'; 416 shadow.style.fontSize = '12px'; 417 shadow.style.lineHeight = '12px'; 418 shadow.style.wordWrap = 'normal'; 419 shadow.style.visibility = 'hidden'; 420 shadow.lang = lang; 421 shadow.style['-webkit-locale'] = "'" + lang + "'"; 422 shadow.innerHTML = testStrings[supportedLangs[lang].script]; 423 bdy.appendChild(shadow); 424 //measure its height 425 computedHeight = shadow.offsetHeight; 426 //remove shadow element 427 bdy.removeChild(shadow); 428 r = !!(computedHeight > 12); 429 supportedBrowserLangs[lang] = r; 430 } else { 431 r = false; 432 } 433 return r; 434 }; 435 return f; 436 }, 437 s; 438 439 if (window.getComputedStyle) { 440 s = window.getComputedStyle(window.document.getElementsByTagName('body')[0], null); 441 if (s.hyphens !== undefined) { 442 support = true; 443 property = 'hyphens'; 444 checkLangSupport = createLangSupportChecker('hyphens'); 445 } else if (s['-webkit-hyphens'] !== undefined) { 446 support = true; 447 property = '-webkit-hyphens'; 448 checkLangSupport = createLangSupportChecker('-webkit-hyphens'); 449 } else if (s.MozHyphens !== undefined) { 450 support = true; 451 property = '-moz-hyphens'; 452 checkLangSupport = createLangSupportChecker('MozHyphens'); 453 } else if (s['-ms-hyphens'] !== undefined) { 454 support = true; 455 property = '-ms-hyphens'; 456 checkLangSupport = createLangSupportChecker('-ms-hyphens'); 457 } 458 } //else we just return the initial values because ancient browsers don't support css3 anyway 459 460 461 return { 462 support: support, 463 property: property, 464 supportedBrowserLangs: supportedBrowserLangs, 465 checkLangSupport: checkLangSupport 466 }; 467 } 468 469 /** 470 * @typedef {Object} Hyphenator~css3_hsupport 471 * @property {boolean} support - if css3-hyphenation is supported 472 * @property {string} property - the css property name to access hyphen-settings (e.g. -webkit-hyphens) 473 * @property {Object.<string, boolean>} supportedBrowserLangs - an object caching tested languages 474 * @property {function} checkLangSupport - a method that checks if the browser supports a requested language 475 */ 476 477 /** 478 * @member {Hyphenator~css3_h9n} Hyphenator~css3_h9n
479 * @desc 480 * A generated object containing information for CSS3-hyphenation support 481 * This is set by {@link Hyphenator~css3_gethsupport} 482 * @default undefined 483 * @access private 484 * @see {@link Hyphenator~css3_gethsupport} 485 * @example 486 * //Check if browser supports a language 487 * css3_h9n.checkLangSupport(<lang>) 488 */ 489 var css3_h9n; 490 491 /** 492 * @member {string} Hyphenator~hyphenateClass 493 * @desc 494 * A string containing the css-class-name for the hyphenate class 495 * @default 'hyphenate' 496 * @access private 497 * @example 498 * <p class = "hyphenate">Text</p> 499 * @see {@link Hyphenator.config} 500 */ 501 var hyphenateClass = 'hyphenate'; 502 503 /** 504 * @member {string} Hyphenator~urlHyphenateClass 505 * @desc 506 * A string containing the css-class-name for the urlhyphenate class 507 * @default 'urlhyphenate' 508 * @access private 509 * @example 510 * <p class = "urlhyphenate">Text</p> 511 * @see {@link Hyphenator.config} 512 */ 513 var urlHyphenateClass = 'urlhyphenate'; 514 515 /** 516 * @member {string} Hyphenator~classPrefix 517 * @desc 518 * A string containing a unique className prefix to be used 519 * whenever Hyphenator sets a CSS-class 520 * @access private 521 */ 522 var classPrefix = 'Hyphenator' + Math.round(Math.random() * 1000); 523 524 /** 525 * @member {string} Hyphenator~hideClass 526 * @desc 527 * The name of the class that hides elements 528 * @access private 529 */ 530 var hideClass = classPrefix + 'hide'; 531 532 /** 533 * @member {RegExp} Hyphenator~hideClassRegExp 534 * @desc 535 * RegExp to remove hideClass from a list of classes 536 * @access private 537 */ 538 var hideClassRegExp = new RegExp("\\s?\\b" + hideClass + "\\b", "g"); 539 540 /** 541 * @member {string} Hyphenator~hideClass 542 * @desc 543 * The name of the class that unhides elements 544 * @access private 545 */ 546 var unhideClass = classPrefix + 'unhide'; 547 548 /** 549 * @member {RegExp} Hyphenator~hideClassRegExp 550 * @desc 551 * RegExp to remove unhideClass from a list of classes 552 * @access private 553 */ 554 var unhideClassRegExp = new RegExp("\\s?\\b" + unhideClass + "\\b", "g"); 555 556 /** 557 * @member {string} Hyphenator~css3hyphenateClass 558 * @desc 559 * The name of the class that hyphenates elements with css3 560 * @access private 561 */ 562 var css3hyphenateClass = classPrefix + 'css3hyphenate'; 563 564 /** 565 * @member {CSSEdit} Hyphenator~css3hyphenateClass 566 * @desc 567 * The var where CSSEdit class is stored 568 * @access private 569 */ 570 var css3hyphenateClassHandle; 571 572 /** 573 * @member {string} Hyphenator~dontHyphenateClass 574 * @desc 575 * A string containing the css-class-name for elements that should not be hyphenated 576 * @default 'donthyphenate' 577 * @access private 578 * @example 579 * <p class = "donthyphenate">Text</p> 580 * @see {@link Hyphenator.config} 581 */ 582 var dontHyphenateClass = 'donthyphenate'; 583 584 /** 585 * @member {number} Hyphenator~min 586 * @desc 587 * A number wich indicates the minimal length of words to hyphenate. 588 * @default 6 589 * @access private 590 * @see {@link Hyphenator.config} 591 */ 592 var min = 6; 593 594 /** 595 * @member {number} Hyphenator~leftmin 596 * @desc 597 * A number wich indicates the minimal length of characters before the first hyphenation. 598 * This value is only used if it is greater than the value in the pattern file. 599 * @default given by pattern file 600 * @access private 601 * @see {@link Hyphenator.config} 602 */ 603 var leftmin = 0; 604 605 /** 606 * @member {number} Hyphenator~rightmin 607 * @desc 608 * A number wich indicates the minimal length of characters after the last hyphenation. 609 * This value is only used if it is greater than the value in the pattern file. 610 * @default given by pattern file 611 * @access private 612 * @see {@link Hyphenator.config} 613 */ 614 var rightmin = 0; 615 616 /** 617 * @member {number} Hyphenator~orphanControl 618 * @desc 619 * Control how the last words of a line are handled: 620 * level 1 (default): last word is hyphenated 621 * level 2: last word is not hyphenated 622 * level 3: last word is not hyphenated and last space is non breaking 623 * @default 1 624 * @access private 625 */ 626 var orphanControl = 1; 627 628 /** 629 * @member {boolean}
629 Hyphenator~isBookmarklet 630 * @desc 631 * True if Hyphanetor runs as bookmarklet. 632 * @access private 633 */ 634 var isBookmarklet = (function () { 635 var loc = null, 636 re = false, 637 scripts = contextWindow.document.getElementsByTagName('script'), 638 i = 0, 639 l = scripts.length; 640 while (!re && i < l) { 641 loc = scripts[i].getAttribute('src'); 642 if (!!loc && loc.indexOf('Hyphenator.js?bm=true') !== -1) { 643 re = true; 644 } 645 i += 1; 646 } 647 return re; 648 }()); 649 650 /** 651 * @member {string|null} Hyphenator~mainLanguage 652 * @desc 653 * The general language of the document. In contrast to {@link Hyphenator~defaultLanguage}, 654 * mainLanguage is defined by the client (i.e. by the html or by a prompt). 655 * @access private 656 * @see {@link Hyphenator~autoSetMainLanguage} 657 */ 658 var mainLanguage = null; 659 660 /** 661 * @member {string|null} Hyphenator~defaultLanguage 662 * @desc 663 * The language defined by the developper. This language setting is defined by a config option. 664 * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can 665 * be found (i.e. just before the prompt). 666 * @access private 667 * @see {@link Hyphenator.config} 668 * @see {@link Hyphenator~autoSetMainLanguage} 669 */ 670 var defaultLanguage = ''; 671 672 /** 673 * @member {ElementCollection} Hyphenator~elements 674 * @desc 675 * A class representing all elements (of type Element) that have to be hyphenated. This var is filled by 676 * {@link Hyphenator~gatherDocumentInfos} 677 * @access private 678 */ 679 var elements = (function () { 680 /** 681 * @constructor Hyphenator~elements~ElementCollection~Element 682 * @desc represents a DOM Element with additional information 683 * @access private 684 */ 685 var makeElement = function (element) { 686 return { 687 /** 688 * @member {Object} Hyphenator~elements~ElementCollection~Element~element 689 * @desc A DOM Element 690 * @access protected 691 */ 692 element: element, 693 /** 694 * @member {boolean} Hyphenator~elements~ElementCollection~Element~hyphenated 695 * @desc Marks if the element has been hyphenated 696 * @access protected 697 */ 698 hyphenated: false, 699 /** 700 * @member {boolean} Hyphenator~elements~ElementCollection~Element~treated 701 * @desc Marks if information of the element has been collected but not hyphenated (e.g. dohyphenation is off) 702 * @access protected 703 */ 704 treated: false 705 }; 706 }, 707 /** 708 * @constructor Hyphenator~elements~ElementCollection 709 * @desc A collection of Elements to be hyphenated 710 * @access protected 711 */ 712 makeElementCollection = function () { 713 /** 714 * @member {number} Hyphenator~elements~ElementCollection~counters 715 * @desc Array of [number of collected elements, number of hyphenated elements] 716 * @access protected 717 */ 718 var counters = [0, 0], 719 /** 720 * @member {Object.<string, Array.<Element>>} Hyphenator~elements~ElementCollection~list 721 * @desc The collection of elements, where the key is a language code and the value is an array of elements 722 * @access protected 723 */ 724 list = {}, 725 /** 726 * @method Hyphenator~elements~ElementCollection.prototype~add 727 * @augments Hyphenator~elements~ElementCollection 728 * @access protected 729 * @desc adds a DOM element to the collection 730 * @param {Object} el - The DOM element 731 * @param {string} lang - The language of the element 732 */ 733 add = function (el, lang) { 734 var elo = makeElement(el); 735 if (!list.hasOwnProperty(lang)) {
736 list[lang] = []; 737 } 738 list[lang].push(elo); 739 counters[0] += 1; 740 return elo; 741 }, 742 /** 743 * @callback Hyphenator~elements~ElementCollection.prototype~each~callback fn - The callback that is executed for each element 744 * @param {string} [k] The key (i.e. language) of the collection 745 * @param {Hyphenator~elements~ElementCollection~Element} element 746 */ 747 748 /** 749 * @method Hyphenator~elements~ElementCollection.prototype~each 750 * @augments Hyphenator~elements~ElementCollection 751 * @access protected 752 * @desc takes each element of the collection as an argument of fn 753 * @param {Hyphenator~elements~ElementCollection.prototype~each~callback} fn - A function that takes an element as an argument 754 */ 755 each = function (fn) { 756 forEachKey(list, function (k) { 757 if (fn.length === 2) { 758 fn(k, list[k]); 759 } else { 760 fn(list[k]); 761 } 762 }); 763 }; 764 765 return { 766 counters: counters, 767 list: list, 768 add: add, 769 each: each 770 }; 771 772 }; 773 return makeElementCollection(); 774 }()); 775 776 777 /** 778 * @member {Object.<sting, string>} Hyphenator~exceptions 779 * @desc 780 * An object containing exceptions as comma separated strings for each language. 781 * When the language-objects are loaded, their exceptions are processed, copied here and then deleted. 782 * Exceptions can also be set by the user. 783 * @see {@link Hyphenator~prepareLanguagesObj} 784 * @access private 785 */ 786 var exceptions = {}; 787 788 /** 789 * @member {Object.<string, boolean>} Hyphenator~docLanguages 790 * @desc 791 * An object holding all languages used in the document. This is filled by 792 * {@link Hyphenator~gatherDocumentInfos} 793 * @access private 794 */ 795 var docLanguages = {}; 796 797 /** 798 * @member {string} Hyphenator~url 799 * @desc 800 * A string containing a insane RegularExpression to match URL's 801 * @access private 802 */ 803 var 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#!:\\.?\\+=&%@!\\-]*)*'; 804 // protocoll usr pwd ip or host tld port path 805 806 /** 807 * @member {string} Hyphenator~mail 808 * @desc 809 * A string containing a RegularExpression to match mail-adresses 810 * @access private 811 */ 812 var mail = '[\\w-\\.]+@[\\w\\.]+'; 813 814 /** 815 * @member {string} Hyphenator~zeroWidthSpace 816 * @desc 817 * A string that holds a char. 818 * Depending on the browser, this is the zero with space or an empty string. 819 * zeroWidthSpace is used to break URLs 820 * @access private 821 */ 822 var zeroWidthSpace = (function () { 823 var zws, ua = window.navigator.userAgent.toLowerCase(); 824 zws = String.fromCharCode(8203); //Unicode zero width space 825 if (ua.indexOf('msie 6') !== -1) { 826 zws = ''; //IE6 doesn't support zws 827 } 828 if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) { 829 zws = ''; //opera 10 on XP doesn't support zws 830 } 831 return zws; 832 }()); 833 834 /** 835 * @method Hyphenator~onBeforeWordHyphenation 836 * @desc 837 * This method is called just before a word is hyphenated. 838 * It is called with two parameters: the word and its language. 839 * The method must return a string (aka the word). 840 * @see {@link Hyphenator.config} 841 * @access private 842 * @param {string} word 843 * @param {string} lang 844 * @return {string} The word that goes into hyphenation 845 */ 846 var onBeforeWordHyphenation = function (word) { 847 return word; 848 }; 849 850 /** 851 * @method Hyphenator~onAfterWordHyphenation
852 * @desc 853 * This method is called for each word after it is hyphenated. 854 * Takes the word as a first parameter and its language as a second parameter. 855 * Returns a string that will replace the word that has been hyphenated. 856 * @see {@link Hyphenator.config} 857 * @access private 858 * @param {string} word 859 * @param {string} lang 860 * @return {string} The word that goes into hyphenation 861 */ 862 var onAfterWordHyphenation = function (word) { 863 return word; 864 }; 865 866 /** 867 * @method Hyphenator~onHyphenationDone 868 * @desc 869 * A method to be called, when the last element has been hyphenated. 870 * If there are frames the method is called for each frame. 871 * Therefore the location.href of the contextWindow calling this method is given as a parameter 872 * @see {@link Hyphenator.config} 873 * @param {string} context 874 * @access private 875 */ 876 var onHyphenationDone = function (context) { 877 return context; 878 }; 879 880 /** 881 * @name Hyphenator~selectorFunction 882 * @desc 883 * A function set by the user that has to return a HTMLNodeList or array of Elements to be hyphenated. 884 * By default this is set to false so we can check if a selectorFunction is set⦠885 * @see {@link Hyphenator.config} 886 * @see {@link Hyphenator~mySelectorFunction} 887 * @default false 888 * @type {function|boolean} 889 * @access private 890 */ 891 var selectorFunction = false; 892 893 /** 894 * @name Hyphenator~flattenNodeList 895 * @desc 896 * Takes a nodeList and returns an array with all elements that are not contained by another element in the nodeList 897 * By using this function the elements returned by selectElements can be 'flattened'. 898 * @see {@link Hyphenator~selectElements} 899 * @param {nodeList} nl 900 * @return {Array} Array of 'parent'-elements 901 * @access private 902 */ 903 function flattenNodeList(nl) { 904 var parentElements = [], 905 i = 1, 906 j = 0, 907 isParent = true; 908 909 parentElements.push(nl[0]); //add the first item, since this is always an parent 910 911 while (i < nl.length) { //cycle through nodeList 912 while (j < parentElements.length) { //cycle through parentElements 913 if (parentElements[j].contains(nl[i])) { 914 isParent = false; 915 break; 916 } 917 j += 1; 918 } 919 if (isParent) { 920 parentElements.push(nl[i]); 921 } 922 isParent = true; 923 i += 1; 924 } 925 926 return parentElements; 927 } 928 929 /** 930 * @method Hyphenator~mySelectorFunction 931 * @desc 932 * A function that returns a HTMLNodeList or array of Elements to be hyphenated. 933 * By default it uses the classname ('hyphenate') to select the elements. 934 * @access private 935 */ 936 function mySelectorFunction(hyphenateClass) { 937 var tmp, 938 el = [], 939 i = 0; 940 if (window.document.getElementsByClassName) { 941 el = contextWindow.document.getElementsByClassName(hyphenateClass); 942 } else if (window.document.querySelectorAll) { 943 el = contextWindow.document.querySelectorAll('.' + hyphenateClass); 944 } else { 945 tmp = contextWindow.document.getElementsByTagName('*'); 946 while (i < tmp.length) {
947 if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) { 948 el.push(tmp[i]); 949 } 950 i += 1; 951 } 952 } 953 return el; 954 } 955 956 /** 957 * @method Hyphenator~selectElements 958 * @desc 959 * A function that uses either selectorFunction set by the user 960 * or the default mySelectorFunction. 961 * @access private 962 */ 963 function selectElements() { 964 var elems; 965 if (selectorFunction) { 966 elems = selectorFunction(); 967 } else { 968 elems = mySelectorFunction(hyphenateClass); 969 } 970 if (elems.length !== 0) { 971 elems = flattenNodeList(elems); 972 } 973 return elems; 974 } 975 976 /** 977 * @member {string} Hyphenator~intermediateState 978 * @desc 979 * The visibility of elements while they are hyphenated: 980 * 'visible': unhyphenated text is visible and then redrawn when hyphenated. 981 * 'hidden': unhyphenated text is made invisible as soon as possible and made visible after hyphenation. 982 * @default 'hidden' 983 * @see {@link Hyphenator.config} 984 * @access private 985 */ 986 var intermediateState = 'hidden'; 987 988 /** 989 * @member {string} Hyphenator~unhide 990 * @desc 991 * How hidden elements unhide: either simultaneous (default: 'wait') or progressively. 992 * 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw) 993 * With 'progressive' Hyphenator.js unhides elements as soon as they are hyphenated. 994 * @see {@link Hyphenator.config} 995 * @access private 996 */ 997 var unhide = 'wait'; 998 999 /** 1000 * @member {Array.<Hyphenator~CSSEdit>} Hyphenator~CSSEditors 1001 * @desc A container array that holds CSSEdit classes 1002 * For each window object one CSSEdit class is inserted 1003 * @access private 1004 */ 1005 var CSSEditors = []; 1006 1007 /** 1008 * @constructor Hyphenator~CSSEdit 1009 * @desc 1010 * This class handles access and editing of StyleSheets. 1011 * Thanks to this styles (e.g. hiding and unhiding elements upon hyphenation) 1012 * can be changed in one place instead for each element. 1013 * @access private 1014 */ 1015 function makeCSSEdit(w) { 1016 w = w || window; 1017 var doc = w.document, 1018 /** 1019 * @member {Object} Hyphenator~CSSEdit~sheet 1020 * @desc 1021 * A StyleSheet, where Hyphenator can write to. 1022 * If no StyleSheet can be found, lets create one. 1023 * @access private 1024 */ 1025 sheet = (function () { 1026 var i = 0, 1027 l = doc.styleSheets.length, 1028 s, 1029 element, 1030 r = false; 1031 while (i < l) { 1032 s = doc.styleSheets[i]; 1033 try { 1034 if (!!s.cssRules) { 1035 r = s; 1036 break; 1037 } 1038 } catch (ignore) {} 1039 i += 1; 1040 } 1041 if (r === false) { 1042 element = doc.createElement('style'); 1043 element.type = 'text/css'; 1044 doc.getElementsByTagName('head')[0].appendChild(element); 1045 r = doc.styleSheets[doc.styleSheets.length - 1]; 1046 } 1047 return r; 1048 }()), 1049 1050 /** 1051 * @typedef {Object} Hyphenator~CSSEdit~changes 1052 * @property {Object} sheet - The StyleSheet where the change was made 1053 * @property {number} index - The index of the changed rule 1054 */ 1055 1056 /** 1057 * @member {Array.<changes>} Hyphenator~CSSEdit~changes 1058 * @desc 1059 * Sets a CSS rule for a specified selector 1060 * @access private 1061 */ 1062 changes = [], 1063 1064 /** 1065 * @typedef Hyphenator~CSSEdit~rule 1066 * @property {number} index - The index of the rule 1067 * @property {Object} rule - The style rule 1068 */ 1069 /** 1070 * @method Hyphenator~CSSEdit~findRule
1071 * @desc 1072 * Searches the StyleSheets for a given selector and returns an object containing the rule. 1073 * If nothing can be found, false is returned. 1074 * @param {string} sel 1075 * @return {Hyphenator~CSSEdit~rule|false} 1076 * @access private 1077 */ 1078 findRule = function (sel) { 1079 var s, 1080 rule, 1081 sheets = w.document.styleSheets, 1082 rules, 1083 i = 0, 1084 j = 0, 1085 r = false; 1086 while (i < sheets.length) { 1087 s = sheets[i]; 1088 try { //FF has issues here with external CSS (s.o.p) 1089 if (!!s.cssRules) { 1090 rules = s.cssRules; 1091 } else if (!!s.rules) { 1092 // IE < 9 1093 rules = s.rules; 1094 } 1095 } catch (ignore) {} 1096 if (!!rules && !!rules.length) { 1097 while (j < rules.length) { 1098 rule = rules[j]; 1099 if (rule.selectorText === sel) { 1100 r = { 1101 index: j, 1102 rule: rule 1103 }; 1104 } 1105 j += 1; 1106 } 1107 } 1108 i += 1; 1109 } 1110 return r; 1111 }, 1112 /** 1113 * @method Hyphenator~CSSEdit~addRule 1114 * @desc 1115 * Adds a rule to the {@link Hyphenator~CSSEdit~sheet} 1116 * @param {string} sel - The selector to be added 1117 * @param {string} rulesStr - The rules for the specified selector 1118 * @return {number} index of the new rule 1119 * @access private 1120 */ 1121 addRule = function (sel, rulesStr) { 1122 var i, r; 1123 if (!!sheet.insertRule) { 1124 if (!!sheet.cssRules) { 1125 i = sheet.cssRules.length; 1126 } else { 1127 i = 0; 1128 } 1129 r = sheet.insertRule(sel + '{' + rulesStr + '}', i); 1130 } else if (!!sheet.addRule) { 1131 // IE < 9 1132 if (!!sheet.rules) { 1133 i = sheet.rules.length; 1134 } else { 1135 i = 0; 1136 } 1137 sheet.addRule(sel, rulesStr, i); 1138 r = i; 1139 } 1140 return r; 1141 }, 1142 /** 1143 * @method Hyphenator~CSSEdit~removeRule 1144 * @desc 1145 * Removes a rule with the specified index from the specified sheet 1146 * @param {Object} sheet - The style sheet 1147 * @param {number} index - the index of the rule 1148 * @access private 1149 */ 1150 removeRule = function (sheet, index) { 1151 if (sheet.deleteRule) { 1152 sheet.deleteRule(index); 1153 } else { 1154 // IE < 9 1155 sheet.removeRule(index); 1156 } 1157 }; 1158 1159 return { 1160 /** 1161 * @method Hyphenator~CSSEdit.setRule 1162 * @desc 1163 * Sets a CSS rule for a specified selector 1164 * @access public 1165 * @param {string} sel - Selector 1166 * @param {string} rulesString - CSS-Rules 1167 */ 1168 setRule: function (sel, rulesString) { 1169 var i, existingRule, cssText; 1170 existingRule = findRule(sel); 1171 if (!!existingRule) { 1172 if (!!existingRule.rule.cssText) { 1173 cssText = existingRule.rule.cssText; 1174 } else { 1175 // IE < 9 1176 cssText = existingRule.rule.style.cssText.toLowerCase(); 1177 } 1178 if (cssText !== sel + ' { ' + rulesString + ' }') { 1179 //cssText of the found rule is not uniquely selector + rulesString, 1180 if (cssText.indexOf(rulesString) !== -1) { 1181 //maybe there are other rules or IE < 9 1182 //clear existing def 1183 existingRule.rule.style.visibility = ''; 1184 } 1185 //add rule and register for later removal 1186 i = addRule(sel, rulesString); 1187 changes.push({sheet: sheet, index: i}); 1188 } 1189 } else { 1190 i = addRule(sel, rulesString); 1191 changes.push({sheet: sheet, index: i}); 1192 } 1193 }, 1194 /** 1195 * @method Hyphenator~CSSEdit.clearChanges
1196 * @desc 1197 * Removes all changes Hyphenator has made from the StyleSheets 1198 * @access public 1199 */ 1200 clearChanges: function () { 1201 var change = changes.pop(); 1202 while (!!change) { 1203 removeRule(change.sheet, change.index); 1204 change = changes.pop(); 1205 } 1206 } 1207 }; 1208 } 1209 1210 /** 1211 * @member {string} Hyphenator~hyphen 1212 * @desc 1213 * A string containing the character for in-word-hyphenation 1214 * @default the soft hyphen 1215 * @access private 1216 * @see {@link Hyphenator.config} 1217 */ 1218 var hyphen = String.fromCharCode(173); 1219 1220 /** 1221 * @member {string} Hyphenator~urlhyphen 1222 * @desc 1223 * A string containing the character for url/mail-hyphenation 1224 * @default the zero width space 1225 * @access private 1226 * @see {@link Hyphenator.config} 1227 * @see {@link Hyphenator~zeroWidthSpace} 1228 */ 1229 var urlhyphen = zeroWidthSpace; 1230 1231 /** 1232 * @method Hyphenator~hyphenateURL 1233 * @desc 1234 * Puts {@link Hyphenator~urlhyphen} (default: zero width space) after each no-alphanumeric char that my be in a URL. 1235 * @param {string} url to hyphenate 1236 * @returns string the hyphenated URL 1237 * @access public 1238 */ 1239 function hyphenateURL(url) { 1240 var tmp = url.replace(/([:\/\.\?#&\-_,;!@]+)/gi, '$&' + urlhyphen), 1241 parts = tmp.split(urlhyphen), 1242 i = 0; 1243 while (i < parts.length) { 1244 if (parts[i].length > (2 * min)) { 1245 parts[i] = parts[i].replace(/(\w{3})(\w)/gi, "$1" + urlhyphen + "$2"); 1246 } 1247 i += 1; 1248 } 1249 if (parts[parts.length - 1] === "") { 1250 parts.pop(); 1251 } 1252 return parts.join(urlhyphen); 1253 } 1254 1255 /** 1256 * @member {boolean} Hyphenator~safeCopy 1257 * @desc 1258 * Defines wether work-around for copy issues is active or not 1259 * @default true 1260 * @access private 1261 * @see {@link Hyphenator.config} 1262 * @see {@link Hyphenator~registerOnCopy} 1263 */ 1264 var safeCopy = true; 1265 1266 /** 1267 * @method Hyphenator~zeroTimeOut 1268 * @desc 1269 * defer execution of a function on the call stack 1270 * Analog to window.setTimeout(fn, 0) but without a clamped delay if postMessage is supported 1271 * @access private 1272 * @see {@link http://dbaron.org/log/20100309-faster-timeouts} 1273 */ 1274 var zeroTimeOut = (function () { 1275 if (window.postMessage && window.addEventListener) { 1276 return (function () { 1277 var timeouts = [], 1278 msg = "Hyphenator_zeroTimeOut_message", 1279 setZeroTimeOut = function (fn) { 1280 timeouts.push(fn); 1281 window.postMessage(msg, "*"); 1282 }, 1283 handleMessage = function (event) { 1284 if (event.source === window && event.data === msg) { 1285 event.stopPropagation(); 1286 if (timeouts.length > 0) { 1287 //var efn = timeouts.shift(); 1288 //efn(); 1289 timeouts.shift()(); 1290 } 1291 } 1292 }; 1293 window.addEventListener("message", handleMessage, true); 1294 return setZeroTimeOut; 1295 }()); 1296 } 1297 return function (fn) { 1298 window.setTimeout(fn, 0); 1299 }; 1300 }()); 1301 1302 /** 1303 * @member {Object} Hyphenator~hyphRunFor 1304 * @desc 1305 * stores location.href for documents where run() has been executed 1306 * to warn when Hyphenator.run() executed multiple times 1307 * @access private 1308 * @see {@link Hyphenator~runWhenLoaded} 1309 */ 1310 var hyphRunFor = {}; 1311 1312 /** 1313 * @method Hyphenator~runWhenLoaded 1314 * @desc 1315 * A crossbrowser solution for the DOMContentLoaded-Event based on 1316 * <a href = "http://jquery.com/">jQuery</a> 1317 * I added some functionality: e.g. support for frames and iframes⦠1318 * @param {Object} w the window-object 1319 * @param {function()} f the function to call when the document is ready 1320 * @access private 1321 */ 1322 function runWhenLoaded(w, f) { 1323 var toplevel, 1324 add = window.document.addEventListener 1325 ? 'addEventListener' 1326 : 'attachEvent', 1327 rem = window.document.addEventListener 1328 ? 'removeEventListener' 1329 : 'detachEvent', 1330 pre = window.document.addEventListener 1331 ? '' 1332 : 'on'; 1333 1334 function init(context) { 1335 if (hyphRunFor[context.location.href]) { 1336 onWarning(new Error("Warning: multiple execution of Hyphenator.run() â This may slow down the script!")); 1337 } 1338 contextWindow = context || window; 1339 f(); 1340 hyphRunFor[contextWindow.location.href] = true; 1341 } 1342 1343 function doScrollCheck() { 1344 try { 1345 // If IE is used, use the trick by Diego Perini 1346 // http://javascript.nwbox.com/IEContentLoaded/ 1347 w.document.documentElement.doScroll("left"); 1348 } catch (ignore) { 1349 window.setTimeout(doScrollCheck, 1); 1350 return; 1351 } 1352 //maybe modern IE fired DOMContentLoaded 1353 if (!hyphRunFor[w.location.href]) { 1354 documentLoaded = true; 1355 init(w); 1356 } 1357 } 1358 1359 function doOnEvent(e) { 1360 var i = 0, 1361 fl, 1362 haveAccess; 1363 if (!!e && e.type === 'readystatechange' && w.document.readyState !== 'interactive' && w.document.readyState !== 'complete') { 1364 return; 1365 } 1366 1367 //DOM is ready/interactive, but frames may not be loaded yet! 1368 //cleanup events 1369 w.document[rem](pre + 'DOMContentLoaded', doOnEvent, false); 1370 w.document[rem](pre + 'readystatechange', doOnEvent, false); 1371 1372 //check frames 1373 fl = w.frames.length; 1374 if (fl === 0 || !doFrames) { 1375 //there are no frames! 1376 //cleanup events 1377 w[rem](pre + 'load', doOnEvent, false); 1378 documentLoaded = true; 1379 init(w); 1380 } else if (doFrames && fl > 0) { 1381 //we have frames, so wait for onload and then initiate runWhenLoaded recursevly for each frame: 1382 if (!!e && e.type === 'load') { 1383 //cleanup events 1384 w[rem](pre + 'load', doOnEvent, false); 1385 while (i < fl) { 1386 haveAccess = undefined; 1387 //try catch isn't enough for webkit 1388 try { 1389 //opera throws only on document.toString-access 1390 haveAccess = w.frames[i].document.toString(); 1391 } catch (ignore) { 1392 haveAccess = undefined; 1393 }
1394 if (!!haveAccess) { 1395 runWhenLoaded(w.frames[i], f); 1396 } 1397 i += 1; 1398 } 1399 init(w); 1400 } 1401 } 1402 } 1403 1404 if (documentLoaded || w.document.readyState === 'complete') { 1405 //Hyphenator has run already (documentLoaded is true) or 1406 //it has been loaded after onLoad 1407 documentLoaded = true; 1408 doOnEvent({type: 'load'}); 1409 } else { 1410 //register events 1411 w.document[add](pre + 'DOMContentLoaded', doOnEvent, false); 1412 w.document[add](pre + 'readystatechange', doOnEvent, false); 1413 w[add](pre + 'load', doOnEvent, false); 1414 toplevel = false; 1415 try { 1416 toplevel = !window.frameElement; 1417 } catch (ignore) {} 1418 if (toplevel && w.document.documentElement.doScroll) { 1419 doScrollCheck(); //calls init() 1420 } 1421 } 1422 } 1423 1424 /** 1425 * @method Hyphenator~getLang 1426 * @desc 1427 * Gets the language of an element. If no language is set, it may use the {@link Hyphenator~mainLanguage}. 1428 * @param {Object} el The first parameter is an DOM-Element-Object 1429 * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator~mainLanguage} 1430 * if there's no language found for the element. 1431 * @return {string} The language of the element 1432 * @access private 1433 */ 1434 function getLang(el, fallback) { 1435 try { 1436 return !!el.getAttribute('lang') 1437 ? el.getAttribute('lang').toLowerCase() 1438 : !!el.getAttribute('xml:lang') 1439 ? el.getAttribute('xml:lang').toLowerCase() 1440 : el.tagName.toLowerCase() !== 'html' 1441 ? getLang(el.parentNode, fallback) 1442 : fallback 1443 ? mainLanguage 1444 : null; 1445 } catch (ignore) {} 1446 } 1447 1448 /** 1449 * @method Hyphenator~autoSetMainLanguage 1450 * @desc 1451 * Retrieves the language of the document from the DOM and sets the lang attribute of the html-tag. 1452 * The function looks in the following places: 1453 * <ul> 1454 * <li>lang-attribute in the html-tag</li> 1455 * <li><meta http-equiv = "content-language" content = "xy" /></li> 1456 * <li><meta name = "DC.Language" content = "xy" /></li> 1457 * <li><meta name = "language" content = "xy" /></li> 1458 * </li> 1459 * If nothing can be found a prompt using {@link Hyphenator~languageHint} and a prompt-string is displayed. 1460 * If the retrieved language is in the object {@link Hyphenator~supportedLangs} it is copied to {@link Hyphenator~mainLanguage} 1461 * @access private 1462 */ 1463 function autoSetMainLanguage(w) { 1464 w = w || contextWindow; 1465 var el = w.document.getElementsByTagName('html')[0], 1466 m = w.document.getElementsByTagName('meta'), 1467 i = 0, 1468 getLangFromUser = function () { 1469 var ml, 1470 text = '', 1471 dH = 300, 1472 dW = 450, 1473 dX = Math.floor((w.outerWidth - dW) / 2) + window.screenX, 1474 dY = Math.floor((w.outerHeight - dH) / 2) + window.screenY, 1475 ul = '', 1476 languageHint; 1477 if (!!window.showModalDialog && (w.location.href.indexOf(basePath) !== -1)) { 1478 ml = window.showModalDialog(basePath + 'modalLangDialog.html', supportedLangs, "dialogWidth: " + dW + "px; dialogHeight: " + dH + "px; dialogtop: " + dY + "; dialogleft: " + dX + "; center: on; resizable: off; scroll: off;"); 1479 } else { 1480 languageHint = (function () { 1481 var r = ''; 1482 forEachKey(supportedLangs, function (k) { 1483 r += k + ', '; 1484 }); 1485 r = r.substring(0, r.length - 2); 1486 return r; 1487 }()); 1488 ul = window.navigator.language || window.navigator.userLanguage; 1489 ul = ul.substring(0, 2); 1490 if (!!supportedLangs[ul] && supportedLangs[ul].prompt !== '') { 1491 text = supportedLangs[ul].prompt; 1492 } else { 1493 text = supportedLangs.en.prompt; 1494 } 1495 text += ' (ISO 639-1)\n\n' + languageHint; 1496 ml = window.prompt(window.unescape(text), ul).toLowerCase(); 1497 } 1498 return ml; 1499 }; 1500 mainLanguage = getLang(el, false); 1501 if (!mainLanguage) { 1502 while (i < m.length) { 1503 //<meta http-equiv = "content-language" content="xy"> 1504 if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) { 1505 mainLanguage = m[i].getAttribute('content').toLowerCase(); 1506 } 1507 //<meta name = "DC.Language" content="xy"> 1508 if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) { 1509 mainLanguage = m[i].getAttribute('content').toLowerCase(); 1510 } 1511 //<meta name = "language" content = "xy"> 1512 if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) { 1513 mainLanguage = m[i].getAttribute('content').toLowerCase(); 1514 } 1515 i += 1; 1516 } 1517 } 1518 //get lang for frame from enclosing document 1519 if (!mainLanguage && doFrames && (!!contextWindow.frameElement)) { 1520 autoSetMainLanguage(window.parent); 1521 } 1522 //fallback to defaultLang if set 1523 if (!mainLanguage && defaultLanguage !== '') { 1524 mainLanguage = defaultLanguage; 1525 } 1526 //ask user for lang 1527 if (!mainLanguage) { 1528 mainLanguage = getLangFromUser(); 1529 } 1530 el.lang = mainLanguage; 1531 } 1532 1533 /** 1534 * @method Hyphenator~gatherDocumentInfos
1535 * @desc 1536 * This method runs through the DOM and executes the process()-function on: 1537 * - every node returned by the {@link Hyphenator~selectorFunction}. 1538 * @access private 1539 */ 1540 function gatherDocumentInfos() { 1541 var elToProcess, 1542 urlhyphenEls, 1543 tmp, 1544 i = 0; 1545 /** 1546 * @method Hyphenator~gatherDocumentInfos 1547 * @desc 1548 * This method copies the element to the elements-variable, sets its visibility 1549 * to intermediateState, retrieves its language and recursivly descends the DOM-tree until 1550 * the child-Nodes aren't of type 1 1551 * @param {Object} el a DOM element 1552 * @param {string} plang the language of the parent element 1553 * @param {boolean} isChild true, if the parent of el has been processed 1554 */ 1555 function process(el, pLang, isChild) { 1556 var n, 1557 j = 0, 1558 hyphenate = true, 1559 eLang, 1560 useCSS3 = function () { 1561 css3hyphenateClassHandle = makeCSSEdit(contextWindow); 1562 css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': auto;'); 1563 css3hyphenateClassHandle.setRule('.' + dontHyphenateClass, css3_h9n.property + ': manual;'); 1564 if ((eLang !== pLang) && css3_h9n.property.indexOf('webkit') !== -1) { 1565 css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, '-webkit-locale : ' + eLang + ';'); 1566 } 1567 el.className = el.className + ' ' + css3hyphenateClass; 1568 }, 1569 useHyphenator = function () { 1570 //quick fix for test111.html 1571 //better: weight elements 1572 if (isBookmarklet && eLang !== mainLanguage) { 1573 return; 1574 } 1575 if (supportedLangs.hasOwnProperty(eLang)) { 1576 docLanguages[eLang] = true; 1577 } else { 1578 if (supportedLangs.hasOwnProperty(eLang.split('-')[0])) { //try subtag 1579 eLang = eLang.split('-')[0]; 1580 docLanguages[eLang] = true; 1581 } else if (!isBookmarklet) { 1582 hyphenate = false; 1583 } 1584 } 1585 if (hyphenate) { 1586 if (intermediateState === 'hidden') { 1587 el.className = el.className + ' ' + hideClass; 1588 } 1589 elements.add(el, eLang); 1590 } 1591 }; 1592 isChild = isChild || false; 1593 if (el.lang && typeof el.lang === 'string') { 1594 eLang = el.lang.toLowerCase(); //copy attribute-lang to internal eLang 1595 } else if (!!pLang && pLang !== '') { 1596 eLang = pLang.toLowerCase(); 1597 } else { 1598 eLang = getLang(el, true); 1599 } 1600 1601 if (!isChild) { 1602 if (css3 && css3_h9n.support && !!css3_h9n.checkLangSupport(eLang)) { 1603 useCSS3(); 1604 } else { 1605 useHyphenator(); 1606 } 1607 } else { 1608 if (eLang !== pLang) { 1609 if (css3 && css3_h9n.support && !!css3_h9n.checkLangSupport(eLang)) { 1610 useCSS3(); 1611 } else { 1612 useHyphenator(); 1613 } 1614 } else { 1615 if (!css3 || !css3_h9n.support || !css3_h9n.checkLangSupport(eLang)) { 1616 useHyphenator(); 1617 } // else do nothing 1618 } 1619 } 1620 n = el.childNodes[j]; 1621 while (!!n) { 1622 if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] && 1623 n.className.indexOf(dontHyphenateClass) === -1 && 1624 n.className.indexOf(urlHyphenateClass) === -1 && !elToProcess[n]) { 1625 process(n, eLang, true); 1626 } 1627 j += 1; 1628 n = el.childNodes[j]; 1629 } 1630 } 1631 function processUrlStyled(el) { 1632 var n, j = 0; 1633 1634 n = el.childNodes[j]; 1635 while (!!n) { 1636 if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
1637 n.className.indexOf(dontHyphenateClass) === -1 && 1638 n.className.indexOf(hyphenateClass) === -1 && !urlhyphenEls[n]) { 1639 processUrlStyled(n); 1640 } else if (n.nodeType === 3) { 1641 n.data = hyphenateURL(n.data); 1642 } 1643 j += 1; 1644 n = el.childNodes[j]; 1645 } 1646 } 1647 1648 if (css3) { 1649 css3_h9n = css3_gethsupport(); 1650 } 1651 if (isBookmarklet) { 1652 elToProcess = contextWindow.document.getElementsByTagName('body')[0]; 1653 process(elToProcess, mainLanguage, false); 1654 } else { 1655 if (!css3 && intermediateState === 'hidden') { 1656 CSSEditors.push(makeCSSEdit(contextWindow)); 1657 CSSEditors[CSSEditors.length - 1].setRule('.' + hyphenateClass, 'visibility: hidden;'); 1658 CSSEditors[CSSEditors.length - 1].setRule('.' + hideClass, 'visibility: hidden;'); 1659 CSSEditors[CSSEditors.length - 1].setRule('.' + unhideClass, 'visibility: visible;'); 1660 } 1661 elToProcess = selectElements(); 1662 tmp = elToProcess[i]; 1663 while (!!tmp) { 1664 process(tmp, '', false); 1665 i += 1; 1666 tmp = elToProcess[i]; 1667 } 1668 1669 urlhyphenEls = mySelectorFunction(urlHyphenateClass); 1670 i = 0; 1671 tmp = urlhyphenEls[i]; 1672 while (!!tmp) { 1673 processUrlStyled(tmp); 1674 i += 1; 1675 tmp = urlhyphenEls[i]; 1676 } 1677 } 1678 if (elements.counters[0] === 0) { 1679 //nothing to hyphenate or all hyphenated by css3 1680 i = 0; 1681 while (i < CSSEditors.length) { 1682 CSSEditors[i].clearChanges(); 1683 i += 1; 1684 } 1685 onHyphenationDone(contextWindow.location.href); 1686 } 1687 } 1688 1689 /** 1690 * @method Hyphenator~createCharMap 1691 * @desc 1692 * reads the charCodes from lo.characters and stores them in a bidi map: 1693 * charMap.int2code = [0: 97, //a 1694 * 1: 98, //b 1695 * 2: 99] //c etc. 1696 * charMap.code2int = {"97": 0, //a 1697 * "98": 1, //b 1698 * "99": 2} //c etc. 1699 * @access private 1700 * @param {Object} language object 1701 */ 1702 function makeCharMap() { 1703 var int2code = [], 1704 code2int = {}, 1705 add = function (newValue) { 1706 if (!code2int[newValue]) { 1707 int2code.push(newValue); 1708 code2int[newValue] = int2code.length - 1; 1709 } 1710 }; 1711 return { 1712 int2code: int2code, 1713 code2int: code2int, 1714 add: add 1715 }; 1716 } 1717 1718 /** 1719 * @constructor Hyphenator~ValueStore 1720 * @desc Storage-Object for storing hyphenation points (aka values) 1721 * @access private 1722 */ 1723 function makeValueStore(len) { 1724 var indexes = (function () { 1725 var arr; 1726 if (Object.prototype.hasOwnProperty.call(window, "Uint32Array")) { //IE<9 doesn't have window.hasOwnProperty (host object) 1727 arr = new window.Uint32Array(3); 1728 arr[0] = 1; //start position of a value set 1729 arr[1] = 1; //next index 1730 arr[2] = 1; //last index with a significant value 1731 } else { 1732 arr = [1, 1, 1]; 1733 } 1734 return arr; 1735 }()), 1736 keys = (function () { 1737 var i, r; 1738 if (Object.prototype.hasOwnProperty.call(window, "Uint8Array")) { //IE<9 doesn't have window.hasOwnProperty (host object) 1739 return new window.Uint8Array(len); 1740 } 1741 r = []; 1742 r.length = len; 1743 i = r.length - 1; 1744 while (i >= 0) { 1745 r[i] = 0; 1746 i -= 1; 1747 } 1748 return r; 1749 }()), 1750 add = function (p) { 1751 keys[indexes[1]] = p; 1752 indexes[2] = indexes[1]; 1753 indexes[1] += 1; 1754 }, 1755 add0 = function () { 1756 //just do a step, since array is initialized with zeroe
1756s 1757 indexes[1] += 1; 1758 }, 1759 finalize = function () { 1760 var start = indexes[0]; 1761 keys[indexes[2] + 1] = 255; //mark end of pattern 1762 indexes[0] = indexes[2] + 2; 1763 indexes[1] = indexes[0]; 1764 return start; 1765 }; 1766 return { 1767 keys: keys, 1768 add: add, 1769 add0: add0, 1770 finalize: finalize 1771 }; 1772 } 1773 1774 /** 1775 * @method Hyphenator~convertPatternsToArray 1776 * @desc 1777 * converts the patterns to a (typed, if possible) array as described by Liang: 1778 * 1779 * 1. Create the CharMap: an alphabet of used character codes mapped to an int (e.g. a: "97" -> 0) 1780 * This map is bidirectional: 1781 * charMap.code2int is an object with charCodes as keys and corresponging ints as values 1782 * charMao.int2code is an array of charCodes at int indizes 1783 * the length of charMao.int2code is equal the length of the alphabet 1784 * 1785 * 2. Create a ValueStore: (typed) array that holds "values", i.e. the digits extracted from the patterns 1786 * The first value set starts at index 1 (since the trie is initialized with zeroes, starting at 0 would create errors) 1787 * Each value set ends with a value of 255; trailing 0's are not stored. So pattern values like e.g. "010200" will become [â¦,0,1,0,2,255,â¦] 1788 * The ValueStore-Object manages handling of indizes automatically. Use ValueStore.add(p) to add a running value. 1789 * Use ValueStore.finalize() when the last value of a pattern is added. It will add the final 255, prepare the valueStore for new values 1790 * and return the starting index of the pattern. 1791 * To prevent doubles we could temporarly store the values in a object {value: startIndex} and only add new values, 1792 * but this object deoptimizes very fast (new hidden map for each entry); here we gain speed and pay memory 1793 * 1794 * 3. Create and zero initialize a (typed) array to store the trie. The trie uses two slots for each entry/node: 1795 * i: a link to another position in the array or -1 if the pattern ends here or more rows have to be added. 1796 * i + 1: a link to a value in the ValueStore or 0 if there's no value for the path to this node. 1797 * Although the array is one-dimensional it can be described as an array of "rows", 1798 * where each "row" is an array of length trieRowLength (see below). 1799 * The first entry of this "row" represents the first character of the alphabet, the second a possible link to value store, 1800 * the third represents the second character of the alphabet and so on⦠1801 * 1802 * 4. Initialize trieRowLength (length of the alphabet * 2) 1803 * 1804 * 5. Now we apply extract to each pattern collection (patterns of the same length are collected and concatenated to one string) 1805 * extract goes through these pattern collections char by char and adds them either to the ValueStore (if they are digits) or 1806 * to the trie (adding more "rows" if necessary, i.e. if the last link pointed to -1). 1807 * So the first "row" holds all starting characters, where the subsequent rows hold the characters that follow the 1808 * character that link to this row. Therefor the array is dense at the beginning and very sparse at the end. 1809 * 1810 * 1811 * @access private 1812 * @param {Object} language object 1813 */ 1814 function convertPatternsToArray(lo) { 1815 var trieNextEmptyRow = 0, 1816 i, 1817 charMapc2i, 1818 valueStore, 1819 indexedTrie, 1820 trieRowLength, 1821 1822 extract = function (patternSizeInt, patterns) { 1823 var charPos = 0, 1824 charCode = 0, 1825 mappedCharCode = 0, 1826 rowStart = 0, 1827 nextRowStart = 0, 1828 prevWasDigit = false; 1829 while (charPos < patterns.length) { 1830 charCode = patterns.charCodeAt(charPos); 1831 if ((charPos + 1) % patternSizeInt !== 0) { 1832 //more to comeâ¦
1833 if (charCode <= 57 && charCode >= 49) { 1834 //charCode is a digit 1835 valueStore.add(charCode - 48); 1836 prevWasDigit = true; 1837 } else { 1838 //charCode is alphabetical 1839 if (!prevWasDigit) { 1840 valueStore.add0(); 1841 } 1842 prevWasDigit = false; 1843 if (nextRowStart === -1) { 1844 nextRowStart = trieNextEmptyRow + trieRowLength; 1845 trieNextEmptyRow = nextRowStart; 1846 indexedTrie[rowStart + mappedCharCode * 2] = nextRowStart; 1847 } 1848 mappedCharCode = charMapc2i[charCode]; 1849 rowStart = nextRowStart; 1850 nextRowStart = indexedTrie[rowStart + mappedCharCode * 2]; 1851 if (nextRowStart === 0) { 1852 indexedTrie[rowStart + mappedCharCode * 2] = -1; 1853 nextRowStart = -1; 1854 } 1855 } 1856 } else { 1857 //last part of pattern
1858 if (charCode <= 57 && charCode >= 49) { 1859 //the last charCode is a digit 1860 valueStore.add(charCode - 48); 1861 indexedTrie[rowStart + mappedCharCode * 2 + 1] = valueStore.finalize(); 1862 } else { 1863 //the last charCode is alphabetical 1864 if (!prevWasDigit) { 1865 valueStore.add0(); 1866 } 1867 valueStore.add0(); 1868 if (nextRowStart === -1) { 1869 nextRowStart = trieNextEmptyRow + trieRowLength; 1870 trieNextEmptyRow = nextRowStart; 1871 indexedTrie[rowStart + mappedCharCode * 2] = nextRowStart; 1872 } 1873 mappedCharCode = charMapc2i[charCode]; 1874 rowStart = nextRowStart; 1875 if (indexedTrie[rowStart + mappedCharCode * 2] === 0) { 1876 indexedTrie[rowStart + mappedCharCode * 2] = -1; 1877 } 1878 indexedTrie[rowStart + mappedCharCode * 2 + 1] = valueStore.finalize(); 1879 } 1880 rowStart = 0; 1881 nextRowStart = 0; 1882 prevWasDigit = false; 1883 } 1884 charPos += 1; 1885 } 1886 };/*, 1887 prettyPrintIndexedTrie = function (rowLength) { 1888 var s = "0: ", 1889 idx; 1890 for (idx = 0; idx < indexedTrie.length; idx += 1) { 1891 s += indexedTrie[idx]; 1892 s += ","; 1893 if ((idx + 1) % rowLength === 0) { 1894 s += "\n" + (idx + 1) + ": "; 1895 } 1896 } 1897 console.log(s); 1898 };*/ 1899 1900 lo.charMap = makeCharMap(); 1901 i = 0; 1902 while (i < lo.patternChars.length) { 1903 lo.charMap.add(lo.patternChars.charCodeAt(i)); 1904 i += 1; 1905 } 1906 charMapc2i = lo.charMap.code2int; 1907 1908 valueStore = makeValueStore(lo.valueStoreLength); 1909 lo.valueStore = valueStore; 1910 1911 if (Object.prototype.hasOwnProperty.call(window, "Int32Array")) { //IE<9 doesn't have window.hasOwnProperty (host object) 1912 lo.indexedTrie = new window.Int32Array(lo.patternArrayLength * 2); 1913 } else { 1914 lo.indexedTrie = []; 1915 lo.indexedTrie.length = lo.patternArrayLength * 2; 1916 i = lo.indexedTrie.length - 1; 1917 while (i >= 0) { 1918 lo.indexedTrie[i] = 0; 1919 i -= 1; 1920 } 1921 } 1922 indexedTrie = lo.indexedTrie; 1923 trieRowLength = lo.charMap.int2code.length * 2; 1924 1925 forEachKey(lo.patterns, function (i) { 1926 extract(parseInt(i, 10), lo.patterns[i]); 1927 }); 1928 //prettyPrintIndexedTrie(lo.charMap.int2code.length * 2); 1929 } 1930 1931 /** 1932 * @method Hyphenator~recreatePattern 1933 * @desc 1934 * Recreates the pattern for the reducedPatternSet 1935 * @param {string} pattern The pattern (chars) 1936 * @param {string} nodePoints The nodePoints (integers) 1937 * @access private 1938 * @return {string} The pattern (chars and numbers) 1939 */ 1940 function recreatePattern(pattern, nodePoints) { 1941 var r = [], 1942 c = pattern.split(''), 1943 i = 0; 1944 while (i <= c.length) { 1945 if (nodePoints[i] && nodePoints[i] !== 0) { 1946 r.push(nodePoints[i]); 1947 } 1948 if (c[i]) { 1949 r.push(c[i]); 1950 } 1951 i += 1; 1952 } 1953 return r.join(''); 1954 } 1955 1956 /** 1957 * @method Hyphenator~convertExceptionsToObject 1958 * @desc 1959 * Converts a list of comma seprated exceptions to an object: 1960 * 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'} 1961 * @access private 1962 * @param {string} exc a comma separated string of exceptions (without spaces) 1963 * @return {Object.<string, string>} 1964 */ 1965 function convertExceptionsToObject(exc) { 1966 var w = exc.split(', '), 1967 r = {}, 1968 i = 0, 1969 l = w.length, 1970 key; 1971 while (i < l) { 1972 key = w[i].replace(/-/g, ''); 1973 if (!r.hasOwnProperty(key)) { 1974 r[key] = w[i]; 1975 } 1976 i += 1; 1977 } 1978 return r; 1979 } 1980 1981 /** 1982 * @method Hyphenator~loadPatterns
1983 * @desc 1984 * Checks if the requested file is available in the network. 1985 * Adds a <script>-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language. 1986 * If the given language is not in the {@link Hyphenator~supportedLangs}-Object it returns. 1987 * One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object 1988 * has a same-origin-policy. This makes the Bookmarklet impossible. 1989 * @param {string} lang The language to load the patterns for 1990 * @access private 1991 * @see {@link Hyphenator~basePath} 1992 */ 1993 function loadPatterns(lang, cb) { 1994 var location, xhr, head, script, done = false; 1995 if (supportedLangs.hasOwnProperty(lang) && !Hyphenator.languages[lang]) { 1996 location = basePath + 'patterns/' + supportedLangs[lang].file; 1997 } else { 1998 return; 1999 } 2000 if (isLocal && !isBookmarklet) { 2001 //check if 'location' is available: 2002 xhr = null; 2003 try { 2004 // Mozilla, Opera, Safari and Internet Explorer (ab v7) 2005 xhr = new window.XMLHttpRequest(); 2006 } catch (ignore) { 2007 try { 2008 //IE>=6 2009 xhr = new window.ActiveXObject("Microsoft.XMLHTTP"); 2010 } catch (ignore) { 2011 try { 2012 //IE>=5 2013 xhr = new window.ActiveXObject("Msxml2.XMLHTTP"); 2014 } catch (ignore) { 2015 xhr = null; 2016 } 2017 } 2018 } 2019 2020 if (xhr) { 2021 xhr.open('HEAD', location, true); 2022 xhr.setRequestHeader('Cache-Control', 'no-cache'); 2023 xhr.onreadystatechange = function () { 2024 if (xhr.readyState === 2) { 2025 if (xhr.status >= 400) { 2026 onError(new Error('Could not load\n' + location)); 2027 delete docLanguages[lang]; 2028 return; 2029 } 2030 xhr.abort(); 2031 } 2032 }; 2033 xhr.send(null); 2034 } 2035 } 2036 if (createElem) { 2037 head = window.document.getElementsByTagName('head').item(0); 2038 script = createElem('script', window); 2039 script.src = location; 2040 script.type = 'text/javascript'; 2041 script.charset = 'utf8'; 2042 script.onreadystatechange = function () { 2043 if (!done && (!script.readyState || script.readyState === "loaded" || script.readyState === "complete")) { 2044 done = true; 2045 2046 cb(); 2047 2048 // Handle memory leak in IE 2049 script.onreadystatechange = null; 2050 script.onload = null; 2051 if (head && script.parentNode) { 2052 head.removeChild(script); 2053 } 2054 } 2055 }; 2056 script.onload = script.onreadystatechange; 2057 head.appendChild(script); 2058 } 2059 } 2060 2061 /** 2062 * @method Hyphenator~prepareLanguagesObj 2063 * @desc 2064 * Adds some feature to the language object: 2065 * - cache 2066 * - exceptions 2067 * Converts the patterns to a trie using {@link Hyphenator~convertPatterns} 2068 * @access private 2069 * @param {string} lang The language of the language object 2070 */ 2071 function prepareLanguagesObj(lang) { 2072 var lo = Hyphenator.languages[lang], wrd; 2073 2074 if (!lo.prepared) { 2075 if (enableCache) { 2076 lo.cache = {}; 2077 //Export 2078 //lo['cache'] = lo.cache; 2079 } 2080 if (enableReducedPatternSet) { 2081 lo.redPatSet = {}; 2082 } 2083 if (leftmin > lo.leftmin) { 2084 lo.leftmin = leftmin; 2085 } 2086 if (rightmin >
2086 lo.rightmin) { 2087 lo.rightmin = rightmin; 2088 } 2089 //add exceptions from the pattern file to the local 'exceptions'-obj 2090 if (lo.hasOwnProperty('exceptions')) { 2091 Hyphenator.addExceptions(lang, lo.exceptions); 2092 delete lo.exceptions; 2093 } 2094 //copy global exceptions to the language specific exceptions 2095 if (exceptions.hasOwnProperty('global')) { 2096 if (exceptions.hasOwnProperty(lang)) { 2097 exceptions[lang] += ', ' + exceptions.global; 2098 } else { 2099 exceptions[lang] = exceptions.global; 2100 } 2101 } 2102 //move exceptions from the the local 'exceptions'-obj to the 'language'-object 2103 if (exceptions.hasOwnProperty(lang)) { 2104 lo.exceptions = convertExceptionsToObject(exceptions[lang]); 2105 delete exceptions[lang]; 2106 } else { 2107 lo.exceptions = {}; 2108 } 2109 convertPatternsToArray(lo); 2110 if (String.prototype.normalize) { 2111 wrd = '[\\w' + lo.specialChars + lo.specialChars.normalize("NFD") + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}'; 2112 } else { 2113 wrd = '[\\w' + lo.specialChars + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}'; 2114 } 2115 lo.genRegExp = new RegExp('(' + wrd + ')|(' + url + ')|(' + mail + ')', 'gi'); 2116 lo.prepared = true; 2117 } 2118 } 2119 2120 /**** 2121 * @method Hyphenator~prepare 2122 * @desc 2123 * This funtion prepares the Hyphenator~Object: If RemoteLoading is turned off, it assumes 2124 * that the patternfiles are loaded, all conversions are made and the callback is called. 2125 * If storage is active the object is retrieved there. 2126 * If RemoteLoading is on (default), it loads the pattern files and repeatedly checks Hyphenator.languages. 2127 * If a patternfile is loaded the patterns are stored in storage (if enabled), 2128 * converted to their object style and the lang-object extended. 2129 * Finally the callback is called. 2130 * @access private 2131 */ 2132 function prepare(callback) { 2133 var tmp1; 2134 2135 function languagesLoaded() { 2136 forEachKey(docLanguages, function (l) { 2137 if (Hyphenator.languages.hasOwnProperty(l)) { 2138 delete docLanguages[l]; 2139 if (!!storage) { 2140 storage.setItem(l, window.JSON.stringify(Hyphenator.languages[l])); 2141 } 2142 prepareLanguagesObj(l); 2143 callback(l); 2144 } 2145 }); 2146 } 2147 2148 if (!enableRemoteLoading) { 2149 forEachKey(Hyphenator.languages, function (lang) { 2150 prepareLanguagesObj(lang); 2151 }); 2152 callback('*'); 2153 return; 2154 } 2155 // get all languages that are used and preload the patterns 2156 forEachKey(docLanguages, function (lang) { 2157 if (!!storage && storage.test(lang)) { 2158 Hyphenator.languages[lang] = window.JSON.parse(storage.getItem(lang)); 2159 prepareLanguagesObj(lang); 2160 if (exceptions.hasOwnProperty('global')) { 2161 tmp1 = convertExceptionsToObject(exceptions.global); 2162 forEachKey(tmp1, function (tmp2) { 2163 Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2]; 2164 }); 2165 } 2166 //Replace exceptions since they may have been changed: 2167 if (exceptions.hasOwnProperty(lang)) { 2168 tmp1 = convertExceptionsToObject(exceptions[lang]); 2169 forEachKey(tmp1, function (tmp2) { 2170 Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2]; 2171 }); 2172 delete exceptions[lang]; 2173 } 2174 //Replace genRegExp since it may have been changed: 2175 if (String.prototype.normalize) { 2176 tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + Hyphenator.languages[lang].specialChars.normalize("NFD") + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}'; 2177 } else { 2178 tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}'; 2179 } 2180 Hyphenator.languages[lang].genRegExp = new RegExp('(' + tmp1 + ')|(' + url + ')|(' + mail + ')', 'gi'); 2181 if (enableCache) { 2182 if (!Hyphenator.languages[lang].cache) { 2183 Hyphenator.languages[lang].cache = {}; 2184 } 2185 } 2186 delete docLanguages[lang]; 2187 callback(lang); 2188 } else { 2189 loadPatterns(lang, languagesLoaded); 2190 } 2191 }); 2192 //call languagesLoaded in case language has been loaded manually 2193 //and remoteLoading is on (onload won't fire) 2194 languagesLoaded(); 2195 } 2196 2197 /** 2198 * @method Hyphenator~toggleBox
2199 * @desc 2200 * Creates the toggleBox: a small button to turn off/on hyphenation on a page. 2201 * @see {@link Hyphenator.config} 2202 * @access private 2203 */ 2204 var toggleBox = function () { 2205 var bdy, 2206 myTextNode, 2207 text = (Hyphenator.doHyphenation 2208 ? 'Hy-phen-a-tion' 2209 : 'Hyphenation'), 2210 myBox = contextWindow.document.getElementById('HyphenatorToggleBox'); 2211 if (!!myBox) { 2212 myBox.firstChild.data = text; 2213 } else { 2214 bdy = contextWindow.document.getElementsByTagName('body')[0]; 2215 myBox = createElem('div', contextWindow); 2216 myBox.setAttribute('id', 'HyphenatorToggleBox'); 2217 myBox.setAttribute('class', dontHyphenateClass); 2218 myTextNode = contextWindow.document.createTextNode(text); 2219 myBox.appendChild(myTextNode); 2220 myBox.onclick = Hyphenator.toggleHyphenation; 2221 myBox.style.position = 'absolute'; 2222 myBox.style.top = '0px'; 2223 myBox.style.right = '0px'; 2224 myBox.style.zIndex = '1000'; 2225 myBox.style.margin = '0'; 2226 myBox.style.backgroundColor = '#AAAAAA'; 2227 myBox.style.color = '#FFFFFF'; 2228 myBox.style.font = '6pt Arial'; 2229 myBox.style.letterSpacing = '0.2em'; 2230 myBox.style.padding = '3px'; 2231 myBox.style.cursor = 'pointer'; 2232 myBox.style.WebkitBorderBottomLeftRadius = '4px'; 2233 myBox.style.MozBorderRadiusBottomleft = '4px'; 2234 myBox.style.borderBottomLeftRadius = '4px'; 2235 bdy.appendChild(myBox); 2236 } 2237 }; 2238 2239 /** 2240 * @method Hyphenator~doCharSubst
2241 * @desc 2242 * Replace chars in a word 2243 * 2244 * @param {Object} loCharSubst Map of substitutions ({'ä': 'a', 'ü': 'u', â¦}) 2245 * @param {string} w the word 2246 * @returns string The word with substituted characers 2247 * @access private 2248 */ 2249 function doCharSubst(loCharSubst, w) { 2250 var r = w; 2251 forEachKey(loCharSubst, function (subst) { 2252 r = r.replace(new RegExp(subst, 'g'), loCharSubst[subst]); 2253 }); 2254 return r; 2255 } 2256 2257 /** 2258 * @member {Array} Hyphenator~wwAsMappedCharCodeStore 2259 * @desc 2260 * Array (typed if supported) container for charCodes 2261 * @access private 2262 * @see {@link Hyphenator~hyphenateWord} 2263 */ 2264 var wwAsMappedCharCodeStore = (function () { 2265 if (Object.prototype.hasOwnProperty.call(window, "Int32Array")) { 2266 return new window.Int32Array(64); 2267 } 2268 return []; 2269 }()); 2270 2271 /** 2272 * @member {Array} Hyphenator~wwhpStore 2273 * @desc 2274 * Array (typed if supported) container for hyphenation points 2275 * @access private 2276 * @see {@link Hyphenator~hyphenateWord} 2277 */ 2278 var wwhpStore = (function () { 2279 var r; 2280 if (Object.prototype.hasOwnProperty.call(window, "Uint8Array")) { 2281 r = new window.Uint8Array(64); 2282 } else { 2283 r = []; 2284 } 2285 return r; 2286 }()); 2287 2288 /** 2289 * @method Hyphenator~hyphenateWord 2290 * @desc 2291 * This function is the heart of Hyphenator.js. It returns a hyphenated word. 2292 * 2293 * If there's already a {@link Hyphenator~hypen} in the word, the word is returned as it is. 2294 * If the word is in the exceptions list or in the cache, it is retrieved from it. 2295 * If there's a '-' hyphenate the parts. 2296 * The hyphenated word is returned and (if acivated) cached. 2297 * Both special Events onBeforeWordHyphenation and onAfterWordHyphenation are called for the word. 2298 * @param {Object} lo A language object (containing the patterns) 2299 * @param {string} lang The language of the word 2300 * @param {string} word The word 2301 * @returns string The hyphenated word 2302 * @access private 2303 */ 2304 function hyphenateWord(lo, lang, word) { 2305 var parts, 2306 i = 0, 2307 pattern = "", 2308 ww, 2309 wwlen, 2310 wwhp = wwhpStore, 2311 pstart = 0, 2312 plen, 2313 hp, 2314 hpc, 2315 wordLength = word.length, 2316 hw = '', 2317 charMap = lo.charMap.code2int, 2318 charCode, 2319 mappedCharCode, 2320 row = 0, 2321 link = 0, 2322 value = 0, 2323 values, 2324 indexedTrie = lo.indexedTrie, 2325 valueStore = lo.valueStore.keys, 2326 wwAsMappedCharCode = wwAsMappedCharCodeStore; 2327 word = onBeforeWordHyphenation(word, lang); 2328 if (word === '') { 2329 hw = ''; 2330 } else if (enableCache && lo.cache && lo.cache.hasOwnProperty(word)) { //the word is in the cache 2331 hw = lo.cache[word]; 2332 } else if (word.indexOf(hyphen) !== -1) { 2333 //word already contains shy; -> leave at it is! 2334 hw = word; 2335 } else if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list 2336 hw = lo.exceptions[word].replace(/-/g, hyphen); 2337 } else if (word.indexOf('-') !== -1) { 2338 //word contains '-' -> hyphenate the parts separated with '-' 2339 parts = word.split('-'); 2340 while (i < parts.length) { 2341 parts[i] = hyphenateWord(lo, lang, parts[i]); 2342 i += 1; 2343 } 2344 hw = parts.join('-'); 2345 } else { 2346 ww = word.toLowerCase(); 2347 if (String.prototype.normalize) { 2348 ww = ww.normalize("NFC"); 2349 } 2350 if (lo.hasOwnProperty("charSubstitution")) { 2351 ww = doCharSubst(lo.charSubstitution, ww); 2352 } 2353 if (word.indexOf("'") !== -1) { 2354 ww = ww.replace(/'/g, "â"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns) 2355 } 2356 ww = '_' + ww + '_'; 2357 wwlen = ww.length; 2358 //prepare wwhp and wwAsMappedCharCode 2359 while (pstart < wwlen) { 2360 wwhp[pstart] = 0;
2361 charCode = ww.charCodeAt(pstart); 2362 wwAsMappedCharCode[pstart] = charMap.hasOwnProperty(charCode) 2363 ? charMap[charCode] 2364 : -1; 2365 pstart += 1; 2366 } 2367 //get hyphenation points for all substrings 2368 pstart = 0; 2369 while (pstart < wwlen) { 2370 row = 0; 2371 pattern = ''; 2372 plen = pstart; 2373 while (plen < wwlen) { 2374 mappedCharCode = wwAsMappedCharCode[plen]; 2375 if (mappedCharCode === -1) { 2376 break; 2377 } 2378 if (enableReducedPatternSet) { 2379 pattern += ww.charAt(plen); 2380 } 2381 link = indexedTrie[row + mappedCharCode * 2]; 2382 value = indexedTrie[row + mappedCharCode * 2 + 1]; 2383 if (value > 0) { 2384 hpc = 0; 2385 hp = valueStore[value + hpc]; 2386 while (hp !== 255) { 2387 if (hp > wwhp[pstart + hpc]) { 2388 wwhp[pstart + hpc] = hp; 2389 } 2390 hpc += 1; 2391 hp = valueStore[value + hpc]; 2392 } 2393 if (enableReducedPatternSet) { 2394 if (!lo.redPatSet) { 2395 lo.redPatSet = {}; 2396 } 2397 if (valueStore.subarray) { 2398 values = valueStore.subarray(value, value + hpc); 2399 } else { 2400 values = valueStore.slice(value, value + hpc); 2401 } 2402 lo.redPatSet[pattern] = recreatePattern(pattern, values); 2403 } 2404 } 2405 if (link > 0) { 2406 row = link; 2407 } else { 2408 break; 2409 } 2410 plen += 1; 2411 } 2412 pstart += 1; 2413 } 2414 //create hyphenated word 2415 hp = 0; 2416 while (hp < wordLength) { 2417 if (hp >= lo.leftmin && hp <= (wordLength - lo.rightmin) && (wwhp[hp + 1] % 2) !== 0) { 2418 hw += hyphen + word.charAt(hp); 2419 } else { 2420 hw += word.charAt(hp); 2421 } 2422 hp += 1; 2423 } 2424 } 2425 hw = onAfterWordHyphenation(hw, lang); 2426 if (enableCache) { //put the word in the cache 2427 lo.cache[word] = hw; 2428 } 2429 return hw; 2430 } 2431 2432 /** 2433 * @method Hyphenator~removeHyphenationFromElement 2434 * @desc 2435 * Removes all hyphens from the element. If there are other elements, the function is 2436 * called recursively. 2437 * Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts. 2438 * @param {Object} el The element where to remove hyphenation. 2439 * @access public 2440 */ 2441 function removeHyphenationFromElement(el) { 2442 var h, u, i = 0, n; 2443 switch (hyphen) { 2444 case '|': 2445 h = '\\|'; 2446 break; 2447 case '+': 2448 h = '\\+'; 2449 break; 2450 case '*': 2451 h = '\\*'; 2452 break; 2453 default: 2454 h = hyphen; 2455 } 2456 switch (urlhyphen) { 2457 case '|': 2458 u = '\\|'; 2459 break; 2460 case '+': 2461 u = '\\+'; 2462 break; 2463 case '*': 2464 u = '\\*'; 2465 break; 2466 default: 2467 u = urlhyphen; 2468 } 2469 n = el.childNodes[i]; 2470 while (!!n) { 2471 if (n.nodeType === 3) { 2472 n.data = n.data.replace(new RegExp(h, 'g'), ''); 2473 n.data = n.data.replace(new RegExp(u, 'g'), ''); 2474 } else if (n.nodeType === 1) { 2475 removeHyphenationFromElement(n); 2476 } 2477 i += 1; 2478 n = el.childNodes[i]; 2479 } 2480 } 2481 2482 var copy = (function () { 2483 var makeCopy = function () { 2484 var oncopyHandler = function (e) { 2485 e = e || window.event; 2486 var shadow, 2487 selection, 2488 range, 2489 rangeShadow, 2490 restore, 2491 target = e.target || e.srcElement, 2492 currDoc = target.ownerDocument, 2493 bdy = currDoc.getElementsByTagName('body')[0], 2494 targetWindow = currDoc.defaultView || currDoc.parentWindow; 2495 if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) { 2496 //Safari needs this 2497 return; 2498 } 2499 //create a hidden shadow element 2500 shadow = currDoc.createElement('div'); 2501 //Moving the element out of the screen doesn't work for IE9 (https://connect.microsoft.com/IE/feedback/details/663981/) 2502 //shadow.style.overflow = 'hidden'; 2503 //shadow.style.position = 'absolute'; 2504 //shadow.style.top = '-5000px'; 2505 //shadow.style.height = '1px'; 2506 //doing this instead: 2507 shadow.style.color = window.getComputedStyle 2508 ? targetWindow.getComputedStyle(bdy, null).backgroundColor 2509 : '#FFFFFF'; 2510 shadow.style.fontSize = '0px'; 2511 bdy.appendChild(shadow); 2512 if (!!window.getSelection) { 2513 //FF3, Webkit, IE9 2514 e.stopPropagation(); 2515 selection = targetWindow.getSelection(); 2516 range = selection.getRangeAt(0); 2517 shadow.appendChild(range.cloneContents()); 2518 removeHyphenationFromElement(shadow); 2519 selection.selectAllChildren(shadow); 2520 restore = function () { 2521 shadow.parentNode.removeChild(shadow); 2522 selection.removeAllRanges(); //IE9 needs that 2523 selection.addRange(range); 2524 }; 2525 } else { 2526 // IE<9 2527 e.cancelBubble = true; 2528 selection = targetWindow.document.selection; 2529 range = selection.createRange(); 2530 shadow.innerHTML = range.htmlText; 2531 removeHyphenationFromElement(shadow); 2532 rangeShadow = bdy.createTextRange(); 2533 rangeShadow.moveToElementText(shadow); 2534 rangeShadow.select(); 2535 restore = function () { 2536 shadow.parentNode.removeChild(shadow); 2537 if (range.text !== "") { 2538 range.select(); 2539 } 2540 }; 2541 } 2542 zeroTimeOut(restore); 2543 }, 2544 removeOnCopy = function (el) { 2545 var body = el.ownerDocument.getElementsByTagName('body')[0]; 2546 if (!body) { 2547 return; 2548 } 2549 el = el || body; 2550 if (window.removeEventListener) { 2551 el.removeEventListener("copy", oncopyHandler, true); 2552 } else { 2553 el.detachEvent("oncopy", oncopyHandler); 2554 } 2555 }, 2556 registerOnCopy = function (el) { 2557 var body = el.ownerDocument.getElementsByTagName('body')[0]; 2558 if (!body) { 2559 return; 2560 } 2561 el = el || body; 2562 if (window.addEventListener) { 2563 el.addEventListener("copy", oncopyHandler, true); 2564 } else { 2565 el.attachEvent("oncopy", oncopyHandler); 2566 } 2567 }; 2568 return { 2569 oncopyHandler: oncopyHandler, 2570 removeOnCopy: removeOnCopy, 2571 registerOnCopy: registerOnCopy 2572 }; 2573 }; 2574 2575 return (safeCopy 2576 ? makeCopy() 2577 : false); 2578 }()); 2579 2580 2581 /** 2582 * @method Hyphenator~checkIfAllDone
2583 * @desc 2584 * Checks if all elements in {@link Hyphenator~elements} are hyphenated, unhides them and fires onHyphenationDone() 2585 * @access private 2586 */ 2587 function checkIfAllDone() { 2588 var allDone = true, 2589 i = 0, 2590 doclist = {}; 2591 elements.each(function (ellist) { 2592 var j = 0, 2593 l = ellist.length; 2594 while (j < l) { 2595 allDone = allDone && ellist[j].hyphenated; 2596 if (!doclist.hasOwnProperty(ellist[j].element.baseURI)) { 2597 doclist[ellist[j].element.ownerDocument.location.href] = true; 2598 } 2599 doclist[ellist[j].element.ownerDocument.location.href] = doclist[ellist[j].element.ownerDocument.location.href] && ellist[j].hyphenated; 2600 j += 1; 2601 } 2602 }); 2603 if (allDone) { 2604 if (intermediateState === 'hidden' && unhide === 'progressive') { 2605 elements.each(function (ellist) { 2606 var j = 0, 2607 l = ellist.length, 2608 el; 2609 while (j < l) { 2610 el = ellist[j].element; 2611 el.className = el.className.replace(unhideClassRegExp, ''); 2612 if (el.className === '') { 2613 el.removeAttribute('class'); 2614 } 2615 j += 1; 2616 } 2617 }); 2618 } 2619 while (i < CSSEditors.length) { 2620 CSSEditors[i].clearChanges(); 2621 i += 1; 2622 } 2623 forEachKey(doclist, function (doc) { 2624 onHyphenationDone(doc); 2625 }); 2626 if (!!storage && storage.deferred.length > 0) { 2627 i = 0; 2628 while (i < storage.deferred.length) { 2629 storage.deferred[i].call(); 2630 i += 1; 2631 } 2632 storage.deferred = []; 2633 } 2634 } 2635 } 2636 2637 /** 2638 * @method Hyphenator~controlOrphans 2639 * @desc 2640 * removes orphans depending on the 'orphanControl'-setting: 2641 * orphanControl === 1: do nothing 2642 * orphanControl === 2: prevent last word to be hyphenated 2643 * orphanControl === 3: prevent one word on a last line (inserts a nobreaking space) 2644 * @param {string} part - The sring where orphans have to be removed 2645 * @access private 2646 */ 2647 function controlOrphans(part) { 2648 var h, r; 2649 switch (hyphen) { 2650 case '|': 2651 h = '\\|'; 2652 break; 2653 case '+': 2654 h = '\\+'; 2655 break; 2656 case '*': 2657 h = '\\*'; 2658 break; 2659 default: 2660 h = hyphen; 2661 } 2662 //strip off blank space at the end (omitted closing tags) 2663 part = part.replace(/[\s]*$/, ''); 2664 if (orphanControl >= 2) { 2665 //remove hyphen points from last word 2666 r = part.split(' '); 2667 r[1] = r[1].replace(new RegExp(h, 'g'), ''); 2668 r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), ''); 2669 r = r.join(' '); 2670 } 2671 if (orphanControl === 3) { 2672 //replace spaces by non breaking spaces 2673 r = r.replace(/[\ ]+/g, String.fromCharCode(160)); 2674 } 2675 return r; 2676 } 2677 2678 /** 2679 * @method Hyphenator~hyphenateElement 2680 * @desc 2681 * Takes the content of the given element and - if there's text - replaces the words 2682 * by hyphenated words. If there's another element, the function is called recursively. 2683 * When all words are hyphenated, the visibility of the element is set to 'visible'. 2684 * @param {string} lang - The language-code of the element 2685 * @param {Element} elo - The element to hyphenate {@link Hyphenator~elements~ElementCollection~Element} 2686 * @access private 2687 */ 2688 function hyphenateElement(lang, elo) { 2689 var el = elo.element, 2690 hyphenate, 2691 n, 2692 i, 2693 lo; 2694 if (Hyphenator.languages.hasOwnProperty(lang) && Hyphenator.doHyphenation) { 2695 lo = Hyphenator.languages[lang]; 2696 hyphenate = function (match, word, url, mail) { 2697 var r; 2698 if (!!url || !!mail) {
2699 r = hyphenateURL(match); 2700 } else { 2701 r = hyphenateWord(lo, lang, word); 2702 } 2703 return r; 2704 }; 2705 if (safeCopy && (el.tagName.toLowerCase() !== 'body')) { 2706 copy.registerOnCopy(el); 2707 } 2708 i = 0; 2709 n = el.childNodes[i]; 2710 while (!!n) { 2711 if (n.nodeType === 3 //type 3 = #text 2712 && (/\S/).test(n.data) //not just white space 2713 && n.data.length >= min) { //longer then min 2714 n.data = n.data.replace(lo.genRegExp, hyphenate); 2715 if (orphanControl !== 1) { 2716 n.data = n.data.replace(/[\S]+\ [\S]+[\s]*$/, controlOrphans); 2717 } 2718 } 2719 i += 1; 2720 n = el.childNodes[i]; 2721 } 2722 } 2723 if (intermediateState === 'hidden' && unhide === 'wait') { 2724 el.className = el.className.replace(hideClassRegExp, ''); 2725 if (el.className === '') { 2726 el.removeAttribute('class'); 2727 } 2728 } 2729 if (intermediateState === 'hidden' && unhide === 'progressive') { 2730 el.className = el.className.replace(hideClassRegExp, ' ' + unhideClass); 2731 } 2732 elo.hyphenated = true; 2733 elements.counters[1] += 1; 2734 if (elements.counters[0] <= elements.counters[1]) { 2735 checkIfAllDone(); 2736 } 2737 } 2738 2739 /** 2740 * @method Hyphenator~hyphenateLanguageElements 2741 * @desc 2742 * Calls hyphenateElement() for all elements of the specified language. 2743 * If the language is '*' then all elements are hyphenated. 2744 * This is done with a setTimout 2745 * to prevent a "long running Script"-alert when hyphenating large pages. 2746 * Therefore a tricky bind()-function was necessary. 2747 * @param {string} lang The language of the elements to hyphenate 2748 * @access private 2749 */ 2750 2751 function hyphenateLanguageElements(lang) { 2752 /*function bind(fun, arg1, arg2) { 2753 return function () { 2754 return fun(arg1, arg2); 2755 }; 2756 }*/ 2757 var i = 0, 2758 l; 2759 if (lang === '*') { 2760 elements.each(function (lang, ellist) { 2761 var j = 0, 2762 le = ellist.length; 2763 while (j < le) { 2764 //zeroTimeOut(bind(hyphenateElement, lang, ellist[j])); 2765 hyphenateElement(lang, ellist[j]); 2766 j += 1; 2767 } 2768 }); 2769 } else { 2770 if (elements.list.hasOwnProperty(lang)) { 2771 l = elements.list[lang].length; 2772 while (i < l) { 2773 //zeroTimeOut(bind(hyphenateElement, lang, elements.list[lang][i])); 2774 hyphenateElement(lang, elements.list[lang][i]); 2775 i += 1; 2776 } 2777 } 2778 } 2779 } 2780 2781 /** 2782 * @method Hyphenator~removeHyphenationFromDocument 2783 * @desc 2784 * Does what it says and unregisters the onCopyEvent from the elements 2785 * @access private 2786 */ 2787 function removeHyphenationFromDocument() { 2788 elements.each(function (ellist) { 2789 var i = 0, 2790 l = ellist.length; 2791 while (i < l) { 2792 removeHyphenationFromElement(ellist[i].element); 2793 if (safeCopy) { 2794 copy.removeOnCopy(ellist[i].element); 2795 } 2796 ellist[i].hyphenated = false; 2797 i += 1; 2798 } 2799 }); 2800 } 2801 2802 /** 2803 * @method Hyphenator~createStorage 2804 * @desc 2805 * inits the private var {@link Hyphenator~storage) depending of the setting in {@link Hyphenator~storageType} 2806 * and the supported features of the system. 2807 * @access private 2808 */ 2809 function createStorage() { 2810 var s; 2811 function makeStorage(s) { 2812 var store = s, 2813 prefix = 'Hyphenator_' + Hyphenator.version + '_', 2814 deferred = [], 2815 test = function (name) { 2816 var val = store.getItem(prefix + name); 2817 return !!val; 2818 }, 2819 getItem = function (name) { 2820 return store.getItem(prefix + name); 2821 }, 2822 setItem = function (name, value) { 2823 try { 2824 store.setItem(prefix + name, value); 2825 } catch (e) { 2826 onError(e); 2827 } 2828 }; 2829 return { 2830 deferred: deferred, 2831 test: test, 2832 getItem: getItem, 2833 setItem: setItem 2834 }; 2835 } 2836 try { 2837 if (storageType !== 'none' && 2838 window.JSON !== undefined && 2839 window.localStorage !== undefined && 2840 window.sessionStorage !== undefined && 2841 window.JSON.stringify !== undefined && 2842 window.JSON.parse !== undefined) { 2843 switch (storageType) { 2844 case 'session': 2845 s = window.sessionStorage; 2846 break; 2847 case 'local': 2848 s = window.localStorage; 2849 break; 2850 default: 2851 s = undefined; 2852 break; 2853 } 2854 //check for private mode 2855 s.setItem('storageTest', '1'); 2856 s.removeItem('storageTest'); 2857 } 2858 } catch (ignore) { 2859 //FF throws an error if DOM.storage.enabled is set to false 2860 s = undefined; 2861 } 2862 if (s) { 2863 storage = makeStorage(s); 2864 } else { 2865 storage = undefined; 2866 } 2867 } 2868 2869 /** 2870 * @method Hyphenator~storeConfiguration
2871 * @desc 2872 * Stores the current config-options in DOM-Storage 2873 * @access private 2874 */ 2875 function storeConfiguration() { 2876 if (!storage) { 2877 return; 2878 } 2879 var settings = { 2880 'STORED': true, 2881 'classname': hyphenateClass, 2882 'urlclassname': urlHyphenateClass, 2883 'donthyphenateclassname': dontHyphenateClass, 2884 'minwordlength': min, 2885 'hyphenchar': hyphen, 2886 'urlhyphenchar': urlhyphen, 2887 'togglebox': toggleBox, 2888 'displaytogglebox': displayToggleBox, 2889 'remoteloading': enableRemoteLoading, 2890 'enablecache': enableCache, 2891 'enablereducedpatternset': enableReducedPatternSet, 2892 'onhyphenationdonecallback': onHyphenationDone, 2893 'onerrorhandler': onError, 2894 'onwarninghandler': onWarning, 2895 'intermediatestate': intermediateState, 2896 'selectorfunction': selectorFunction || mySelectorFunction, 2897 'safecopy': safeCopy, 2898 'doframes': doFrames, 2899 'storagetype': storageType, 2900 'orphancontrol': orphanControl, 2901 'dohyphenation': Hyphenator.doHyphenation, 2902 'persistentconfig': persistentConfig, 2903 'defaultlanguage': defaultLanguage, 2904 'useCSS3hyphenation': css3, 2905 'unhide': unhide, 2906 'onbeforewordhyphenation': onBeforeWordHyphenation, 2907 'onafterwordhyphenation': onAfterWordHyphenation 2908 }; 2909 storage.setItem('config', window.JSON.stringify(settings)); 2910 } 2911 2912 /** 2913 * @method Hyphenator~restoreConfiguration 2914 * @desc 2915 * Retrieves config-options from DOM-Storage and does configuration accordingly 2916 * @access private 2917 */ 2918 function restoreConfiguration() { 2919 var settings; 2920 if (storage.test('config')) { 2921 settings = window.JSON.parse(storage.getItem('config')); 2922 Hyphenator.config(settings); 2923 } 2924 } 2925 2926 /**EXPORTED VALUES**/ 2927 2928 /** 2929 * @member {string} Hyphenator.version 2930 * @desc 2931 * String containing the actual version of Hyphenator.js 2932 * [major release].[minor releas].[bugfix release] 2933 * major release: new API, new Features, big changes 2934 * minor release: new languages, improvements 2935 * @access public 2936 */ 2937 var version = '5.2.0(devel)'; 2938 2939 /** 2940 * @member {boolean} Hyphenator.doHyphenation 2941 * @desc 2942 * If doHyphenation is set to false, hyphenateDocument() isn't called. 2943 * All other actions are performed. 2944 * @default true 2945 */ 2946 var doHyphenation = true; 2947 2948 /** 2949 * @typedef {Object} Hyphenator.languages.language 2950 * @property {Number} leftmin - The minimum of chars to remain on the old line 2951 * @property {Number} rightmin - The minimum of chars to go on the new line 2952 * @property {string} specialChars - Non-ASCII chars in the alphabet. 2953 * @property {Object.<number, string>} patterns - the patterns in a compressed format. The key is the length of the patterns in the value string. 2954 * @property {Object.<string, string>} charSubstitution - optional: a hash table with chars that are replaced during hyphenation 2955 * @property {string | Object.<string, string>} exceptions - optional: a csv string containing exceptions 2956 */ 2957 2958 /** 2959 * @member {Object.<string, Hyphenator.languages.language>} Hyphenator.languages 2960 * @desc 2961 * Objects that holds key-value pairs, where key is the language and the value is the 2962 * language-object loaded from (and set by) the pattern file. 2963 * @namespace Hyphenator.languages 2964 * @access public 2965 */ 2966 var languages = {}; 2967 2968 /** 2969 * @method Hyphenator.config 2970 * @desc 2971 * The Hyphenator.config() function that takes an object as an argument. The object contains key-value-pairs 2972 * containig Hyphenator-settings. 2973 * @param {Hyphenator.config} obj 2974 * @access public 2975 * @example 2976 * <script src = "Hyphenator.js" type = "text/javascript"></script> 2977 Â * <script type = "text/javascript"> 2978 Â * Hyphenator.config({'minwordlength':4,'hyphenchar':'|'}); 2979 * Hyphenator.run(); 2980 Â * </script> 2981 */ 2982 function config(obj) { 2983 var assert = function (name, type) { 2984 var r, 2985 t; 2986 t = typeof obj[name]; 2987 if (t === type) { 2988 r = true; 2989 } else { 2990 onError(new Error('Config onError: ' + name + ' must be of type ' + type)); 2991 r = false;
2992 } 2993 return r; 2994 }; 2995 2996 if (obj.hasOwnProperty('storagetype')) { 2997 if (assert('storagetype', 'string')) { 2998 storageType = obj.storagetype; 2999 } 3000 if (!storage) { 3001 createStorage(); 3002 } 3003 } 3004 if (!obj.hasOwnProperty('STORED') && storage && obj.hasOwnProperty('persistentconfig') && obj.persistentconfig === true) { 3005 restoreConfiguration(); 3006 } 3007 3008 forEachKey(obj, function (key) { 3009 switch (key) { 3010 case 'STORED': 3011 break; 3012 case 'classname': 3013 if (assert('classname', 'string')) { 3014 hyphenateClass = obj[key]; 3015 } 3016 break; 3017 case 'urlclassname': 3018 if (assert('urlclassname', 'string')) { 3019 urlHyphenateClass = obj[key]; 3020 } 3021 break; 3022 case 'donthyphenateclassname': 3023 if (assert('donthyphenateclassname', 'string')) { 3024 dontHyphenateClass = obj[key]; 3025 } 3026 break; 3027 case 'minwordlength': 3028 if (assert('minwordlength', 'number')) { 3029 min = obj[key]; 3030 } 3031 break; 3032 case 'hyphenchar': 3033 if (assert('hyphenchar', 'string')) { 3034 if (obj.hyphenchar === '­') { 3035 obj.hyphenchar = String.fromCharCode(173); 3036 } 3037 hyphen = obj[key]; 3038 } 3039 break; 3040 case 'urlhyphenchar': 3041 if (obj.hasOwnProperty('urlhyphenchar')) { 3042 if (assert('urlhyphenchar', 'string')) { 3043 urlhyphen = obj[key]; 3044 } 3045 } 3046 break; 3047 case 'togglebox': 3048 if (assert('togglebox', 'function')) { 3049 toggleBox = obj[key]; 3050 } 3051 break; 3052 case 'displaytogglebox': 3053 if (assert('displaytogglebox', 'boolean')) { 3054 displayToggleBox = obj[key]; 3055 } 3056 break; 3057 case 'remoteloading': 3058 if (assert('remoteloading', 'boolean')) { 3059 enableRemoteLoading = obj[key]; 3060 } 3061 break; 3062 case 'enablecache': 3063 if (assert('enablecache', 'boolean')) { 3064 enableCache = obj[key]; 3065 } 3066 break; 3067 case 'enablereducedpatternset': 3068 if (assert('enablereducedpatternset', 'boolean')) { 3069 enableReducedPatternSet = obj[key]; 3070 } 3071 break; 3072 case 'onhyphenationdonecallback': 3073 if (assert('onhyphenationdonecallback', 'function')) { 3074 onHyphenationDone = obj[key]; 3075 } 3076 break; 3077 case 'onerrorhandler': 3078 if (assert('onerrorhandler', 'function')) { 3079 onError = obj[key]; 3080 } 3081 break; 3082 case 'onwarninghandler': 3083 if (assert('onwarninghandler', 'function')) { 3084 onWarning = obj[key]; 3085 } 3086 break; 3087 case 'intermediatestate': 3088 if (assert('intermediatestate', 'string')) { 3089 intermediateState = obj[key]; 3090 } 3091 break; 3092 case 'selectorfunction': 3093 if (assert('selectorfunction', 'function')) { 3094 selectorFunction = obj[key]; 3095 } 3096 break; 3097 case 'safecopy': 3098 if (assert('safecopy', 'boolean')) { 3099 safeCopy = obj[key]; 3100 } 3101 break; 3102 case 'doframes': 3103 if (assert('doframes', 'boolean')) { 3104 doFrames = obj[key]; 3105 } 3106 break; 3107 case 'storagetype': 3108 if (assert('storagetype', 'string')) { 3109 storageType = obj[key]; 3110 } 3111 break; 3112 case 'orphancontrol': 3113 if (assert('orphancontrol', 'number')) { 3114 orphanControl = obj[key]; 3115 } 3116 break; 3117 case 'dohyphenation': 3118 if (assert('dohyphenation', 'boolean')) { 3119 Hyphenator.doHyphenation = obj[key]; 3120 } 3121 break; 3122 case 'persistentconfig': 3123 if (assert('persistentconfig', 'boolean')) { 3124 persistentConfig = obj[key]; 3125 } 3126 break; 3127 case 'defaultlanguage': 3128 if (assert('defaultlanguage', 'string')) { 3129 defaultLanguage = obj[key]; 3130 } 3131 break; 3132 case 'useCSS3hyphenation': 3133 if (assert('useCSS3hyphenation', 'boolean')) { 3134 css3 = obj[key]; 3135 } 3136 break; 3137 case 'unhide': 3138 if (assert('unhide', 'string')) { 3139 unhide = obj[key]; 3140 } 3141 break; 3142 case 'onbeforewordhyphenation': 3143 if (assert('onbeforewordhyphenation', 'function')) { 3144 onBeforeWordHyphenation = obj[key]; 3145 } 3146 break; 3147 case 'onafterwordhyphenation': 3148 if (assert('onafterwordhyphenation', 'function')) { 3149 onAfterWordHyphenation = obj[key]; 3150 } 3151 break; 3152 case 'leftmin': 3153 if (assert('leftmin', 'number')) { 3154 leftmin = obj[key]; 3155 } 3156 break; 3157 case 'rightmin': 3158 if (assert('rightmin', 'number')) { 3159 rightmin = obj[key]; 3160 } 3161 break; 3162 default: 3163 onError(new Error('Hyphenator.config: property ' + key + ' not known.')); 3164 } 3165 }); 3166 if (storage && persistentConfig) { 3167 storeConfiguration(); 3168 } 3169 } 3170 3171 /** 3172 * @method Hyphenator.run
3173 * @desc 3174 * Bootstrap function that starts all hyphenation processes when called: 3175 * Tries to create storage if required and calls {@link Hyphenator~runWhenLoaded} on 'window' handing over the callback 'process' 3176 * @access public 3177 * @example 3178 * <script src = "Hyphenator.js" type = "text/javascript"></script> 3179 Â * <script type = "text/javascript"> 3180 Â * Â Hyphenator.run(); 3181 Â * </script> 3182 */ 3183 function run() { 3184 /** 3185 *@callback Hyphenator.run~process process - The function is called when the DOM has loaded (or called for each frame) 3186 */ 3187 var process = function () { 3188 try { 3189 if (contextWindow.document.getElementsByTagName('frameset').length > 0) { 3190 return; //we are in a frameset 3191 } 3192 autoSetMainLanguage(undefined); 3193 gatherDocumentInfos(); 3194 if (displayToggleBox) { 3195 toggleBox(); 3196 } 3197 prepare(hyphenateLanguageElements); 3198 } catch (e) { 3199 onError(e); 3200 } 3201 }; 3202 3203 if (!storage) { 3204 createStorage(); 3205 } 3206 runWhenLoaded(window, process); 3207 } 3208 3209 /** 3210 * @method Hyphenator.addExceptions 3211 * @desc 3212 * Adds the exceptions from the string to the appropriate language in the 3213 * {@link Hyphenator~languages}-object 3214 * @param {string} lang The language 3215 * @param {string} words A comma separated string of hyphenated words WITH spaces. 3216 * @access public 3217 * @example <script src = "Hyphenator.js" type = "text/javascript"></script> 3218 Â * <script type = "text/javascript"> 3219 Â * Â Hyphenator.addExceptions('de','ziem-lich, Wach-stube'); 3220 * Hyphenator.run(); 3221 Â * </script> 3222 */ 3223 function addExceptions(lang, words) { 3224 if (lang === '') { 3225 lang = 'global'; 3226 } 3227 if (exceptions.hasOwnProperty(lang)) { 3228 exceptions[lang] += ", " + words; 3229 } else { 3230 exceptions[lang] = words; 3231 } 3232 } 3233 3234 /** 3235 * @method Hyphenator.hyphenate 3236 * @access public 3237 * @desc 3238 * Hyphenates the target. The language patterns must be loaded. 3239 * If the target is a string, the hyphenated string is returned, 3240 * if it's an object, the values are hyphenated directly and undefined (aka nothing) is returned 3241 * @param {string|Object} target the target to be hyphenated 3242 * @param {string} lang the language of the target 3243 * @returns {string|undefined} 3244 * @example <script src = "Hyphenator.js" type = "text/javascript"></script> 3245 * <script src = "patterns/en.js" type = "text/javascript"></script> 3246 Â * <script type = "text/javascript"> 3247 * var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation 3248 * </script> 3249 */ 3250 function hyphenate(target, lang) { 3251 var turnout, n, i, lo; 3252 lo = Hyphenator.languages[lang]; 3253 if (Hyphenator.languages.hasOwnProperty(lang)) { 3254 if (!lo.prepared) { 3255 prepareLanguagesObj(lang); 3256 } 3257 turnout = function (match, word, url, mail) { 3258 var r; 3259 if (!!url || !!mail) { 3260 r = hyphenateURL(match); 3261 } else { 3262 r = hyphenateWord(lo, lang, word); 3263 } 3264 return r; 3265 }; 3266 if (typeof target === 'object' && !(typeof target === 'string' || target.constructor === String)) { 3267 i = 0; 3268 n = target.childNodes[i]; 3269 while (!!n) { 3270 if (n.nodeType === 3 //type 3 = #text 3271 && (/\S/).test(n.data) //not just white space 3272 && n.data.length >= min) { //longer then min 3273 n.data = n.data.replace(lo.genRegExp, turnout); 3274 } else if (n.nodeType === 1) { 3275 if (n.lang !== '') { 3276 Hyphenator.hyphenate(n, n.lang); 3277 } else { 3278 Hyphenator.hyphenate(n, lang); 3279 } 3280 } 3281 i += 1; 3282 n = target.childNodes[i]; 3283 } 3284 } else if (typeof target === 'string' || target.constructor === String) { 3285 return target.replace(lo.genRegExp, turnout); 3286 } 3287 } else { 3288 onError(new Error('Language "' + lang + '" is not loaded.')); 3289 } 3290 } 3291 3292 /** 3293 * @method Hyphenator.getRedPatternSet
3294 * @desc 3295 * Returns the reduced pattern set: an object looking like: {'patk': pat} 3296 * @param {string} lang the language patterns are stored for 3297 * @returns {Object.<string, string>} 3298 * @access public 3299 */ 3300 function getRedPatternSet(lang) { 3301 return Hyphenator.languages[lang].redPatSet; 3302 } 3303 3304 /** 3305 * @method Hyphenator.getConfigFromURI 3306 * @desc 3307 * reads and sets configurations from GET parameters in the URI 3308 * @access public 3309 */ 3310 function getConfigFromURI() { 3311 var loc = null, 3312 re = {}, 3313 jsArray = contextWindow.document.getElementsByTagName('script'), 3314 i = 0, 3315 j = 0, 3316 l = jsArray.length, 3317 s, 3318 gp, 3319 option; 3320 while (i < l) { 3321 if (!!jsArray[i].getAttribute('src')) { 3322 loc = jsArray[i].getAttribute('src'); 3323 } 3324 if (loc && (loc.indexOf('Hyphenator.js?') !== -1)) { 3325 s = loc.indexOf('Hyphenator.js?'); 3326 gp = loc.substring(s + 14).split('&'); 3327 while (j < gp.length) { 3328 option = gp[j].split('='); 3329 if (option[0] !== 'bm') { 3330 if (option[1] === 'true') { 3331 option[1] = true; 3332 } else if (option[1] === 'false') { 3333 option[1] = false; 3334 } else if (isFinite(option[1])) { 3335 option[1] = parseInt(option[1], 10); 3336 } 3337 if (option[0] === 'togglebox' || 3338 option[0] === 'onhyphenationdonecallback' || 3339 option[0] === 'onerrorhandler' || 3340 option[0] === 'selectorfunction' || 3341 option[0] === 'onbeforewordhyphenation' || 3342 option[0] === 'onafterwordhyphenation') { 3343 option[1] = new Function('', option[1]); 3344 } 3345 re[option[0]] = option[1]; 3346 } 3347 j += 1; 3348 } 3349 break; 3350 } 3351 i += 1; 3352 } 3353 return re; 3354 } 3355 3356 /** 3357 * @method Hyphenator.toggleHyphenation 3358 * @desc 3359 * Checks the current state of the ToggleBox and removes or does hyphenation. 3360 * @access public 3361 */ 3362 function toggleHyphenation() { 3363 if (Hyphenator.doHyphenation) { 3364 if (!!css3hyphenateClassHandle) { 3365 css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': none;'); 3366 } 3367 removeHyphenationFromDocument(); 3368 Hyphenator.doHyphenation = false; 3369 storeConfiguration(); 3370 toggleBox(); 3371 } else { 3372 if (!!css3hyphenateClassHandle) { 3373 css3hyphenateClassHandle.setRule('.' + css3hyphenateClass, css3_h9n.property + ': auto;'); 3374 } 3375 Hyphenator.doHyphenation = true; 3376 hyphenateLanguageElements('*'); 3377 storeConfiguration(); 3378 toggleBox(); 3379 } 3380 } 3381 3382 return { 3383 version: version, 3384 doHyphenation: doHyphenation, 3385 languages: languages, 3386 config: config, 3387 run: run, 3388 addExceptions: addExceptions, 3389 hyphenate: hyphenate, 3390 getRedPatternSet: getRedPatternSet, 3391 isBookmarklet: isBookmarklet, 3392 getConfigFromURI: getConfigFromURI, 3393 toggleHyphenation: toggleHyphenation 3394 }; 3395 3396}(window)); 3397 3398//Export properties/methods (for google closure compiler) 3399/**** to be moved to external file 3400Hyphenator['languages'] = Hyphenator.languages; 3401Hyphenator['config'] = Hyphenator.config; 3402Hyphenator['run'] = Hyphenator.run; 3403Hyphenator['addExceptions'] = Hyphenator.addExceptions; 3404Hyphenator['hyphenate'] = Hyphenator.hyphenate; 3405Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet; 3406Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet; 3407Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI; 3408Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation; 3409window['Hyphenator'] = Hyphenator; 3410*/ 3411 3412/* 3413 * call Hyphenator if it is a Bookmarklet 3414 */ 3415if (Hyphenator.isBookmarklet) { 3416 Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', storagetype: 'local', doframes: true, useCSS3hyphenation: true}); 3417 Hyphenator.config(Hyphenator.getConfigFromURI()); 3418 Hyphenator.run(); 3419} 3420 3421jQuery(function() { 3422 Hyphenator.config({ 3423 displaytogglebox : false, 3424 minwordlength : 6, 3425 classname: hyphenator.selector 3426 }); 3427 Hyphenator.run(); 3428}) 3429
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.