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