@danielhaim/titlecaser 1.2.53 → 1.2.54
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/package.json +1 -1
- package/src/TitleCaser.js +15 -9
- package/src/TitleCaserUtils.js +44 -8
package/package.json
CHANGED
package/src/TitleCaser.js
CHANGED
|
@@ -28,15 +28,19 @@ export class TitleCaser {
|
|
|
28
28
|
const {
|
|
29
29
|
style = "ap",
|
|
30
30
|
neverCapitalize = [],
|
|
31
|
-
replaceTermList = wordReplacementsList
|
|
31
|
+
replaceTermList = wordReplacementsList,
|
|
32
|
+
smartQuotes = false // Set to false by default
|
|
32
33
|
} = this.options;
|
|
34
|
+
|
|
35
|
+
|
|
33
36
|
const ignoreList = [ "nl2br", ...neverCapitalize ];
|
|
34
37
|
const {
|
|
35
38
|
articlesList,
|
|
36
39
|
shortConjunctionsList,
|
|
37
40
|
shortPrepositionsList,
|
|
38
41
|
neverCapitalizedList,
|
|
39
|
-
replaceTerms
|
|
42
|
+
replaceTerms,
|
|
43
|
+
smartQuotes: mergedSmartQuotes // Rename for clarity
|
|
40
44
|
} = TitleCaserUtils.getTitleCaseOptions ( this.options, commonAbbreviationList, wordReplacementsList );
|
|
41
45
|
|
|
42
46
|
// Prerocess the replaceTerms array to make it easier to search for.
|
|
@@ -50,21 +54,20 @@ export class TitleCaser {
|
|
|
50
54
|
'&': '&',
|
|
51
55
|
'<': '<',
|
|
52
56
|
'>': '>',
|
|
57
|
+
// '\u2018': '\u2019', // Smart single quote
|
|
58
|
+
// '\u201C': '\u201D', // Smart double quote
|
|
53
59
|
'"': '"',
|
|
54
60
|
"'": '''
|
|
55
61
|
};
|
|
56
62
|
|
|
57
63
|
// Remove extra spaces and replace <br> tags with a placeholder.
|
|
58
64
|
let inputString = str.trim ();
|
|
59
|
-
|
|
65
|
+
|
|
60
66
|
// Replace <br> and <br /> tags with a placeholder.
|
|
61
67
|
inputString = inputString.replace(/<\s*br\s*\/?\s*>/gi, " nl2br ");
|
|
62
68
|
|
|
63
69
|
// Remove extra spaces and replace <br> tags with a placeholder.
|
|
64
70
|
inputString = inputString.replace ( / {2,}/g, ( match ) => match.slice ( 0, 1 ) );
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// console.log(inputString);
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
// Split the string into an array of words.
|
|
@@ -143,10 +146,13 @@ export class TitleCaser {
|
|
|
143
146
|
|
|
144
147
|
// Replace the nl2br placeholder with <br> tags.
|
|
145
148
|
inputString = inputString.replace(/nl2br/gi, "<br />");
|
|
146
|
-
|
|
147
|
-
// BEFORE WE RETURN THE STRING
|
|
148
|
-
// CHECK THE LAST WORD AND IF IT IS INTENTIONALLY UPPERCASED, IF IT IS, RETURN THE STRING.
|
|
149
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
|
+
|
|
150
156
|
// Return the string.
|
|
151
157
|
return inputString;
|
|
152
158
|
} catch ( error ) {
|
package/src/TitleCaserUtils.js
CHANGED
|
@@ -71,12 +71,12 @@ export class TitleCaserUtils {
|
|
|
71
71
|
return TitleCaserUtils.titleCaseOptionsCache.get ( cacheKey );
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// Merge the default options with the user-provided options
|
|
75
74
|
const mergedOptions = {
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
...titleCaseDefaultOptionsList[options.style || "ap"],
|
|
76
|
+
...options,
|
|
77
|
+
smartQuotes: options.hasOwnProperty('smartQuotes') ? options.smartQuotes : false
|
|
78
78
|
};
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Merge the default articles with user-provided articles and lowercase words
|
|
81
81
|
const mergedArticles = mergedOptions.articlesList.concat ( lowercaseWords )
|
|
82
82
|
.filter ( ( word, index, array ) => array.indexOf ( word ) === index );
|
|
@@ -97,14 +97,15 @@ export class TitleCaserUtils {
|
|
|
97
97
|
];
|
|
98
98
|
|
|
99
99
|
// Return the merged options
|
|
100
|
-
|
|
100
|
+
const result = {
|
|
101
101
|
articlesList: mergedArticles,
|
|
102
102
|
shortConjunctionsList: mergedShortConjunctions,
|
|
103
103
|
shortPrepositionsList: mergedShortPrepositions,
|
|
104
|
-
neverCapitalizedList: [
|
|
104
|
+
neverCapitalizedList: [...mergedOptions.neverCapitalizedList],
|
|
105
105
|
replaceTerms: mergedReplaceTerms,
|
|
106
|
+
smartQuotes: mergedOptions.smartQuotes // Add smartQuotes option to result
|
|
106
107
|
};
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
// Add the merged options to the cache and return them
|
|
109
110
|
TitleCaserUtils.titleCaseOptionsCache.set ( cacheKey, result );
|
|
110
111
|
return result;
|
|
@@ -387,7 +388,42 @@ export class TitleCaserUtils {
|
|
|
387
388
|
// Check if the targetWord is in the wordList
|
|
388
389
|
return wordList.some ( ( word ) => word.toLowerCase () === targetWord.toLowerCase () );
|
|
389
390
|
}
|
|
390
|
-
|
|
391
|
+
|
|
392
|
+
static convertQuotesToCurly(input) {
|
|
393
|
+
const curlyQuotes = {
|
|
394
|
+
"'": ['\u2018', '\u2019'],
|
|
395
|
+
'"': ['\u201C', '\u201D'],
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
let replacedText = '';
|
|
399
|
+
|
|
400
|
+
for (let i = 0; i < input.length; i++) {
|
|
401
|
+
const char = input[i];
|
|
402
|
+
const curlyQuotePair = curlyQuotes[char];
|
|
403
|
+
|
|
404
|
+
if (curlyQuotePair) {
|
|
405
|
+
const prevChar = input[i - 1];
|
|
406
|
+
const nextChar = input[i + 1];
|
|
407
|
+
|
|
408
|
+
// Determine whether to use left or right curly quote
|
|
409
|
+
const isLeftAligned = (!prevChar || prevChar === ' ' || prevChar === '\n');
|
|
410
|
+
const curlyQuote = isLeftAligned ? curlyQuotePair[0] : curlyQuotePair[1];
|
|
411
|
+
replacedText += curlyQuote;
|
|
412
|
+
|
|
413
|
+
// Handle cases where right curly quote is followed by punctuation or space
|
|
414
|
+
if (curlyQuote === curlyQuotePair[1] && /[.,;!?()\[\]{}:]/.test(nextChar)) {
|
|
415
|
+
replacedText += nextChar;
|
|
416
|
+
i++; // Skip the next character
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
replacedText += char;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return replacedText;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
391
427
|
// This function is used to replace a word with a term in the replaceTerms object
|
|
392
428
|
static replaceTerm ( word, replaceTermObj ) {
|
|
393
429
|
// Validate input
|