@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielhaim/titlecaser",
3
- "version": "1.2.53",
3
+ "version": "1.2.54",
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",
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
  '<': '&lt;',
52
56
  '>': '&gt;',
57
+ // '\u2018': '\u2019', // Smart single quote
58
+ // '\u201C': '\u201D', // Smart double quote
53
59
  '"': '&quot;',
54
60
  "'": '&#039;'
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 ) {
@@ -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
- ...titleCaseDefaultOptionsList[options.style || "ap"],
77
- ...options
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
- const result = {
100
+ const result = {
101
101
  articlesList: mergedArticles,
102
102
  shortConjunctionsList: mergedShortConjunctions,
103
103
  shortPrepositionsList: mergedShortPrepositions,
104
- neverCapitalizedList: [ ...mergedOptions.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