@danielhaim/titlecaser 1.2.56 → 1.2.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -292,43 +292,65 @@ $ npm run test
292
292
  ```
293
293
 
294
294
  ```bash
295
+ Testing Acronym/Pronoun of Alpha2/3 Country Codes
296
+ ✓ Capitalizes country code "US" correctly in a geopolitical context (4 ms)
297
+ ✓ Does not capitalize "us" when used as a pronoun (2 ms)
298
+ ✓ Capitalizes country code "UK" with preceding indicator and trailing comma (2 ms)
299
+ ✓ Handles multiple instances of country codes and pronouns (2 ms)
300
+ ✓ Does not capitalize "us" when used in common phrases (2 ms)
301
+ ✓ Capitalizes "USA" in a formal context (1 ms)
302
+ ✓ Capitalizes "US" before a government-related word (1 ms)
303
+ ✓ Does not capitalize "us" before a government-related word (1 ms)
304
+ ✓ Capitalizes "UK" with preceding indicator before a government-related word (2 ms)
305
+ ✓ Handles multiple instances of country codes and pronouns before government-related words (1 ms)
306
+ ✓ Capitalizes "US" before a military-related word (1 ms)
307
+ ✓ Does not capitalize "us" before a military-related word (1 ms)
308
+ ✓ Capitalizes "UK" with preceding indicator before a territory-related word (2 ms)
309
+ ✓ Handles multiple instances of country codes and pronouns before talks-related words (3 ms)
310
+ ✓ Does not capitalize "us" before a talks-related word (1 ms)
311
+ ✓ Capitalizes "USA" in a formal context before a bill-related word (1 ms)
312
+ ✓ Handles multiple instances of country codes and pronouns before a bill-related word (2 ms)
313
+
295
314
  Test Basic Options
296
315
  ✓ Default title case conversion
