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