@danielhaim/titlecaser 1.2.57 → 1.2.59

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/src/TitleCaser.js CHANGED
@@ -1,236 +1,346 @@
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
- '&': '&',
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
+ this.correctPhraseCasingList = correctPhraseCasingList;
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")
27
+ throw new TypeError("Invalid options: options must be an object.");
28
+
29
+ const {
30
+ style = "ap",
31
+ neverCapitalize = [],
32
+ replaceTermList = this.wordReplacementsList,
33
+ smartQuotes = false, // Set to false by default
34
+ } = this.options;
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(
50
+ replaceTermList.map((term) => [Object.keys(term)[0].toLowerCase(), Object.values(term)[0]]),
51
+ );
52
+
53
+ // console.log(replaceTermsArray);
54
+ // console.log(this.wordReplacementsList);
55
+
56
+ const map = {
57
+ "&": "&amp;",
58
+ "<": "&lt;",
59
+ ">": "&gt;",
60
+ // '\u2018': '\u2019', // Smart single quote
61
+ // '\u201C': '\u201D', // Smart double quote
62
+ '"': "&quot;",
63
+ "'": "&#039;",
64
+ };
65
+
66
+ // Remove extra spaces and replace <br> tags with a placeholder.
67
+ let inputString = str.trim();
68
+
69
+ // Replace <br> and <br /> tags with a placeholder.
70
+ inputString = inputString.replace(/<\s*br\s*\/?\s*>/gi, " nl2br ");
71
+
72
+ // Remove extra spaces and replace <br> tags with a placeholder.
73
+ inputString = inputString.replace(/ {2,}/g, (match) => match.slice(0, 1));
74
+
75
+ // Split the string into an array of words.
76
+ const words = inputString.split(" ");
77
+
78
+ const wordsInTitleCase = words.map((word, i) => {
79
+ switch (true) {
80
+ case TitleCaserUtils.isWordAmpersand(word):
81
+ // if the word is an ampersand, return it as is.
82
+ return word;
83
+ case TitleCaserUtils.hasHtmlBreak(word):
84
+ // If the word is a <br> tag, return it as is.
85
+ return word;
86
+ case TitleCaserUtils.isWordIgnored(word, ignoreList):
87
+ // If the word is in the ignore list, return it as is.
88
+ return word;
89
+ case replaceTermsArray.includes(word.toLowerCase()):
90
+ // If the word is in the replaceTerms array, return the replacement.
91
+ return replaceTermObj[word.toLowerCase()];
92
+ case TitleCaserUtils.isWordInArray(word, correctTitleCasingList):
93
+ // If the word is in the correctTitleCasingList array, return the correct casing.
94
+ return TitleCaserUtils.correctTerm(word, correctTitleCasingList);
95
+ case TitleCaserUtils.hasHyphen(word):
96
+ // Separate the base word from any trailing punctuation
97
+ const baseWord = word.replace(/[\W_]+$/, "");
98
+ const trailingPunctuation = word.slice(baseWord.length);
99
+
100
+ // Split the base word at the hyphen and process each part
101
+ const parts = baseWord.split("-");
102
+ const replacedParts = parts.map((part) => {
103
+ const lowerCasePart = part.toLowerCase();
104
+ if (replaceTermsArray.includes(lowerCasePart)) {
105
+ return replaceTermObj[lowerCasePart];
106
+ }
107
+ return part;
108
+ });
109
+
110
+ // Determine if any part was replaced
111
+ const isReplaced = !replacedParts.every((part, index) => part === parts[index]);
112
+
113
+ // Reassemble the word with the hyphen, reattach trailing punctuation, and return
114
+ return (
115
+ (isReplaced ? replacedParts.join("-") : TitleCaserUtils.correctTermHyphenated(word, style)) +
116
+ trailingPunctuation
117
+ );
118
+ case TitleCaserUtils.hasSuffix(word, style):
119
+ // If the word has a suffix, return the correct casing.
120
+ return TitleCaserUtils.correctSuffix(word, correctTitleCasingList);
121
+ case TitleCaserUtils.hasUppercaseIntentional(word):
122
+ // If the word has an intentional uppercase letter, return the correct casing.
123
+ return word;
124
+ case TitleCaserUtils.isShortWord(word, style) && i !== 0:
125
+ // If the word is a short word, return the correct casing.
126
+ return i > 0 && TitleCaserUtils.endsWithSymbol(words[i - 1], [":", "?", "!", "."])
127
+ ? word.charAt(0).toUpperCase() + word.slice(1)
128
+ : word.toLowerCase();
129
+ case TitleCaserUtils.endsWithSymbol(word):
130
+ // console.log("ends with symbol: ", word);
131
+ // If the word ends with a symbol, return the correct casing.
132
+ const splitWord = word.split(/([.,\/#!$%\^&\*;:{}=\-_`~()?])/g);
133
+ // console.log(splitWord);
134
+ // Process each part for correct casing
135
+ const processedWords = splitWord.map((part) => {
136
+ // Check if part is a symbol
137
+ if (TitleCaserUtils.endsWithSymbol(part)) {
138
+ // console.log(part);
139
+ return part;
140
+ } else {
141
+ // If it's a word, process it for correct casing
142
+ if (TitleCaserUtils.isWordInArray(part, correctTitleCasingList)) {
143
+ return TitleCaserUtils.correctTerm(part, correctTitleCasingList);
144
+ } else if (replaceTermsArray.includes(part)) {
145
+ return replaceTermObj[part];
146
+ } else {
147
+ // Apply the correct casing for words not in the list
148
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
149
+ }
150
+ }
151
+ });
152
+
153
+ // Join the processed words and return them.
154
+ return processedWords.join("");
155
+ case TitleCaserUtils.startsWithSymbol(word):
156
+ // If the word starts with a symbol, return the correct casing.
157
+ return !TitleCaserUtils.isWordInArray(word, correctTitleCasingList)
158
+ ? word
159
+ : TitleCaserUtils.correctTerm(word);
160
+ case TitleCaserUtils.hasRomanNumeral(word):
161
+ // If the word has a roman numeral, return the correct casing.
162
+ return word.toUpperCase();
163
+ case TitleCaserUtils.hasNumbers(word):
164
+ // If the word has numbers, return the correct casing.
165
+ return word;
166
+ default:
167
+ // Default to returning the word with the correct casing.
168
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
169
+ }
170
+ });
171
+
172
+ // Join the words in the array into a string.
173
+ inputString = wordsInTitleCase.join(" ");
174
+
175
+ for (const [phrase, replacement] of Object.entries(this.correctPhraseCasingList)) {
176
+ // Create a regular expression for case-insensitive matching of the phrase
177
+ const regex = new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
178
+
179
+ // Replace the phrase in the input string with its corresponding replacement
180
+ inputString = inputString.replace(regex, replacement);
181
+ }
182
+
183
+ // Replace the nl2br placeholder with <br> tags.
184
+ inputString = inputString.replace(/nl2br/gi, "<br>");
185
+
186
+ // Convert quotation marks to smart quotes if enabled
187
+ // Refer to: https://github.com/danielhaim1/TitleCaser/issues/4
188
+ if (smartQuotes) {
189
+ inputString = TitleCaserUtils.convertQuotesToCurly(inputString);
190
+ }
191
+
192
+ const newWords = inputString.split(" ");
193
+
194
+ for (let i = 0; i < newWords.length; i++) {
195
+ const prevWord = i > 0 ? newWords[i - 1] : null;
196
+ let currentWord = newWords[i];
197
+ const nextWord = i < newWords.length - 1 ? newWords[i + 1] : null;
198
+
199
+ // Capture punctuation at the end of the word
200
+ const punctuationMatch = currentWord.match(/[.,!?;:]+$/);
201
+ let punctuation = "";
202
+
203
+ if (punctuationMatch) {
204
+ punctuation = punctuationMatch[0];
205
+ currentWord = currentWord.replace(/[.,!?;:]+$/, ""); // Remove punctuation at the end
206
+ }
207
+
208
+ let cleanCurrentWord = currentWord.replace(/[.,!?;:]/g, "");
209
+ let cleanNextWord = nextWord ? nextWord.replace(/[.,!?;:]/g, "") : nextWord;
210
+
211
+ if (cleanCurrentWord === "Us") {
212
+ if (TitleCaserUtils.isAcronym(currentWord, prevWord, nextWord)) {
213
+ if (punctuation === "") {
214
+ newWords[i] = "US";
215
+ } else {
216
+ newWords[i] = "US" + punctuation;
217
+ }
218
+ } else {
219
+ if (punctuation === "") {
220
+ newWords[i] = "Us";
221
+ } else {
222
+ newWords[i] = "Us" + punctuation;
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ inputString = newWords.join(" ");
229
+
230
+ return inputString;
231
+ } catch (error) {
232
+ throw new Error(error);
233
+ }
234
+ }
235
+
236
+ setReplaceTerms(terms) {
237
+ if (!Array.isArray(terms)) {
238
+ throw new TypeError("Invalid argument: setReplaceTerms must be an array of objects.");
239
+ }
240
+ // Iterate over each term-replacement object in the array
241
+ terms.forEach((termObject) => {
242
+ if (termObject && typeof termObject === "object") {
243
+ const [term, replacement] = Object.entries(termObject)[0];
244
+ const index = this.wordReplacementsList.findIndex((obj) => obj.hasOwnProperty(term));
245
+ if (index !== -1) {
246
+ // Update the existing term
247
+ this.wordReplacementsList[index][term] = replacement;
248
+ } else {
249
+ // Add the new term
250
+ this.wordReplacementsList.push({ [term]: replacement });
251
+ }
252
+ } else {
253
+ // Handle non-object entries in the array, if required
254
+ console.warn("Invalid entry in terms array:", termObject);
255
+ }
256
+ });
257
+
258
+ this.options.wordReplacementsList = this.wordReplacementsList;
259
+
260
+ // Log the updated wordReplacementsList array
261
+ // console.log(this.wordReplacementsList);
262
+ }
263
+
264
+ addReplaceTerm(term, replacement) {
265
+ if (typeof term !== "string" || typeof replacement !== "string") {
266
+ throw new TypeError("Invalid argument: term and replacement must be strings.");
267
+ }
268
+
269
+ if (index !== -1) {
270
+ // If the term already exists in the array, update the replacement value
271
+ this.wordReplacementsList[index][term] = replacement;
272
+ } else {
273
+ // If the term doesn't exist in the array, add a new object with the term and replacement
274
+ this.wordReplacementsList.push({ [term]: replacement });
275
+ }
276
+
277
+ // Update the replace terms option
278
+ this.options.wordReplacementsList = this.wordReplacementsList;
279
+ }
280
+
281
+ removeReplaceTerm(term) {
282
+ if (typeof term !== "string") {
283
+ throw new TypeError("Invalid argument: term must be a string.");
284
+ }
285
+
286
+ // Find the index of the term in the wordReplacementsList array
287
+ const index = this.wordReplacementsList.findIndex((obj) => Object.keys(obj)[0] === term);
288
+
289
+ // If the term is not found in the array, throw an error
290
+ if (index === -1) {
291
+ throw new Error(`Term '${term}' not found in word replacements list.`);
292
+ }
293
+
294
+ // Remove the term from the array
295
+ this.wordReplacementsList.splice(index, 1);
296
+
297
+ // Update the replace terms option
298
+ this.options.wordReplacementsList = this.wordReplacementsList;
299
+
300
+ // Log the updated wordReplacementsList array
301
+ // console.log(this.wordReplacementsList);
302
+ }
303
+
304
+ addExactPhraseReplacements(newPhrases) {
305
+ if (!Array.isArray(newPhrases)) {
306
+ throw new TypeError("Invalid argument: newPhrases must be an array.");
307
+ }
308
+
309
+ newPhrases.forEach((item) => {
310
+ // If the item is an object with a single key-value pair
311
+ if (typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 1) {
312
+ const key = Object.keys(item)[0];
313
+ const value = item[key];
314
+ if (typeof key === "string" && typeof value === "string") {
315
+ this.correctPhraseCasingList[key] = value;
316
+ } else {
317
+ throw new TypeError("Invalid argument: Each key-value pair must contain strings.");
318
+ }
319
+ }
320
+ // If the item is already a key-value pair
321
+ else if (typeof item === "object" && !Array.isArray(item)) {
322
+ Object.entries(item).forEach(([key, value]) => {
323
+ if (typeof key === "string" && typeof value === "string") {
324
+ this.correctPhraseCasingList[key] = value;
325
+ } else {
326
+ throw new TypeError("Invalid argument: Each key-value pair must contain strings.");
327
+ }
328
+ });
329
+ }
330
+ // Invalid format
331
+ else {
332
+ throw new TypeError("Invalid argument: Each item must be an object with a single key-value pair.");
333
+ }
334
+ });
335
+
336
+ // console.log(this.correctPhraseCasingList);
337
+ }
338
+
339
+ setStyle(style) {
340
+ if (typeof style !== "string") {
341
+ throw new TypeError("Invalid argument: style must be a string.");
342
+ }
343
+
344
+ this.options.style = style;
345
+ }
346
+ }