297
- ✓ Customized title case conversion
298
- ✓ AP-style title case conversion with replacements
299
- AP-style title case conversion with replacements
300
- Capitalize suffix word in sentence
316
+ ✓ Customized title case conversion (1 ms)
317
+ ✓ AP-style title case conversion with replacements (3 ms)
318
+ Testing Word Replacement (Kellogs -> Kellogg\'s)
319
+ AP-style title case conversion with replacements (1 ms)
320
+ ✓ Capitalize suffix word in sentence (1 ms)
301
321
 
302
322
  Test Methods
303
- ✓ removeReplaceTerm
304
- ✓ setReplaceTerms
323
+ ✓ removeReplaceTerm (1 ms)
324
+ ✓ setReplaceTerms (1 ms)
305
325
 
306
326
  Test Variation Stability
307
- ✓ Hyphenated, colon, and short word replacements
308
327
  ✓ Capitalization and word replacements
309
- ✓ AP-style title case with possessive and colon
328
+ ✓ AP-style title case with possessive and colon (1 ms)
310
329
  ✓ AP-style title case with lowercase back/front-end terms
311
- ✓ Chicago style title case with comparison and colon
312
- ✓ APA style title case with colon
313
- ✓ Wikipedia style title case with acronym and hyphen
314
- APA style title case with colon and apostrophe
330
+ ✓ Chicago style title case with comparison and colon (1 ms)
331
+ ✓ APA style title case with colon (2 ms)
332
+ ✓ Wikipedia style title case with acronym and hyphen (1 ms)
333
+ Hyphenated, colon, and short word replacements (2 ms)
334
+ ✓ I Love Connecting with My Online Friends, but Sometimes I Prefer to Hang Out with My Friends IRL (2 ms)
335
+ ✓ Test Smart Quotes (1 ms)
336
+ ✓ Wikipedia style capitalization test with special term and colon (1 ms)
337
+ ✓ APA style title case with colon and apostrophe (2 ms)
315
338
  ✓ Chicago style title case with custom term replacements
316
- ✓ AP-style capitalization test with special terms and colon
317
- ✓ NYT-style capitalization test with special terms and colon
318
- ✓ APA style capitalization test with short conjunction terms and colon
319
- ✓ Correct phrase casing list testing
320
- ✓ Wikipedia style capitalization test with special term and colon
339
+ ✓ AP-style capitalization test with special terms and colon (1 ms)
340
+ ✓ NYT-style capitalization test with special terms and colon (1 ms)
341
+ ✓ APA style capitalization test with short conjunction terms and colon (2 ms)
342
+ ✓ Correct phrase casing list testing (1 ms)
321
343
 
322
344
  Test Reserved Words
323
- Reserved word
324
- Reserved word with colon
325
- Reserved word, posessive
326
- Hyphenated reserved word
327
- Hyphenated reserved word, possessive
328
- HTML line break nl2br, <br /> tag
329
- ✓ HTML line break nl2br, <br /> tag
330
- ✓ HTML line break nl2br, <br /> tag
331
- ✓ Ampersand in a sentence should return & and not &Amp;
345
+ Title case transformation for a single reserved word
346
+ Title case transformation for a sentence with a reserved word and colon (1 ms)
347
+ Title case transformation for a reserved word with a possessive form (1 ms)
348
+ Title case transformation for specific brand names
349
+ Title case transformation for a sentence with HTML line break (nl2br) using <br> tag (1 ms)
350
+ Title case transformation for a sentence with untrimmed white spaces
351
+ Title case transformation for a sentence with HTML line break (nl2br) using <br> tag (1 ms)
352
+ Title case transformation for a sentence with HTML line break (nl2br) without space after colon using <br> tag (1 ms)
353
+ ✓ Ampersand in a sentence should return & and not &Amp; (1 ms)
332
354
  ✓ Untrimmed white spaces
333
355
  ```
334
356
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielhaim/titlecaser",
3
- "version": "1.2.56",
3
+ "version": "1.2.58",
4
4
  "description": "Converts a string to title case with multiple style options, ability to ignore certain words, and handle acronyms",
5
5
  "keywords": [
6
6
  "title case",
@@ -67,8 +67,8 @@
67
67
  "jest-environment-jsdom": "^29.5.0",
68
68
  "jest-environment-puppeteer": "^9.0.0",
69
69
  "jest-puppeteer": "^9.0.0",
70
- "puppeteer": "^20.5.0",
71
- "puppeteer-core": "^20.5.0",
70
+ "puppeteer": "^21.5.2",
71
+ "puppeteer-core": "^21.5.2",
72
72
  "terser-webpack-plugin": "^5.3.9",
73
73
  "webpack": "^5.86.0",
74
74
  "webpack-cli": "^5.1.4",
package/src/TitleCaser.js CHANGED
@@ -1,236 +1,272 @@
1
1
  import {
2
- commonAbbreviationList,
3
- correctTitleCasingList,
4
- correctPhraseCasingList,
5
- wordReplacementsList,
6
- }
7
- from "./TitleCaserConsts.js";
2
+ commonAbbreviationList,
3
+ correctTitleCasingList,
4
+ correctPhraseCasingList,
5
+ wordReplacementsList,
6
+ } from "./TitleCaserConsts.js";
8
7
 
9
8
  import { TitleCaserUtils } from "./TitleCaserUtils.js";
10
9
 
11
10
  export class TitleCaser {
12
- constructor(options = {}) {
13
- this.options = options;
14
- this.wordReplacementsList = wordReplacementsList;
15
- }
16
-
17
- toTitleCase(str) {
18
- try {
19
- // If input is empty, throw an error.
20
- if (str.trim().length === 0) throw new TypeError("Invalid input: input must not be empty.");
21
-
22
- // If input is not a string, throw an error.
23
- if (typeof str !== 'string') throw new TypeError("Invalid input: input must be a string.");
24
-
25
- // If options is not an object, throw an error.
26
- if (typeof this.options !== "undefined" && typeof this.options !== "object") throw new TypeError("Invalid options: options must be an object.");
27
-
28
- const {
29
- style = "ap",
30
- neverCapitalize = [],
31
- replaceTermList = wordReplacementsList,
32
- smartQuotes = false // Set to false by default
33
- } = this.options;
34
-
35
-
36
- const ignoreList = ["nl2br", ...neverCapitalize];
37
- const {
38
- articlesList,
39
- shortConjunctionsList,
40
- shortPrepositionsList,
41
- neverCapitalizedList,
42
- replaceTerms,
43
- smartQuotes: mergedSmartQuotes // Rename for clarity
44
- } = TitleCaserUtils.getTitleCaseOptions(this.options, commonAbbreviationList, wordReplacementsList);
45
-
46
- // Prerocess the replaceTerms array to make it easier to search for.
47
- const replaceTermsArray = replaceTermList.map(term => Object.keys(term)[0].toLowerCase());
48
- // Create an object from the replaceTerms array to make it easier to search for.
49
- const replaceTermObj = Object.fromEntries(replaceTermList.map(
50
- term => [Object.keys(term)[0].toLowerCase(), Object.values(term)[0]]
51
- ));
52
-
53
- const map = {
54
- '&': '&amp;',
55
- '<': '&lt;',
56
- '>': '&gt;',
57
- // '\u2018': '\u2019', // Smart single quote
58
- // '\u201C': '\u201D', // Smart double quote
59
- '"': '&quot;',
60
- "'": '&#039;'
61
- };
62
-
63
- // Remove extra spaces and replace <br> tags with a placeholder.
64
- let inputString = str.trim();
65
-
66
- // Replace <br> and <br /> tags with a placeholder.
67
- inputString = inputString.replace(/<\s*br\s*\/?\s*>/gi, " nl2br ");
68
-
69
- // Remove extra spaces and replace <br> tags with a placeholder.
70
- inputString = inputString.replace(/ {2,}/g, (match) => match.slice(0, 1));
71
-
72
-
73
- // Split the string into an array of words.
74
- const words = inputString.split(" ");
75
-
76
- const wordsInTitleCase = words.map((word, i) => {
77
- switch (true) {
78
- case TitleCaserUtils.isWordAmpersand(word):
79
- // if the word is an ampersand, return it as is.
80
- return word;
81
- case TitleCaserUtils.hasHtmlBreak(word):
82
- // If the word is a <br> tag, return it as is.
83
- return word;
84
- case TitleCaserUtils.isWordIgnored(word, ignoreList):
85
- // If the word is in the ignore list, return it as is.
86
- return word;
87
- case replaceTermsArray.includes(word.toLowerCase()):
88
- // If the word is in the replaceTerms array, return the replacement.
89
- return replaceTermObj[word.toLowerCase()];
90
- case TitleCaserUtils.isWordInArray(word, correctTitleCasingList):
91
- // If the word is in the correctTitleCasingList array, return the correct casing.
92
- return TitleCaserUtils.correctTerm(word, correctTitleCasingList);
93
- case TitleCaserUtils.hasSuffix(word, style):
94
- // If the word has a suffix, return the correct casing.
95
- return TitleCaserUtils.correctSuffix(word, correctTitleCasingList);
96
- case TitleCaserUtils.hasHyphen(word):
97
- // If the word has a hyphen, return the correct casing.
98
- return TitleCaserUtils.correctTermHyphenated(word, style);
99
- case TitleCaserUtils.hasUppercaseIntentional(word):
100
- // If the word has an intentional uppercase letter, return the correct casing.
101
- return word;
102
- case TitleCaserUtils.isShortWord(word, style) && i !== 0:
103
- // If the word is a short word, return the correct casing.
104
- return (i > 0 && TitleCaserUtils.endsWithSymbol(words[i - 1],
105
- [':', '?', '!', '.'])) ? word.charAt(0).toUpperCase() + word.slice(1) : word.toLowerCase();
106
- case TitleCaserUtils.endsWithSymbol(word):
107
- // If the word ends with a symbol, return the correct casing.
108
- const splitWord = word.split(/([.,\/#!$%\^&\*;:{}=\-_`~()])/g);
109
- const processedWords = splitWord.map((splitWord, j) => {
110
- // If the word is in the correctTitleCasingList array, return the correct casing.
111
- if (TitleCaserUtils.isWordInArray(splitWord, correctTitleCasingList)) return TitleCaserUtils.correctTerm(splitWord, correctTitleCasingList);
112
- // Else return the word with the correct casing.
113
- else return (j > 0 && TitleCaserUtils.endsWithSymbol(splitWord)) ? splitWord.charAt(0)
114
- .toUpperCase() + splitWord.slice(1) : splitWord.charAt(0)
115
- .toUpperCase() + splitWord.slice(1);
116
- });
117
- // Join the processed words and return them.
118
- return processedWords.join("");
119
- case TitleCaserUtils.startsWithSymbol(word):
120
- // If the word starts with a symbol, return the correct casing.
121
- return !TitleCaserUtils.isWordInArray(word, correctTitleCasingList) ? word : TitleCaserUtils.correctTerm(word);
122
- case TitleCaserUtils.hasRomanNumeral(word):
123
- // If the word has a roman numeral, return the correct casing.
124
- return word.toUpperCase();
125
- case TitleCaserUtils.hasNumbers(word):
126
- // If the word has numbers, return the correct casing.
127
- return word;
128
- default:
129
- // Default to returning the word with the correct casing.
130
- return word.charAt(0)
131
- .toUpperCase() + word.slice(1)
132
- .toLowerCase();
133
- }
134
- });
135
-
136
- // Join the words in the array into a string.
137
- inputString = wordsInTitleCase.join(" ");
138
-
139
- for (const phrase of correctPhraseCasingList) {
140
- // If the phrase is in the input string, replace it with the correct casing.
141
- if (inputString.toLowerCase()
142
- .includes(phrase.toLowerCase())) {
143
- inputString = inputString.replace(new RegExp(phrase, 'gi'), phrase);
144
- }
145
- }
146
-
147
- // Replace the nl2br placeholder with <br> tags.
148
- inputString = inputString.replace(/nl2br/gi, "<br />");
149
-
150
- // Convert quotation marks to smart quotes if enabled
151
- // Refer to: https://github.com/danielhaim1/TitleCaser/issues/4
152
- if (smartQuotes) {
153
- inputString = TitleCaserUtils.convertQuotesToCurly(inputString);
154
- }
155
-
156
- // Return the string.
157
- return inputString;
158
- } catch (error) {
159
- throw new Error(error);
160
- }
161
- }
162
-
163
- setReplaceTerms(terms) {
164
- if (typeof terms !== 'object') {
165
- throw new TypeError('Invalid argument: replace terms must be an object.');
166
- }
167
-
168
- // Add the new replace terms to the wordReplacementsList array
169
- Object.entries(terms).forEach(([term, replacement]) => {
170
- const index = wordReplacementsList.findIndex(obj => obj[term]);
171
- if (index !== -1) {
172
- // If the term already exists in the array, update the replacement value
173
- wordReplacementsList[index][term] = replacement;
174
- } else {
175
- // If the term doesn't exist in the array, add a new object with the term and replacement
176
- wordReplacementsList.push({ [term]: replacement });
177
- }
178
- });
179
-
180
- // Log the updated wordReplacementsList array
181
- // console.log(wordReplacementsList);
182
-
183
- // Update the replace terms option
184
- this.options.wordReplacementsList = wordReplacementsList;
185
- }
186
-
187
- addReplaceTerm(term, replacement) {
188
- if (typeof term !== 'string' || typeof replacement !== 'string') {
189
- throw new TypeError('Invalid argument: term and replacement must be strings.');
190
- }
191
-
192
- const index = this.wordReplacementsList.findIndex(obj => obj[term]);
193
-
194
- if (index !== -1) {
195
- // If the term already exists in the array, update the replacement value
196
- this.wordReplacementsList[index][term] = replacement;
197
- } else {
198
- // If the term doesn't exist in the array, add a new object with the term and replacement
199
- this.wordReplacementsList.push({ [term]: replacement });
200
- }
201
-
202
- // Update the replace terms option
203
- this.options.wordReplacementsList = this.wordReplacementsList;
204
- }
205
-
206
- removeReplaceTerm(term) {
207
- if (typeof term !== 'string') {
208
- throw new TypeError('Invalid argument: term must be a string.');
209
- }
210
-
211
- // Find the index of the term in the wordReplacementsList array
212
- const index = this.wordReplacementsList.findIndex(obj => Object.keys(obj)[0] === term);
213
-
214
- // If the term is not found in the array, throw an error
215
- if (index === -1) {
216
- throw new Error(`Term '${term}' not found in word replacements list.`);
217
- }
218
-
219
- // Remove the term from the array
220
- this.wordReplacementsList.splice(index, 1);
221
-
222
- // Log the updated wordReplacementsList array
223
- // console.log(this.wordReplacementsList);
224
-
225
- // Update the replace terms option
226
- this.options.wordReplacementsList = this.wordReplacementsList;
227
- }
228
-
229
- setStyle(style) {
230
- if (typeof style !== 'string') {
231
- throw new TypeError('Invalid argument: style must be a string.');
232
- }
233
-
234
- this.options.style = style;
235
- }
236
- }
11
+ constructor(options = {}) {
12
+ this.options = options;
13
+ this.wordReplacementsList = wordReplacementsList;
14
+ }
15
+
16
+ toTitleCase(str) {
17
+ try {
18
+ // If input is empty, throw an error.
19
+ if (str.trim().length === 0) throw new TypeError("Invalid input: input must not be empty.");
20
+
21
+ // If input is not a string, throw an error.
22
+ if (typeof str !== "string") throw new TypeError("Invalid input: input must be a string.");
23
+
24
+ // If options is not an object, throw an error.
25
+ if (typeof this.options !== "undefined" && typeof this.options !== "object")
26
+ throw new TypeError("Invalid options: options must be an object.");
27
+
28
+ const {
29
+ style = "ap",
30
+ neverCapitalize = [],
31
+ replaceTermList = this.wordReplacementsList,
32
+ smartQuotes = false, // Set to false by default
33
+ } = this.options;
34
+
35
+ const ignoreList = ["nl2br", ...neverCapitalize];
36
+ const {
37
+ articlesList,
38
+ shortConjunctionsList,
39
+ shortPrepositionsList,
40
+ neverCapitalizedList,
41
+ replaceTerms,
42
+ smartQuotes: mergedSmartQuotes, // Rename for clarity
43
+ } = TitleCaserUtils.getTitleCaseOptions(this.options, commonAbbreviationList, wordReplacementsList);
44
+
45
+ // Prerocess the replaceTerms array to make it easier to search for.
46
+ const replaceTermsArray = replaceTermList.map((term) => Object.keys(term)[0].toLowerCase());
47
+ // Create an object from the replaceTerms array to make it easier to search for.
48
+ const replaceTermObj = Object.fromEntries(
49
+ replaceTermList.map((term) => [Object.keys(term)[0].toLowerCase(), Object.values(term)[0]]),
50
+ );
51
+
52
+ const map = {
53
+ "&": "&amp;",
54
+ "<": "&lt;",
55
+ ">": "&gt;",
56
+ // '\u2018': '\u2019', // Smart single quote
57
+ // '\u201C': '\u201D', // Smart double quote
58
+ '"': "&quot;",
59
+ "'": "&#039;",
60
+ };
61
+
62
+ // Remove extra spaces and replace <br> tags with a placeholder.
63
+ let inputString = str.trim();
64
+
65
+ // Replace <br> and <br /> tags with a placeholder.
66
+ inputString = inputString.replace(/<\s*br\s*\/?\s*>/gi, " nl2br ");
67
+
68
+ // Remove extra spaces and replace <br> tags with a placeholder.
69
+ inputString = inputString.replace(/ {2,}/g, (match) => match.slice(0, 1));
70
+
71
+ // Split the string into an array of words.
72
+ const words = inputString.split(" ");
73
+
74
+ const wordsInTitleCase = words.map((word, i) => {
75
+ switch (true) {
76
+ case TitleCaserUtils.isWordAmpersand(word):
77
+ // if the word is an ampersand, return it as is.
78
+ return word;
79
+ case TitleCaserUtils.hasHtmlBreak(word):
80
+ // If the word is a <br> tag, return it as is.
81
+ return word;
82
+ case TitleCaserUtils.isWordIgnored(word, ignoreList):
83
+ // If the word is in the ignore list, return it as is.
84
+ return word;
85
+ case replaceTermsArray.includes(word.toLowerCase()):
86
+ // If the word is in the replaceTerms array, return the replacement.
87
+ return replaceTermObj[word.toLowerCase()];
88
+ case TitleCaserUtils.isWordInArray(word, correctTitleCasingList):
89
+ // If the word is in the correctTitleCasingList array, return the correct casing.
90
+ return TitleCaserUtils.correctTerm(word, correctTitleCasingList);
91
+ case TitleCaserUtils.hasHyphen(word):
92
+ return TitleCaserUtils.correctTermHyphenated(word, style);
93
+ case TitleCaserUtils.hasSuffix(word, style):
94
+ // If the word has a suffix, return the correct casing.
95
+ return TitleCaserUtils.correctSuffix(word, correctTitleCasingList);
96
+ case TitleCaserUtils.hasUppercaseIntentional(word):
97
+ // If the word has an intentional uppercase letter, return the correct casing.
98
+ return word;
99
+ case TitleCaserUtils.isShortWord(word, style) && i !== 0:
100
+ // If the word is a short word, return the correct casing.
101
+ return i > 0 && TitleCaserUtils.endsWithSymbol(words[i - 1], [":", "?", "!", "."])
102
+ ? word.charAt(0).toUpperCase() + word.slice(1)
103
+ : word.toLowerCase();
104
+ case TitleCaserUtils.endsWithSymbol(word):
105
+ // If the word ends with a symbol, return the correct casing.
106
+ const splitWord = word.split(/([.,\/#!$%\^&\*;:{}=\-_`~()])/g);
107
+ const processedWords = splitWord.map((splitWord, j) => {
108
+ // If the word is in the correctTitleCasingList array, return the correct casing.
109
+ if (TitleCaserUtils.isWordInArray(splitWord, correctTitleCasingList))
110
+ return TitleCaserUtils.correctTerm(splitWord, correctTitleCasingList);
111
+ // Else return the word with the correct casing.
112
+ else
113
+ return j > 0 && TitleCaserUtils.endsWithSymbol(splitWord)
114
+ ? splitWord.charAt(0).toUpperCase() + splitWord.slice(1)
115
+ : splitWord.charAt(0).toUpperCase() + splitWord.slice(1);
116
+ });
117
+ // Join the processed words and return them.
118
+ return processedWords.join("");
119
+ case TitleCaserUtils.startsWithSymbol(word):
120
+ // If the word starts with a symbol, return the correct casing.
121
+ return !TitleCaserUtils.isWordInArray(word, correctTitleCasingList)
122
+ ? word
123
+ : TitleCaserUtils.correctTerm(word);
124
+ case TitleCaserUtils.hasRomanNumeral(word):
125
+ // If the word has a roman numeral, return the correct casing.
126
+ return word.toUpperCase();
127
+ case TitleCaserUtils.hasNumbers(word):
128
+ // If the word has numbers, return the correct casing.
129
+ return word;
130
+ default:
131
+ // Default to returning the word with the correct casing.
132
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
133
+ }
134
+ });
135
+
136
+ // Join the words in the array into a string.
137
+ inputString = wordsInTitleCase.join(" ");
138
+
139
+ for (const phrase of correctPhraseCasingList) {
140
+ // If the phrase is in the input string, replace it with the correct casing.
141
+ if (inputString.toLowerCase().includes(phrase.toLowerCase())) {
142
+ inputString = inputString.replace(new RegExp(phrase, "gi"), phrase);
143
+ }
144
+ }
145
+
146
+ // Replace the nl2br placeholder with <br> tags.
147
+ inputString = inputString.replace(/nl2br/gi, "<br>");
148
+
149
+ // Convert quotation marks to smart quotes if enabled
150
+ // Refer to: https://github.com/danielhaim1/TitleCaser/issues/4
151
+ if (smartQuotes) {
152
+ inputString = TitleCaserUtils.convertQuotesToCurly(inputString);
153
+ }
154
+
155
+ const newWords = inputString.split(" ");
156
+
157
+ for (let i = 0; i < newWords.length; i++) {
158
+ const prevWord = i > 0 ? newWords[i - 1] : null;
159
+ let currentWord = newWords[i];
160
+ const nextWord = i < newWords.length - 1 ? newWords[i + 1] : null;
161
+
162
+ // Capture punctuation at the end of the word
163
+ const punctuationMatch = currentWord.match(/[.,!?;:]+$/);
164
+ let punctuation = "";
165
+
166
+ if (punctuationMatch) {
167
+ punctuation = punctuationMatch[0];
168
+ currentWord = currentWord.replace(/[.,!?;:]+$/, ""); // Remove punctuation at the end
169
+ }
170
+
171
+ let cleanCurrentWord = currentWord.replace(/[.,!?;:]/g, "");
172
+ let cleanNextWord = nextWord ? nextWord.replace(/[.,!?;:]/g, "") : nextWord;
173
+
174
+ if (cleanCurrentWord === "Us") {
175
+ if (TitleCaserUtils.isAcronym(currentWord, prevWord, nextWord)) {
176
+ if (punctuation === "") {
177
+ newWords[i] = "US";
178
+ } else {
179
+ newWords[i] = "US" + punctuation;
180
+ }
181
+ } else {
182
+ if (punctuation === "") {
183
+ newWords[i] = "Us";
184
+ } else {
185
+ newWords[i] = "Us" + punctuation;
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ inputString = newWords.join(" ");
192
+
193
+ return inputString;
194
+ } catch (error) {
195
+ throw new Error(error);
196
+ }
197
+ }
198
+
199
+ setReplaceTerms(terms) {
200
+ if (typeof terms !== "object") {
201
+ throw new TypeError("Invalid argument: replace terms must be an object.");
202
+ }
203
+
204
+ // Add the new replace terms to the wordReplacementsList array
205
+ Object.entries(terms).forEach(([term, replacement]) => {
206
+ const index = wordReplacementsList.findIndex((obj) => obj[term]);
207
+ if (index !== -1) {
208
+ // If the term already exists in the array, update the replacement value
209
+ wordReplacementsList[index][term] = replacement;
210
+ } else {
211
+ // If the term doesn't exist in the array, add a new object with the term and replacement
212
+ wordReplacementsList.push({ [term]: replacement });
213
+ }
214
+ });
215
+
216
+ // Log the updated wordReplacementsList array
217
+ // console.log(wordReplacementsList);
218
+
219
+ // Update the replace terms option
220
+ this.options.wordReplacementsList = wordReplacementsList;
221
+ }
222
+
223
+ addReplaceTerm(term, replacement) {
224
+ if (typeof term !== "string" || typeof replacement !== "string") {
225
+ throw new TypeError("Invalid argument: term and replacement must be strings.");
226
+ }
227
+
228
+ const index = this.wordReplacementsList.findIndex((obj) => obj[term]);
229
+
230
+ if (index !== -1) {
231
+ // If the term already exists in the array, update the replacement value
232
+ this.wordReplacementsList[index][term] = replacement;
233
+ } else {
234
+ // If the term doesn't exist in the array, add a new object with the term and replacement
235
+ this.wordReplacementsList.push({ [term]: replacement });
236
+ }
237
+
238
+ // Update the replace terms option
239
+ this.options.wordReplacementsList = this.wordReplacementsList;
240
+ }
241
+
242
+ removeReplaceTerm(term) {
243
+ if (typeof term !== "string") {
244
+ throw new TypeError("Invalid argument: term must be a string.");
245
+ }
246
+
247
+ // Find the index of the term in the wordReplacementsList array
248
+ const index = this.wordReplacementsList.findIndex((obj) => Object.keys(obj)[0] === term);
249
+
250
+ // If the term is not found in the array, throw an error
251
+ if (index === -1) {
252
+ throw new Error(`Term '${term}' not found in word replacements list.`);
253
+ }
254
+
255
+ // Remove the term from the array
256
+ this.wordReplacementsList.splice(index, 1);
257
+
258
+ // Log the updated wordReplacementsList array
259
+ // console.log(this.wordReplacementsList);
260
+
261
+ // Update the replace terms option
262
+ this.options.wordReplacementsList = this.wordReplacementsList;
263
+ }
264
+
265
+ setStyle(style) {
266
+ if (typeof style !== "string") {
267
+ throw new TypeError("Invalid argument: style must be a string.");
268
+ }
269
+
270
+ this.options.style = style;
271
+ }
272
+ }