UNPKG

closure-builder

Version:

Simple Closure, Soy and JavaScript Build system

450 lines (417 loc) 20.5 kB
// Copyright 2006 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Detect Grapheme Cluster Break in a pair of codepoints. Follows * Unicode 10 UAX#29. Tailoring for Virama × Indic Letters is used. * * Reference: http://unicode.org/reports/tr29 * */ goog.provide('goog.i18n.GraphemeBreak'); goog.require('goog.asserts'); goog.require('goog.i18n.uChar'); goog.require('goog.structs.InversionMap'); /** * Enum for all Grapheme Cluster Break properties. * These enums directly corresponds to Grapheme_Cluster_Break property values * mentioned in http://unicode.org/reports/tr29 table 2. VIRAMA and * INDIC_LETTER are for the Virama × Base tailoring mentioned in the notes. * * @protected @enum {number} */ goog.i18n.GraphemeBreak.property = { OTHER: 0, CONTROL: 1, EXTEND: 2, PREPEND: 3, SPACING_MARK: 4, INDIC_LETTER: 5, VIRAMA: 6, L: 7, V: 8, T: 9, LV: 10, LVT: 11, CR: 12, LF: 13, REGIONAL_INDICATOR: 14, ZWJ: 15, E_BASE: 16, GLUE_AFTER_ZWJ: 17, E_MODIFIER: 18, E_BASE_GAZ: 19 }; /** * Grapheme Cluster Break property values for all codepoints as inversion map. * Constructed lazily. * * @private {?goog.structs.InversionMap} */ goog.i18n.GraphemeBreak.inversions_ = null; /** * Indicates if a and b form a grapheme cluster. * * This implements the rules in: * http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules * * @param {number|string} a Code point or string with the first side of * grapheme cluster. * @param {number|string} b Code point or string with the second side of * grapheme cluster. * @param {boolean} extended If true, indicates extended grapheme cluster; * If false, indicates legacy cluster. * @return {boolean} True if a & b do not form a cluster; False otherwise. * @private */ goog.i18n.GraphemeBreak.applyBreakRules_ = function(a, b, extended) { var prop = goog.i18n.GraphemeBreak.property; var aCode = goog.isString(a) ? goog.i18n.GraphemeBreak.getCodePoint_(a, a.length - 1) : a; var bCode = goog.isString(b) ? goog.i18n.GraphemeBreak.getCodePoint_(b, 0) : b; var aProp = goog.i18n.GraphemeBreak.getBreakProp_(aCode); var bProp = goog.i18n.GraphemeBreak.getBreakProp_(bCode); var isString = goog.isString(a); // GB3. if (aProp === prop.CR && bProp === prop.LF) { return false; } // GB4. if (aProp === prop.CONTROL || aProp === prop.CR || aProp === prop.LF) { return true; } // GB5. if (bProp === prop.CONTROL || bProp === prop.CR || bProp === prop.LF) { return true; } // GB6. if (aProp === prop.L && (bProp === prop.L || bProp === prop.V || bProp === prop.LV || bProp === prop.LVT)) { return false; } // GB7. if ((aProp === prop.LV || aProp === prop.V) && (bProp === prop.V || bProp === prop.T)) { return false; } // GB8. if ((aProp === prop.LVT || aProp === prop.T) && bProp === prop.T) { return false; } // GB9. if (bProp === prop.EXTEND || bProp === prop.ZWJ || bProp === prop.VIRAMA) { return false; } // GB9a, GB9b. if (extended && (aProp === prop.PREPEND || bProp === prop.SPACING_MARK)) { return false; } // Tailorings for basic aksara support. if (extended && aProp === prop.VIRAMA && bProp === prop.INDIC_LETTER) { return false; } var aStr, index, codePoint, codePointProp; // GB10. if (isString) { if (bProp === prop.E_MODIFIER) { // If using new API, consume the string's code points starting from the // end and test the left side of: (E_Base | EBG) Extend* × E_Modifier. aStr = /** @type {string} */ (a); index = aStr.length - 1; codePoint = aCode; codePointProp = aProp; while (index > 0 && codePointProp === prop.EXTEND) { index -= goog.i18n.uChar.charCount(codePoint); codePoint = goog.i18n.GraphemeBreak.getCodePoint_(aStr, index); codePointProp = goog.i18n.GraphemeBreak.getBreakProp_(codePoint); } if (codePointProp === prop.E_BASE || codePointProp === prop.E_BASE_GAZ) { return false; } } } else { // If using legacy API, return best effort by testing: // (E_Base | EBG) × E_Modifier. if ((aProp === prop.E_BASE || aProp === prop.E_BASE_GAZ) && bProp === prop.E_MODIFIER) { return false; } } // GB11. if (aProp === prop.ZWJ && (bProp === prop.GLUE_AFTER_ZWJ || bProp === prop.E_BASE_GAZ)) { return false; } // GB12, GB13. if (isString) { if (bProp === prop.REGIONAL_INDICATOR) { // If using new API, consume the string's code points starting from the // end and test the left side of these rules: // - sot (RI RI)* RI × RI // - [^RI] (RI RI)* RI × RI. var numberOfRi = 0; aStr = /** @type {string} */ (a); index = aStr.length - 1; codePoint = aCode; codePointProp = aProp; while (index > 0 && codePointProp === prop.REGIONAL_INDICATOR) { numberOfRi++; index -= goog.i18n.uChar.charCount(codePoint); codePoint = goog.i18n.GraphemeBreak.getCodePoint_(aStr, index); codePointProp = goog.i18n.GraphemeBreak.getBreakProp_(codePoint); } if (codePointProp === prop.REGIONAL_INDICATOR) { numberOfRi++; } if (numberOfRi % 2 === 1) { return false; } } } else { // If using legacy API, return best effort by testing: RI × RI. if (aProp === prop.REGIONAL_INDICATOR && bProp === prop.REGIONAL_INDICATOR) { return false; } } // GB999. return true; }; /** * Method to return property enum value of the code point. If it is Hangul LV or * LVT, then it is computed; for the rest it is picked from the inversion map. * * @param {number} codePoint The code point value of the character. * @return {number} Property enum value of code point. * @private */ goog.i18n.GraphemeBreak.getBreakProp_ = function(codePoint) { if (0xAC00 <= codePoint && codePoint <= 0xD7A3) { var prop = goog.i18n.GraphemeBreak.property; if (codePoint % 0x1C === 0x10) { return prop.LV; } return prop.LVT; } else { if (!goog.i18n.GraphemeBreak.inversions_) { goog.i18n.GraphemeBreak.inversions_ = new goog.structs.InversionMap( [ 0, 10, 1, 2, 1, 18, 95, 33, 13, 1, 594, 112, 275, 7, 263, 45, 1, 1, 1, 2, 1, 2, 1, 1, 56, 6, 10, 11, 1, 1, 46, 21, 16, 1, 101, 7, 1, 1, 6, 2, 2, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 9, 34, 4, 1, 9, 1, 3, 1, 5, 43, 3, 120, 14, 1, 32, 1, 17, 37, 1, 1, 1, 1, 3, 8, 4, 1, 2, 1, 7, 8, 2, 2, 21, 7, 1, 1, 2, 17, 39, 1, 1, 1, 2, 6, 6, 1, 9, 5, 4, 2, 2, 12, 2, 15, 2, 1, 17, 39, 2, 3, 12, 4, 8, 6, 17, 2, 3, 14, 1, 17, 39, 1, 1, 3, 8, 4, 1, 20, 2, 29, 1, 2, 17, 39, 1, 1, 2, 1, 6, 6, 9, 6, 4, 2, 2, 13, 1, 16, 1, 18, 41, 1, 1, 1, 12, 1, 9, 1, 40, 1, 3, 17, 31, 1, 5, 4, 3, 5, 7, 8, 3, 2, 8, 2, 29, 1, 2, 17, 39, 1, 1, 1, 1, 2, 1, 3, 1, 5, 1, 8, 9, 1, 3, 2, 29, 1, 2, 17, 38, 3, 1, 2, 5, 7, 1, 1, 8, 1, 10, 2, 30, 2, 22, 48, 5, 1, 2, 6, 7, 1, 18, 2, 13, 46, 2, 1, 1, 1, 6, 1, 12, 8, 50, 46, 2, 1, 1, 1, 9, 11, 6, 14, 2, 58, 2, 27, 1, 1, 1, 1, 1, 4, 2, 49, 14, 1, 4, 1, 1, 2, 5, 48, 9, 1, 57, 33, 12, 4, 1, 6, 1, 2, 2, 2, 1, 16, 2, 4, 2, 2, 4, 3, 1, 3, 2, 7, 3, 4, 13, 1, 1, 1, 2, 6, 1, 1, 14, 1, 98, 96, 72, 88, 349, 3, 931, 15, 2, 1, 14, 15, 2, 1, 14, 15, 2, 15, 15, 14, 35, 17, 2, 1, 7, 8, 1, 2, 9, 1, 1, 9, 1, 45, 3, 1, 118, 2, 34, 1, 87, 28, 3, 3, 4, 2, 9, 1, 6, 3, 20, 19, 29, 44, 84, 23, 2, 2, 1, 4, 45, 6, 2, 1, 1, 1, 8, 1, 1, 1, 2, 8, 6, 13, 48, 84, 1, 14, 33, 1, 1, 5, 1, 1, 5, 1, 1, 1, 7, 31, 9, 12, 2, 1, 7, 23, 1, 4, 2, 2, 2, 2, 2, 11, 3, 2, 36, 2, 1, 1, 2, 3, 1, 1, 3, 2, 12, 36, 8, 8, 2, 2, 21, 3, 128, 3, 1, 13, 1, 7, 4, 1, 4, 2, 1, 3, 2, 198, 64, 523, 1, 1, 1, 2, 24, 7, 49, 16, 96, 33, 1324, 1, 34, 1, 1, 1, 82, 2, 98, 1, 14, 1, 1, 4, 86, 1, 1418, 3, 141, 1, 96, 32, 554, 6, 105, 2, 30164, 4, 1, 10, 32, 2, 80, 2, 272, 1, 3, 1, 4, 1, 23, 2, 2, 1, 24, 30, 4, 4, 3, 8, 1, 1, 13, 2, 16, 34, 16, 1, 1, 26, 18, 24, 24, 4, 8, 2, 23, 11, 1, 1, 12, 32, 3, 1, 5, 3, 3, 36, 1, 2, 4, 2, 1, 3, 1, 36, 1, 32, 35, 6, 2, 2, 2, 2, 12, 1, 8, 1, 1, 18, 16, 1, 3, 6, 1, 1, 1, 3, 48, 1, 1, 3, 2, 2, 5, 2, 1, 1, 32, 9, 1, 2, 2, 5, 1, 1, 201, 14, 2, 1, 1, 9, 8, 2, 1, 2, 1, 2, 1, 1, 1, 18, 11184, 27, 49, 1028, 1024, 6942, 1, 737, 16, 16, 16, 207, 1, 158, 2, 89, 3, 513, 1, 226, 1, 149, 5, 1670, 15, 40, 7, 1, 165, 2, 1305, 1, 1, 1, 53, 14, 1, 56, 1, 2, 1, 45, 3, 4, 2, 1, 1, 2, 1, 66, 3, 36, 5, 1, 6, 2, 62, 1, 12, 2, 1, 48, 3, 9, 1, 1, 1, 2, 6, 3, 95, 3, 3, 2, 1, 1, 2, 6, 1, 160, 1, 3, 7, 1, 21, 2, 2, 56, 1, 1, 1, 1, 1, 12, 1, 9, 1, 10, 4, 15, 192, 3, 8, 2, 1, 2, 1, 1, 105, 1, 2, 6, 1, 1, 2, 1, 1, 2, 1, 1, 1, 235, 1, 2, 6, 4, 2, 1, 1, 1, 27, 2, 82, 3, 8, 2, 1, 1, 1, 1, 106, 1, 1, 1, 2, 6, 1, 1, 101, 3, 2, 4, 1, 4, 1, 1283, 1, 14, 1, 1, 82, 23, 1, 7, 1, 2, 1, 2, 20025, 5, 59, 7, 1050, 62, 4, 19722, 2, 1, 4, 5313, 1, 1, 3, 3, 1, 5, 8, 8, 2, 7, 30, 4, 148, 3, 1979, 55, 4, 50, 8, 1, 14, 1, 22, 1424, 2213, 7, 109, 7, 2203, 26, 264, 1, 53, 1, 52, 1, 17, 1, 13, 1, 16, 1, 3, 1, 25, 3, 2, 1, 2, 3, 30, 1, 1, 1, 13, 5, 66, 2, 2, 11, 21, 4, 4, 1, 1, 9, 3, 1, 4, 3, 1, 3, 3, 1, 30, 1, 16, 2, 106, 1, 4, 1, 71, 2, 4, 1, 21, 1, 4, 2, 81, 1, 92, 3, 3, 5, 48, 1, 17, 1, 16, 1, 16, 3, 9, 1, 11, 1, 587, 5, 1, 1, 7, 1, 9, 10, 3, 2, 788162, 31 ], [ 1, 13, 1, 12, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 3, 0, 2, 0, 1, 0, 2, 0, 2, 0, 2, 3, 0, 2, 0, 2, 0, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 3, 2, 4, 0, 5, 2, 4, 2, 0, 4, 2, 4, 6, 4, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4, 6, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0, 5, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4, 0, 5, 2, 0, 4, 2, 4, 6, 0, 2, 0, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4, 6, 2, 5, 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 2, 4, 6, 0, 2, 0, 2, 4, 0, 5, 0, 5, 0, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4, 0, 5, 2, 0, 4, 2, 4, 2, 4, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4, 0, 5, 0, 2, 4, 2, 4, 6, 3, 0, 2, 0, 2, 0, 4, 0, 5, 6, 2, 4, 2, 4, 2, 0, 4, 0, 5, 0, 2, 0, 4, 2, 6, 0, 2, 0, 5, 0, 2, 0, 4, 2, 0, 2, 0, 5, 0, 2, 0, 2, 0, 2, 0, 2, 0, 4, 5, 2, 4, 2, 6, 0, 2, 0, 2, 0, 2, 0, 5, 0, 2, 4, 2, 0, 6, 4, 2, 5, 0, 5, 0, 4, 2, 5, 2, 5, 0, 5, 0, 5, 2, 5, 2, 0, 4, 2, 0, 2, 5, 0, 2, 0, 7, 8, 9, 0, 2, 0, 5, 2, 6, 0, 5, 2, 6, 0, 5, 2, 0, 5, 2, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 2, 0, 2, 0, 2, 1, 0, 2, 0, 2, 0, 5, 0, 2, 4, 2, 4, 2, 4, 2, 0, 5, 0, 5, 0, 5, 2, 4, 2, 0, 5, 0, 5, 4, 2, 4, 2, 6, 0, 2, 0, 2, 4, 2, 0, 2, 4, 0, 5, 2, 4, 2, 4, 2, 4, 2, 4, 6, 5, 0, 2, 0, 2, 4, 0, 5, 4, 2, 4, 2, 6, 2, 5, 0, 5, 0, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 0, 5, 4, 2, 4, 2, 0, 5, 0, 2, 0, 2, 4, 2, 0, 2, 0, 4, 2, 0, 2, 0, 2, 0, 1, 2, 15, 1, 0, 1, 0, 1, 0, 2, 0, 16, 0, 17, 0, 17, 0, 17, 0, 16, 0, 17, 0, 16, 0, 17, 0, 2, 0, 6, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 6, 5, 2, 5, 4, 2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 0, 4, 0, 5, 4, 6, 2, 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 6, 0, 7, 2, 4, 0, 5, 0, 5, 2, 4, 2, 4, 2, 4, 6, 0, 2, 0, 5, 2, 4, 2, 4, 2, 0, 2, 0, 2, 4, 0, 5, 0, 5, 0, 5, 0, 2, 0, 5, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 5, 4, 2, 4, 0, 4, 6, 0, 5, 0, 5, 0, 5, 0, 4, 2, 4, 2, 4, 0, 4, 6, 0, 11, 8, 9, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 6, 0, 2, 0, 4, 2, 4, 0, 2, 6, 0, 6, 2, 4, 0, 4, 2, 4, 6, 2, 0, 3, 0, 2, 0, 2, 4, 2, 6, 0, 2, 0, 2, 4, 0, 4, 2, 4, 6, 0, 3, 0, 2, 0, 4, 2, 4, 2, 6, 2, 0, 2, 0, 2, 4, 2, 6, 0, 2, 4, 0, 2, 0, 2, 4, 2, 4, 6, 0, 2, 0, 4, 2, 0, 4, 2, 4, 6, 2, 4, 2, 0, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 6, 2, 0, 2, 4, 2, 4, 2, 4, 6, 2, 0, 2, 0, 4, 2, 4, 2, 4, 6, 2, 0, 2, 4, 2, 4, 2, 6, 2, 0, 2, 4, 2, 4, 2, 6, 0, 4, 2, 4, 6, 0, 2, 4, 2, 4, 2, 4, 2, 0, 2, 0, 2, 0, 4, 2, 0, 2, 0, 1, 0, 2, 4, 2, 0, 4, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 14, 0, 17, 0, 17, 0, 17, 0, 16, 0, 17, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 17, 0, 17, 0, 18, 0, 16, 0, 16, 0, 19, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 17, 0, 16, 0, 17, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 16, 0, 17, 0, 16, 0, 16, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 1, 2 ], true); } return /** @type {number} */ ( goog.i18n.GraphemeBreak.inversions_.at(codePoint)); } }; /** * Extracts a code point from a string at the specified index. * * @param {string} str * @param {number} index * @return {number} Extracted code point. * @private */ goog.i18n.GraphemeBreak.getCodePoint_ = function(str, index) { var codePoint = goog.i18n.uChar.getCodePointAround(str, index); return (codePoint < 0) ? -codePoint : codePoint; }; /** * Indicates if there is a grapheme cluster boundary between a and b. * * Legacy function. Does not cover cases where a sequence of code points is * required in order to decide if there is a grapheme cluster boundary, such as * emoji modifier sequences and emoji flag sequences. To cover all cases please * use `hasGraphemeBreakStrings`. * * There are two kinds of grapheme clusters: 1) Legacy 2) Extended. This method * is to check for both using a boolean flag to switch between them. If no flag * is provided rules for the extended clusters will be used by default. * * @param {number} a The code point value of the first character. * @param {number} b The code point value of the second character. * @param {boolean=} opt_extended If true, indicates extended grapheme cluster; * If false, indicates legacy cluster. Default value is true. * @return {boolean} True if there is a grapheme cluster boundary between * a and b; False otherwise. */ goog.i18n.GraphemeBreak.hasGraphemeBreak = function(a, b, opt_extended) { return goog.i18n.GraphemeBreak.applyBreakRules_(a, b, opt_extended !== false); }; /** * Indicates if there is a grapheme cluster boundary between a and b. * * There are two kinds of grapheme clusters: 1) Legacy 2) Extended. This method * is to check for both using a boolean flag to switch between them. If no flag * is provided rules for the extended clusters will be used by default. * * @param {string} a String with the first sequence of characters. * @param {string} b String with the second sequence of characters. * @param {boolean=} opt_extended If true, indicates extended grapheme cluster; * If false, indicates legacy cluster. Default value is true. * @return {boolean} True if there is a grapheme cluster boundary between * a and b; False otherwise. */ goog.i18n.GraphemeBreak.hasGraphemeBreakStrings = function(a, b, opt_extended) { goog.asserts.assert(goog.isDef(a), 'First string should be defined.'); goog.asserts.assert(goog.isDef(b), 'Second string should be defined.'); // Break if any of the strings is empty. if (a.length === 0 || b.length === 0) { return true; } return goog.i18n.GraphemeBreak.applyBreakRules_(a, b, opt_extended !== false); };