From cf3882c7755e9d010281a71a42687f2d42539da5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 27 Jul 2015 09:11:16 +0530 Subject: [PATCH] E-book viewer: Improve performance of hyphenation Also fix a bug in the hyphenation algorithm that could cause the word "undefined" to be mangled during hyphenation. Fixes #1478292 [E-book Viewer display error on word "undefined"](https://bugs.launchpad.net/calibre/+bug/1478292) Both these changes come from updating the Hyphenator library to version 5.1.0 --- resources/viewer/hyphenate/Hyphenator.js | 2769 +++++++++++++--------- resources/viewer/hyphenate/patterns.zip | Bin 615320 -> 628424 bytes 2 files changed, 1639 insertions(+), 1130 deletions(-) diff --git a/resources/viewer/hyphenate/Hyphenator.js b/resources/viewer/hyphenate/Hyphenator.js index 3a969c554d..6923518cba 100644 --- a/resources/viewer/hyphenate/Hyphenator.js +++ b/resources/viewer/hyphenate/Hyphenator.js @@ -1,115 +1,84 @@ -/** @license Hyphenator 4.2.0 - client side hyphenation for webbrowsers - * Copyright (C) 2013 Mathias Nater, Zürich (mathias at mnn dot ch) - * Project and Source hosted on http://code.google.com/p/hyphenator/ + +/** @license Hyphenator 5.1.0 - client side hyphenation for webbrowsers + * Copyright (C) 2015 Mathias Nater, Zürich (mathiasnater at gmail dot com) + * https://github.com/mnater/Hyphenator * - * This JavaScript code is free software: you can redistribute - * it and/or modify it under the terms of the GNU Lesser - * General Public License (GNU LGPL) as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) - * any later version. The code is distributed WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. - * - * As additional permission under GNU GPL version 3 section 7, you - * may distribute non-source (e.g., minimized or compacted) forms of - * that code without the copy of the GNU GPL normally required by - * section 4, provided you include this license notice and a URL - * through which recipients can access the Corresponding Source. - * - * - * Hyphenator.js contains code from Bram Steins hypher.js-Project: - * https://github.com/bramstein/Hypher - * - * Code from this project is marked in the source and belongs - * to the following license: - * - * Copyright (c) 2011, Bram Stein - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * Released under the MIT license + * http://mnater.github.io/Hyphenator/LICENSE.txt */ -/* - * Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/ +/* + * Comments are jsdoc3 formatted. See http://usejsdoc.org + * Use mergeAndPack.html to get rid of the comments and to reduce the file size of this script! */ /* The following comment is for JSLint: */ -/*global window */ /*jslint browser: true */ /** - * @constructor - * @description Provides all functionality to do hyphenation, except the patterns that are loaded - * externally. - * @author Mathias Nater, mathias@mnn.ch - * @version 4.2.0 - * @namespace Holds all methods and properties + * @desc Provides all functionality to do hyphenation, except the patterns that are loaded externally + * @global + * @namespace Hyphenator + * @author Mathias Nater, + * @version 5.1.0 * @example * <script src = "Hyphenator.js" type = "text/javascript"></script> - * <script type = "text/javascript"> - *   Hyphenator.run(); - * </script> + * <script type = "text/javascript"> + * Hyphenator.run(); + * </script> */ var Hyphenator = (function (window) { 'use strict'; - /** - * @name Hyphenator-contextWindow - * @private - * @description - * contextWindow stores the window for the document to be hyphenated. - * If there are frames this will change. - * So use contextWindow instead of window! - */ + /** + * @member Hyphenator~contextWindow + * @access private + * @desc + * contextWindow stores the window for the actual document to be hyphenated. + * If there are frames this will change. + * So use contextWindow instead of window! + */ var contextWindow = window, + /** - * @name Hyphenator-supportedLangs - * @description - * A key-value object that stores supported languages and meta data. - * The key is the bcp47 code of the language and the value - * is an object containing following informations about the language: - * file: filename of the pattern file, - * script: script type of the language (e.g. 'latin' for english), this type is abbreviated by an id, - * prompt: the sentence prompted to the user, if Hyphenator.js doesn't find a language hint. - * @type {Object., Object>} - * @private - * @example - * Check if language lang is supported: + * @member {Object.} Hyphenator~supportedLangs + * @desc + * A generated key-value object that stores supported languages and meta data. + * The key is the {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language and the value + * is an object of type {@link Hyphenator~supportedLangs~supportedLanguage} + * @namespace Hyphenator~supportedLangs + * @access private + * //Check if language lang is supported: * if (supportedLangs.hasOwnProperty(lang)) */ supportedLangs = (function () { + /** + * @typedef {Object} Hyphenator~supportedLangs~supportedLanguage + * @property {string} file - The name of the pattern file + * @property {number} script - The script type of the language (e.g. 'latin' for english), this type is abbreviated by an id + * @property {string} prompt - The sentence prompted to the user, if Hyphenator.js doesn't find a language hint + */ + + /** + * @lends Hyphenator~supportedLangs + */ var r = {}, + /** + * @method Hyphenator~supportedLangs~o + * @desc + * Sets a value of Hyphenator~supportedLangs + * @access protected + * @param {string} code The {@link http://tools.ietf.org/rfc/bcp/bcp47.txt bcp47} code of the language + * @param {string} file The name of the pattern file + * @param {Number} script A shortcut for a specific script: latin:0, cyrillic: 1, arabic: 2, armenian:3, bengali: 4, devangari: 5, greek: 6 + * gujarati: 7, kannada: 8, lao: 9, malayalam: 10, oriya: 11, persian: 12, punjabi: 13, tamil: 14, telugu: 15 + * @param {string} prompt The sentence prompted to the user, if Hyphenator.js doesn't find a language hint + */ o = function (code, file, script, prompt) { r[code] = {'file': file, 'script': script, 'prompt': prompt}; }; - //latin:0, cyrillic: 1, arabic: 2, armenian:3, bengali: 4, devangari: 5, greek: 6 - //gujarati: 7, kannada: 8, lao: 9, malayalam: 10, oriya: 11, persian: 12, punjabi: 13, tamil: 14, telugu: 15 - // - //(language code, file name, script, prompt) o('be', 'be.js', 1, 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:'); o('ca', 'ca.js', 0, ''); o('cs', 'cs.js', 0, 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:'); @@ -149,6 +118,7 @@ var Hyphenator = (function (window) { o('ru', 'ru.js', 1, 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:'); o('sk', 'sk.js', 0, ''); o('sl', 'sl.js', 0, 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:'); + o('sr-cyrl', 'sr-cyrl.js', 1, 'Језик овог сајта није детектован аутоматски. Молим вас наведите језик:'); o('sr-latn', 'sr-latn.js', 0, 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:'); o('sv', 'sv.js', 0, 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:'); o('ta', 'ta.js', 14, ''); @@ -162,15 +132,14 @@ var Hyphenator = (function (window) { /** - * @name Hyphenator-basePath - * @description + * @member {string} Hyphenator~basePath + * @desc * A string storing the basepath from where Hyphenator.js was loaded. - * This is used to load the patternfiles. + * This is used to load the pattern files. * The basepath is determined dynamically by searching all script-tags for Hyphenator.js - * If the path cannot be determined http://hyphenator.googlecode.com/svn/trunk/ is used as fallback. - * @type {string} - * @private - * @see Hyphenator-loadPatterns + * If the path cannot be determined {@link http://mnater.github.io/Hyphenator/} is used as fallback. + * @access private + * @see {@link Hyphenator~loadPatterns} */ basePath = (function () { var s = contextWindow.document.getElementsByTagName('script'), i = 0, p, src, t = s[i], r = ''; @@ -185,15 +154,15 @@ var Hyphenator = (function (window) { i += 1; t = s[i]; } - return !!r ? r : 'http://hyphenator.googlecode.com/svn/trunk/'; + return !!r ? r : '//mnater.github.io/Hyphenator/'; }()), /** - * @name Hyphenator-isLocal - * @private - * @description + * @member {boolean} Hyphenator~isLocal + * @access private + * @desc * isLocal is true, if Hyphenator is loaded from the same domain, as the webpage, but false, if - * it's loaded from an external source (i.e. directly from google.code) + * it's loaded from an external source (i.e. directly from github) */ isLocal = (function () { var re = false; @@ -204,132 +173,133 @@ var Hyphenator = (function (window) { }()), /** - * @name Hyphenator-documentLoaded - * @private - * @description - * documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded + * @member {boolean} Hyphenator~documentLoaded + * @access private + * @desc + * documentLoaded is true, when the DOM has been loaded. This is set by {@link Hyphenator~runWhenLoaded} */ documentLoaded = false, /** - * @name Hyphenator-persistentConfig - * @private - * @description + * @member {boolean} Hyphenator~persistentConfig + * @access private + * @desc * if persistentConfig is set to true (defaults to false), config options and the state of the * toggleBox are stored in DOM-storage (according to the storage-setting). So they haven't to be * set for each page. + * @default false + * @see {@link Hyphenator.config} */ persistentConfig = false, /** - * @name Hyphenator-doFrames - * @private - * @description - * switch to control if frames/iframes should be hyphenated, too + * @member {boolean} Hyphenator~doFrames + * @access private + * @desc + * switch to control if frames/iframes should be hyphenated, too. * defaults to false (frames are a bag of hurt!) + * @default false + * @see {@link Hyphenator.config} */ doFrames = false, /** - * @name Hyphenator-dontHyphenate - * @description + * @member {Object.} Hyphenator~dontHyphenate + * @desc * A key-value object containing all html-tags whose content should not be hyphenated - * @type {Object.} - * @private - * @see Hyphenator-hyphenateElement + * @access private */ - 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}, + 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}, /** - * @name Hyphenator-enableCache - * @description + * @member {boolean} Hyphenator~enableCache + * @desc * A variable to set if caching is enabled or not - * @type boolean * @default true - * @private - * @see Hyphenator.config - * @see hyphenateWord + * @access private + * @see {@link Hyphenator.config} */ enableCache = true, /** - * @name Hyphenator-storageType - * @description + * @member {string} Hyphenator~storageType + * @desc * A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session') - * @type {string} * @default 'local' - * @private - * @see Hyphenator.config + * @access private + * @see {@link Hyphenator.config} */ storageType = 'local', /** - * @name Hyphenator-storage - * @description - * An alias to the storage-Method defined in storageType. - * Set by Hyphenator.run() - * @type {Object|undefined} + * @member {Object|undefined} Hyphenator~storage + * @desc + * An alias to the storage defined in storageType. This is set by {@link Hyphenator~createStorage}. + * Set by {@link Hyphenator.run} * @default null - * @private - * @see Hyphenator.run + * @access private + * @see {@link Hyphenator~createStorage} */ storage, /** - * @name Hyphenator-enableReducedPatternSet - * @description + * @member {boolean} Hyphenator~enableReducedPatternSet + * @desc * A variable to set if storing the used patterns is set - * @type boolean * @default false - * @private - * @see Hyphenator.config - * @see hyphenateWord - * @see Hyphenator.getRedPatternSet + * @access private + * @see {@link Hyphenator.config} + * @see {@link Hyphenator.getRedPatternSet} */ enableReducedPatternSet = false, /** - * @name Hyphenator-enableRemoteLoading - * @description + * @member {boolean} Hyphenator~enableRemoteLoading + * @desc * A variable to set if pattern files should be loaded remotely or not - * @type boolean * @default true - * @private - * @see Hyphenator.config - * @see Hyphenator-loadPatterns + * @access private + * @see {@link Hyphenator.config} */ enableRemoteLoading = true, /** - * @name Hyphenator-displayToggleBox - * @description + * @member {boolean} Hyphenator~displayToggleBox + * @desc * A variable to set if the togglebox should be displayed or not - * @type boolean * @default false - * @private - * @see Hyphenator.config - * @see Hyphenator-toggleBox + * @access private + * @see {@link Hyphenator.config} */ displayToggleBox = false, /** - * @name Hyphenator-onError - * @description + * @method Hyphenator~onError + * @desc * A function that can be called upon an error. - * @see Hyphenator.config - * @type {function(Object)} - * @private + * @see {@link Hyphenator.config} + * @access private */ onError = function (e) { window.alert("Hyphenator.js says:\n\nAn Error occurred:\n" + e.message); }, /** - * @name Hyphenator-createElem - * @description + * @method Hyphenator~onWarning + * @desc + * A function that can be called upon a warning. + * @see {@link Hyphenator.config} + * @access private + */ + onWarning = function (e) { + window.console.log(e.message); + }, + + /** + * @method Hyphenator~createElem + * @desc * A function alias to document.createElementNS or document.createElement - * @type {function(string, Object)} - * @private + * @access private */ createElem = function (tagname, context) { context = context || contextWindow; @@ -343,39 +313,44 @@ var Hyphenator = (function (window) { }, /** - * @name Hyphenator-css3 - * @description + * @member {boolean} Hyphenator~css3 + * @desc * A variable to set if css3 hyphenation should be used - * @type boolean * @default false - * @private - * @see Hyphenator.config + * @access private + * @see {@link Hyphenator.config} */ css3 = false, /** - * @name Hyphenator-css3_hsupport - * @description + * @typedef {Object} Hyphenator~css3_hsupport + * @property {boolean} support - if css3-hyphenation is supported + * @property {string} property - the css property name to access hyphen-settings (e.g. -webkit-hyphens) + * @property {Object.} supportedBrowserLangs - an object caching tested languages + * @property {function} checkLangSupport - a method that checks if the browser supports a requested language + */ + + /** + * @member {Hyphenator~css3_h9n} Hyphenator~css3_h9n + * @desc * A generated object containing information for CSS3-hyphenation support - * { - * support: boolean, - * property: , - * languages: - * } - * @type object + * This is set by {@link Hyphenator~css3_gethsupport} * @default undefined - * @private - * @see Hyphenator-css3_gethsupport + * @access private + * @see {@link Hyphenator~css3_gethsupport} + * @example + * //Check if browser supports a language + * css3_h9n.checkLangSupport(<lang>) */ css3_h9n, /** - * @name Hyphenator-css3_gethsupport - * @description - * This function sets Hyphenator-css3_h9n for the current UA + * @method Hyphenator~css3_gethsupport + * @desc + * This function sets {@link Hyphenator~css3_h9n} for the current UA * @type function - * @private - * @see Hyphenator-css3_h9n + * @access private + * @see Hyphenator~css3_h9n */ css3_gethsupport = function () { var s, @@ -417,11 +392,15 @@ var Hyphenator = (function (window) { f = function (lang) { var shadow, computedHeight, - bdy = window.document.getElementsByTagName('body')[0], + bdy, r = false; - if (supportedLangs.hasOwnProperty(lang)) { + //check if lang has already been tested + if (this.supportedBrowserLangs.hasOwnProperty(lang)) { + r = this.supportedBrowserLangs[lang]; + } else if (supportedLangs.hasOwnProperty(lang)) { //create and append shadow-test-element + bdy = window.document.getElementsByTagName('body')[0]; shadow = createElem('div', window); shadow.id = 'Hyphenator_LanguageChecker'; shadow.style.width = '5em'; @@ -439,6 +418,7 @@ var Hyphenator = (function (window) { //remove shadow element bdy.removeChild(shadow); r = (computedHeight > 12) ? true : false; + this.supportedBrowserLangs[lang] = r; } else { r = false; } @@ -448,19 +428,24 @@ var Hyphenator = (function (window) { }, r = { support: false, + supportedBrowserLangs: {}, property: '', - checkLangSupport: function () {} + checkLangSupport: {} }; if (window.getComputedStyle) { - s = contextWindow.getComputedStyle(contextWindow.document.getElementsByTagName('body')[0], null); + s = window.getComputedStyle(window.document.getElementsByTagName('body')[0], null); } else { //ancient Browsers don't support CSS3 anyway css3_h9n = r; return; } - if (s['-webkit-hyphens'] !== undefined) { + if (s.hyphens !== undefined) { + r.support = true; + r.property = 'hyphens'; + r.checkLangSupport = createLangSupportChecker('hyphens'); + } else if (s['-webkit-hyphens'] !== undefined) { r.support = true; r.property = '-webkit-hyphens'; r.checkLangSupport = createLangSupportChecker('-webkit-hyphens'); @@ -477,126 +462,125 @@ var Hyphenator = (function (window) { }, /** - * @name Hyphenator-hyphenateClass - * @description + * @member {string} Hyphenator~hyphenateClass + * @desc * A string containing the css-class-name for the hyphenate class - * @type {string} * @default 'hyphenate' - * @private + * @access private * @example * <p class = "hyphenate">Text</p> - * @see Hyphenator.config + * @see {@link Hyphenator.config} */ hyphenateClass = 'hyphenate', /** - * @name Hyphenator-classPrefix - * @description + * @member {string} Hyphenator~urlHyphenateClass + * @desc + * A string containing the css-class-name for the urlhyphenate class + * @default 'urlhyphenate' + * @access private + * @example + * <p class = "urlhyphenate">Text</p> + * @see {@link Hyphenator.config} + */ + urlHyphenateClass = 'urlhyphenate', + + /** + * @member {string} Hyphenator~classPrefix + * @desc * A string containing a unique className prefix to be used * whenever Hyphenator sets a CSS-class - * @type {string} - * @private + * @access private */ classPrefix = 'Hyphenator' + Math.round(Math.random() * 1000), /** - * @name Hyphenator-hideClass - * @description + * @member {string} Hyphenator~hideClass + * @desc * The name of the class that hides elements - * @type {string} - * @private + * @access private */ hideClass = classPrefix + 'hide', /** - * @name Hyphenator-hideClassRegExp - * @description + * @member {RegExp} Hyphenator~hideClassRegExp + * @desc * RegExp to remove hideClass from a list of classes - * @type {RegExp} - * @private + * @access private */ hideClassRegExp = new RegExp("\\s?\\b" + hideClass + "\\b", "g"), /** - * @name Hyphenator-hideClass - * @description + * @member {string} Hyphenator~hideClass + * @desc * The name of the class that unhides elements - * @type {string} - * @private + * @access private */ unhideClass = classPrefix + 'unhide', /** - * @name Hyphenator-hideClassRegExp - * @description + * @member {RegExp} Hyphenator~hideClassRegExp + * @desc * RegExp to remove unhideClass from a list of classes - * @type {RegExp} - * @private + * @access private */ unhideClassRegExp = new RegExp("\\s?\\b" + unhideClass + "\\b", "g"), /** - * @name Hyphenator-css3hyphenateClass - * @description + * @member {string} Hyphenator~css3hyphenateClass + * @desc * The name of the class that hyphenates elements with css3 - * @type {string} - * @private + * @access private */ css3hyphenateClass = classPrefix + 'css3hyphenate', /** - * @name Hyphenator-css3hyphenateClass - * @description + * @member {CSSEdit} Hyphenator~css3hyphenateClass + * @desc * The var where CSSEdit class is stored - * @type {Object} - * @private + * @access private */ css3hyphenateClassHandle, /** - * @name Hyphenator-dontHyphenateClass - * @description + * @member {string} Hyphenator~dontHyphenateClass + * @desc * A string containing the css-class-name for elements that should not be hyphenated - * @type {string} * @default 'donthyphenate' - * @private + * @access private * @example * <p class = "donthyphenate">Text</p> - * @see Hyphenator.config + * @see {@link Hyphenator.config} */ dontHyphenateClass = 'donthyphenate', /** - * @name Hyphenator-min - * @description + * @member {number} Hyphenator~min + * @desc * A number wich indicates the minimal length of words to hyphenate. - * @type {number} * @default 6 - * @private - * @see Hyphenator.config + * @access private + * @see {@link Hyphenator.config} */ min = 6, /** - * @name Hyphenator-orphanControl - * @description + * @member {number} Hyphenator~orphanControl + * @desc * Control how the last words of a line are handled: * level 1 (default): last word is hyphenated * level 2: last word is not hyphenated * level 3: last word is not hyphenated and last space is non breaking - * @type {number} * @default 1 - * @private + * @access private */ orphanControl = 1, /** - * @name Hyphenator-isBookmarklet - * @description - * Indicates if Hyphanetor runs as bookmarklet or not. - * @type boolean - * @default false - * @private + * @member {boolean} Hyphenator~isBookmarklet + * @desc + * True if Hyphanetor runs as bookmarklet. + * @access private */ isBookmarklet = (function () { var loc = null, @@ -615,56 +599,146 @@ var Hyphenator = (function (window) { }()), /** - * @name Hyphenator-mainLanguage - * @description - * The general language of the document. In contrast to {@link Hyphenator-defaultLanguage}, + * @member {string|null} Hyphenator~mainLanguage + * @desc + * The general language of the document. In contrast to {@link Hyphenator~defaultLanguage}, * mainLanguage is defined by the client (i.e. by the html or by a prompt). - * @type {string|null} - * @private - * @see Hyphenator-autoSetMainLanguage + * @access private + * @see {@link Hyphenator~autoSetMainLanguage} */ mainLanguage = null, /** - * @name Hyphenator-defaultLanguage - * @description + * @member {string|null} Hyphenator~defaultLanguage + * @desc * The language defined by the developper. This language setting is defined by a config option. * It is overwritten by any html-lang-attribute and only taken in count, when no such attribute can * be found (i.e. just before the prompt). - * @type {string|null} - * @private - * @see Hyphenator-autoSetMainLanguage + * @access private + * @see {@link Hyphenator.config} + * @see {@link Hyphenator~autoSetMainLanguage} */ defaultLanguage = '', - /** - * @name Hyphenator-elements - * @description - * An object holding all elements that have to be hyphenated. This var is filled by - * {@link Hyphenator-gatherDocumentInfos} - * @type {Array} - * @private + * @member {ElementCollection} Hyphenator~elements + * @desc + * A class representing all elements (of type Element) that have to be hyphenated. This var is filled by + * {@link Hyphenator~gatherDocumentInfos} + * @access private */ elements = (function () { + /** + * @constructor Hyphenator~elements~ElementCollection~Element + * @desc represents a DOM Element with additional information + * @access private + */ var Element = function (element) { + /** + * @member {Object} Hyphenator~elements~ElementCollection~Element~element + * @desc A DOM Element + * @access protected + */ this.element = element; + /** + * @member {boolean} Hyphenator~elements~ElementCollection~Element~hyphenated + * @desc Marks if the element has been hyphenated + * @access protected + */ this.hyphenated = false; - this.treated = false; //collected but not hyphenated (dohyphenation is off) + /** + * @member {boolean} Hyphenator~elements~ElementCollection~Element~treated + * @desc Marks if information of the element has been collected but not hyphenated (e.g. dohyphenation is off) + * @access protected + */ + this.treated = false; }, + /** + * @constructor Hyphenator~elements~ElementCollection + * @desc A collection of Elements to be hyphenated + * @access protected + */ ElementCollection = function () { + /** + * @member {number} Hyphenator~elements~ElementCollection~count + * @desc The Number of collected Elements + * @access protected + */ this.count = 0; + /** + * @member {number} Hyphenator~elements~ElementCollection~hyCount + * @desc The Number of hyphenated Elements + * @access protected + */ this.hyCount = 0; + /** + * @member {Object.>} Hyphenator~elements~ElementCollection~list + * @desc The collection of elements, where the key is a language code and the value is an array of elements + * @access protected + */ this.list = {}; }; + /** + * @member {Object} Hyphenator~elements~ElementCollection.prototype + * @augments Hyphenator~elements~ElementCollection + * @access protected + */ ElementCollection.prototype = { + /** + * @method Hyphenator~elements~ElementCollection.prototype~add + * @augments Hyphenator~elements~ElementCollection + * @access protected + * @desc adds a DOM element to the collection + * @param {Object} el - The DOM element + * @param {string} lang - The language of the element + */ add: function (el, lang) { + var elo = new Element(el); if (!this.list.hasOwnProperty(lang)) { this.list[lang] = []; } - this.list[lang].push(new Element(el)); + this.list[lang].push(elo); this.count += 1; + return elo; }, + + /** + * @method Hyphenator~elements~ElementCollection.prototype~remove + * @augments Hyphenator~elements~ElementCollection + * @access protected + * @desc removes a DOM element from the collection + * @param {Object} el - The DOM element + */ + remove: function (el) { + var lang, i, e, l; + for (lang in this.list) { + if (this.list.hasOwnProperty(lang)) { + for (i = 0; i < this.list[lang].length; i += 1) { + if (this.list[lang][i].element === el) { + e = i; + l = lang; + break; + } + } + } + } + this.list[l].splice(e, 1); + this.count -= 1; + this.hyCount -= 1; + }, + /** + * @callback Hyphenator~elements~ElementCollection.prototype~each~callback fn - The callback that is executed for each element + * @param {string} [k] The key (i.e. language) of the collection + * @param {Hyphenator~elements~ElementCollection~Element} element + */ + + /** + * @method Hyphenator~elements~ElementCollection.prototype~each + * @augments Hyphenator~elements~ElementCollection + * @access protected + * @desc takes each element of the collection as an argument of fn + * @param {Hyphenator~elements~ElementCollection.prototype~each~callback} fn - A function that takes an element as an argument + */ each: function (fn) { var k; for (k in this.list) { @@ -683,76 +757,49 @@ var Hyphenator = (function (window) { /** - * @name Hyphenator-exceptions - * @description + * @member {Object.} Hyphenator~exceptions + * @desc * An object containing exceptions as comma separated strings for each language. * When the language-objects are loaded, their exceptions are processed, copied here and then deleted. - * @see Hyphenator-prepareLanguagesObj - * @type {Object} - * @private + * Exceptions can also be set by the user. + * @see {@link Hyphenator~prepareLanguagesObj} + * @access private */ exceptions = {}, /** - * @name Hyphenator-docLanguages - * @description + * @member {Object.} Hyphenator~docLanguages + * @desc * An object holding all languages used in the document. This is filled by - * {@link Hyphenator-gatherDocumentInfos} - * @type {Object} - * @private + * {@link Hyphenator~gatherDocumentInfos} + * @access private */ docLanguages = {}, /** - * @name Hyphenator-state - * @description - * A number that inidcates the current state of the script - * 0: not initialized - * 1: loading patterns - * 2: ready - * 3: hyphenation done - * 4: hyphenation removed - * @type {number} - * @private + * @member {string} Hyphenator~url + * @desc + * A string containing a insane RegularExpression to match URL's + * @access private */ - state = 0, - - /** - * @name Hyphenator-url - * @description - * A string containing a RegularExpression to match URL's - * @type {string} - * @private - */ - 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#!:\\.?\\+=&%@!\\-]*)*', + 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#!:\\.?\\+=&%@!\\-]*)*', // protocoll usr pwd ip or host tld port path /** - * @name Hyphenator-mail - * @description + * @member {string} Hyphenator~mail + * @desc * A string containing a RegularExpression to match mail-adresses - * @type {string} - * @private + * @access private */ mail = '[\\w-\\.]+@[\\w\\.]+', /** - * @name Hyphenator-urlRE - * @description - * A RegularExpressions-Object for url- and mail adress matching - * @type {RegExp} - * @private - */ - urlOrMailRE = new RegExp('(' + url + ')|(' + mail + ')', 'i'), - - /** - * @name Hyphenator-zeroWidthSpace - * @description + * @member {string} Hyphenator~zeroWidthSpace + * @desc * A string that holds a char. * Depending on the browser, this is the zero with space or an empty string. * zeroWidthSpace is used to break URLs - * @type {string} - * @private + * @access private */ zeroWidthSpace = (function () { var zws, ua = window.navigator.userAgent.toLowerCase(); @@ -767,61 +814,104 @@ var Hyphenator = (function (window) { }()), /** - * @name Hyphenator-onBeforeWordHyphenation - * @description - * A method to be called for each word to be hyphenated before it is hyphenated. - * Takes the word as a first parameter and its language as a second parameter. - * Returns a string that will replace the word to be hyphenated. - * @see Hyphenator.config - * @type {function()} - * @private + * @method Hyphenator~onBeforeWordHyphenation + * @desc + * This method is called just before a word is hyphenated. + * It is called with two parameters: the word and its language. + * The method must return a string (aka the word). + * @see {@link Hyphenator.config} + * @access private + * @param {string} word + * @param {string} lang + * @return {string} The word that goes into hyphenation */ onBeforeWordHyphenation = function (word) { return word; }, /** - * @name Hyphenator-onAfterWordHyphenation - * @description - * A method to be called for each word to be hyphenated after it is hyphenated. + * @method Hyphenator~onAfterWordHyphenation + * @desc + * This method is called for each word after it is hyphenated. * Takes the word as a first parameter and its language as a second parameter. * Returns a string that will replace the word that has been hyphenated. - * @see Hyphenator.config - * @type {function()} - * @private + * @see {@link Hyphenator.config} + * @access private + * @param {string} word + * @param {string} lang + * @return {string} The word that goes into hyphenation */ onAfterWordHyphenation = function (word) { return word; }, /** - * @name Hyphenator-onHyphenationDone - * @description - * A method to be called, when the last element has been hyphenated - * @see Hyphenator.config - * @type {function()} - * @private + * @method Hyphenator~onHyphenationDone + * @desc + * A method to be called, when the last element has been hyphenated. + * If there are frames the method is called for each frame. + * Therefore the location.href of the contextWindow calling this method is given as a parameter + * @see {@link Hyphenator.config} + * @param {string} context + * @access private */ - onHyphenationDone = function () {}, + onHyphenationDone = function (context) { + return context; + }, /** - * @name Hyphenator-selectorFunction - * @description + * @name Hyphenator~selectorFunction + * @desc * A function set by the user that has to return a HTMLNodeList or array of Elements to be hyphenated. * By default this is set to false so we can check if a selectorFunction is set… - * @see Hyphenator.config - * @type {function()} - * @private + * @see {@link Hyphenator.config} + * @see {@link Hyphenator~mySelectorFunction} + * @default false + * @type {function|boolean} + * @access private */ selectorFunction = false, /** - * @name Hyphenator-mySelectorFunction - * @description - * A function that has to return a HTMLNodeList or array of Elements to be hyphenated. + * @name Hyphenator~flattenNodeList + * @desc + * Takes a nodeList and returns an array with all elements that are not contained by another element in the nodeList + * By using this function the elements returned by selectElements can be 'flattened'. + * @see {@link Hyphenator~selectElements} + * @param {nodeList} nl + * @return {Array} Array of 'parent'-elements + * @access private + */ + flattenNodeList = function (nl) { + var parentElements = [], + i = 0, + j = 0, + isParent = true; + + parentElements.push(nl[0]); //add the first item, since this is always an parent + + for (i = 1; i < nl.length; i += 1) { //cycle through nodeList + for (j = 0; j < parentElements.length; j += 1) { //cycle through parentElements + if (parentElements[j].contains(nl[i])) { + isParent = false; + break; + } + } + if (isParent) { + parentElements.push(nl[i]); + } + isParent = true; + } + + return parentElements; + }, + + /** + * @method Hyphenator~mySelectorFunction + * @desc + * A function that returns a HTMLNodeList or array of Elements to be hyphenated. * By default it uses the classname ('hyphenate') to select the elements. - * @type {function()} - * @private + * @access private */ mySelectorFunction = function (hyphenateClass) { var tmp, el = [], i, l; @@ -842,84 +932,88 @@ var Hyphenator = (function (window) { }, /** - * @name Hyphenator-selectElements - * @description - * A function that has to return a HTMLNodeList or array of Elements to be hyphenated. - * It uses either selectorFunction set by the user (and adds a unique class to each element) + * @method Hyphenator~selectElements + * @desc + * A function that uses either selectorFunction set by the user * or the default mySelectorFunction. - * @type {function()} - * @private + * @access private */ selectElements = function () { - var elements; + var elems; if (selectorFunction) { - elements = selectorFunction(); + elems = selectorFunction(); } else { - elements = mySelectorFunction(hyphenateClass); + elems = mySelectorFunction(hyphenateClass); } - - return elements; + if (elems.length !== 0) { + elems = flattenNodeList(elems); + } + return elems; }, /** - * @name Hyphenator-intermediateState - * @description - * The value of style.visibility of the text while it is hyphenated. - * @see Hyphenator.config - * @type {string} - * @private + * @member {string} Hyphenator~intermediateState + * @desc + * The visibility of elements while they are hyphenated: + * 'visible': unhyphenated text is visible and then redrawn when hyphenated. + * 'hidden': unhyphenated text is made invisible as soon as possible and made visible after hyphenation. + * @default 'hidden' + * @see {@link Hyphenator.config} + * @access private */ intermediateState = 'hidden', /** - * @name Hyphenator-unhide - * @description + * @member {string} Hyphenator~unhide + * @desc * How hidden elements unhide: either simultaneous (default: 'wait') or progressively. * 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw) * With 'progressive' Hyphenator.js unhides elements as soon as they are hyphenated. - * @see Hyphenator.config - * @type {string} - * @private + * @see {@link Hyphenator.config} + * @access private */ unhide = 'wait', /** - * @name Hyphenator-CSSEditors - * @description A container array that holds CSSEdit classes + * @member {Array.} Hyphenator~CSSEditors + * @desc A container array that holds CSSEdit classes * For each window object one CSSEdit class is inserted - * @see Hyphenator-CSSEdit - * @type {array} - * @private + * @access private */ CSSEditors = [], /** - * @name Hyphenator-CSSEditors - * @description A custom class with two public methods: setRule() and clearChanges() - * Rather sets style for CSS-classes then for single elements - * This is used to hide/unhide elements when they are hyphenated. - * @see Hyphenator-gatherDocumentInfos - * @type {function ()} - * @private + * @constructor Hyphenator~CSSEdit + * @desc + * This class handles access and editing of StyleSheets. + * Thanks to this styles (e.g. hiding and unhiding elements upon hyphenation) + * can be changed in one place instead for each element. + * @access private */ CSSEdit = function (w) { w = w || window; var doc = w.document, - //find/create an accessible StyleSheet + /** + * @member {Object} Hyphenator~CSSEdit~sheet + * @desc + * A StyleSheet, where Hyphenator can write to. + * If no StyleSheet can be found, lets create one. + * @access private + */ sheet = (function () { var i, l = doc.styleSheets.length, - sheet, + s, element, r = false; for (i = 0; i < l; i += 1) { - sheet = doc.styleSheets[i]; + s = doc.styleSheets[i]; try { - if (!!sheet.cssRules) { - r = sheet; + if (!!s.cssRules) { + r = s; break; } - } catch (e) {} + } catch (ignore) {} } if (r === false) { element = doc.createElement('style'); @@ -929,22 +1023,47 @@ var Hyphenator = (function (window) { } return r; }()), + + /** + * @typedef {Object} Hyphenator~CSSEdit~changes + * @property {Object} sheet - The StyleSheet where the change was made + * @property {number} index - The index of the changed rule + */ + + /** + * @member {Array.} Hyphenator~CSSEdit~changes + * @desc + * Sets a CSS rule for a specified selector + * @access private + */ changes = [], + + /** + * @typedef Hyphenator~CSSEdit~rule + * @property {number} index - The index of the rule + * @property {Object} rule - The style rule + */ + /** + * @method Hyphenator~CSSEdit~findRule + * @desc + * Searches the StyleSheets for a given selector and returns an object containing the rule. + * If nothing can be found, false is returned. + * @param {string} sel + * @return {Hyphenator~CSSEdit~rule|false} + * @access private + */ findRule = function (sel) { - var sheet, rule, sheets = window.document.styleSheets, rules, i, j, r = false; + var s, rule, sheets = w.document.styleSheets, rules, i, j, r = false; for (i = 0; i < sheets.length; i += 1) { - sheet = sheets[i]; + s = sheets[i]; try { //FF has issues here with external CSS (s.o.p) - if (!!sheet.cssRules) { - rules = sheet.cssRules; - } else if (!!sheet.rules) { + if (!!s.cssRules) { + rules = s.cssRules; + } else if (!!s.rules) { // IE < 9 - rules = sheet.rules; + rules = s.rules; } - } catch (e) { - //do nothing - //console.log(e); - } + } catch (ignore) {} if (!!rules && !!rules.length) { for (j = 0; j < rules.length; j += 1) { rule = rules[j]; @@ -959,6 +1078,15 @@ var Hyphenator = (function (window) { } return r; }, + /** + * @method Hyphenator~CSSEdit~addRule + * @desc + * Adds a rule to the {@link Hyphenator~CSSEdit~sheet} + * @param {string} sel - The selector to be added + * @param {string} rulesStr - The rules for the specified selector + * @return {number} index of the new rule + * @access private + */ addRule = function (sel, rulesStr) { var i, r; if (!!sheet.insertRule) { @@ -980,6 +1108,14 @@ var Hyphenator = (function (window) { } return r; }, + /** + * @method Hyphenator~CSSEdit~removeRule + * @desc + * Removes a rule with the specified index from the specified sheet + * @param {Object} sheet - The style sheet + * @param {number} index - the index of the rule + * @access private + */ removeRule = function (sheet, index) { if (sheet.deleteRule) { sheet.deleteRule(index); @@ -990,6 +1126,14 @@ var Hyphenator = (function (window) { }; return { + /** + * @method Hyphenator~CSSEdit.setRule + * @desc + * Sets a CSS rule for a specified selector + * @access public + * @param {string} sel - Selector + * @param {string} rulesString - CSS-Rules + */ setRule: function (sel, rulesString) { var i, existingRule, cssText; existingRule = findRule(sel); @@ -1000,26 +1144,28 @@ var Hyphenator = (function (window) { // IE < 9 cssText = existingRule.rule.style.cssText.toLowerCase(); } - if (cssText === '.' + hyphenateClass + ' { visibility: hidden; }') { - //browsers w/o IE < 9 and no additional style defs: - //add to [changes] for later removal - changes.push({sheet: existingRule.rule.parentStyleSheet, index: existingRule.index}); - } else if (cssText.indexOf('visibility: hidden') !== -1) { - // IE < 9 or additional style defs: - // add new rule + if (cssText !== sel + ' { ' + rulesString + ' }') { + //cssText of the found rule is not uniquely selector + rulesString, + if (cssText.indexOf(rulesString) !== -1) { + //maybe there are other rules or IE < 9 + //clear existing def + existingRule.rule.style.visibility = ''; + } + //add rule and register for later removal i = addRule(sel, rulesString); - //add to [changes] for later removal changes.push({sheet: sheet, index: i}); - // clear existing def - existingRule.rule.style.visibility = ''; - } else { - addRule(sel, rulesString); } } else { i = addRule(sel, rulesString); changes.push({sheet: sheet, index: i}); } }, + /** + * @method Hyphenator~CSSEdit.clearChanges + * @desc + * Removes all changes Hyphenator has made from the StyleSheets + * @access public + */ clearChanges: function () { var change = changes.pop(); while (!!change) { @@ -1031,167 +1177,217 @@ var Hyphenator = (function (window) { }, /** - * @name Hyphenator-hyphen - * @description + * @member {string} Hyphenator~hyphen + * @desc * A string containing the character for in-word-hyphenation - * @type {string} * @default the soft hyphen - * @private - * @see Hyphenator.config + * @access private + * @see {@link Hyphenator.config} */ hyphen = String.fromCharCode(173), /** - * @name Hyphenator-urlhyphen - * @description + * @member {string} Hyphenator~urlhyphen + * @desc * A string containing the character for url/mail-hyphenation - * @type {string} * @default the zero width space - * @private - * @see Hyphenator.config - * @see Hyphenator-zeroWidthSpace + * @access private + * @see {@link Hyphenator.config} + * @see {@link Hyphenator~zeroWidthSpace} */ urlhyphen = zeroWidthSpace, /** - * @name Hyphenator-safeCopy - * @description + * @method Hyphenator~hyphenateURL + * @desc + * Puts {@link Hyphenator~urlhyphen} (default: zero width space) after each no-alphanumeric char that my be in a URL. + * @param {string} url to hyphenate + * @returns string the hyphenated URL + * @access public + */ + hyphenateURL = function (url) { + var tmp = url.replace(/([:\/\.\?#&\-_,;!@]+)/gi, '$&' + urlhyphen), + parts = tmp.split(urlhyphen), + i; + for (i = 0; i < parts.length; i += 1) { + if (parts[i].length > (2 * min)) { + parts[i] = parts[i].replace(/(\w{3})(\w)/gi, "$1" + urlhyphen + "$2"); + } + } + if (parts[parts.length - 1] === "") { + parts.pop(); + } + return parts.join(urlhyphen); + }, + + /** + * @member {boolean} Hyphenator~safeCopy + * @desc * Defines wether work-around for copy issues is active or not - * Not supported by Opera (no onCopy handler) - * @type boolean * @default true - * @private - * @see Hyphenator.config - * @see Hyphenator-registerOnCopy + * @access private + * @see {@link Hyphenator.config} + * @see {@link Hyphenator~registerOnCopy} */ safeCopy = true, - /* - * runOnContentLoaded is based od jQuery.bindReady() - * see - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ /** - * @name Hyphenator-runOnContentLoaded - * @description - * A crossbrowser solution for the DOMContentLoaded-Event based on jQuery - * 0) { + //var efn = timeouts.shift(); + //efn(); + timeouts.shift()(); + } + } + }; + window.addEventListener("message", handleMessage, true); + return setZeroTimeOut; + }()); + } + return function (fn) { + window.setTimeout(fn, 0); + }; + }()), + + /** + * @member {Object} Hyphenator~hyphRunFor + * @desc + * stores location.href for documents where run() has been executed + * to warn when Hyphenator.run() executed multiple times + * @access private + * @see {@link Hyphenator~runWhenLoaded} + */ + hyphRunFor = {}, + + /** + * @method Hyphenator~runWhenLoaded + * @desc + * A crossbrowser solution for the DOMContentLoaded-Event based on + * jQuery * I added some functionality: e.g. support for frames and iframes… * @param {Object} w the window-object - * @param {function()} f the function to call onDOMContentLoaded - * @private + * @param {function()} f the function to call when the document is ready + * @access private */ - runOnContentLoaded = function (w, f) { - var - toplevel, hyphRunForThis = {}, + runWhenLoaded = function (w, f) { + var toplevel, add = window.document.addEventListener ? 'addEventListener' : 'attachEvent', rem = window.document.addEventListener ? 'removeEventListener' : 'detachEvent', pre = window.document.addEventListener ? '' : 'on', init = function (context) { - contextWindow = context || window; - if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || !!contextWindow.frameElement)) { - documentLoaded = true; - f(); - hyphRunForThis[contextWindow.location.href] = true; + if (hyphRunFor[context.location.href]) { + onWarning(new Error("Warning: multiple execution of Hyphenator.run() – This may slow down the script!")); } + contextWindow = context || window; + f(); + hyphRunFor[contextWindow.location.href] = true; }, doScrollCheck = function () { try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ - contextWindow.document.documentElement.doScroll("left"); + w.document.documentElement.doScroll("left"); } catch (error) { window.setTimeout(doScrollCheck, 1); return; } - - // and execute any waiting functions - init(window); - }, - - doOnLoad = function () { - var i, haveAccess, fl = window.frames.length; - if (doFrames && fl > 0) { - for (i = 0; i < fl; i += 1) { - haveAccess = undefined; - //try catch isn't enough for webkit - try { - //opera throws only on document.toString-access - haveAccess = window.frames[i].document.toString(); - } catch (e) { - haveAccess = undefined; - } - if (!!haveAccess) { - if (window.frames[i].location.href !== 'about:blank') { - init(window.frames[i]); - } - } - } - contextWindow = window; - f(); - hyphRunForThis[window.location.href] = true; - } else { - init(window); + //maybe modern IE fired DOMContentLoaded + if (!hyphRunFor[w.location.href]) { + documentLoaded = true; + init(w); } }, - // Cleanup functions for the document ready method - DOMContentLoaded = function (e) { - if (e.type === 'readystatechange' && contextWindow.document.readyState !== 'complete') { + doOnEvent = function (e) { + var i, fl, haveAccess; + if (!!e && e.type === 'readystatechange' && w.document.readyState !== 'interactive' && w.document.readyState !== 'complete') { return; } - contextWindow.document[rem](pre + e.type, DOMContentLoaded, false); - if (!doFrames && window.frames.length === 0) { - init(window); - } /* else { - //we are in a frameset, so do nothing but wait for onload to fire - - }*/ + + //DOM is ready/interactive, but frames may not be loaded yet! + //cleanup events + w.document[rem](pre + 'DOMContentLoaded', doOnEvent, false); + w.document[rem](pre + 'readystatechange', doOnEvent, false); + + //check frames + fl = w.frames.length; + if (fl === 0 || !doFrames) { + //there are no frames! + //cleanup events + w[rem](pre + 'load', doOnEvent, false); + documentLoaded = true; + init(w); + } else if (doFrames && fl > 0) { + //we have frames, so wait for onload and then initiate runWhenLoaded recursevly for each frame: + if (!!e && e.type === 'load') { + //cleanup events + w[rem](pre + 'load', doOnEvent, false); + for (i = 0; i < fl; i += 1) { + haveAccess = undefined; + //try catch isn't enough for webkit + try { + //opera throws only on document.toString-access + haveAccess = w.frames[i].document.toString(); + } catch (err) { + haveAccess = undefined; + } + if (!!haveAccess) { + runWhenLoaded(w.frames[i], f); + } + } + init(w); + } + } }; - - if (documentLoaded && !hyphRunForThis[w.location.href]) { - f(); - hyphRunForThis[w.location.href] = true; - return; - } - - if (contextWindow.document.readyState === "complete" || contextWindow.document.readyState === "interactive") { - //Running Hyphenator.js if it has been loaded later - //Thanks to davenewtron http://code.google.com/p/hyphenator/issues/detail?id=158#c10 - window.setTimeout(doOnLoad, 1); + if (documentLoaded || w.document.readyState === 'complete') { + //Hyphenator has run already (documentLoaded is true) or + //it has been loaded after onLoad + documentLoaded = true; + doOnEvent({type: 'load'}); } else { - //registering events - contextWindow.document[add](pre + "DOMContentLoaded", DOMContentLoaded, false); - contextWindow.document[add](pre + 'readystatechange', DOMContentLoaded, false); - window[add](pre + 'load', doOnLoad, false); + //register events + w.document[add](pre + 'DOMContentLoaded', doOnEvent, false); + w.document[add](pre + 'readystatechange', doOnEvent, false); + w[add](pre + 'load', doOnEvent, false); toplevel = false; try { toplevel = !window.frameElement; - } catch (e) {} - if (contextWindow.document.documentElement.doScroll && toplevel) { - doScrollCheck(); + } catch (ignore) {} + if (toplevel && w.document.documentElement.doScroll) { + doScrollCheck(); //calls init() } } }, /** - * @name Hyphenator-getLang - * @description - * Gets the language of an element. If no language is set, it may use the {@link Hyphenator-mainLanguage}. + * @method Hyphenator~getLang + * @desc + * Gets the language of an element. If no language is set, it may use the {@link Hyphenator~mainLanguage}. * @param {Object} el The first parameter is an DOM-Element-Object - * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator-mainLanguage} + * @param {boolean} fallback The second parameter is a boolean to tell if the function should return the {@link Hyphenator~mainLanguage} * if there's no language found for the element. - * @private + * @return {string} The language of the element + * @access private */ getLang = function (el, fallback) { try { @@ -1200,13 +1396,13 @@ var Hyphenator = (function (window) { el.tagName.toLowerCase() !== 'html' ? getLang(el.parentNode, fallback) : fallback ? mainLanguage : null; - } catch (e) {} + } catch (ignore) {} }, /** - * @name Hyphenator-autoSetMainLanguage - * @description - * Retrieves the language of the document from the DOM. + * @method Hyphenator~autoSetMainLanguage + * @desc + * Retrieves the language of the document from the DOM and sets the lang attribute of the html-tag. * The function looks in the following places: *