UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

654 lines (597 loc) 20.3 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([ "./HyphenationTestingWords", "sap/ui/base/ManagedObject", "sap/base/Log", "sap/ui/core/Locale", "sap/ui/core/Configuration" ], function ( HyphenationTestingWords, ManagedObject, Log, Locale, Configuration ) { "use strict"; /** * Flat list of languages that are supported by Hyphenopoly. * @type {Object<string,boolean>} * @private */ var oThirdPartySupportedLanguages = { 'bg': true, 'ca': true, 'hr': true, 'cs': false, // no valid license 'da': true, 'nl': true, 'en-us': true, 'et': true, 'fi': true, 'fr': true, 'de': true, 'el-monoton': true, 'hi': true, 'hu': true, 'it': true, 'lt': true, 'nb-no': true, 'pl': false, // no valid license 'pt': true, 'ru': true, 'sr': false, // no valid license 'sl': true, 'es': true, 'sv': true, 'th': true, 'tr': true, 'uk': true }; /** * Holds a map of names of languages in english. Like <code>{"de" => "German"}</code> * @type {Object<string,string>} * @private */ var mLanguageNamesInEnglish = { "bg": "Bulgarian", "ca": "Catalan", "hr": "Croatian", "cs": "Czech", "da": "Danish", "nl": "Dutch", "en": "English", "et": "Estonian", "fi": "Finnish", "fr": "French", "de": "German", "el": "Greek", "hi": "Hindi", "hu": "Hungarian", "it": "Italian", "lt": "Lithuanian", "nb": "Norwegian Bokmål", "no": "Norwegian", "pl": "Polish", "pt": "Portuguese", "ru": "Russian", "sr": "Serbian", "sl": "Slovenian", "es": "Spanish", "sv": "Swedish", "th": "Thai", "tr": "Turkish", "uk": "Ukrainian" }; var oBrowserSupportCSS = {}; var oSupportCheck = {}; var oThirdPartySupportCheck = {}; var oHyphenationInstance = null; var fakeBody = null; var oHyphenateMethods = {}; /** * Calls Hyphenopoly to initialize a language. * Loads language-specific resources. * * @param {string} sLanguage What language to initialize * @returns {Promise} Promise that resolves with the hyphenator function for that language * @private */ function initializeLanguage(sLanguage) { Log.info( "[UI5 Hyphenation] Initializing third-party module for language " + getLanguageDisplayName(sLanguage), "sap.ui.core.hyphenation.Hyphenation.initialize()" ); var oHyphenopolyConfig = createHyphenopolyConfig(); oHyphenopolyConfig.require[sLanguage] = "FORCEHYPHENOPOLY"; // force loading of the engine for this language return loadScript(sap.ui.require.toUrl("sap/ui/thirdparty/hyphenopoly/"), "Hyphenopoly_Loader.js") .then(function () { delete oHyphenopolyConfig.require[sLanguage]; return window.Hyphenopoly.hyphenators[sLanguage]; }); } function createHyphenopolyConfig() { if (!window.Hyphenopoly) { window.Hyphenopoly = { require: {}, setup: { selectors: { ".hyphenate": { // .hyphenate is the default CSS class (hence this is the default configuration for all words and langs) hyphen: "\u00AD", leftmin: 3, rightmin: 3, compound: "all" // hyphenate the parts and insert a zero-width space after the hyphen } }, hide: "DONT_HIDE" // prevent visiblity: hidden; of html tag while the engine is loading }, handleEvent: { error: function (e) { // Hyphenopoly will try to find DOM elements and hyphenate them, // but since we use only the hyphenators, prevent the warning if (e.msg.match(/engine for language .* loaded, but no elements found./)) { e.preventDefault(); //don't show error message in console } } } }; } return window.Hyphenopoly; } /** * Loads a <code>javascript</code> file. * * @param {string} sPath The root path * @param {string} sFilename File to be loaded * @returns {Promise} A promise which resolves when the script is loaded * @private */ function loadScript(sPath, sFilename) { return new Promise(function (resolve, reject) { var script = document.createElement('script'); script.async = true; script.src = sPath + sFilename; script.addEventListener('load', resolve); script.addEventListener('error', function () { return reject('Error loading script: ' + sFilename); }); script.addEventListener('abort', function () { return reject(sFilename + ' Script loading aborted.'); }); document.head.appendChild(script); }); } /** * Holds CSS for a test div for test of native hyphenation. * @type {string} */ var css = (function createCss() { var props = [ "visibility:hidden;", "-moz-hyphens:auto;", "-webkit-hyphens:auto;", "hyphens:auto;", "width:48px;", "font-size:12px;", "line-height:12px;", "border:none;", "padding:0;", "word-wrap:normal" ]; return props.join(""); }()); /** * Creates and appends div with CSS-hyphenated word. * * @param {string} sLanguageOnThePage Language (<code>lang</code> attribute of the HTML page) * @param {string} sTestingWord Long word for that language * @private */ function createTest(sLanguageOnThePage, sTestingWord) { if (!fakeBody) { fakeBody = document.createElement("body"); } var testDiv = document.createElement("div"); testDiv.lang = sLanguageOnThePage; testDiv.id = sLanguageOnThePage; testDiv.style.cssText = css; testDiv.appendChild(document.createTextNode(sTestingWord)); fakeBody.appendChild(testDiv); } /** * Appends fakeBody with tests to target. * * @param {Element} oTarget Where to append fakeBody * @returns {Element|null} The body element or null, if no tests * @private */ function appendTests(oTarget) { if (fakeBody) { oTarget.appendChild(fakeBody); return fakeBody; } return null; } /** * Removes fakeBody. * * @private */ function clearTests() { if (fakeBody) { fakeBody.parentNode.removeChild(fakeBody); } } /** * Checks if hyphens (ev.prefixed) is set to auto for the element. * * @param {Element} oElement The element with applied <code>hyphens=auto</code> styles * @returns {boolean} The result of the check * @private */ function checkCSSHyphensSupport(oElement) { return ( oElement.style.hyphens === "auto" || oElement.style.webkitHyphens === "auto" || oElement.style.msHyphens === "auto" || oElement.style["-moz-hyphens"] === "auto" ); } /** * Gets global language code or the given language code. * * @param {string} [sLang] The language to get. If left empty - the global application language will be returned * @returns {string} The language code * @private */ function getLanguage(sLang) { var oLocale; if (sLang) { oLocale = new Locale(sLang); } else { oLocale = Configuration.getLocale(); } var sLanguage = oLocale.getLanguage().toLowerCase(); // adjustment of the language to correspond to Hyphenopoly pattern files (.hpb files) switch (sLanguage) { case "en": sLanguage = "en-us"; break; case "nb": sLanguage = "nb-no"; break; case "no": sLanguage = "nb-no"; break; case "el": sLanguage = "el-monoton"; break; default: break; } return sLanguage; } /** * The <code>lang</code> attribute of the closest parent determines the behavior of the native hyphenation. * Typically this is the HTML tag and its value can be read with the <code>getLocale</code> function. * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] The language to get. If left empty - the global application language will be returned * @returns {string} The language code * @private */ function getLanguageAsSetOnThePage(sLang) { if (sLang) { return new Locale(sLang).toString(); } return Configuration.getLocale().toString(); } /** * Gets language code from pattern name (hbp file name). * * @param {string} sPatternName The hpb file name * @return {string} Language code */ function getLanguageFromPattern(sPatternName) { if (typeof sPatternName === "string") { return sPatternName.substring(0, 2); // get the main language code only } else { return null; } } /** * Gets a human readable english name for the language. * If not found - returns a string with the language code. * * @param {string} sPatternName The pattern name (hpb file name) * @return {string} Returns a human readable english name for the language */ function getLanguageDisplayName(sPatternName) { var sLang = getLanguageFromPattern(sPatternName); if (mLanguageNamesInEnglish.hasOwnProperty(sLang)) { return "'" + mLanguageNamesInEnglish[sLang] + "' (code:'" + sLang + "')"; } else { return "'" + sLang + "'"; } } /** * Logs an error and fires the error event. * * @param {string} sErrorMessage The message of the error which is thrown/logged * @private */ function fireError(sErrorMessage) { oHyphenationInstance.fireError(sErrorMessage); Log.error("[UI5 Hyphenation] " + sErrorMessage, "sap.ui.core.hyphenation.Hyphenation"); } /** * @class * This class provides methods for evaluating the possibility of using browser-native hyphenation or initializing and using a third-party hyphenation module. * * <h3>Overview</h3> * By using this API, a developer can check if browser-native hyphenation is supported for a particular language. * * When browser-native hyphenation is not supported or if otherwise required, the API can be used to hyphenate texts. A third-party library "Hyphenopoly" is used in that case. * * It is used internally by controls that support the <code>wrappingType:{@link sap.m.WrappingType WrappingType.Hyphenated}</code> property. * * As the class is singleton, an instance should be acquired from {@link sap.ui.core.hyphenation.Hyphenation.getInstance Hyphenation.getInstance}. * * <h3>Usage</h3> * <h4>When to use:</h4> * <ul> * <li>To check if browser-native hyphenation is available for particular language.</li> * <li>To hyphenate texts if browser-native hyphenation is not available.</li> * </ul> * <h4>When not to use:</h4> * <ul> * <li> * If the use case is covered by controls that support the property <code>wrappingType:{@link sap.m.WrappingType WrappingType.Hyphenated}</code>. * This functionality is supported by {@link sap.m.Title sap.m.Title}, {@link sap.m.Label sap.m.Label} and {@link sap.m.Text sap.m.Text}. * </li> * <li>If browser-native hyphenation is available</li> * </ul> * * <h3>Example</h3> * <pre> * if (!Hyphenation.getInstance().canUseNativeHyphenation("en")) { * Hyphenation.getInstance().initialize("en").then(function() { * console.log(Hyphenation.getInstance().hyphenate("An example text to hyphenate.", "en")); * }); * } * </pre> * * For more information, see {@link topic:6322164936f047de941ec522b95d7b70 Hyphenation for Text Controls}. * * <code>Caution:</code> Note that as the hyphenation feature uses third-party * and browser-native tools, we are not responsible for any grammatical incorrectness * or inconsistencies of the hyphenation. Also, the variety of supported languages is * outside the scope of our control and may be subject to future changes. * * @see {@link topic:6322164936f047de941ec522b95d7b70 Hyphenation for Text Controls} * @extends sap.ui.base.ManagedObject * @author SAP SE * @version 1.111.5 * @hideconstructor * @public * @since 1.60 * @alias sap.ui.core.hyphenation.Hyphenation */ var Hyphenation = ManagedObject.extend("sap.ui.core.hyphenation.Hyphenation", { metadata: { library: "sap.ui.core", events: { /** * Fired if an error with initialization or hyphenation occurs. * @private */ error: { parameters: { /** * The message of the error. */ sErrorMessage: {type : "string"} } } } } }); /** * Checks if native hyphenation works in the current browser for the given language. * This check is performed against the value of the "lang" HTML attribute of the page. * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] For what language to check. The global application language is the default one * @returns {(boolean|null)} True if native hyphenation works for the given language. False if native hyphenation will not work. Null if the language is not known to the Hyphenation API * @public */ Hyphenation.prototype.canUseNativeHyphenation = function (sLang) { var sLanguageOnThePage = getLanguageAsSetOnThePage(sLang), sMappedLanguage = getLanguage(sLang), bCanUseNativeHyphenation; if (!this.isLanguageSupported(sMappedLanguage)) { return null; } if (!oBrowserSupportCSS.hasOwnProperty(sLanguageOnThePage)) { createTest(sLanguageOnThePage, HyphenationTestingWords[sMappedLanguage.toLowerCase()]); var testContainer = appendTests(document.documentElement); if (testContainer !== null) { var el = document.getElementById(sLanguageOnThePage); if (checkCSSHyphensSupport(el) && el.offsetHeight > 12) { bCanUseNativeHyphenation = true; } else { bCanUseNativeHyphenation = false; } clearTests(); } oBrowserSupportCSS[sLanguageOnThePage] = bCanUseNativeHyphenation; if (bCanUseNativeHyphenation) { Log.info( "[UI5 Hyphenation] Browser-native hyphenation can be used for language " + getLanguageDisplayName(sLanguageOnThePage), "sap.ui.core.hyphenation.Hyphenation.canUseNativeHyphenation()" ); } else { Log.info( "[UI5 Hyphenation] Browser-native hyphenation is not supported by current platform for language " + getLanguageDisplayName(sLanguageOnThePage), "sap.ui.core.hyphenation.Hyphenation.canUseNativeHyphenation()" ); } } else { bCanUseNativeHyphenation = oBrowserSupportCSS[sLanguageOnThePage]; } return bCanUseNativeHyphenation; }; /** * Checks if third-party hyphenation works for the given language. * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] For what language to check. The global application language is the default one. * @returns {boolean|null} True if third-party hyphenation works for the given language. False if third-party hyphenation doesn't work. Null if the language is not known to the <code>Hyphenation</code> API. * @public */ Hyphenation.prototype.canUseThirdPartyHyphenation = function (sLang) { var sLanguage = getLanguage(sLang), bCanUseThirdPartyHyphenation; if (!this.isLanguageSupported(sLang)) { return null; } if (!oThirdPartySupportCheck.hasOwnProperty(sLanguage)) { bCanUseThirdPartyHyphenation = oThirdPartySupportedLanguages.hasOwnProperty(sLanguage) && oThirdPartySupportedLanguages[sLanguage]; if (bCanUseThirdPartyHyphenation) { Log.info( "[UI5 Hyphenation] Third-party hyphenation can be used for language " + getLanguageDisplayName(sLanguage), "sap.ui.core.hyphenation.Hyphenation.canUseThirdPartyHyphenation()" ); } else { Log.info( "[UI5 Hyphenation] Third-party hyphenation is not supported for language " + getLanguageDisplayName(sLanguage), "sap.ui.core.hyphenation.Hyphenation.canUseThirdPartyHyphenation()" ); } oThirdPartySupportCheck[sLanguage] = bCanUseThirdPartyHyphenation; } else { bCanUseThirdPartyHyphenation = oThirdPartySupportCheck[sLanguage]; } return bCanUseThirdPartyHyphenation; }; /** * Checks if <code>Hyphenation</code> API knows about the given language. * * If it is a known language, the API can be used to check browser-native and third-party support. * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] For what language to check. The global application language is the default one. * @returns {boolean} True if the language is known to the <code>Hyphenation</code> API * @public */ Hyphenation.prototype.isLanguageSupported = function (sLang) { var sLanguage = getLanguage(sLang), bIsSupported; if (!oSupportCheck.hasOwnProperty(sLanguage)) { bIsSupported = HyphenationTestingWords.hasOwnProperty(sLanguage); if (!bIsSupported) { Log.info( "[UI5 Hyphenation] Language " + getLanguageDisplayName(sLanguage) + " is not known to the Hyphenation API", "sap.ui.core.hyphenation.Hyphenation.isLanguageSupported()" ); } oSupportCheck[sLanguage] = bIsSupported; } else { bIsSupported = oSupportCheck[sLanguage]; } return bIsSupported; }; /** * Hyphenates the given text with the third-party library. * * Adds the soft hyphen symbol at the places where words can break. * * @param {string} sText The text to hyphenate * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] The language of the text. The global application language is the default one * @returns {string} The text with the hyphens symbol added * @public */ Hyphenation.prototype.hyphenate = function (sText, sLang) { var sLanguage = getLanguage(sLang); if (!oHyphenateMethods.hasOwnProperty(sLanguage)) { fireError("Language " + getLanguageDisplayName(sLanguage) + " is not initialized. You have to initialize it first with method 'initialize()'"); return sText; } return oHyphenateMethods[sLanguage](sText); }; /** * What languages were initialized with {@link sap.ui.core.hyphenation.Hyphenation#initialize Hyphenation#initialize} * * @returns {Array} List of languages which were initialized * @public */ Hyphenation.prototype.getInitializedLanguages = function () { return Object.keys(oHyphenateMethods).map(function(sLangPattern) { return getLanguageFromPattern(sLangPattern); }); }; /** * Checks if the given language was initialized with {@link sap.ui.core.hyphenation.Hyphenation#initialize Hyphenation#initialize} * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] The language to check for * @returns {boolean} True if the language was initialized * @public */ Hyphenation.prototype.isLanguageInitialized = function (sLang) { var sLang = getLanguage(sLang); return Object.keys(oHyphenateMethods).indexOf(sLang) != -1; }; /** * Initializes the third-party library for the given language. * * Loads required third-party resources and language-specific resources. * * @example * Hyphenation.getInstance().initialize("en").then(function() { * console.log(Hyphenation.getInstance().hyphenate("An example text to hyphenate.", "en")); * }); * * @param {string} [sLang=sap.ui.getCore().getConfiguration().getLocale().toString()] The language for which the third-party library should be initialized. The global application language is the default one * @returns {Promise} A promise which resolves when all language resources are loaded. Rejects if the language is not supported * @public */ Hyphenation.prototype.initialize = function (sLang) { var sLanguage = getLanguage(sLang); if (!oThirdPartySupportedLanguages[sLanguage]) { var sMessage = "Language " + getLanguageDisplayName(sLang) + " can not be initialized. It is either not supported by the third-party module or an error occurred"; fireError(sMessage); return Promise.reject(sMessage); } if (oHyphenateMethods[sLanguage]) { return Promise.resolve(); } var pInitLanguage; if (!this._pInitLanguage) { pInitLanguage = this._pInitLanguage = initializeLanguage(sLanguage) .then(function (fnHyphenator) { oHyphenateMethods[sLanguage] = fnHyphenator; this._pInitLanguage = null; }.bind(this)); } else { // await the loading of the previous language, then initialize the current pInitLanguage = this._pInitLanguage.then(function () { return this.initialize(sLang); }.bind(this)); } return pInitLanguage; }; /** * Returns the singleton instance of the Hyphenation API. * * @see sap.ui.core.hyphenation.Hyphenation * @returns {sap.ui.core.hyphenation.Hyphenation} The singleton instance of the Hyphenation API * @static * @public */ Hyphenation.getInstance = function () { if (!oHyphenationInstance) { oHyphenationInstance = new Hyphenation(); } return oHyphenationInstance; }; return Hyphenation; });