1 /* 2 * CaseMapper.js - define upper- and lower-case mapper 3 * 4 * Copyright © 2014-2015, 2018, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 var ilib = require("./ilib.js"); 21 22 var Locale = require("./Locale.js"); 23 var IString = require("./IString.js"); 24 25 /** 26 * @class 27 * Create a new string mapper instance that maps strings to upper or 28 * lower case. This mapping will work for any string as characters 29 * that have no case will be returned unchanged.<p> 30 * 31 * The options may contain any of the following properties: 32 * 33 * <ul> 34 * <li><i>locale</i> - locale to use when loading the mapper. Some maps are 35 * locale-dependent, and this locale selects the right one. Default if this is 36 * not specified is the current locale. 37 * 38 * <li><i>direction</i> - "toupper" for upper-casing, or "tolower" for lower-casing. 39 * Default if not specified is "toupper". 40 * </ul> 41 * 42 * 43 * @constructor 44 * @param {Object=} options options to initialize this mapper 45 */ 46 var CaseMapper = function (options) { 47 this.up = true; 48 this.locale = new Locale(); 49 50 if (options) { 51 if (typeof(options.locale) !== 'undefined') { 52 this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale; 53 } 54 55 this.up = (!options.direction || options.direction === "toupper"); 56 } 57 58 this.mapData = this.up ? { 59 "ß": "SS", // German 60 'ΐ': 'Ι', // Greek 61 'ά': 'Α', 62 'έ': 'Ε', 63 'ή': 'Η', 64 'ί': 'Ι', 65 'ΰ': 'Υ', 66 'ϊ': 'Ι', 67 'ϋ': 'Υ', 68 'ό': 'Ο', 69 'ύ': 'Υ', 70 'ώ': 'Ω', 71 'Ӏ': 'Ӏ', // Russian and slavic languages 72 'ӏ': 'Ӏ' 73 } : { 74 'Ӏ': 'Ӏ' // Russian and slavic languages 75 }; 76 77 switch (this.locale.getLanguage()) { 78 case "az": 79 case "tr": 80 case "crh": 81 case "kk": 82 case "krc": 83 case "tt": 84 var lower = "iı"; 85 var upper = "İI"; 86 this._setUpMap(lower, upper); 87 break; 88 } 89 90 if (ilib._getBrowser() === "ie" || ilib._getBrowser() === "Edge") { 91 // IE is missing these mappings for some reason 92 if (this.up) { 93 this.mapData['ς'] = 'Σ'; 94 } 95 this._setUpMap("ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⳁⳉⳋ", "ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⳀⳈⳊ"); // Coptic 96 // Georgian Nuskhuri <-> Asomtavruli 97 this._setUpMap("ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ", "ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ"); 98 } 99 }; 100 101 CaseMapper.prototype = { 102 /** 103 * @private 104 */ 105 _charMapper: function(string) { 106 if (!string) { 107 return string; 108 } 109 var input = (typeof(string) === 'string') ? new IString(string) : string.toString(); 110 var ret = ""; 111 var it = input.charIterator(); 112 var c; 113 114 while (it.hasNext()) { 115 c = it.next(); 116 if (!this.up && c === 'Σ') { 117 if (it.hasNext()) { 118 c = it.next(); 119 var code = c.charCodeAt(0); 120 // if the next char is not a greek letter, this is the end of the word so use the 121 // final form of sigma. Otherwise, use the mid-word form. 122 ret += ((code < 0x0388 && code !== 0x0386) || code > 0x03CE) ? 'ς' : 'σ'; 123 ret += c.toLowerCase(); 124 } else { 125 // no next char means this is the end of the word, so use the final form of sigma 126 ret += 'ς'; 127 } 128 } else { 129 if (this.mapData[c]) { 130 ret += this.mapData[c]; 131 } else { 132 ret += this.up ? c.toUpperCase() : c.toLowerCase(); 133 } 134 } 135 } 136 137 return ret; 138 }, 139 140 /** @private */ 141 _setUpMap: function(lower, upper) { 142 var from, to; 143 if (this.up) { 144 from = lower; 145 to = upper; 146 } else { 147 from = upper; 148 to = lower; 149 } 150 for (var i = 0; i < upper.length; i++) { 151 this.mapData[from[i]] = to[i]; 152 } 153 }, 154 155 /** 156 * Return the locale that this mapper was constructed with. 157 * @returns {Locale} the locale that this mapper was constructed with 158 */ 159 getLocale: function () { 160 return this.locale; 161 }, 162 163 /** 164 * Map a string to lower case in a locale-sensitive manner. 165 * 166 * @param {string|undefined} string 167 * @return {string|undefined} 168 */ 169 map: function (string) { 170 return this._charMapper(string); 171 } 172 }; 173 174 module.exports = CaseMapper; 175