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