mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
2389 lines
71 KiB
JavaScript
2389 lines
71 KiB
JavaScript
/** @license Hyphenator 4.0.0 - client side hyphenation for webbrowsers
|
||
* Copyright (C) 2011 Mathias Nater, Zürich (mathias at mnn dot ch)
|
||
* Project and Source hosted on http://code.google.com/p/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.
|
||
*
|
||
*/
|
||
|
||
/*
|
||
* Comments are jsdoctoolkit formatted. See http://code.google.com/p/jsdoc-toolkit/
|
||
*/
|
||
|
||
/* The following comment is for JSLint: */
|
||
/*global window, ActiveXObject, unescape */
|
||
/*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 */
|
||
|
||
|
||
/**
|
||
* @constructor
|
||
* @description Provides all functionality to do hyphenation, except the patterns that are loaded
|
||
* externally.
|
||
* @author Mathias Nater, <a href = "mailto:mathias@mnn.ch">mathias@mnn.ch</a>
|
||
* @version X.Y.Z
|
||
* @namespace Holds all methods and properties
|
||
* @example
|
||
* <script src = "Hyphenator.js" type = "text/javascript"></script>
|
||
* <script type = "text/javascript">
|
||
* Hyphenator.run();
|
||
* </script>
|
||
*/
|
||
var Hyphenator = (function (window) {
|
||
|
||
var
|
||
/**
|
||
* @name Hyphenator-supportedLang
|
||
* @description
|
||
* A key-value object that stores supported languages.
|
||
* The key is the bcp47 code of the language and the value
|
||
* is the (abbreviated) filename of the pattern file.
|
||
* @type {Object.<string, string>}
|
||
* @private
|
||
* @example
|
||
* Check if language lang is supported:
|
||
* if (supportedLang.hasOwnProperty(lang))
|
||
*/
|
||
supportedLang = {
|
||
'be': 'be.js',
|
||
'ca': 'ca.js',
|
||
'cs': 'cs.js',
|
||
'da': 'da.js',
|
||
'bn': 'bn.js',
|
||
'de': 'de.js',
|
||
'el': 'el-monoton.js',
|
||
'el-monoton': 'el-monoton.js',
|
||
'el-polyton': 'el-polyton.js',
|
||
'en': 'en-us.js',
|
||
'en-gb': 'en-gb.js',
|
||
'en-us': 'en-us.js',
|
||
'es': 'es.js',
|
||
'fi': 'fi.js',
|
||
'fr': 'fr.js',
|
||
'grc': 'grc.js',
|
||
'gu': 'gu.js',
|
||
'hi': 'hi.js',
|
||
'hu': 'hu.js',
|
||
'hy': 'hy.js',
|
||
'it': 'it.js',
|
||
'kn': 'kn.js',
|
||
'la': 'la.js',
|
||
'lt': 'lt.js',
|
||
'lv': 'lv.js',
|
||
'ml': 'ml.js',
|
||
'nb': 'nb-no.js',
|
||
'no': 'nb-no.js',
|
||
'nb-no': 'nb-no.js',
|
||
'nl': 'nl.js',
|
||
'or': 'or.js',
|
||
'pa': 'pa.js',
|
||
'pl': 'pl.js',
|
||
'pt': 'pt.js',
|
||
'ru': 'ru.js',
|
||
'sk': 'sk.js',
|
||
'sl': 'sl.js',
|
||
'sv': 'sv.js',
|
||
'ta': 'ta.js',
|
||
'te': 'te.js',
|
||
'tr': 'tr.js',
|
||
'uk': 'uk.js'
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-languageHint
|
||
* @description
|
||
* An automatically generated string to be displayed in a prompt if the language can't be guessed.
|
||
* The string is generated using the supportedLang-object.
|
||
* @see Hyphenator-supportedLang
|
||
* @type {string}
|
||
* @private
|
||
* @see Hyphenator-autoSetMainLanguage
|
||
*/
|
||
|
||
languageHint = (function () {
|
||
var k, r = '';
|
||
for (k in supportedLang) {
|
||
if (supportedLang.hasOwnProperty(k)) {
|
||
r += k + ', ';
|
||
}
|
||
}
|
||
r = r.substring(0, r.length - 2);
|
||
return r;
|
||
}()),
|
||
|
||
/**
|
||
* @name Hyphenator-prompterStrings
|
||
* @description
|
||
* A key-value object holding the strings to be displayed if the language can't be guessed
|
||
* If you add hyphenation patterns change this string.
|
||
* @type {Object.<string,string>}
|
||
* @private
|
||
* @see Hyphenator-autoSetMainLanguage
|
||
*/
|
||
prompterStrings = {
|
||
'be': 'Мова гэтага сайта не можа быць вызначаны аўтаматычна. Калі ласка пакажыце мову:',
|
||
'cs': 'Jazyk této internetové stránky nebyl automaticky rozpoznán. Určete prosím její jazyk:',
|
||
'da': 'Denne websides sprog kunne ikke bestemmes. Angiv venligst sprog:',
|
||
'de': 'Die Sprache dieser Webseite konnte nicht automatisch bestimmt werden. Bitte Sprache angeben:',
|
||
'en': 'The language of this website could not be determined automatically. Please indicate the main language:',
|
||
'es': 'El idioma del sitio no pudo determinarse autom%E1ticamente. Por favor, indique el idioma principal:',
|
||
'fi': 'Sivun kielt%E4 ei tunnistettu automaattisesti. M%E4%E4rit%E4 sivun p%E4%E4kieli:',
|
||
'fr': 'La langue de ce site n%u2019a pas pu %EAtre d%E9termin%E9e automatiquement. Veuillez indiquer une langue, s.v.p.%A0:',
|
||
'hu': 'A weboldal nyelvét nem sikerült automatikusan megállapítani. Kérem adja meg a nyelvet:',
|
||
'hy': 'Չհաջողվեց հայտնաբերել այս կայքի լեզուն։ Խնդրում ենք նշեք հիմնական լեզուն՝',
|
||
'it': 'Lingua del sito sconosciuta. Indicare una lingua, per favore:',
|
||
'kn': 'ಜಾಲ ತಾಣದ ಭಾಷೆಯನ್ನು ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ದಯವಿಟ್ಟು ಮುಖ್ಯ ಭಾಷೆಯನ್ನು ಸೂಚಿಸಿ:',
|
||
'lt': 'Nepavyko automatiškai nustatyti šios svetainės kalbos. Prašome įvesti kalbą:',
|
||
'lv': 'Šīs lapas valodu nevarēja noteikt automātiski. Lūdzu norādiet pamata valodu:',
|
||
'ml': 'ഈ വെ%u0D2C%u0D4D%u200Cസൈറ്റിന്റെ ഭാഷ കണ്ടുപിടിയ്ക്കാ%u0D28%u0D4D%u200D കഴിഞ്ഞില്ല. ഭാഷ ഏതാണെന്നു തിരഞ്ഞെടുക്കുക:',
|
||
'nl': 'De taal van deze website kan niet automatisch worden bepaald. Geef de hoofdtaal op:',
|
||
'no': 'Nettstedets språk kunne ikke finnes automatisk. Vennligst oppgi språk:',
|
||
'pt': 'A língua deste site não pôde ser determinada automaticamente. Por favor indique a língua principal:',
|
||
'ru': 'Язык этого сайта не может быть определен автоматически. Пожалуйста укажите язык:',
|
||
'sl': 'Jezika te spletne strani ni bilo mogoče samodejno določiti. Prosim navedite jezik:',
|
||
'sv': 'Spr%E5ket p%E5 den h%E4r webbplatsen kunde inte avg%F6ras automatiskt. V%E4nligen ange:',
|
||
'tr': 'Bu web sitesinin dili otomatik olarak tespit edilememiştir. Lütfen dökümanın dilini seçiniz%A0:',
|
||
'uk': 'Мова цього веб-сайту не може бути визначена автоматично. Будь ласка, вкажіть головну мову:'
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-basePath
|
||
* @description
|
||
* A string storing the basepath from where Hyphenator.js was loaded.
|
||
* This is used to load the patternfiles.
|
||
* 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
|
||
*/
|
||
basePath = (function () {
|
||
var s = document.getElementsByTagName('script'), i = 0, p, src, t;
|
||
while (!!(t = s[i++])) {
|
||
if (!t.src) {
|
||
continue;
|
||
}
|
||
src = t.src;
|
||
p = src.indexOf('Hyphenator.js');
|
||
if (p !== -1) {
|
||
return src.substring(0, p);
|
||
}
|
||
}
|
||
return 'http://hyphenator.googlecode.com/svn/trunk/';
|
||
}()),
|
||
|
||
/**
|
||
* @name Hyphenator-isLocal
|
||
* @description
|
||
* 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)
|
||
*/
|
||
isLocal = (function () {
|
||
var re = false;
|
||
if (window.location.href.indexOf(basePath) !== -1) {
|
||
re = true;
|
||
}
|
||
return re;
|
||
}()),
|
||
|
||
/**
|
||
* @name Hyphenator-documentLoaded
|
||
* @description
|
||
* documentLoaded is true, when the DOM has been loaded. This is set by runOnContentLoaded
|
||
*/
|
||
documentLoaded = false,
|
||
documentCount = 0,
|
||
|
||
/**
|
||
* @name Hyphenator-persistentConfig
|
||
* @description
|
||
* 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.
|
||
*/
|
||
persistentConfig = false,
|
||
|
||
/**
|
||
* @name Hyphenator-contextWindow
|
||
* @description
|
||
* contextWindow stores the window for the document to be hyphenated.
|
||
* If there are frames this will change.
|
||
* So use contextWindow instead of window!
|
||
*/
|
||
contextWindow = window,
|
||
|
||
/**
|
||
* @name Hyphenator-doFrames
|
||
* @description
|
||
* switch to control if frames/iframes should be hyphenated, too
|
||
* defaults to false (frames are a bag of hurt!)
|
||
*/
|
||
doFrames = false,
|
||
|
||
/**
|
||
* @name Hyphenator-dontHyphenate
|
||
* @description
|
||
* A key-value object containing all html-tags whose content should not be hyphenated
|
||
* @type {Object.<string,boolean>}
|
||
* @private
|
||
* @see Hyphenator-hyphenateElement
|
||
*/
|
||
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},
|
||
|
||
/**
|
||
* @name Hyphenator-enableCache
|
||
* @description
|
||
* A variable to set if caching is enabled or not
|
||
* @type boolean
|
||
* @default true
|
||
* @private
|
||
* @see Hyphenator.config
|
||
* @see hyphenateWord
|
||
*/
|
||
enableCache = true,
|
||
|
||
/**
|
||
* @name Hyphenator-storageType
|
||
* @description
|
||
* A variable to define what html5-DOM-Storage-Method is used ('none', 'local' or 'session')
|
||
* @type {string}
|
||
* @default 'none'
|
||
* @private
|
||
* @see Hyphenator.config
|
||
*/
|
||
storageType = 'local',
|
||
|
||
/**
|
||
* @name Hyphenator-storage
|
||
* @description
|
||
* An alias to the storage-Method defined in storageType.
|
||
* Set by Hyphenator.run()
|
||
* @type {Object|undefined}
|
||
* @default null
|
||
* @private
|
||
* @see Hyphenator.run
|
||
*/
|
||
storage,
|
||
|
||
/**
|
||
* @name Hyphenator-enableReducedPatternSet
|
||
* @description
|
||
* A variable to set if storing the used patterns is set
|
||
* @type boolean
|
||
* @default false
|
||
* @private
|
||
* @see Hyphenator.config
|
||
* @see hyphenateWord
|
||
* @see Hyphenator.getRedPatternSet
|
||
*/
|
||
enableReducedPatternSet = false,
|
||
|
||
/**
|
||
* @name Hyphenator-enableRemoteLoading
|
||
* @description
|
||
* A variable to set if pattern files should be loaded remotely or not
|
||
* @type boolean
|
||
* @default true
|
||
* @private
|
||
* @see Hyphenator.config
|
||
* @see Hyphenator-loadPatterns
|
||
*/
|
||
enableRemoteLoading = true,
|
||
|
||
/**
|
||
* @name Hyphenator-displayToggleBox
|
||
* @description
|
||
* A variable to set if the togglebox should be displayed or not
|
||
* @type boolean
|
||
* @default false
|
||
* @private
|
||
* @see Hyphenator.config
|
||
* @see Hyphenator-toggleBox
|
||
*/
|
||
displayToggleBox = false,
|
||
|
||
/**
|
||
* @name Hyphenator-css3
|
||
* @description
|
||
* A variable to set if css3 hyphenation should be used
|
||
* @type boolean
|
||
* @default false
|
||
* @private
|
||
* @see Hyphenator.config
|
||
*/
|
||
css3 = false,
|
||
/**
|
||
* @name Hyphenator-css3_hsupport
|
||
* @description
|
||
* A generated object containing information for CSS3-hyphenation support
|
||
* {
|
||
* support: boolean,
|
||
* property: <the property name to access hyphen-settings>,
|
||
* languages: <an object containing supported languages>
|
||
* }
|
||
* @type object
|
||
* @default undefined
|
||
* @private
|
||
* @see Hyphenator-css3_gethsupport
|
||
*/
|
||
css3_h9n,
|
||
/**
|
||
* @name Hyphenator-css3_gethsupport
|
||
* @description
|
||
* This function sets Hyphenator-css3_h9n for the current UA
|
||
* @type function
|
||
* @private
|
||
* @see Hyphenator-css3_h9n
|
||
*/
|
||
css3_gethsupport = function () {
|
||
var s,
|
||
ua = navigator.userAgent,
|
||
r = {
|
||
support: false,
|
||
property: '',
|
||
languages: {}
|
||
};
|
||
if (window.getComputedStyle) {
|
||
s = window.getComputedStyle(window.document.getElementsByTagName('body')[0]);
|
||
} else {
|
||
//ancient Browser don't support CSS3 anyway
|
||
css3_h9n = r;
|
||
return;
|
||
}
|
||
if (ua.indexOf('Chrome') !== -1) {
|
||
//Chrome actually knows -webkit-hyphens but does no hyphenation
|
||
r.support = false;
|
||
} else if ((ua.indexOf('Safari') !== -1) && (s['-webkit-hyphens'] !== undefined)) {
|
||
r.support = true;
|
||
r.property = '-webkit-hyphens';
|
||
if (ua.indexOf('Mobile') !== -1) {
|
||
//iOS only hyphenates in systemlanguage
|
||
r.languages[navigator.language.split('-')[0]] = true;
|
||
} else {
|
||
//Desktop Safari only hyphenates some languages:
|
||
r.languages = {
|
||
de: true,
|
||
en: true,
|
||
es: true,
|
||
fr: true,
|
||
it: true,
|
||
nl: true,
|
||
ru: true,
|
||
zh: true
|
||
};
|
||
}
|
||
} else if ((ua.indexOf('Firefox') !== -1) && (s['MozHyphens'] !== undefined)) {
|
||
r.support = true;
|
||
r.property = 'MozHyphens';
|
||
r.languages = {
|
||
en: true
|
||
};
|
||
}
|
||
css3_h9n = r;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-hyphenateClass
|
||
* @description
|
||
* A string containing the css-class-name for the hyphenate class
|
||
* @type {string}
|
||
* @default 'hyphenate'
|
||
* @private
|
||
* @example
|
||
* <p class = "hyphenate">Text</p>
|
||
* @see Hyphenator.config
|
||
*/
|
||
hyphenateClass = 'hyphenate',
|
||
|
||
/**
|
||
* @name Hyphenator-dontHyphenateClass
|
||
* @description
|
||
* A string containing the css-class-name for elements that should not be hyphenated
|
||
* @type {string}
|
||
* @default 'donthyphenate'
|
||
* @private
|
||
* @example
|
||
* <p class = "donthyphenate">Text</p>
|
||
* @see Hyphenator.config
|
||
*/
|
||
dontHyphenateClass = 'donthyphenate',
|
||
|
||
/**
|
||
* @name Hyphenator-min
|
||
* @description
|
||
* A number wich indicates the minimal length of words to hyphenate.
|
||
* @type {number}
|
||
* @default 6
|
||
* @private
|
||
* @see Hyphenator.config
|
||
*/
|
||
min = 6,
|
||
|
||
/**
|
||
* @name Hyphenator-orphanControl
|
||
* @description
|
||
* 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
|
||
*/
|
||
orphanControl = 1,
|
||
|
||
/**
|
||
* @name Hyphenator-isBookmarklet
|
||
* @description
|
||
* Indicates if Hyphanetor runs as bookmarklet or not.
|
||
* @type boolean
|
||
* @default false
|
||
* @private
|
||
*/
|
||
isBookmarklet = (function () {
|
||
var loc = null, re = false, jsArray = document.getElementsByTagName('script'), i, l;
|
||
for (i = 0, l = jsArray.length; i < l; i++) {
|
||
if (!!jsArray[i].getAttribute('src')) {
|
||
loc = jsArray[i].getAttribute('src');
|
||
}
|
||
if (!loc) {
|
||
continue;
|
||
} else if (loc.indexOf('Hyphenator.js?bm=true') !== -1) {
|
||
re = true;
|
||
}
|
||
}
|
||
return re;
|
||
}()),
|
||
|
||
/**
|
||
* @name Hyphenator-mainLanguage
|
||
* @description
|
||
* 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
|
||
*/
|
||
mainLanguage = null,
|
||
|
||
/**
|
||
* @name Hyphenator-defaultLanguage
|
||
* @description
|
||
* 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
|
||
*/
|
||
defaultLanguage = '',
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-elements
|
||
* @description
|
||
* An array holding all elements that have to be hyphenated. This var is filled by
|
||
* {@link Hyphenator-gatherDocumentInfos}
|
||
* @type {Array}
|
||
* @private
|
||
*/
|
||
elements = (function () {
|
||
var Element = function (element, data) {
|
||
this.element = element;
|
||
this.hyphenated = false;
|
||
this.treated = false; //collected but not hyphenated (dohyphenation is off)
|
||
this.data = data;
|
||
},
|
||
ElementCollection = function () {
|
||
this.count = 0;
|
||
this.hyCount = 0;
|
||
this.list = {};
|
||
};
|
||
ElementCollection.prototype = {
|
||
add: function (el, lang, data) {
|
||
if (!this.list.hasOwnProperty(lang)) {
|
||
this.list[lang] = [];
|
||
}
|
||
this.list[lang].push(new Element(el, data));
|
||
this.count += 1;
|
||
},
|
||
each: function (fn) {
|
||
var k;
|
||
for (k in this.list) {
|
||
if (this.list.hasOwnProperty(k)) {
|
||
fn(k, this.list[k]);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
return new ElementCollection();
|
||
}()),
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-exceptions
|
||
* @description
|
||
* 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 = {},
|
||
|
||
/**
|
||
* @name Hyphenator-docLanguages
|
||
* @description
|
||
* An object holding all languages used in the document. This is filled by
|
||
* {@link Hyphenator-gatherDocumentInfos}
|
||
* @type {Object}
|
||
* @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
|
||
*/
|
||
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#!:\\.?\\+=&%@!\\-]*)*',
|
||
// protocoll usr pwd ip or host tld port path
|
||
/**
|
||
* @name Hyphenator-mail
|
||
* @description
|
||
* A string containing a RegularExpression to match mail-adresses
|
||
* @type {string}
|
||
* @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
|
||
* 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
|
||
*/
|
||
zeroWidthSpace = (function () {
|
||
var zws, ua = navigator.userAgent.toLowerCase();
|
||
zws = String.fromCharCode(8203); //Unicode zero width space
|
||
if (ua.indexOf('msie 6') !== -1) {
|
||
zws = ''; //IE6 doesn't support zws
|
||
}
|
||
if (ua.indexOf('opera') !== -1 && ua.indexOf('version/10.00') !== -1) {
|
||
zws = ''; //opera 10 on XP doesn't support zws
|
||
}
|
||
return zws;
|
||
}()),
|
||
|
||
/**
|
||
* @name Hyphenator-createElem
|
||
* @description
|
||
* A function alias to document.createElementNS or document.createElement
|
||
* @type {function(string, Object)}
|
||
* @private
|
||
*/
|
||
createElem = function (tagname, context) {
|
||
context = context || contextWindow;
|
||
if (document.createElementNS) {
|
||
return context.document.createElementNS('http://www.w3.org/1999/xhtml', tagname);
|
||
} else if (document.createElement) {
|
||
return context.document.createElement(tagname);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-onHyphenationDone
|
||
* @description
|
||
* A method to be called, when the last element has been hyphenated or the hyphenation has been
|
||
* removed from the last element.
|
||
* @see Hyphenator.config
|
||
* @type {function()}
|
||
* @private
|
||
*/
|
||
onHyphenationDone = function () {},
|
||
|
||
/**
|
||
* @name Hyphenator-onError
|
||
* @description
|
||
* A function that can be called upon an error.
|
||
* @see Hyphenator.config
|
||
* @type {function(Object)}
|
||
* @private
|
||
*/
|
||
onError = function (e) {
|
||
window.alert("Hyphenator.js says:\n\nAn Error ocurred:\n" + e.message);
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-selectorFunction
|
||
* @description
|
||
* A function that has to return a HTMLNodeList of Elements to be hyphenated.
|
||
* By default it uses the classname ('hyphenate') to select the elements.
|
||
* @see Hyphenator.config
|
||
* @type {function()}
|
||
* @private
|
||
*/
|
||
selectorFunction = function () {
|
||
var tmp, el = [], i, l;
|
||
if (document.getElementsByClassName) {
|
||
el = contextWindow.document.getElementsByClassName(hyphenateClass);
|
||
} else {
|
||
tmp = contextWindow.document.getElementsByTagName('*');
|
||
l = tmp.length;
|
||
for (i = 0; i < l; i++)
|
||
{
|
||
if (tmp[i].className.indexOf(hyphenateClass) !== -1 && tmp[i].className.indexOf(dontHyphenateClass) === -1) {
|
||
el.push(tmp[i]);
|
||
}
|
||
}
|
||
}
|
||
return el;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-intermediateState
|
||
* @description
|
||
* The value of style.visibility of the text while it is hyphenated.
|
||
* @see Hyphenator.config
|
||
* @type {string}
|
||
* @private
|
||
*/
|
||
intermediateState = 'hidden',
|
||
|
||
/**
|
||
* @name Hyphenator-unhide
|
||
* @description
|
||
* How hidden elements unhide: either simultaneous (default: 'wait') or progressively.
|
||
* 'wait' makes Hyphenator.js to wait until all elements are hyphenated (one redraw)
|
||
* With 'progressiv' Hyphenator.js unhides elements as soon as they are hyphenated.
|
||
* @see Hyphenator.config
|
||
* @type {string}
|
||
* @private
|
||
*/
|
||
unhide = 'wait',
|
||
|
||
/**
|
||
* @name Hyphenator-hyphen
|
||
* @description
|
||
* A string containing the character for in-word-hyphenation
|
||
* @type {string}
|
||
* @default the soft hyphen
|
||
* @private
|
||
* @see Hyphenator.config
|
||
*/
|
||
hyphen = String.fromCharCode(173),
|
||
|
||
/**
|
||
* @name Hyphenator-urlhyphen
|
||
* @description
|
||
* A string containing the character for url/mail-hyphenation
|
||
* @type {string}
|
||
* @default the zero width space
|
||
* @private
|
||
* @see Hyphenator.config
|
||
* @see Hyphenator-zeroWidthSpace
|
||
*/
|
||
urlhyphen = zeroWidthSpace,
|
||
|
||
/**
|
||
* @name Hyphenator-safeCopy
|
||
* @description
|
||
* 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
|
||
*/
|
||
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
|
||
* <a href = "http://jquery.com/</a>
|
||
* 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
|
||
*/
|
||
runOnContentLoaded = function (w, f) {
|
||
var DOMContentLoaded = function () {}, toplevel, hyphRunForThis = {};
|
||
if (documentLoaded && !hyphRunForThis[w.location.href]) {
|
||
f();
|
||
hyphRunForThis[w.location.href] = true;
|
||
return;
|
||
}
|
||
function init(context) {
|
||
contextWindow = context || window;
|
||
if (!hyphRunForThis[contextWindow.location.href] && (!documentLoaded || contextWindow != window.parent)) {
|
||
documentLoaded = true;
|
||
f();
|
||
hyphRunForThis[contextWindow.location.href] = true;
|
||
}
|
||
}
|
||
|
||
function doScrollCheck() {
|
||
try {
|
||
// If IE is used, use the trick by Diego Perini
|
||
// http://javascript.nwbox.com/IEContentLoaded/
|
||
document.documentElement.doScroll("left");
|
||
} catch (error) {
|
||
setTimeout(doScrollCheck, 1);
|
||
return;
|
||
}
|
||
|
||
// and execute any waiting functions
|
||
init(window);
|
||
}
|
||
|
||
function doOnLoad() {
|
||
var i, haveAccess, fl = window.frames.length;
|
||
if (doFrames && fl > 0) {
|
||
for (i = 0; i < fl; i++) {
|
||
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) {
|
||
init(window.frames[i]);
|
||
}
|
||
}
|
||
contextWindow = window;
|
||
f();
|
||
hyphRunForThis[window.location.href] = true;
|
||
} else {
|
||
init(window);
|
||
}
|
||
}
|
||
|
||
// Cleanup functions for the document ready method
|
||
if (document.addEventListener) {
|
||
DOMContentLoaded = function () {
|
||
document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
|
||
if (doFrames && window.frames.length > 0) {
|
||
//we are in a frameset, so do nothing but wait for onload to fire
|
||
return;
|
||
} else {
|
||
init(window);
|
||
}
|
||
};
|
||
|
||
} else if (document.attachEvent) {
|
||
DOMContentLoaded = function () {
|
||
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
|
||
if (document.readyState === "complete") {
|
||
document.detachEvent("onreadystatechange", DOMContentLoaded);
|
||
if (doFrames && window.frames.length > 0) {
|
||
//we are in a frameset, so do nothing but wait for onload to fire
|
||
return;
|
||
} else {
|
||
init(window);
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// Mozilla, Opera and webkit nightlies currently support this event
|
||
if (document.addEventListener) {
|
||
// Use the handy event callback
|
||
document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
|
||
|
||
// A fallback to window.onload, that will always work
|
||
window.addEventListener("load", doOnLoad, false);
|
||
|
||
// If IE event model is used
|
||
} else if (document.attachEvent) {
|
||
// ensure firing before onload,
|
||
// maybe late but safe also for iframes
|
||
document.attachEvent("onreadystatechange", DOMContentLoaded);
|
||
|
||
// A fallback to window.onload, that will always work
|
||
window.attachEvent("onload", doOnLoad);
|
||
|
||
// If IE and not a frame
|
||
// continually check to see if the document is ready
|
||
toplevel = false;
|
||
try {
|
||
toplevel = window.frameElement === null;
|
||
} catch (e) {}
|
||
|
||
if (document.documentElement.doScroll && toplevel) {
|
||
doScrollCheck();
|
||
}
|
||
}
|
||
|
||
},
|
||
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-getLang
|
||
* @description
|
||
* 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}
|
||
* if there's no language found for the element.
|
||
* @private
|
||
*/
|
||
getLang = function (el, fallback) {
|
||
if (!!el.getAttribute('lang')) {
|
||
return el.getAttribute('lang').toLowerCase();
|
||
}
|
||
// The following doesn't work in IE due to a bug when getAttribute('xml:lang') in a table
|
||
/*if (!!el.getAttribute('xml:lang')) {
|
||
return el.getAttribute('xml:lang').substring(0, 2);
|
||
}*/
|
||
//instead, we have to do this (thanks to borgzor):
|
||
try {
|
||
if (!!el.getAttribute('xml:lang')) {
|
||
return el.getAttribute('xml:lang').toLowerCase();
|
||
}
|
||
} catch (ex) {}
|
||
if (el.tagName !== 'HTML') {
|
||
return getLang(el.parentNode, true);
|
||
}
|
||
if (fallback) {
|
||
return mainLanguage;
|
||
}
|
||
return null;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-autoSetMainLanguage
|
||
* @description
|
||
* Retrieves the language of the document from the DOM.
|
||
* The function looks in the following places:
|
||
* <ul>
|
||
* <li>lang-attribute in the html-tag</li>
|
||
* <li><meta http-equiv = "content-language" content = "xy" /></li>
|
||
* <li><meta name = "DC.Language" content = "xy" /></li>
|
||
* <li><meta name = "language" content = "xy" /></li>
|
||
* </li>
|
||
* If nothing can be found a prompt using {@link Hyphenator-languageHint} and {@link Hyphenator-prompterStrings} is displayed.
|
||
* If the retrieved language is in the object {@link Hyphenator-supportedLang} it is copied to {@link Hyphenator-mainLanguage}
|
||
* @private
|
||
*/
|
||
autoSetMainLanguage = function (w) {
|
||
w = w || contextWindow;
|
||
var el = w.document.getElementsByTagName('html')[0],
|
||
m = w.document.getElementsByTagName('meta'),
|
||
i, text, e, ul;
|
||
mainLanguage = getLang(el, false);
|
||
if (!mainLanguage) {
|
||
for (i = 0; i < m.length; i++) {
|
||
//<meta http-equiv = "content-language" content="xy">
|
||
if (!!m[i].getAttribute('http-equiv') && (m[i].getAttribute('http-equiv').toLowerCase() === 'content-language')) {
|
||
mainLanguage = m[i].getAttribute('content').toLowerCase();
|
||
}
|
||
//<meta name = "DC.Language" content="xy">
|
||
if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'dc.language')) {
|
||
mainLanguage = m[i].getAttribute('content').toLowerCase();
|
||
}
|
||
//<meta name = "language" content = "xy">
|
||
if (!!m[i].getAttribute('name') && (m[i].getAttribute('name').toLowerCase() === 'language')) {
|
||
mainLanguage = m[i].getAttribute('content').toLowerCase();
|
||
}
|
||
}
|
||
}
|
||
//get lang for frame from enclosing document
|
||
if (!mainLanguage && doFrames && contextWindow != window.parent) {
|
||
autoSetMainLanguage(window.parent);
|
||
}
|
||
//fallback to defaultLang if set
|
||
if (!mainLanguage && defaultLanguage !== '') {
|
||
mainLanguage = defaultLanguage;
|
||
}
|
||
//ask user for lang
|
||
if (!mainLanguage) {
|
||
text = '';
|
||
ul = navigator.language ? navigator.language : navigator.userLanguage;
|
||
ul = ul.substring(0, 2);
|
||
if (prompterStrings.hasOwnProperty(ul)) {
|
||
text = prompterStrings[ul];
|
||
} else {
|
||
text = prompterStrings.en;
|
||
}
|
||
text += ' (ISO 639-1)\n\n' + languageHint;
|
||
mainLanguage = window.prompt(unescape(text), ul).toLowerCase();
|
||
}
|
||
if (!supportedLang.hasOwnProperty(mainLanguage)) {
|
||
if (supportedLang.hasOwnProperty(mainLanguage.split('-')[0])) { //try subtag
|
||
mainLanguage = mainLanguage.split('-')[0];
|
||
} else {
|
||
e = new Error('The language "' + mainLanguage + '" is not yet supported.');
|
||
throw e;
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-gatherDocumentInfos
|
||
* @description
|
||
* This method runs through the DOM and executes the process()-function on:
|
||
* - every node returned by the {@link Hyphenator-selectorFunction}.
|
||
* The process()-function copies the element to the elements-variable, sets its visibility
|
||
* to intermediateState, retrieves its language and recursivly descends the DOM-tree until
|
||
* the child-Nodes aren't of type 1
|
||
* @private
|
||
*/
|
||
gatherDocumentInfos = function () {
|
||
var elToProcess, tmp, i = 0,
|
||
process = function (el, hide, lang) {
|
||
var n, i = 0, hyphenatorSettings = {};
|
||
|
||
if (el.lang && typeof(el.lang) === 'string') {
|
||
lang = el.lang.toLowerCase(); //copy attribute-lang to internal lang
|
||
} else if (lang) {
|
||
lang = lang.toLowerCase();
|
||
} else {
|
||
lang = getLang(el, true);
|
||
}
|
||
|
||
//if css3-hyphenation is supported: use it!
|
||
if (css3 && css3_h9n.support && !!css3_h9n.languages[lang]) {
|
||
el.style[css3_h9n.property] = "auto";
|
||
el.style['-webkit-locale'] = "'" + lang + "'";
|
||
} else {
|
||
if (intermediateState === 'hidden') {
|
||
if (!!el.getAttribute('style')) {
|
||
hyphenatorSettings.hasOwnStyle = true;
|
||
} else {
|
||
hyphenatorSettings.hasOwnStyle = false;
|
||
}
|
||
hyphenatorSettings.isHidden = true;
|
||
el.style.visibility = 'hidden';
|
||
}
|
||
if (supportedLang[lang]) {
|
||
docLanguages[lang] = true;
|
||
} else {
|
||
if (supportedLang.hasOwnProperty(lang.split('-')[0])) { //try subtag
|
||
lang = lang.split('-')[0];
|
||
hyphenatorSettings.language = lang;
|
||
} else if (!isBookmarklet) {
|
||
onError(new Error('Language ' + lang + ' is not yet supported.'));
|
||
}
|
||
}
|
||
elements.add(el, lang, hyphenatorSettings);
|
||
}
|
||
while (!!(n = el.childNodes[i++])) {
|
||
if (n.nodeType === 1 && !dontHyphenate[n.nodeName.toLowerCase()] &&
|
||
n.className.indexOf(dontHyphenateClass) === -1 && !(n in elToProcess)) {
|
||
process(n, false, lang);
|
||
}
|
||
}
|
||
};
|
||
if (css3) {
|
||
css3_gethsupport();
|
||
}
|
||
if (isBookmarklet) {
|
||
elToProcess = contextWindow.document.getElementsByTagName('body')[0];
|
||
process(elToProcess, false, mainLanguage);
|
||
} else {
|
||
elToProcess = selectorFunction();
|
||
while (!!(tmp = elToProcess[i++]))
|
||
{
|
||
process(tmp, true, '');
|
||
}
|
||
}
|
||
if (elements.count === 0) {
|
||
//nothing to hyphenate or all hyphenated b css3
|
||
state = 3;
|
||
onHyphenationDone();
|
||
}
|
||
},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-createTrie
|
||
* @description
|
||
* converts patterns of the given language in a trie
|
||
* @private
|
||
* @param {string} lang the language whose patterns shall be converted
|
||
*/
|
||
convertPatterns = function (lang) {
|
||
/** @license BSD licenced code
|
||
* The following code is based on code from hypher.js and adapted for Hyphenator.js
|
||
* Copyright (c) 2011, Bram Stein
|
||
*/
|
||
var size = 0,
|
||
tree = {
|
||
tpoints: []
|
||
},
|
||
patterns, pattern, i, j, k,
|
||
patternObject = Hyphenator.languages[lang].patterns,
|
||
c, chars, points, t, p, codePoint,
|
||
getPoints = (function () {
|
||
//IE<9 doesn't act like other browsers
|
||
if ('in3se'.split(/\D/).length === 1) {
|
||
return function (pattern) {
|
||
var chars = pattern.split(''), c, i, r = [],
|
||
numb3rs = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}, lastWasNum = false;
|
||
i = 0;
|
||
while (!!(c = chars[i])) {
|
||
if (numb3rs.hasOwnProperty(c)) {
|
||
r.push(c);
|
||
i += 2;
|
||
lastWasNum = true;
|
||
} else {
|
||
r.push('');
|
||
i += 1;
|
||
lastWasNum = false;
|
||
}
|
||
}
|
||
if (!lastWasNum) {
|
||
r.push('');
|
||
}
|
||
return r;
|
||
};
|
||
} else {
|
||
return function (pattern) {
|
||
return pattern.split(/\D/);
|
||
};
|
||
}
|
||
}());
|
||
|
||
for (size in patternObject) {
|
||
if (patternObject.hasOwnProperty(size)) {
|
||
patterns = patternObject[size].match(new RegExp('.{1,' + (+size) + '}', 'g'));
|
||
i = 0;
|
||
while (!!(pattern = patterns[i++])) {
|
||
chars = pattern.replace(/[\d]/g, '').split('');
|
||
points = getPoints(pattern);
|
||
t = tree;
|
||
|
||
j = 0;
|
||
while (!!(c = chars[j++])) {
|
||
codePoint = c.charCodeAt(0);
|
||
|
||
if (!t[codePoint]) {
|
||
t[codePoint] = {};
|
||
}
|
||
t = t[codePoint];
|
||
}
|
||
|
||
t.tpoints = [];
|
||
for (k = 0; k < points.length; k++) {
|
||
p = points[k];
|
||
t.tpoints.push((p == "") ? 0 : p);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Hyphenator.languages[lang].patterns = tree;
|
||
/**
|
||
* end of BSD licenced code from hypher.js
|
||
*/
|
||
},
|
||
|
||
recreatePattern = function (pattern, nodePoints) {
|
||
var r = [], c = pattern.split(''), i;
|
||
for (i = 0; i < nodePoints.length; i++) {
|
||
if (nodePoints[i] !== 0) {
|
||
r.push(nodePoints[i]);
|
||
}
|
||
if (c[i]) {
|
||
r.push(c[i]);
|
||
}
|
||
}
|
||
return r.join('');
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-convertExceptionsToObject
|
||
* @description
|
||
* Converts a list of comma seprated exceptions to an object:
|
||
* 'Fortran,Hy-phen-a-tion' -> {'Fortran':'Fortran','Hyphenation':'Hy-phen-a-tion'}
|
||
* @private
|
||
* @param {string} exc a comma separated string of exceptions (without spaces)
|
||
*/
|
||
convertExceptionsToObject = function (exc) {
|
||
var w = exc.split(', '),
|
||
r = {},
|
||
i, l, key;
|
||
for (i = 0, l = w.length; i < l; i++) {
|
||
key = w[i].replace(/-/g, '');
|
||
if (!r.hasOwnProperty(key)) {
|
||
r[key] = w[i];
|
||
}
|
||
}
|
||
return r;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-loadPatterns
|
||
* @description
|
||
* Adds a <script>-Tag to the DOM to load an externeal .js-file containing patterns and settings for the given language.
|
||
* If the given language is not in the {@link Hyphenator-supportedLang}-Object it returns.
|
||
* One may ask why we are not using AJAX to load the patterns. The XMLHttpRequest-Object
|
||
* has a same-origin-policy. This makes the isBookmarklet-functionality impossible.
|
||
* @param {string} lang The language to load the patterns for
|
||
* @private
|
||
* @see Hyphenator-basePath
|
||
*/
|
||
loadPatterns = function (lang) {
|
||
var url, xhr, head, script;
|
||
if (supportedLang[lang] && !Hyphenator.languages[lang]) {
|
||
url = basePath + 'patterns/' + supportedLang[lang];
|
||
} else {
|
||
return;
|
||
}
|
||
if (isLocal && !isBookmarklet) {
|
||
//check if 'url' is available:
|
||
xhr = null;
|
||
if (typeof XMLHttpRequest !== 'undefined') {
|
||
xhr = new XMLHttpRequest();
|
||
}
|
||
if (!xhr) {
|
||
try {
|
||
xhr = new ActiveXObject("Msxml2.XMLHTTP");
|
||
} catch (e) {
|
||
xhr = null;
|
||
}
|
||
}
|
||
if (xhr) {
|
||
xhr.open('HEAD', url, false);
|
||
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
||
xhr.send(null);
|
||
if (xhr.status === 404) {
|
||
onError(new Error('Could not load\n' + url));
|
||
delete docLanguages[lang];
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
if (createElem) {
|
||
head = window.document.getElementsByTagName('head').item(0);
|
||
script = createElem('script', window);
|
||
script.src = url;
|
||
script.type = 'text/javascript';
|
||
head.appendChild(script);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-prepareLanguagesObj
|
||
* @description
|
||
* Adds a cache to each language and converts the exceptions-list to an object.
|
||
* If storage is active the object is stored there.
|
||
* @private
|
||
* @param {string} lang the language ob the lang-obj
|
||
*/
|
||
prepareLanguagesObj = function (lang) {
|
||
var lo = Hyphenator.languages[lang], wrd;
|
||
if (!lo.prepared) {
|
||
if (enableCache) {
|
||
lo.cache = {};
|
||
//Export
|
||
lo['cache'] = lo.cache;
|
||
}
|
||
if (enableReducedPatternSet) {
|
||
lo.redPatSet = {};
|
||
}
|
||
//add exceptions from the pattern file to the local 'exceptions'-obj
|
||
if (lo.hasOwnProperty('exceptions')) {
|
||
Hyphenator.addExceptions(lang, lo.exceptions);
|
||
delete lo.exceptions;
|
||
}
|
||
//copy global exceptions to the language specific exceptions
|
||
if (exceptions.hasOwnProperty('global')) {
|
||
if (exceptions.hasOwnProperty(lang)) {
|
||
exceptions[lang] += ', ' + exceptions.global;
|
||
} else {
|
||
exceptions[lang] = exceptions.global;
|
||
}
|
||
}
|
||
//move exceptions from the the local 'exceptions'-obj to the 'language'-object
|
||
if (exceptions.hasOwnProperty(lang)) {
|
||
lo.exceptions = convertExceptionsToObject(exceptions[lang]);
|
||
delete exceptions[lang];
|
||
} else {
|
||
lo.exceptions = {};
|
||
}
|
||
convertPatterns(lang);
|
||
wrd = '[\\w' + lo.specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
|
||
lo.genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + wrd + ')', 'gi');
|
||
lo.prepared = true;
|
||
}
|
||
if (!!storage) {
|
||
try {
|
||
storage.setItem('Hyphenator_' + lang, window.JSON.stringify(lo));
|
||
} catch (e) {
|
||
//onError(e);
|
||
}
|
||
}
|
||
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-prepare
|
||
* @description
|
||
* This funtion prepares the Hyphenator-Object: If RemoteLoading is turned off, it assumes
|
||
* that the patternfiles are loaded, all conversions are made and the callback is called.
|
||
* If storage is active the object is retrieved there.
|
||
* If RemoteLoading is on (default), it loads the pattern files and waits until they are loaded,
|
||
* by repeatedly checking Hyphenator.languages. If a patterfile is loaded the patterns are
|
||
* converted to their object style and the lang-object extended.
|
||
* Finally the callback is called.
|
||
* @private
|
||
*/
|
||
prepare = function (callback) {
|
||
var lang, interval, tmp1, tmp2;
|
||
if (!enableRemoteLoading) {
|
||
for (lang in Hyphenator.languages) {
|
||
if (Hyphenator.languages.hasOwnProperty(lang)) {
|
||
prepareLanguagesObj(lang);
|
||
}
|
||
}
|
||
state = 2;
|
||
callback('*');
|
||
return;
|
||
}
|
||
// get all languages that are used and preload the patterns
|
||
state = 1;
|
||
for (lang in docLanguages) {
|
||
if (docLanguages.hasOwnProperty(lang)) {
|
||
if (!!storage && storage.getItem('Hyphenator_' + lang)) {
|
||
Hyphenator.languages[lang] = window.JSON.parse(storage.getItem('Hyphenator_' + lang));
|
||
if (exceptions.hasOwnProperty('global')) {
|
||
tmp1 = convertExceptionsToObject(exceptions.global);
|
||
for (tmp2 in tmp1) {
|
||
if (tmp1.hasOwnProperty(tmp2)) {
|
||
Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
|
||
}
|
||
}
|
||
}
|
||
//Replace exceptions since they may have been changed:
|
||
if (exceptions.hasOwnProperty(lang)) {
|
||
tmp1 = convertExceptionsToObject(exceptions[lang]);
|
||
for (tmp2 in tmp1) {
|
||
if (tmp1.hasOwnProperty(tmp2)) {
|
||
Hyphenator.languages[lang].exceptions[tmp2] = tmp1[tmp2];
|
||
}
|
||
}
|
||
delete exceptions[lang];
|
||
}
|
||
//Replace genRegExp since it may have been changed:
|
||
tmp1 = '[\\w' + Hyphenator.languages[lang].specialChars + '@' + String.fromCharCode(173) + String.fromCharCode(8204) + '-]{' + min + ',}';
|
||
Hyphenator.languages[lang].genRegExp = new RegExp('(' + url + ')|(' + mail + ')|(' + tmp1 + ')', 'gi');
|
||
|
||
delete docLanguages[lang];
|
||
callback(lang);
|
||
continue;
|
||
} else {
|
||
loadPatterns(lang);
|
||
}
|
||
}
|
||
}
|
||
// else async wait until patterns are loaded, then hyphenate
|
||
interval = window.setInterval(function () {
|
||
var finishedLoading = true, lang;
|
||
for (lang in docLanguages) {
|
||
if (docLanguages.hasOwnProperty(lang)) {
|
||
finishedLoading = false;
|
||
if (!!Hyphenator.languages[lang]) {
|
||
delete docLanguages[lang];
|
||
//do conversion while other patterns are loading:
|
||
prepareLanguagesObj(lang);
|
||
callback(lang);
|
||
}
|
||
}
|
||
}
|
||
if (finishedLoading) {
|
||
//console.log('callig callback for ' + contextWindow.location.href);
|
||
window.clearInterval(interval);
|
||
state = 2;
|
||
}
|
||
}, 100);
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-switchToggleBox
|
||
* @description
|
||
* Creates or hides the toggleBox: a small button to turn off/on hyphenation on a page.
|
||
* @see Hyphenator.config
|
||
* @private
|
||
*/
|
||
toggleBox = function () {
|
||
var myBox, bdy, myIdAttribute, myTextNode, myClassAttribute,
|
||
text = (Hyphenator.doHyphenation ? 'Hy-phen-a-tion' : 'Hyphenation');
|
||
if (!!(myBox = contextWindow.document.getElementById('HyphenatorToggleBox'))) {
|
||
myBox.firstChild.data = text;
|
||
} else {
|
||
bdy = contextWindow.document.getElementsByTagName('body')[0];
|
||
myBox = createElem('div', contextWindow);
|
||
myIdAttribute = contextWindow.document.createAttribute('id');
|
||
myIdAttribute.nodeValue = 'HyphenatorToggleBox';
|
||
myClassAttribute = contextWindow.document.createAttribute('class');
|
||
myClassAttribute.nodeValue = dontHyphenateClass;
|
||
myTextNode = contextWindow.document.createTextNode(text);
|
||
myBox.appendChild(myTextNode);
|
||
myBox.setAttributeNode(myIdAttribute);
|
||
myBox.setAttributeNode(myClassAttribute);
|
||
myBox.onclick = Hyphenator.toggleHyphenation;
|
||
myBox.style.position = 'absolute';
|
||
myBox.style.top = '0px';
|
||
myBox.style.right = '0px';
|
||
myBox.style.margin = '0';
|
||
myBox.style.backgroundColor = '#AAAAAA';
|
||
myBox.style.color = '#FFFFFF';
|
||
myBox.style.font = '6pt Arial';
|
||
myBox.style.letterSpacing = '0.2em';
|
||
myBox.style.padding = '3px';
|
||
myBox.style.cursor = 'pointer';
|
||
myBox.style.WebkitBorderBottomLeftRadius = '4px';
|
||
myBox.style.MozBorderRadiusBottomleft = '4px';
|
||
bdy.appendChild(myBox);
|
||
}
|
||
},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-hyphenateWord
|
||
* @description
|
||
* This function is the heart of Hyphenator.js. It returns a hyphenated word.
|
||
*
|
||
* If there's already a {@link Hyphenator-hypen} in the word, the word is returned as it is.
|
||
* If the word is in the exceptions list or in the cache, it is retrieved from it.
|
||
* If there's a '-' put a zeroWidthSpace after the '-' and hyphenate the parts.
|
||
* @param {string} lang The language of the word
|
||
* @param {string} word The word
|
||
* @returns string The hyphenated word
|
||
* @public
|
||
*/
|
||
hyphenateWord = function (lang, word) {
|
||
var lo = Hyphenator.languages[lang], parts, l, subst,
|
||
w, characters, originalCharacters, wordLength, i, j, k, node, points = [],
|
||
characterPoints = [], nodePoints, nodePointsLength, m = Math.max, trie,
|
||
result = [''], pattern;
|
||
if (word === '') {
|
||
return '';
|
||
}
|
||
if (word.indexOf(hyphen) !== -1) {
|
||
//word already contains shy; -> leave at it is!
|
||
return word;
|
||
}
|
||
if (enableCache && lo.cache.hasOwnProperty(word)) { //the word is in the cache
|
||
return lo.cache[word];
|
||
}
|
||
if (lo.exceptions.hasOwnProperty(word)) { //the word is in the exceptions list
|
||
return lo.exceptions[word].replace(/-/g, hyphen);
|
||
}
|
||
if (word.indexOf('-') !== -1) {
|
||
//word contains '-' -> hyphenate the parts separated with '-'
|
||
parts = word.split('-');
|
||
for (i = 0, l = parts.length; i < l; i++) {
|
||
parts[i] = hyphenateWord(lang, parts[i]);
|
||
}
|
||
return parts.join('-');
|
||
}
|
||
w = word = '_' + word + '_';
|
||
if (!!lo.charSubstitution) {
|
||
for (subst in lo.charSubstitution) {
|
||
if (lo.charSubstitution.hasOwnProperty(subst)) {
|
||
w = w.replace(new RegExp(subst, 'g'), lo.charSubstitution[subst]);
|
||
}
|
||
}
|
||
}
|
||
if (word.indexOf("'") !== -1) {
|
||
w = w.replace("'", "’"); //replace APOSTROPHE with RIGHT SINGLE QUOTATION MARK (since the latter is used in the patterns)
|
||
}
|
||
/** @license BSD licenced code
|
||
* The following code is based on code from hypher.js
|
||
* Copyright (c) 2011, Bram Stein
|
||
*/
|
||
characters = w.toLowerCase().split('');
|
||
originalCharacters = word.split('');
|
||
wordLength = characters.length;
|
||
trie = lo.patterns;
|
||
for (i = 0; i < wordLength; i += 1) {
|
||
points[i] = 0;
|
||
characterPoints[i] = characters[i].charCodeAt(0);
|
||
}
|
||
for (i = 0; i < wordLength; i += 1) {
|
||
pattern = '';
|
||
node = trie;
|
||
for (j = i; j < wordLength; j += 1) {
|
||
node = node[characterPoints[j]];
|
||
if (node) {
|
||
if (enableReducedPatternSet) {
|
||
pattern += String.fromCharCode(characterPoints[j]);
|
||
}
|
||
nodePoints = node.tpoints;
|
||
if (nodePoints) {
|
||
if (enableReducedPatternSet) {
|
||
if (!lo.redPatSet) {
|
||
lo.redPatSet = {};
|
||
}
|
||
lo.redPatSet[pattern] = recreatePattern(pattern, nodePoints);
|
||
}
|
||
for (k = 0, nodePointsLength = nodePoints.length; k < nodePointsLength; k += 1) {
|
||
points[i + k] = m(points[i + k], nodePoints[k]);
|
||
}
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
for (i = 1; i < wordLength - 1; i += 1) {
|
||
if (i > lo.leftmin && i < (wordLength - lo.rightmin) && points[i] % 2) {
|
||
result.push(originalCharacters[i]);
|
||
} else {
|
||
result[result.length - 1] += originalCharacters[i];
|
||
}
|
||
}
|
||
return result.join(hyphen);
|
||
/**
|
||
* end of BSD licenced code from hypher.js
|
||
*/
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-hyphenateURL
|
||
* @description
|
||
* Puts {@link Hyphenator-urlhyphen} after each no-alphanumeric char that my be in a URL.
|
||
* @param {string} url to hyphenate
|
||
* @returns string the hyphenated URL
|
||
* @public
|
||
*/
|
||
hyphenateURL = function (url) {
|
||
return url.replace(/([:\/\.\?#&_,;!@]+)/gi, '$&' + urlhyphen);
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-removeHyphenationFromElement
|
||
* @description
|
||
* Removes all hyphens from the element. If there are other elements, the function is
|
||
* called recursively.
|
||
* Removing hyphens is usefull if you like to copy text. Some browsers are buggy when the copy hyphenated texts.
|
||
* @param {Object} el The element where to remove hyphenation.
|
||
* @public
|
||
*/
|
||
removeHyphenationFromElement = function (el) {
|
||
var h, i = 0, n;
|
||
switch (hyphen) {
|
||
case '|':
|
||
h = '\\|';
|
||
break;
|
||
case '+':
|
||
h = '\\+';
|
||
break;
|
||
case '*':
|
||
h = '\\*';
|
||
break;
|
||
default:
|
||
h = hyphen;
|
||
}
|
||
while (!!(n = el.childNodes[i++])) {
|
||
if (n.nodeType === 3) {
|
||
n.data = n.data.replace(new RegExp(h, 'g'), '');
|
||
n.data = n.data.replace(new RegExp(zeroWidthSpace, 'g'), '');
|
||
} else if (n.nodeType === 1) {
|
||
removeHyphenationFromElement(n);
|
||
}
|
||
}
|
||
},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-registerOnCopy
|
||
* @description
|
||
* Huge work-around for browser-inconsistency when it comes to
|
||
* copying of hyphenated text.
|
||
* The idea behind this code has been provided by http://github.com/aristus/sweet-justice
|
||
* sweet-justice is under BSD-License
|
||
* @private
|
||
*/
|
||
registerOnCopy = function (el) {
|
||
var body = el.ownerDocument.getElementsByTagName('body')[0],
|
||
shadow,
|
||
selection,
|
||
range,
|
||
rangeShadow,
|
||
restore,
|
||
oncopyHandler = function (e) {
|
||
e = e || window.event;
|
||
var target = e.target || e.srcElement,
|
||
currDoc = target.ownerDocument,
|
||
body = currDoc.getElementsByTagName('body')[0],
|
||
targetWindow = 'defaultView' in currDoc ? currDoc.defaultView : currDoc.parentWindow;
|
||
if (target.tagName && dontHyphenate[target.tagName.toLowerCase()]) {
|
||
//Safari needs this
|
||
return;
|
||
}
|
||
//create a hidden shadow element
|
||
shadow = currDoc.createElement('div');
|
||
//Moving the element out of the screen doesn't work for IE9 (https://connect.microsoft.com/IE/feedback/details/663981/)
|
||
//shadow.style.overflow = 'hidden';
|
||
//shadow.style.position = 'absolute';
|
||
//shadow.style.top = '-5000px';
|
||
//shadow.style.height = '1px';
|
||
//doing this instead:
|
||
shadow.style.color = window.getComputedStyle ? targetWindow.getComputedStyle(body).backgroundColor : '#FFFFFF';
|
||
shadow.style.fontSize = '0px';
|
||
body.appendChild(shadow);
|
||
if (!!window.getSelection) {
|
||
//FF3, Webkit, IE9
|
||
e.stopPropagation();
|
||
selection = targetWindow.getSelection();
|
||
range = selection.getRangeAt(0);
|
||
shadow.appendChild(range.cloneContents());
|
||
removeHyphenationFromElement(shadow);
|
||
selection.selectAllChildren(shadow);
|
||
restore = function () {
|
||
shadow.parentNode.removeChild(shadow);
|
||
selection.removeAllRanges(); //IE9 needs that
|
||
selection.addRange(range);
|
||
};
|
||
} else {
|
||
// IE<9
|
||
e.cancelBubble = true;
|
||
selection = targetWindow.document.selection;
|
||
range = selection.createRange();
|
||
shadow.innerHTML = range.htmlText;
|
||
removeHyphenationFromElement(shadow);
|
||
rangeShadow = body.createTextRange();
|
||
rangeShadow.moveToElementText(shadow);
|
||
rangeShadow.select();
|
||
restore = function () {
|
||
shadow.parentNode.removeChild(shadow);
|
||
if (range.text !== "") {
|
||
range.select();
|
||
}
|
||
};
|
||
}
|
||
window.setTimeout(restore, 0);
|
||
};
|
||
if (!body) {
|
||
return;
|
||
}
|
||
el = el || body;
|
||
if (window.addEventListener) {
|
||
el.addEventListener("copy", oncopyHandler, true);
|
||
} else {
|
||
el.attachEvent("oncopy", oncopyHandler);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-unhideElement
|
||
* @description
|
||
* Unhides an element and removes the visibility attr if set by hyphenator
|
||
* @param Object The Element object from ElementCollection
|
||
* @private
|
||
*/
|
||
unhideElement = function (elo) {
|
||
var el = elo.element,
|
||
hyphenatorSettings = elo.data;
|
||
el.style.visibility = 'visible';
|
||
elo.data.isHidden = false;
|
||
if (!hyphenatorSettings.hasOwnStyle) {
|
||
el.setAttribute('style', ''); // without this, removeAttribute doesn't work in Safari (thanks to molily)
|
||
el.removeAttribute('style');
|
||
} else {
|
||
if (el.style.removeProperty) {
|
||
el.style.removeProperty('visibility');
|
||
} else if (el.style.removeAttribute) { // IE
|
||
el.style.removeAttribute('visibility');
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-checkIfAllDone
|
||
* @description
|
||
* Checks if all Elements are hyphenated, unhides them and fires onHyphenationDone()
|
||
* @private
|
||
*/
|
||
checkIfAllDone = function () {
|
||
var allDone = true;
|
||
elements.each(function (lang, list) {
|
||
var i, l = list.length;
|
||
for (i = 0; i < l; i++) {
|
||
allDone = allDone && list[i].hyphenated;
|
||
if (intermediateState === 'hidden' && unhide === 'wait') {
|
||
unhideElement(list[i]);
|
||
}
|
||
}
|
||
});
|
||
if (allDone) {
|
||
state = 3;
|
||
onHyphenationDone();
|
||
}
|
||
},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-hyphenateElement
|
||
* @description
|
||
* Takes the content of the given element and - if there's text - replaces the words
|
||
* by hyphenated words. If there's another element, the function is called recursively.
|
||
* When all words are hyphenated, the visibility of the element is set to 'visible'.
|
||
* @param {Object} el The element to hyphenate
|
||
* @private
|
||
*/
|
||
hyphenateElement = function (lang, elo) {
|
||
var hyphenatorSettings = elo.data,
|
||
el = elo.element,
|
||
hyphenate, n, i,
|
||
controlOrphans = function (part) {
|
||
var h, r;
|
||
switch (hyphen) {
|
||
case '|':
|
||
h = '\\|';
|
||
break;
|
||
case '+':
|
||
h = '\\+';
|
||
break;
|
||
case '*':
|
||
h = '\\*';
|
||
break;
|
||
default:
|
||
h = hyphen;
|
||
}
|
||
if (orphanControl >= 2) {
|
||
//remove hyphen points from last word
|
||
r = part.split(' ');
|
||
r[1] = r[1].replace(new RegExp(h, 'g'), '');
|
||
r[1] = r[1].replace(new RegExp(zeroWidthSpace, 'g'), '');
|
||
r = r.join(' ');
|
||
}
|
||
if (orphanControl === 3) {
|
||
//replace spaces by non breaking spaces
|
||
r = r.replace(/[ ]+/g, String.fromCharCode(160));
|
||
}
|
||
return r;
|
||
};
|
||
if (Hyphenator.languages.hasOwnProperty(lang)) {
|
||
hyphenate = function (word) {
|
||
if (!Hyphenator.doHyphenation) {
|
||
return word;
|
||
} else if (urlOrMailRE.test(word)) {
|
||
return hyphenateURL(word);
|
||
} else {
|
||
return hyphenateWord(lang, word);
|
||
}
|
||
};
|
||
if (safeCopy && (el.tagName.toLowerCase() !== 'body')) {
|
||
registerOnCopy(el);
|
||
}
|
||
i = 0;
|
||
while (!!(n = el.childNodes[i++])) {
|
||
if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
|
||
n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
|
||
if (orphanControl !== 1) {
|
||
n.data = n.data.replace(/[\S]+ [\S]+$/, controlOrphans);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (hyphenatorSettings.isHidden && intermediateState === 'hidden' && unhide === 'progressive') {
|
||
unhideElement(elo);
|
||
}
|
||
elo.hyphenated = true;
|
||
elements.hyCount += 1;
|
||
if (elements.count <= elements.hyCount) {
|
||
checkIfAllDone();
|
||
}
|
||
},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator-hyphenateLanguageElements
|
||
* @description
|
||
* Calls hyphenateElement() for all elements of the specified language.
|
||
* If the language is '*' then all elements are hyphenated.
|
||
* This is done with a setTimout
|
||
* to prevent a "long running Script"-alert when hyphenating large pages.
|
||
* Therefore a tricky bind()-function was necessary.
|
||
* @private
|
||
*/
|
||
hyphenateLanguageElements = function (lang) {
|
||
function bind(fun, arg1, arg2) {
|
||
return function () {
|
||
return fun(arg1, arg2);
|
||
};
|
||
}
|
||
var el, i, l;
|
||
if (lang === '*') {
|
||
elements.each(function (lang, langels) {
|
||
var i, l = langels.length;
|
||
for (i = 0; i < l; i++) {
|
||
window.setTimeout(bind(hyphenateElement, lang, langels[i]), 0);
|
||
}
|
||
});
|
||
} else {
|
||
if (elements.list.hasOwnProperty(lang)) {
|
||
l = elements.list[lang].length;
|
||
for (i = 0; i < l; i++) {
|
||
window.setTimeout(bind(hyphenateElement, lang, elements.list[lang][i]), 0);
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-removeHyphenationFromDocument
|
||
* @description
|
||
* Does what it says ;-)
|
||
* @private
|
||
*/
|
||
removeHyphenationFromDocument = function () {
|
||
elements.each(function (lang, elo) {
|
||
var i, l = elo.length, el;
|
||
for (i = 0; i < l; i++) {
|
||
removeHyphenationFromElement(elo[i].element);
|
||
elo[i].hyphenated = false;
|
||
}
|
||
});
|
||
state = 4;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-createStorage
|
||
* @description
|
||
* inits the private var storage depending of the setting in storageType
|
||
* and the supported features of the system.
|
||
* @private
|
||
*/
|
||
createStorage = function () {
|
||
try {
|
||
if (storageType !== 'none' &&
|
||
typeof(window.localStorage) !== 'undefined' &&
|
||
typeof(window.sessionStorage) !== 'undefined' &&
|
||
typeof(window.JSON.stringify) !== 'undefined' &&
|
||
typeof(window.JSON.parse) !== 'undefined') {
|
||
switch (storageType) {
|
||
case 'session':
|
||
storage = window.sessionStorage;
|
||
break;
|
||
case 'local':
|
||
storage = window.localStorage;
|
||
break;
|
||
default:
|
||
storage = undefined;
|
||
break;
|
||
}
|
||
}
|
||
} catch (f) {
|
||
//FF throws an error if DOM.storage.enabled is set to false
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-storeConfiguration
|
||
* @description
|
||
* Stores the current config-options in DOM-Storage
|
||
* @private
|
||
*/
|
||
storeConfiguration = function () {
|
||
if (!storage) {
|
||
return;
|
||
}
|
||
var settings = {
|
||
'STORED': true,
|
||
'classname': hyphenateClass,
|
||
'donthyphenateclassname': dontHyphenateClass,
|
||
'minwordlength': min,
|
||
'hyphenchar': hyphen,
|
||
'urlhyphenchar': urlhyphen,
|
||
'togglebox': toggleBox,
|
||
'displaytogglebox': displayToggleBox,
|
||
'remoteloading': enableRemoteLoading,
|
||
'enablecache': enableCache,
|
||
'onhyphenationdonecallback': onHyphenationDone,
|
||
'onerrorhandler': onError,
|
||
'intermediatestate': intermediateState,
|
||
'selectorfunction': selectorFunction,
|
||
'safecopy': safeCopy,
|
||
'doframes': doFrames,
|
||
'storagetype': storageType,
|
||
'orphancontrol': orphanControl,
|
||
'dohyphenation': Hyphenator.doHyphenation,
|
||
'persistentconfig': persistentConfig,
|
||
'defaultlanguage': defaultLanguage
|
||
};
|
||
storage.setItem('Hyphenator_config', window.JSON.stringify(settings));
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator-restoreConfiguration
|
||
* @description
|
||
* Retrieves config-options from DOM-Storage and does configuration accordingly
|
||
* @private
|
||
*/
|
||
restoreConfiguration = function () {
|
||
var settings;
|
||
if (storage.getItem('Hyphenator_config')) {
|
||
settings = window.JSON.parse(storage.getItem('Hyphenator_config'));
|
||
Hyphenator.config(settings);
|
||
}
|
||
};
|
||
|
||
return {
|
||
|
||
/**
|
||
* @name Hyphenator.version
|
||
* @memberOf Hyphenator
|
||
* @description
|
||
* String containing the actual version of Hyphenator.js
|
||
* [major release].[minor releas].[bugfix release]
|
||
* major release: new API, new Features, big changes
|
||
* minor release: new languages, improvements
|
||
* @public
|
||
*/
|
||
version: '4.0.0',
|
||
|
||
/**
|
||
* @name Hyphenator.doHyphenation
|
||
* @description
|
||
* If doHyphenation is set to false (defaults to true), hyphenateDocument() isn't called.
|
||
* All other actions are performed.
|
||
*/
|
||
doHyphenation: true,
|
||
|
||
/**
|
||
* @name Hyphenator.languages
|
||
* @memberOf Hyphenator
|
||
* @description
|
||
* Objects that holds key-value pairs, where key is the language and the value is the
|
||
* language-object loaded from (and set by) the pattern file.
|
||
* The language object holds the following members:
|
||
* <table>
|
||
* <tr><th>key</th><th>desc></th></tr>
|
||
* <tr><td>leftmin</td><td>The minimum of chars to remain on the old line</td></tr>
|
||
* <tr><td>rightmin</td><td>The minimum of chars to go on the new line</td></tr>
|
||
* <tr><td>shortestPattern</td><td>The shortes pattern (numbers don't count!)</td></tr>
|
||
* <tr><td>longestPattern</td><td>The longest pattern (numbers don't count!)</td></tr>
|
||
* <tr><td>specialChars</td><td>Non-ASCII chars in the alphabet.</td></tr>
|
||
* <tr><td>patterns</td><td>the patterns</td></tr>
|
||
* </table>
|
||
* And optionally (or after prepareLanguagesObj() has been called):
|
||
* <table>
|
||
* <tr><td>exceptions</td><td>Excpetions for the secified language</td></tr>
|
||
* </table>
|
||
* @public
|
||
*/
|
||
languages: {},
|
||
|
||
|
||
/**
|
||
* @name Hyphenator.config
|
||
* @description
|
||
* Config function that takes an object as an argument. The object contains key-value-pairs
|
||
* containig Hyphenator-settings. This is a shortcut for calling Hyphenator.set...-Methods.
|
||
* @param {Object} obj <table>
|
||
* <tr><th>key</th><th>values</th><th>default</th></tr>
|
||
* <tr><td>classname</td><td>string</td><td>'hyphenate'</td></tr>
|
||
* <tr><td>donthyphenateclassname</td><td>string</td><td>''</td></tr>
|
||
* <tr><td>minwordlength</td><td>integer</td><td>6</td></tr>
|
||
* <tr><td>hyphenchar</td><td>string</td><td>'&shy;'</td></tr>
|
||
* <tr><td>urlhyphenchar</td><td>string</td><td>'zero with space'</td></tr>
|
||
* <tr><td>togglebox</td><td>function</td><td>see code</td></tr>
|
||
* <tr><td>displaytogglebox</td><td>boolean</td><td>false</td></tr>
|
||
* <tr><td>remoteloading</td><td>boolean</td><td>true</td></tr>
|
||
* <tr><td>enablecache</td><td>boolean</td><td>true</td></tr>
|
||
* <tr><td>enablereducedpatternset</td><td>boolean</td><td>false</td></tr>
|
||
* <tr><td>onhyphenationdonecallback</td><td>function</td><td>empty function</td></tr>
|
||
* <tr><td>onerrorhandler</td><td>function</td><td>alert(onError)</td></tr>
|
||
* <tr><td>intermediatestate</td><td>string</td><td>'hidden'</td></tr>
|
||
* <tr><td>selectorfunction</td><td>function</td><td>[…]</td></tr>
|
||
* <tr><td>safecopy</td><td>boolean</td><td>true</td></tr>
|
||
* <tr><td>doframes</td><td>boolean</td><td>false</td></tr>
|
||
* <tr><td>storagetype</td><td>string</td><td>'none'</td></tr>
|
||
* </table>
|
||
* @public
|
||
* @example <script src = "Hyphenator.js" type = "text/javascript"></script>
|
||
* <script type = "text/javascript">
|
||
* Hyphenator.config({'minwordlength':4,'hyphenchar':'|'});
|
||
* Hyphenator.run();
|
||
* </script>
|
||
*/
|
||
config: function (obj) {
|
||
var assert = function (name, type) {
|
||
if (typeof obj[name] === type) {
|
||
return true;
|
||
} else {
|
||
onError(new Error('Config onError: ' + name + ' must be of type ' + type));
|
||
return false;
|
||
}
|
||
},
|
||
key;
|
||
|
||
if (obj.hasOwnProperty('storagetype')) {
|
||
if (assert('storagetype', 'string')) {
|
||
storageType = obj.storagetype;
|
||
}
|
||
if (!storage) {
|
||
createStorage();
|
||
}
|
||
}
|
||
if (!obj.hasOwnProperty('STORED') && storage && obj.hasOwnProperty('persistentconfig') && obj.persistentconfig === true) {
|
||
restoreConfiguration();
|
||
}
|
||
|
||
for (key in obj) {
|
||
if (obj.hasOwnProperty(key)) {
|
||
switch (key) {
|
||
case 'STORED':
|
||
break;
|
||
case 'classname':
|
||
if (assert('classname', 'string')) {
|
||
hyphenateClass = obj[key];
|
||
}
|
||
break;
|
||
case 'donthyphenateclassname':
|
||
if (assert('donthyphenateclassname', 'string')) {
|
||
dontHyphenateClass = obj[key];
|
||
}
|
||
break;
|
||
case 'minwordlength':
|
||
if (assert('minwordlength', 'number')) {
|
||
min = obj[key];
|
||
}
|
||
break;
|
||
case 'hyphenchar':
|
||
if (assert('hyphenchar', 'string')) {
|
||
if (obj.hyphenchar === '­') {
|
||
obj.hyphenchar = String.fromCharCode(173);
|
||
}
|
||
hyphen = obj[key];
|
||
}
|
||
break;
|
||
case 'urlhyphenchar':
|
||
if (obj.hasOwnProperty('urlhyphenchar')) {
|
||
if (assert('urlhyphenchar', 'string')) {
|
||
urlhyphen = obj[key];
|
||
}
|
||
}
|
||
break;
|
||
case 'togglebox':
|
||
if (assert('togglebox', 'function')) {
|
||
toggleBox = obj[key];
|
||
}
|
||
break;
|
||
case 'displaytogglebox':
|
||
if (assert('displaytogglebox', 'boolean')) {
|
||
displayToggleBox = obj[key];
|
||
}
|
||
break;
|
||
case 'remoteloading':
|
||
if (assert('remoteloading', 'boolean')) {
|
||
enableRemoteLoading = obj[key];
|
||
}
|
||
break;
|
||
case 'enablecache':
|
||
if (assert('enablecache', 'boolean')) {
|
||
enableCache = obj[key];
|
||
}
|
||
break;
|
||
case 'enablereducedpatternset':
|
||
if (assert('enablereducedpatternset', 'boolean')) {
|
||
enableReducedPatternSet = obj[key];
|
||
}
|
||
break;
|
||
case 'onhyphenationdonecallback':
|
||
if (assert('onhyphenationdonecallback', 'function')) {
|
||
onHyphenationDone = obj[key];
|
||
}
|
||
break;
|
||
case 'onerrorhandler':
|
||
if (assert('onerrorhandler', 'function')) {
|
||
onError = obj[key];
|
||
}
|
||
break;
|
||
case 'intermediatestate':
|
||
if (assert('intermediatestate', 'string')) {
|
||
intermediateState = obj[key];
|
||
}
|
||
break;
|
||
case 'selectorfunction':
|
||
if (assert('selectorfunction', 'function')) {
|
||
selectorFunction = obj[key];
|
||
}
|
||
break;
|
||
case 'safecopy':
|
||
if (assert('safecopy', 'boolean')) {
|
||
safeCopy = obj[key];
|
||
}
|
||
break;
|
||
case 'doframes':
|
||
if (assert('doframes', 'boolean')) {
|
||
doFrames = obj[key];
|
||
}
|
||
break;
|
||
case 'storagetype':
|
||
if (assert('storagetype', 'string')) {
|
||
storageType = obj[key];
|
||
}
|
||
break;
|
||
case 'orphancontrol':
|
||
if (assert('orphancontrol', 'number')) {
|
||
orphanControl = obj[key];
|
||
}
|
||
break;
|
||
case 'dohyphenation':
|
||
if (assert('dohyphenation', 'boolean')) {
|
||
Hyphenator.doHyphenation = obj[key];
|
||
}
|
||
break;
|
||
case 'persistentconfig':
|
||
if (assert('persistentconfig', 'boolean')) {
|
||
persistentConfig = obj[key];
|
||
}
|
||
break;
|
||
case 'defaultlanguage':
|
||
if (assert('defaultlanguage', 'string')) {
|
||
defaultLanguage = obj[key];
|
||
}
|
||
break;
|
||
case 'useCSS3hyphenation':
|
||
if (assert('useCSS3hyphenation', 'boolean')) {
|
||
css3 = obj[key];
|
||
}
|
||
break;
|
||
case 'unhide':
|
||
if (assert('unhide', 'string')) {
|
||
unhide = obj[key];
|
||
}
|
||
break;
|
||
default:
|
||
onError(new Error('Hyphenator.config: property ' + key + ' not known.'));
|
||
}
|
||
}
|
||
}
|
||
if (storage && persistentConfig) {
|
||
storeConfiguration();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.run
|
||
* @description
|
||
* Bootstrap function that starts all hyphenation processes when called.
|
||
* @public
|
||
* @example <script src = "Hyphenator.js" type = "text/javascript"></script>
|
||
* <script type = "text/javascript">
|
||
* Hyphenator.run();
|
||
* </script>
|
||
*/
|
||
run: function () {
|
||
documentCount = 0;
|
||
var process = function () {
|
||
try {
|
||
if (contextWindow.document.getElementsByTagName('frameset').length > 0) {
|
||
return; //we are in a frameset
|
||
}
|
||
documentCount++;
|
||
autoSetMainLanguage(undefined);
|
||
gatherDocumentInfos();
|
||
//console.log('preparing for ' + contextWindow.location.href);
|
||
prepare(hyphenateLanguageElements);
|
||
if (displayToggleBox) {
|
||
toggleBox();
|
||
}
|
||
} catch (e) {
|
||
onError(e);
|
||
}
|
||
}, i, haveAccess, fl = window.frames.length;
|
||
|
||
if (!storage) {
|
||
createStorage();
|
||
}
|
||
if (!documentLoaded && !isBookmarklet) {
|
||
runOnContentLoaded(window, process);
|
||
}
|
||
if (isBookmarklet || documentLoaded) {
|
||
if (doFrames && fl > 0) {
|
||
for (i = 0; i < fl; i++) {
|
||
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) {
|
||
contextWindow = window.frames[i];
|
||
process();
|
||
}
|
||
}
|
||
}
|
||
contextWindow = window;
|
||
process();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.addExceptions
|
||
* @description
|
||
* Adds the exceptions from the string to the appropriate language in the
|
||
* {@link Hyphenator-languages}-object
|
||
* @param {string} lang The language
|
||
* @param {string} words A comma separated string of hyphenated words WITH spaces.
|
||
* @public
|
||
* @example <script src = "Hyphenator.js" type = "text/javascript"></script>
|
||
* <script type = "text/javascript">
|
||
* Hyphenator.addExceptions('de','ziem-lich, Wach-stube');
|
||
* Hyphenator.run();
|
||
* </script>
|
||
*/
|
||
addExceptions: function (lang, words) {
|
||
if (lang === '') {
|
||
lang = 'global';
|
||
}
|
||
if (exceptions.hasOwnProperty(lang)) {
|
||
exceptions[lang] += ", " + words;
|
||
} else {
|
||
exceptions[lang] = words;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.hyphenate
|
||
* @public
|
||
* @description
|
||
* Hyphenates the target. The language patterns must be loaded.
|
||
* If the target is a string, the hyphenated string is returned,
|
||
* if it's an object, the values are hyphenated directly.
|
||
* @param {string|Object} target the target to be hyphenated
|
||
* @param {string} lang the language of the target
|
||
* @returns string
|
||
* @example <script src = "Hyphenator.js" type = "text/javascript"></script>
|
||
* <script src = "patterns/en.js" type = "text/javascript"></script>
|
||
* <script type = "text/javascript">
|
||
* var t = Hyphenator.hyphenate('Hyphenation', 'en'); //Hy|phen|ation
|
||
* </script>
|
||
*/
|
||
hyphenate: function (target, lang) {
|
||
var hyphenate, n, i;
|
||
if (Hyphenator.languages.hasOwnProperty(lang)) {
|
||
if (!Hyphenator.languages[lang].prepared) {
|
||
prepareLanguagesObj(lang);
|
||
}
|
||
hyphenate = function (word) {
|
||
if (urlOrMailRE.test(word)) {
|
||
return hyphenateURL(word);
|
||
} else {
|
||
return hyphenateWord(lang, word);
|
||
}
|
||
};
|
||
if (typeof target === 'string' || target.constructor === String) {
|
||
return target.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
|
||
} else if (typeof target === 'object') {
|
||
i = 0;
|
||
while (!!(n = target.childNodes[i++])) {
|
||
if (n.nodeType === 3 && n.data.length >= min) { //type 3 = #text -> hyphenate!
|
||
n.data = n.data.replace(Hyphenator.languages[lang].genRegExp, hyphenate);
|
||
} else if (n.nodeType === 1) {
|
||
if (n.lang !== '') {
|
||
Hyphenator.hyphenate(n, n.lang);
|
||
} else {
|
||
Hyphenator.hyphenate(n, lang);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
onError(new Error('Language "' + lang + '" is not loaded.'));
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.getRedPatternSet
|
||
* @description
|
||
* Returns {@link Hyphenator-isBookmarklet}.
|
||
* @param {string} lang the language patterns are stored for
|
||
* @returns object {'patk': pat}
|
||
* @public
|
||
*/
|
||
getRedPatternSet: function (lang) {
|
||
return Hyphenator.languages[lang].redPatSet;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.isBookmarklet
|
||
* @description
|
||
* Returns {@link Hyphenator-isBookmarklet}.
|
||
* @returns boolean
|
||
* @public
|
||
*/
|
||
isBookmarklet: function () {
|
||
return isBookmarklet;
|
||
},
|
||
|
||
getConfigFromURI: function () {
|
||
var loc = null, re = {}, jsArray = document.getElementsByTagName('script'), i, j, l, s, gp, option;
|
||
for (i = 0, l = jsArray.length; i < l; i++) {
|
||
if (!!jsArray[i].getAttribute('src')) {
|
||
loc = jsArray[i].getAttribute('src');
|
||
}
|
||
if (!loc) {
|
||
continue;
|
||
} else {
|
||
s = loc.indexOf('Hyphenator.js?');
|
||
if (s === -1) {
|
||
continue;
|
||
}
|
||
gp = loc.substring(s + 14).split('&');
|
||
for (j = 0; j < gp.length; j++) {
|
||
option = gp[j].split('=');
|
||
if (option[0] === 'bm') {
|
||
continue;
|
||
}
|
||
if (option[1] === 'true') {
|
||
re[option[0]] = true;
|
||
continue;
|
||
}
|
||
if (option[1] === 'false') {
|
||
re[option[0]] = false;
|
||
continue;
|
||
}
|
||
if (isFinite(option[1])) {
|
||
re[option[0]] = parseInt(option[1], 10);
|
||
continue;
|
||
}
|
||
if (option[0] === 'onhyphenationdonecallback') {
|
||
re[option[0]] = new Function('', option[1]);
|
||
continue;
|
||
}
|
||
re[option[0]] = option[1];
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
return re;
|
||
},
|
||
|
||
/**
|
||
* @name Hyphenator.toggleHyphenation
|
||
* @description
|
||
* Checks the current state of the ToggleBox and removes or does hyphenation.
|
||
* @public
|
||
*/
|
||
toggleHyphenation: function () {
|
||
if (Hyphenator.doHyphenation) {
|
||
removeHyphenationFromDocument();
|
||
Hyphenator.doHyphenation = false;
|
||
storeConfiguration();
|
||
toggleBox();
|
||
} else {
|
||
hyphenateLanguageElements('*');
|
||
Hyphenator.doHyphenation = true;
|
||
storeConfiguration();
|
||
toggleBox();
|
||
}
|
||
}
|
||
};
|
||
}(window));
|
||
|
||
//Export properties/methods (for google closure compiler)
|
||
Hyphenator['languages'] = Hyphenator.languages;
|
||
Hyphenator['config'] = Hyphenator.config;
|
||
Hyphenator['run'] = Hyphenator.run;
|
||
Hyphenator['addExceptions'] = Hyphenator.addExceptions;
|
||
Hyphenator['hyphenate'] = Hyphenator.hyphenate;
|
||
Hyphenator['getRedPatternSet'] = Hyphenator.getRedPatternSet;
|
||
Hyphenator['isBookmarklet'] = Hyphenator.isBookmarklet;
|
||
Hyphenator['getConfigFromURI'] = Hyphenator.getConfigFromURI;
|
||
Hyphenator['toggleHyphenation'] = Hyphenator.toggleHyphenation;
|
||
window['Hyphenator'] = Hyphenator;
|
||
|
||
if (Hyphenator.isBookmarklet()) {
|
||
Hyphenator.config({displaytogglebox: true, intermediatestate: 'visible', doframes: true});
|
||
Hyphenator.config(Hyphenator.getConfigFromURI());
|
||
Hyphenator.run();
|
||
} |