@danielhaim/titlecaser 1.2.45 → 1.2.48

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,223 +1,227 @@
1
1
  import {
2
- commonAbbreviationList,
3
- correctTitleCasingList,
4
- correctPhraseCasingList,
5
- wordReplacementsList,
2
+ commonAbbreviationList,
3
+ correctTitleCasingList,
4
+ correctPhraseCasingList,
5
+ wordReplacementsList,
6
6
  }
7
- from "./TitleCaserConsts.js";
7
+ from "./TitleCaserConsts.js";
8
8
 
9
9
  import { TitleCaserUtils } from "./TitleCaserUtils.js";
10
10
 
11
11
  export class TitleCaser {
12
- constructor (options = {}) {
13
- this.options = options;
14
- this.wordReplacementsList = wordReplacementsList;
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") throw new TypeError("Invalid options: options must be an object.");
26
-
27
- const {
28
- style = "ap",
29
- neverCapitalize = [],
30
- replaceTermList = wordReplacementsList
31
- } = this.options;
32
- const ignoreList = ["nl2br", ...neverCapitalize];
33
- const {
34
- articlesList,
35
- shortConjunctionsList,
36
- shortPrepositionsList,
37
- neverCapitalizedList,
38
- replaceTerms
39
- } = TitleCaserUtils.getTitleCaseOptions(this.options, commonAbbreviationList, wordReplacementsList);
40
-
41
- // Prerocess the replaceTerms array to make it easier to search for.
42
- const replaceTermsArray = replaceTermList.map(term => Object.keys(term)[0].toLowerCase());
43
- // Create an object from the replaceTerms array to make it easier to search for.
44
- const replaceTermObj = Object.fromEntries(replaceTermList.map(
45
- term => [Object.keys(term)[0].toLowerCase(), Object.values(term)[0]]
46
- ));
47
-
48
- const map = {
49
- '&': '&',
50
- '<': '&lt;',
51
- '>': '&gt;',
52
- '"': '&quot;',
53
- "'": '&#039;'
54
- };
55
-
56
- // Remove extra spaces and replace <br> tags with a placeholder.
57
- let inputString = str.trim();
58
-
59
- // Remove extra spaces and replace <br> tags with a placeholder.
60
- inputString = inputString.replace(/ {2,}/g, (match) => match.slice(0, 1));
61
-
62
- // Replace <br> tags with a placeholder.
63
- inputString = inputString.replace(/<br\s*\/?>/gi, "nl2br ");
64
-
65
- // Split the string into an array of words.
66
- const words = inputString.split(" ");
67
-
68
- const wordsInTitleCase = words.map((word, i) => {
69
- switch (true) {
70
- case TitleCaserUtils.isWordAmpersand(word):
71
- // if the word is an ampersand, return it as is.
72
- return word;
73
- case TitleCaserUtils.hasHtmlBreak(word):
74
- // If the word is a <br> tag, return it as is.
75
- return word;
76
- case TitleCaserUtils.isWordIgnored(word, ignoreList):
77
- // If the word is in the ignore list, return it as is.
78
- return word;
79
- case replaceTermsArray.includes(word.toLowerCase()):
80
- // If the word is in the replaceTerms array, return the replacement.
81
- return replaceTermObj[word.toLowerCase()];
82
- case TitleCaserUtils.isWordInArray(word, correctTitleCasingList):
83
- // If the word is in the correctTitleCasingList array, return the correct casing.
84
- return TitleCaserUtils.correctTerm(word, correctTitleCasingList);
85
- case TitleCaserUtils.hasSuffix(word, style):
86
- // If the word has a suffix, return the correct casing.
87
- return TitleCaserUtils.correctSuffix(word, correctTitleCasingList);
88
- case TitleCaserUtils.hasHyphen(word):
89
- // If the word has a hyphen, return the correct casing.
90
- return TitleCaserUtils.correctTermHyphenated(word, style);
91
- case TitleCaserUtils.hasUppercaseIntentional(word):
92
- // If the word has an intentional uppercase letter, return the correct casing.
93
- return word;
94
- case TitleCaserUtils.isShortWord(word, style) && i !== 0:
95
- // If the word is a short word, return the correct casing.
96
- return (i > 0 && TitleCaserUtils.endsWithSymbol(words[i - 1],
97
- [':', '?', '!', '.'])) ? word.charAt(0).toUpperCase() + word.slice(1) : word.toLowerCase();
98
- case TitleCaserUtils.endsWithSymbol(word):
99
- // If the word ends with a symbol, return the correct casing.
100
- const splitWord = word.split(/([.,\/#!$%\^&\*;:{}=\-_`~()])/g);
101
- const processedWords = splitWord.map((splitWord, j) => {
102
- // If the word is in the correctTitleCasingList array, return the correct casing.
103
- if (TitleCaserUtils.isWordInArray(splitWord, correctTitleCasingList)) return TitleCaserUtils.correctTerm(splitWord, correctTitleCasingList);
104
- // Else return the word with the correct casing.
105
- else return (j > 0 && TitleCaserUtils.endsWithSymbol(splitWord)) ? splitWord.charAt(0)
106
- .toUpperCase() + splitWord.slice(1) : splitWord.charAt(0)
107
- .toUpperCase() + splitWord.slice(1);
108
- });
109
- // Join the processed words and return them.
110
- return processedWords.join("");
111
- case TitleCaserUtils.startsWithSymbol(word):
112
- // If the word starts with a symbol, return the correct casing.
113
- return !TitleCaserUtils.isWordInArray(word, correctTitleCasingList) ? word : TitleCaserUtils.correctTerm(word);
114
- case TitleCaserUtils.hasRomanNumeral(word):
115
- // If the word has a roman numeral, return the correct casing.
116
- return word.toUpperCase();
117
- case TitleCaserUtils.hasNumbers(word):
118
- // If the word has numbers, return the correct casing.
119
- return word;
120
- default:
121
- // Default to returning the word with the correct casing.
122
- return word.charAt(0)
123
- .toUpperCase() + word.slice(1)
124
- .toLowerCase();
125
- }
126
- });
127
-
128
- // Join the words in the array into a string.
129
- inputString = wordsInTitleCase.join(" ");
130
-
131
- for (const phrase of correctPhraseCasingList) {
132
- // If the phrase is in the input string, replace it with the correct casing.
133
- if (inputString.toLowerCase()
134
- .includes(phrase.toLowerCase())) {
135
- inputString = inputString.replace(new RegExp(phrase, 'gi'), phrase);
136
- }
137
- }
138
-
139
- // Replace the nl2br placeholder with <br> tags.
140
- inputString = inputString.replace(/nl2br /gi, "<br />");
141
-
142
- // Return the string.
143
- return inputString;
144
- }
145
- catch (error) {
146
- throw new Error(error);
147
- }
148
- }
149
-
150
- setReplaceTerms(terms) {
151
- if (typeof terms !== 'object') {
152
- throw new TypeError('Invalid argument: replace terms must be an object.');
153
- }
154
-
155
- // Add the new replace terms to the wordReplacementsList array
156
- Object.entries(terms).forEach(([term, replacement]) => {
157
- const index = wordReplacementsList.findIndex(obj => obj[term]);
158
- if (index !== -1) {
159
- // If the term already exists in the array, update the replacement value
160
- wordReplacementsList[index][term] = replacement;
161
- } else {
162
- // If the term doesn't exist in the array, add a new object with the term and replacement
163
- wordReplacementsList.push({ [term]: replacement });
164
- }
165
- });
166
-
167
- // Log the updated wordReplacementsList array
168
- // console.log(wordReplacementsList);
169
-
170
- // Update the replace terms option
171
- this.options.wordReplacementsList = wordReplacementsList;
172
- }
173
-
174
- addReplaceTerm(term, replacement) {
175
- if (typeof term !== 'string' || typeof replacement !== 'string') {
176
- throw new TypeError('Invalid argument: term and replacement must be strings.');
177
- }
178
-
179
- const index = this.wordReplacementsList.findIndex(obj => obj[term]);
180
-
181
- if (index !== -1) {
182
- // If the term already exists in the array, update the replacement value
183
- this.wordReplacementsList[index][term] = replacement;
184
- } else {
185
- // If the term doesn't exist in the array, add a new object with the term and replacement
186
- this.wordReplacementsList.push({ [term]: replacement });
187
- }
188
-
189
- // Update the replace terms option
190
- this.options.wordReplacementsList = this.wordReplacementsList;
191
- }
192
-
193
- removeReplaceTerm(term) {
194
- if (typeof term !== 'string') {
195
- throw new TypeError('Invalid argument: term must be a string.');
196
- }
197
-
198
- // Find the index of the term in the wordReplacementsList array
199
- const index = this.wordReplacementsList.findIndex(obj => Object.keys(obj)[0] === term);
200
-
201
- // If the term is not found in the array, throw an error
202
- if (index === -1) {
203
- throw new Error(`Term '${term}' not found in word replacements list.`);
204
- }
205
-
206
- // Remove the term from the array
207
- this.wordReplacementsList.splice(index, 1);
208
-
209
- // Log the updated wordReplacementsList array
210
- // console.log(this.wordReplacementsList);
211
-
212
- // Update the replace terms option
213
- this.options.wordReplacementsList = this.wordReplacementsList;
214
- }
215
-
216
- setStyle(style) {
217
- if (typeof style !== 'string') {
218
- throw new TypeError('Invalid argument: style must be a string.');
219
- }
220
-
221
- this.options.style = style;
222
- }
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
+ } = this.options;
33
+ const ignoreList = [ "nl2br", ...neverCapitalize ];
34
+ const {
35
+ articlesList,
36
+ shortConjunctionsList,
37
+ shortPrepositionsList,
38
+ neverCapitalizedList,
39
+ replaceTerms
40
+ } = TitleCaserUtils.getTitleCaseOptions ( this.options, commonAbbreviationList, wordReplacementsList );
41
+
42
+ // Prerocess the replaceTerms array to make it easier to search for.
43
+ const replaceTermsArray = replaceTermList.map ( term => Object.keys ( term )[0].toLowerCase () );
44
+ // Create an object from the replaceTerms array to make it easier to search for.
45
+ const replaceTermObj = Object.fromEntries ( replaceTermList.map (
46
+ term => [ Object.keys ( term )[0].toLowerCase (), Object.values ( term )[0] ]
47
+ ) );
48
+
49
+ const map = {
50
+ '&': '&amp;',
51
+ '<': '&lt;',
52
+ '>': '&gt;',
53
+ '"': '&quot;',
54
+ "'": '&#039;'
55
+ };
56
+
57
+ // Remove extra spaces and replace <br> tags with a placeholder.
58
+ let inputString = str.trim ();
59
+
60
+ // Replace <br> and <br /> tags with a placeholder.
61
+ inputString = inputString.replace(/<\s*br\s*\/?\s*>/gi, " nl2br ");
62
+
63
+ // Remove extra spaces and replace <br> tags with a placeholder.
64
+ inputString = inputString.replace ( / {2,}/g, ( match ) => match.slice ( 0, 1 ) );
65
+
66
+
67
+ // console.log(inputString);
68
+
69
+
70
+ // Split the string into an array of words.
71
+ const words = inputString.split ( " " );
72
+
73
+ const wordsInTitleCase = words.map ( ( word, i ) => {
74
+ switch (true) {
75
+ case TitleCaserUtils.isWordAmpersand ( word ):
76
+ // if the word is an ampersand, return it as is.
77
+ return word;
78
+ case TitleCaserUtils.hasHtmlBreak ( word ):
79
+ // If the word is a <br> tag, return it as is.
80
+ return word;
81
+ case TitleCaserUtils.isWordIgnored ( word, ignoreList ):
82
+ // If the word is in the ignore list, return it as is.
83
+ return word;
84
+ case replaceTermsArray.includes ( word.toLowerCase () ):
85
+ // If the word is in the replaceTerms array, return the replacement.
86
+ return replaceTermObj[word.toLowerCase ()];
87
+ case TitleCaserUtils.isWordInArray ( word, correctTitleCasingList ):
88
+ // If the word is in the correctTitleCasingList array, return the correct casing.
89
+ return TitleCaserUtils.correctTerm ( word, correctTitleCasingList );
90
+ case TitleCaserUtils.hasSuffix ( word, style ):
91
+ // If the word has a suffix, return the correct casing.
92
+ return TitleCaserUtils.correctSuffix ( word, correctTitleCasingList );
93
+ case TitleCaserUtils.hasHyphen ( word ):
94
+ // If the word has a hyphen, return the correct casing.
95
+ return TitleCaserUtils.correctTermHyphenated ( word, style );
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 ) : word.toLowerCase ();
103
+ case TitleCaserUtils.endsWithSymbol ( word ):
104
+ // If the word ends with a symbol, return the correct casing.
105
+ const splitWord = word.split ( /([.,\/#!$%\^&\*;:{}=\-_`~()])/g );
106
+ const processedWords = splitWord.map ( ( splitWord, j ) => {
107
+ // If the word is in the correctTitleCasingList array, return the correct casing.
108
+ if ( TitleCaserUtils.isWordInArray ( splitWord, correctTitleCasingList ) ) return TitleCaserUtils.correctTerm ( splitWord, correctTitleCasingList );
109
+ // Else return the word with the correct casing.
110
+ else return (j > 0 && TitleCaserUtils.endsWithSymbol ( splitWord )) ? splitWord.charAt ( 0 )
111
+ .toUpperCase () + splitWord.slice ( 1 ) : splitWord.charAt ( 0 )
112
+ .toUpperCase () + splitWord.slice ( 1 );
113
+ } );
114
+ // Join the processed words and return them.
115
+ return processedWords.join ( "" );
116
+ case TitleCaserUtils.startsWithSymbol ( word ):
117
+ // If the word starts with a symbol, return the correct casing.
118
+ return !TitleCaserUtils.isWordInArray ( word, correctTitleCasingList ) ? word : TitleCaserUtils.correctTerm ( word );
119
+ case TitleCaserUtils.hasRomanNumeral ( word ):
120
+ // If the word has a roman numeral, return the correct casing.
121
+ return word.toUpperCase ();
122
+ case TitleCaserUtils.hasNumbers ( word ):
123
+ // If the word has numbers, return the correct casing.
124
+ return word;
125
+ default:
126
+ // Default to returning the word with the correct casing.
127
+ return word.charAt ( 0 )
128
+ .toUpperCase () + word.slice ( 1 )
129
+ .toLowerCase ();
130
+ }
131
+ } );
132
+
133
+ // Join the words in the array into a string.
134
+ inputString = wordsInTitleCase.join ( " " );
135
+
136
+ for ( const phrase of correctPhraseCasingList ) {
137
+ // If the phrase is in the input string, replace it with the correct casing.
138
+ if ( inputString.toLowerCase ()
139
+ .includes ( phrase.toLowerCase () ) ) {
140
+ inputString = inputString.replace ( new RegExp ( phrase, 'gi' ), phrase );
141
+ }
142
+ }
143
+
144
+ // Replace the nl2br placeholder with <br> tags.
145
+ inputString = inputString.replace(/nl2br/gi, "<br />");
146
+
147
+ // Return the string.
148
+ return inputString;
149
+ } catch ( error ) {
150
+ throw new Error ( error );
151
+ }
152
+ }
153
+
154
+ setReplaceTerms ( terms ) {
155
+ if ( typeof terms !== 'object' ) {
156
+ throw new TypeError ( 'Invalid argument: replace terms must be an object.' );
157
+ }
158
+
159
+ // Add the new replace terms to the wordReplacementsList array
160
+ Object.entries ( terms ).forEach ( ( [ term, replacement ] ) => {
161
+ const index = wordReplacementsList.findIndex ( obj => obj[term] );
162
+ if ( index !== -1 ) {
163
+ // If the term already exists in the array, update the replacement value
164
+ wordReplacementsList[index][term] = replacement;
165
+ } else {
166
+ // If the term doesn't exist in the array, add a new object with the term and replacement
167
+ wordReplacementsList.push ( { [term]: replacement } );
168
+ }
169
+ } );
170
+
171
+ // Log the updated wordReplacementsList array
172
+ // console.log(wordReplacementsList);
173
+
174
+ // Update the replace terms option
175
+ this.options.wordReplacementsList = wordReplacementsList;
176
+ }
177
+
178
+ addReplaceTerm ( term, replacement ) {
179
+ if ( typeof term !== 'string' || typeof replacement !== 'string' ) {
180
+ throw new TypeError ( 'Invalid argument: term and replacement must be strings.' );
181
+ }
182
+
183
+ const index = this.wordReplacementsList.findIndex ( obj => obj[term] );
184
+
185
+ if ( index !== -1 ) {
186
+ // If the term already exists in the array, update the replacement value
187
+ this.wordReplacementsList[index][term] = replacement;
188
+ } else {
189
+ // If the term doesn't exist in the array, add a new object with the term and replacement
190
+ this.wordReplacementsList.push ( { [term]: replacement } );
191
+ }
192
+
193
+ // Update the replace terms option
194
+ this.options.wordReplacementsList = this.wordReplacementsList;
195
+ }
196
+
197
+ removeReplaceTerm ( term ) {
198
+ if ( typeof term !== 'string' ) {
199
+ throw new TypeError ( 'Invalid argument: term must be a string.' );
200
+ }
201
+
202
+ // Find the index of the term in the wordReplacementsList array
203
+ const index = this.wordReplacementsList.findIndex ( obj => Object.keys ( obj )[0] === term );
204
+
205
+ // If the term is not found in the array, throw an error
206
+ if ( index === -1 ) {
207
+ throw new Error ( `Term '${ term }' not found in word replacements list.` );
208
+ }
209
+
210
+ // Remove the term from the array
211
+ this.wordReplacementsList.splice ( index, 1 );
212
+
213
+ // Log the updated wordReplacementsList array
214
+ // console.log(this.wordReplacementsList);
215
+
216
+ // Update the replace terms option
217
+ this.options.wordReplacementsList = this.wordReplacementsList;
218
+ }
219
+
220
+ setStyle ( style ) {
221
+ if ( typeof style !== 'string' ) {
222
+ throw new TypeError ( 'Invalid argument: style must be a string.' );
223
+ }
224
+
225
+ this.options.style = style;
226
+ }
223
227
  }