@danielhaim/titlecaser 1.2.23

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Daniel Haim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # TitleCaser
2
+
3
+ Transform any text to proper title case format using popular style guides such as APA, AP, Chicago, NYT, Wikipedia, and British. Customize options to achieve greater flexibility and consistency.
4
+
5
+ ## Demo
6
+
7
+ <a target="_blank" href="https://danielhaim1.github.io/titlecaser/"><img src="dist/demo.png" width="100%" height="auto"></a>
8
+
9
+ [![npm version](https://badge.fury.io/js/titlecaser.svg?t=1623701119)](https://badge.fury.io/js/titlecaser)
10
+ [![Downloads](https://img.shields.io/npm/dt/titlecaser.svg)](https://www.npmjs.com/package/titlecaser)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+
13
+ The Language Conventions and Style Module is a comprehensive library designed to help web content developers adhere to the latest style guides and English language conventions. It offers a wide range of features, including support for various style guides such as AP, APA, Chicago, NY Times, Wikipedia, and British styles, and customizable preferences to suit your specific needs.
14
+
15
+ To streamline workflow, the Language Conventions and Style Module is available in both browser and node environment versions and includes a command-line interface for building, testing, and minimizing the module. Additionally, it features a filter ability that allows users to ignore certain phrases containing short words, preventing the module from mistakenly flagging instances where short words are used as part of a larger term or phrase.
16
+
17
+ The module has been designed to handle various capitalization scenarios, including hyphenated words, prefixes, suffixes, reserved words, Roman numerals, proper nouns that contain lowercase letters, and words that require capitalization in specific contexts. This ensures that your content meets the appropriate style and formatting guidelines, regardless of the context. It also offers word replacement capabilities, as well as ignored phrases to create consistency in cases where certain terms may be capitalized differently depending on the context, such as converting variations of gOogle to Google, front-end to Frontend.
18
+
19
+ So whether you're developing web content for a major news organization or simply looking to improve your writing skills, the Language Conventions and Style Module is an essential tool that can help ensure your work is accurate, consistent, and conforms to the latest style guidelines.
20
+
21
+ ## Key Features:
22
+
23
+ - Support for popular style guides and customizable preferences
24
+ - Advanced capitalization handling for suffixes, prefixes, hyphenated words, and reserved words
25
+ - Support for proper capitalization of Roman numerals and exclusion of specific words and phrases from title capitalization
26
+ - Word replacement capabilities for consistency in capitalization
27
+ Command-line interface for building, testing, and minimizing the module
28
+ - Pre-defined word lists for articles, conjunctions, prepositions, and uncapitalized words in titles
29
+ - Exclusion of common phrases from title capitalization
30
+
31
+ ## Installation
32
+ You can install this module via npm:
33
+
34
+ ```bash
35
+ npm i @danielhaim/titlecaser
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ The package can be imported and used in both Node.js and browser environments using the following syntax:
41
+
42
+ ```js
43
+ // CommonJS
44
+ const { TitleCaser } = require('./path/to/titlecaser');
45
+ const TitleCaser = require('./path/to/titlecaser');
46
+
47
+ // ECMAScript
48
+ import { TitleCaser } from './TitleCaser';
49
+ ```
50
+
51
+ Here's an example of how to use the modulate function:
52
+
53
+ ```js
54
+ const options = {
55
+ style: 'chicago'
56
+ };
57
+
58
+ const titleCaser = new TitleCaser(options);
59
+
60
+ const input = 'the book of life';
61
+ const output = titleCaser.toTitleCase(input);
62
+
63
+ console.log(output); // 'The Book of Life'
64
+ ```
65
+
66
+ ## Usage in the Browser
67
+
68
+ The function can also be used in a browser environment by including the `titlecase.browser.js` script in your HTML file:
69
+
70
+ Here's an example of how to use the modulate function:
71
+
72
+ ```html
73
+ <script src="./path/to/titlecase.browser.js"></script>
74
+ ```
75
+
76
+ After that, the `toTitleCase()` function can be accessed in your JavaScript code like this:
77
+
78
+ ```js
79
+ const options = {
80
+ style: 'apa'
81
+ };
82
+ const input = 'the future of devops: the next era';
83
+ const output = input.toTitleCase(options);
84
+
85
+ console.log(output); // The Future of DevOps: The Next Era
86
+ ```
87
+
88
+ ## Options
89
+
90
+ The `{options}` parameter is an object that contains the settings for the conversion process.
91
+
92
+ - `style`: determines the specific title case style to be applied. Permissible values include: `['ap', 'apa', 'british', 'chicago', 'nyt', 'wikipedia']`
93
+ - `articlesList` refers to the words that should be treated as articles in title case.
94
+ - `shortConjunctionsList` pertains to the words that should be treated as short conjunctions in title case.
95
+ - `shortPrepositionsList` relates to the words that should be treated as short prepositions in title case.
96
+ - `neverCapitalizedList` contains the words that should never be capitalized in title case.
97
+ - `wordReplacementsList` is a map of terms that will be replaced during the title case conversion process.
98
+
99
+ ## Methods
100
+
101
+ - `setReplaceTerms(terms: object)`: Sets word replacement terms to be used during title casing. Multiple calls can be made to add or update multiple word replacements.
102
+ - `removeReplaceTerm(term: string)`: Removes a replace term from the `wordReplacementsList` array in the options object of the `TitleCaser` instance. Throws an error if the term is not found in the array, otherwise removes it from the array and updates the options object.
103
+ - `addReplaceTerm(term: string, replacement: string)`: Adds a new replace term to the wordReplacementsList array in the options object of the TitleCaser instance. The method takes two string arguments: term specifies the word to be replaced, and replacement specifies the replacement for the word. If the term already exists in the array, the method updates its replacement value. Otherwise, it adds a new object with the term and replacement to the array. The method then updates the wordReplacementsList property in the options object.
104
+ - `setStyle(style: string)`: Sets the style option in the options object of the TitleCaser instance. The method takes a string argument style that specifies the style to use for the title casing. If the argument is not a string, the method throws a TypeError. Otherwise, it updates the style option in the options object.
105
+
106
+ ## Examples
107
+
108
+ The example below demonstrates how to use the TitleCaser class to convert a string to title case with custom options.
109
+
110
+ ### Basic Usage
111
+
112
+ ```js
113
+ // Instantiate a new TitleCaser object with the options
114
+ const titleCaser = new TitleCaser();
115
+
116
+ const input = 'the Book of lIfe';
117
+ const output = titleCaser.toTitleCase(input);
118
+
119
+ console.log(output); // The Book of Life
120
+ ```
121
+
122
+ ### Customizing Word Replacements Method
123
+
124
+ In the example below, we create a new instance of the `TitleCaser` class with the `APA` style option. We then set multiple replacement terms using two separate calls to the `setReplaceTerms()` method. Descriptive variable names are used for the input string and expected output. We call `toTitleCase()` to convert the input string to title case.
125
+
126
+ ```js
127
+ // CommonJS
128
+ const titleCaser = new TitleCaser({
129
+ style: 'apa'
130
+ });
131
+
132
+ // Set multiple replacement terms using two separate calls to setReplaceTerms()
133
+ titleCaser.setReplaceTerms({
134
+ 'hello world': 'Hello World',
135
+ 'replace me': 'Replace Me'
136
+ });
137
+ titleCaser.setReplaceTerms({
138
+ 'apa': 'APA'
139
+ });
140
+
141
+ // Use descriptive variable names for the input and expected output
142
+ const inputString = "hello world, replace me!";
143
+ const expectedOutput = "Hello World, Replace Me!";
144
+
145
+ // Call toTitleCase() to convert the input string to title case
146
+ const outputString = titleCaser.toTitleCase(inputString);
147
+ ```
148
+
149
+ ### Customizing TitleCaser
150
+
151
+ The example below demonstrates how to use the TitleCaser class to convert a string to title case with specific settings.
152
+
153
+ ```js
154
+ // Set the options object
155
+ const options = {
156
+ style: "nyt",
157
+ wordReplacementsList: {
158
+ "nodejs": "Node.js",
159
+ "javascript": "JavaScript",
160
+ "mongodb": "MongoDB"
161
+ }
162
+ };
163
+
164
+ // Instantiate a new TitleCaser object with the options
165
+ const titleCaser = new TitleCaser(options);
166
+
167
+ // Set the input string to test
168
+ const input = "the basics of nodejs development with mongodb";
169
+
170
+ // Set the expected output
171
+ const expectedOutput = "The Basics of Node.js Development with MongoDB";
172
+
173
+ // Call the toTitleCase method and store the result in actualOutput
174
+ const actualOutput = titleCaser.toTitleCase(input);
175
+
176
+ // Log the actual output
177
+ console.log(actualOutput);
178
+ ```
179
+
180
+ ### TitleCaser With Default Word Replacement
181
+
182
+ The example below demonstrates how to use the TitleCaser class to convert a string to title case with AP style formatting, including hyphenated words and word/brand replacement.
183
+
184
+ ```js
185
+ // Instantiate a new TitleCaser object with AP style formatting
186
+ const titleCaser = new TitleCaser({ style: 'ap' });
187
+
188
+ // Set the input string to test
189
+ const input = 'nodejs development on aws: an in-depth tutorial on server-side javascript deployment';
190
+
191
+ // Set the expected output
192
+ const expectedOutput = 'Node.js Development on AWS: An In-depth Tutorial on Server-side JavaScript Deployment';
193
+
194
+ // Call the toTitleCase method and store the result in actualOutput
195
+ const actualOutput = titleCaser.toTitleCase(input);
196
+
197
+ // Log the actual output
198
+ console.log(actualOutput);
199
+ ```
200
+
201
+ ### TitleCaser With Possessive Noun and a Colon
202
+
203
+ The example below demonstrates how to use the TitleCaser class to convert a string to title case with AP style formatting, including a possessive noun and a colon.
204
+
205
+ ```js
206
+ // Instantiate a new TitleCaser object with AP style formatting
207
+ const titleCaser = new TitleCaser({ style: "ap" });
208
+
209
+ // Set the input string to test
210
+ const input = "the iphone's impact on modern communication: a sociolinguistic analysis";
211
+
212
+ // Set the expected output
213
+ const expectedOutput = "The iPhone's Impact on Modern Communication: A Sociolinguistic Analysis";
214
+
215
+ // Call the toTitleCase method and store the result in actualOutput
216
+ const actualOutput = titleCaser.toTitleCase(input);
217
+
218
+ // Log the actual output
219
+ console.log(actualOutput);
220
+ ```
221
+
222
+ ## Build Process
223
+
224
+ ```bash
225
+ $ npm run build-package
226
+ $ npm run build-docs
227
+ $ npm run copy-package-to-docs
228
+ $ npm run test
229
+ ```
230
+
231
+ ## Test
232
+
233
+ ```bash
234
+ $ npm run test
235
+ ```
236
+
237
+ ```bash
238
+ Test Basic Options
239
+ ✓ Default title case conversion
240
+ ✓ Customized title case conversion
241
+ ✓ AP-style title case conversion with replacements
242
+ ✓ AP-style title case conversion with replacements
243
+ ✓ Capitalize suffix word in sentence
244
+
245
+ Test Methods
246
+ ✓ removeReplaceTerm
247
+ ✓ setReplaceTerms
248
+
249
+ Test Variation Stability
250
+ ✓ Hyphenated, colon, and short word replacements
251
+ ✓ Capitalization and word replacements
252
+ ✓ AP-style title case with possessive and colon
253
+ ✓ AP-style title case with lowercase back/front-end terms
254
+ ✓ Chicago style title case with comparison and colon
255
+ ✓ APA style title case with colon
256
+ ✓ Wikipedia style title case with acronym and hyphen
257
+ ✓ APA style title case with colon and apostrophe
258
+ ✓ Chicago style title case with custom term replacements
259
+ ✓ AP-style capitalization test with special terms and colon
260
+ ✓ NYT-style capitalization test with special terms and colon
261
+ ✓ APA style capitalization test with short conjunction terms and colon
262
+ ✓ Correct phrase casing list testing
263
+ ✓ Wikipedia style capitalization test with special term and colon
264
+
265
+ Test Reserved Words
266
+ ✓ Reserved word
267
+ ✓ Reserved word with colon
268
+ ✓ Reserved word, posessive
269
+ ✓ Hyphenated reserved word
270
+ ✓ Hyphenated reserved word, possessive
271
+ ✓ HTML line break nl2br, <br /> tag
272
+ ✓ Untrimmed white spaces
273
+ ```
274
+
275
+ ## Resources
276
+
277
+ Useful materials for improving your knowledge of writing and language style guides. These resources include various books and manuals, such as the Publication Manual of the American Psychological Association, the Chicago Manual of Style, and the AP Stylebook, which are widely recognized as authoritative sources on grammar, punctuation, and capitalization rules.
278
+
279
+ - [AP Stylebook, 56th Edition](https://store.stylebooks.com/ap-stylebook-56th-edition-print.html)
280
+ - [Publication Manual of the American Psychological Association, Seventh Edition (2020)](https://apastyle.apa.org/products/publication-manual-7th-edition)
281
+ - [Chicago Manual of Style: Capitalization](https://chat.openai.com/chat/643828ec-d4b5-4f21-b035-62946dd2cec3#:~:text=Chicago%20Manual%20of%20Style%3A%20Capitalization)
282
+ - [The Bluebook: A Uniform System of Citation. 21st ed. Cambridge: Harvard Law Review Association, 2020](https://open.mitchellhamline.edu/cgi/viewcontent.cgi?article=2782&context=wmlr)
283
+ - [The Chicago Manual of Style, 17th Edition](https://press.uchicago.edu/ucp/books/book/chicago/C/bo25956703.html)
284
+ - [The New York Times Manual of Style and Usage](https://www.worldcat.org/title/946964415)
285
+ - [Wikipedia: Letter case](https://chat.openai.com/chat/643828ec-d4b5-4f21-b035-62946dd2cec3#:~:text=Wikipedia%3A%20Letter%20case)
286
+ - [Wikipedia:Manual of Style/Titles of works](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Titles_of_works#Capital_letters)
287
+
288
+ ## Report Bugs
289
+
290
+ If you encounter any bugs or issues while using the library or the demo page, please report them by opening a new issue in the repository's issue tracker.
291
+
292
+ When reporting a bug, please provide as much detail as possible, including the steps to reproduce the issue and any error messages that you see. I appreciate any contribution to improve this library.
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@danielhaim/titlecaser",
3
+ "version": "1.2.23",
4
+ "description": "Converts a string to title case with multiple style options, ability to ignore certain words, and handle acronyms",
5
+ "keywords": [
6
+ "titlecase",
7
+ "text transformation",
8
+ "capitalization",
9
+ "formatting"
10
+ ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/danielhaim1/titlecaser.git"
14
+ },
15
+ "main": "src/TitleCaser.js",
16
+ "scripts": {
17
+ "test": "jest",
18
+ "build-docs": "cd docs && bundle install && bundle exec jekyll build",
19
+ "build-package": "webpack --mode production && terser dist/titlecaser.js -o dist/titlecaser.min.js",
20
+ "copy-package-to-docs": "cp -R dist/ docs/assets/js",
21
+ "tree": "tree -I 'node_modules'",
22
+ "release": "npm version patch && npm run build-package && npm run copy-package-to-docs && git add package.json package-lock.json dist/ docs/assets/js && git commit -m 'Bump version and build package for release' && git push"
23
+ },
24
+ "files": [
25
+ "LICENSE",
26
+ "README.md",
27
+ "package.json",
28
+ "src/index.js",
29
+ "src/TitleCaseConsts.js",
30
+ "src/TitleCaser.js",
31
+ "src/index.js"
32
+ ],
33
+ "author": {
34
+ "name": "Daniel Haim",
35
+ "url": "https://github.com/danielhaim1"
36
+ },
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "@babel/cli": "^7.21.0",
40
+ "@babel/core": "^7.21.0",
41
+ "@babel/plugin-transform-modules-commonjs": "^7.21.2",
42
+ "@babel/preset-env": "^7.20.2",
43
+ "@babel/runtime-corejs3": "^7.21.0",
44
+ "babel-loader": "^9.1.2",
45
+ "esbuild-jest": "^0.5.0",
46
+ "exports-loader": "^4.0.0",
47
+ "terser-webpack-plugin": "^5.3.7",
48
+ "webpack": "^5.76.3",
49
+ "webpack-cli": "^5.0.1",
50
+ "webpack-node-externals": "^3.0.0",
51
+ "@jest/expect": "^29.5.0",
52
+ "babel-jest": "^29.5.0",
53
+ "jest": "^29.5.0",
54
+ "jest-environment-jsdom": "^29.5.0",
55
+ "jest-environment-puppeteer": "^8.0.5",
56
+ "jest-puppeteer": "^8.0.5",
57
+ "puppeteer": "^19.8.2",
58
+ "puppeteer-core": "^19.8.0"
59
+ }
60
+ }
@@ -0,0 +1,214 @@
1
+ export const commonAbbreviationList = [
2
+ 'a', 'an', 'the', 'as', 'at', 'by', 'for', 'in', 'of', 'on',
3
+ 'to', 'up', 'yet', 'so', 'but', 'nor', 'or', 'and',
4
+ ];
5
+
6
+ export const correctTitleCasingList = [
7
+ // Web Technologies
8
+ 'AJAX', 'CSS', 'DOM', 'ES6', 'HTML', 'JavaScript', 'jQuery',
9
+ 'MobX', 'SCSS', 'TypeScript', 'Vue.js', '.NET', 'ASP', 'ASPX',
10
+ 'MySQL', 'PHP', 'PostgreSQL', 'Python', 'SQL', 'GraphQL',
11
+ 'HTML5',
12
+
13
+ // Acronyms/Abbreviations
14
+ 'API', 'APIs', 'ASCII', 'CI', 'CircleCI', 'CLI', 'DLL', 'DNS',
15
+ 'EC2', 'FTP', 'HTTP', 'HTTPs', 'ICMP', 'IDE', 'IP', 'ISP',
16
+ 'JSON', 'JSP', 'LPWAN', 'M2M', 'MQTT', 'OOP', 'REST', 'SSH',
17
+ 'SSL', 'TCP', 'UDP', 'URL', 'WLAN', 'WYSIWYG', 'XML', 'YAML',
18
+ 'YML', 'IMAP', 'RSS', 'IaaS', 'PaaS', 'SaaS', 'CaaS', 'FaaS',
19
+ 'XaaS', 'RaaS', 'IoE', 'IoT', 'LoRa', 'NB-IoT', 'RFID', 'RF',
20
+ 'RFI', 'RFQ', 'ECMAScript', 'IO', 'I/O', 'DevOps', 'SecOps',
21
+ 'DDoS', 'VoIP', 'AI', 'AR', 'ML', 'VR',
22
+
23
+ // Misc.
24
+ 'w/', 'w/o',
25
+
26
+ // 'eTerms'
27
+ 'e-Book', 'e-Books', 'eBook', 'eBooks', 'eCommerce',
28
+ 'eMarket', 'eMarketplace', 'eMarketplaces', 'eMarkets',
29
+ 'eReader', 'eShop', 'eShops', 'eStore', 'eStores',
30
+ 'E-commerce',
31
+
32
+ // Accounting terms
33
+ 'AP', 'COGS', 'EBIT', 'EPS', 'FIFO', 'GAAP', 'LIFO',
34
+ 'P&L', 'ROI', 'SOX', 'TCO', 'VAT',
35
+
36
+ // Investment terms
37
+ 'CAGR', 'DCF', 'ETF', 'IPO', 'IRR', 'M&A', 'NAV', 'PE', 'PEG',
38
+ 'PPE', 'ROE', 'S&P', 'TVM', 'VC',
39
+
40
+ // Marketing terms
41
+ 'B2B', 'B2C', 'CMO', 'CPA', 'CPC', 'CPL', 'CPM', 'CRM', 'CTA',
42
+ 'CTR', 'SEO', 'SEM', 'SMM', 'USP', 'A/B', 'CTA', 'CTOR',
43
+ 'CTR', 'KPI', 'PWA', 'SEM', 'SERP', 'SERPs', 'SMM', 'SMO',
44
+ 'FAQ', 'FAQA', 'FAQS', 'UI', 'UI/UX', 'UX', 'T&C', 'TOS',
45
+ 'PP', 'CRM', 'PoE', 'PoW', 'PoC', 'A11Y', 'PR',
46
+
47
+ // Sales terms
48
+ 'BANT', 'GAP', 'KPI', 'MQL', 'NPS', 'POS', 'SPIN', 'SQL',
49
+ 'SWOT',
50
+
51
+ // Legal terms
52
+ 'AFA', 'ADR', 'CCPA', 'CFAA', 'CISG', 'DMCA', 'EULA', 'GDPR',
53
+ 'HIPAA', 'NDA', 'SOW', 'TOS',
54
+
55
+ // Roles and titles
56
+ 'CEO', 'CEOs', 'CFO', 'CFOs', 'CIO', 'CIOs', 'CMO', 'CMOs',
57
+ 'COO', 'COOs', 'CPO', 'CPOs', 'CRO', 'CROs', 'CSO', 'CSOs',
58
+ 'CTO', 'CTOs', 'EVP', 'EVPs', 'HR', 'HRs', 'SVP', 'SVPs',
59
+ 'VP', 'VPs',
60
+
61
+ // Non-profit organizations
62
+ 'NGO', 'NPO', 'NGOs', 'NPOs', 'UN', 'UNESCO', 'UNICEF',
63
+ 'UNHCR', 'UNODC', 'UNDP', 'UNFPA', 'UNEP',
64
+
65
+ 'Adobe', 'Airbnb', 'Alibaba', 'Allstate', 'American Express', 'Apple',
66
+ 'AT&T', 'BMW', 'Boeing', 'Cisco', 'Citigroup', 'Coca', 'Deloitte', 'Disney',
67
+ 'Dropbox', 'ExxonMobil', 'Ford', 'GE', 'General', 'Goldman Sachs', 'Google',
68
+ 'Hilton', 'HP', 'IBM', 'Intel', 'JPMorgan', 'Johnson & Johnson', 'LinkedIn',
69
+ "McDonald's", 'Mercedes-Benz', 'Microsoft', 'Nestle', 'Nike', 'Nissan',
70
+ 'Oracle', 'PepsiCo', 'Pfizer', 'Salesforce', 'Samsung', 'Shell', 'Sony',
71
+ 'Tesla', 'Toyota', 'Uber', 'Verizon', 'Visa', 'Walmart', 'Wells Fargo',
72
+ 'Yahoo', 'Zara', 'IKEA', 'Facebook', 'YouTube', 'Instagram', 'Twitter',
73
+ 'TensorFlow', 'Amazon', 'Netflix', 'eBay', 'iPhone', 'iPad', 'iPod',
74
+ 'PlayStation', 'PayPal', 'GitHub', 'GitLab', 'CodeIgniter', 'WordPress',
75
+ 'WooCommerce', 'MongoDB', 'JIRA', 'HubSpot', 'AirDrop', 'AirPlay', 'AirPods',
76
+ 'AirTags', 'FinalCut', 'GarageBand', 'iBooks', 'iCloud', 'iLife', 'iMac',
77
+ 'iMessage', 'iMovie', 'iPhoto', 'iWatch', 'iWork', 'LogicPro', 'macOS',
78
+ 'ProTools', 'QuickTime', 'AdWords', 'AdSense', 'TikTok', 'Slack', 'Trello',
79
+ 'Zoom', 'Twitch', 'Snapchat', 'WhatsApp', 'Telegram', 'Discord', 'Reddit',
80
+ 'Quora', 'StackOverflow', 'StackExchange', 'Coca-Cola',
81
+ 'AWS', 'GCP', 'VMware', 'CVS',
82
+
83
+ // Sports
84
+ 'NBA', 'NCAA', 'NFL', 'WWE', 'WWF', 'FIFA',
85
+
86
+ // Time-related, numbers, and measurements: Includes abbreviations for
87
+ // time-related terms, numbers, and measurements.
88
+ 'a.m.', 'p.m.', 'ca.', 'cc.', 'fig.', 'pl.', 'pt.', 'rev.',
89
+ 'sr.', 'v.', 'vol.', 'et al.', 'pp.', 'p.',
90
+
91
+ // Professional abbreviations, degrees, and titles: Includes abbreviations
92
+ // for professional titles, degrees, and certifications.
93
+ 'ph.d.', 'm.d.', 'd.d.s.', 'd.m.d.', 'd.o.', 'd.c.', 'd.v.m.',
94
+ 'd.n.p.', 'd.p.m.', 'd.s.w.', 'd.s.n.', 'd.n.sc.', 'd.n.a.',
95
+ 'd.n.t.', 'd.n.p.t.', 'd.n.o.', 'd.n.m.', 'd.n.e.', 'd.n.s.',
96
+ 'd.n.p.s.',
97
+
98
+ // Academic & literary abbreviations: Includes abbreviations for academic
99
+ // and literary terms, such as 'ed.' for 'edition' and 'vol.' for 'volume'.
100
+ 'adj.', 'adv.', 'cf.', 'cm.', 'co.', 'corp.', 'dept.',
101
+ 'dist.', 'ed.', 'edn.', 'esp.', 'etc.', 'ex.', 'i.e.', 'e.g.',
102
+ 'op. cit.', 'vs.',
103
+
104
+ // Commercial
105
+ 'Ltd.', 'Co.', 'Inc.', 'St.', 'Ave.', 'Bldg.', 'No.',
106
+ ];
107
+
108
+ export const wordReplacementsList = [
109
+ { 'a.k.a': 'AKA' },
110
+ { 'a.s.a.p': 'ASAP' },
111
+ { 'angularjs': 'Angular.js' },
112
+ { 'back-end': 'Backend' },
113
+ { 'd.i.y': 'DIY' },
114
+ { 'e-book': 'eBook' },
115
+ { 'e-books': 'eBooks' },
116
+ { 'e-commerce': 'eCommerce' },
117
+ { 'ecom': 'eCommerce' },
118
+ { 'ecommerce': 'eCommerce' },
119
+ { 'f.a.q': 'FAQ' },
120
+ { 'f.a.q.a': 'FAQs' },
121
+ { 'f.a.q.s': 'FAQs' },
122
+ { 'f.y.i': 'FYI' },
123
+ { 'front-end': 'Frontend' },
124
+ { 'full-stack': 'Fullstack' },
125
+ { 'nextjs': 'Next.js' },
126
+ { 'nodejs': 'Node.js' },
127
+ { 'nuxtjs': 'Nuxt.js' },
128
+ { 'reactjs': 'React.js' },
129
+ { 't.b.d': 'TBD' },
130
+ { 'vuejs': 'Vue.js' },
131
+ { 'phd': 'ph.d.' },
132
+ ];
133
+
134
+ // ! TODO
135
+ // const wordReplacementsList = [
136
+ // { word: 'a.k.a', replacement: 'AKA' },
137
+ // { word: 'a.s.a.p', replacement: 'ASAP' },
138
+ // { word: 'angularjs', replacement: 'Angular.js' },
139
+ // { word: 'back-end', replacement: 'Backend' },
140
+ // { word: 'd.i.y', replacement: 'DIY' },
141
+ // { word: 'e-book', replacement: 'eBook' },
142
+ // { word: 'e-books', replacement: 'eBooks' },
143
+ // { word: 'e-commerce', replacement: 'eCommerce' },
144
+ // { word: 'ecom', replacement: 'eCommerce' },
145
+ // { word: 'ecommerce', replacement: 'eCommerce' },
146
+ // { word: 'f.a.q', replacement: 'FAQ' },
147
+ // { word: 'f.a.q.a', replacement: 'FAQs' },
148
+ // { word: 'f.a.q.s', replacement: 'FAQs' },
149
+ // { word: 'f.y.i', replacement: 'FYI' },
150
+ // { word: 'front-end', replacement: 'Frontend' },
151
+ // { word: 'full-stack', replacement: 'Fullstack' },
152
+ // { word: 'nextjs', replacement: 'Next.js' },
153
+ // { word: 'nodejs', replacement: 'Node.js' },
154
+ // { word: 'nuxtjs', replacement: 'Nuxt.js' },
155
+ // { word: 'reactjs', replacement: 'React.js' },
156
+ // { word: 't.b.d', replacement: 'TBD' },
157
+ // { word: 'vuejs', replacement: 'Vue.js' },
158
+ // { word: 'phd', replacement: 'ph.d.' },
159
+ // ];
160
+
161
+ export const titleCaseStylesList = Object.freeze({
162
+ AP: 'ap',
163
+ APA: 'apa',
164
+ BRITISH: 'british',
165
+ CHICAGO: 'chicago',
166
+ NYT: 'nyt',
167
+ WIKIPEDIA: 'wikipedia'
168
+ });
169
+ export const allowedTitleCaseStylesList = Object.values(titleCaseStylesList);
170
+ export const titleCaseDefaultOptionsList = Object.freeze({
171
+ ap: {
172
+ shortConjunctionsList: ['and', 'but', 'or', 'for', 'nor', 'yet', 'so'],
173
+ articlesList: ['a', 'an', 'the'],
174
+ shortPrepositionsList: ['as', 'at', 'by', 'in', 'of', 'on', 'to', 'up', 'via'],
175
+ neverCapitalizedList: []
176
+ },
177
+ apa: {
178
+ shortConjunctionsList: ['and', 'as', 'but', 'by', 'for', 'in', 'nor', 'of', 'on', 'or', 'so', 'to', 'yet'],
179
+ articlesList: ['a', 'an', 'the'],
180
+ shortPrepositionsList: ['as', 'at', 'by', 'for', 'in', 'of', 'on', 'to', 'up', 'via'],
181
+ neverCapitalizedList: []
182
+ },
183
+ british: {
184
+ shortConjunctionsList: ['and', 'but', 'or', 'for', 'nor', 'yet', 'so'],
185
+ articlesList: ['a', 'an', 'the'],
186
+ shortPrepositionsList: ['as', 'at', 'by', 'in', 'of', 'on', 'to', 'up', 'via'],
187
+ neverCapitalizedList: []
188
+ },
189
+ chicago: {
190
+ shortConjunctionsList: ['and', 'but', 'or', 'for', 'nor', 'yet', 'so'],
191
+ articlesList: ['a', 'an', 'the'],
192
+ shortPrepositionsList: ['as', 'at', 'by', 'for', 'in', 'of', 'on', 'to', 'up', 'with', 'via'],
193
+ neverCapitalizedList: ['etc.']
194
+ },
195
+ nyt: {
196
+ shortConjunctionsList: ['and', 'but', 'or', 'for', 'nor', 'yet', 'so'],
197
+ articlesList: ['a', 'an', 'the'],
198
+ shortPrepositionsList: ['as', 'at', 'by', 'in', 'of', 'on', 'to', 'up', 'via'],
199
+ neverCapitalizedList: []
200
+ },
201
+ wikipedia: {
202
+ shortConjunctionsList: ['and', 'as', 'but', 'for', 'if', 'nor', 'or', 'so', 'yet'],
203
+ articlesList: ['a', 'an', 'the'],
204
+ shortPrepositionsList: ['as', 'at', 'by', 'in', 'of', 'on', 'to', 'up', 'via'],
205
+ neverCapitalizedList: []
206
+ },
207
+ });
208
+
209
+ export const ignoredWordsList = [];
210
+ export const correctPhraseCasingList = [
211
+ 'The Cybersmile Foundation',
212
+ 'CO. by Colgate',
213
+ "The Simpsons",
214
+ ];
@@ -0,0 +1,220 @@
1
+ import {
2
+ commonAbbreviationList,
3
+ correctTitleCasingList,
4
+ correctPhraseCasingList,
5
+ wordReplacementsList,
6
+ }
7
+ from "./TitleCaseConsts.js";
8
+
9
+ import TitleCaseHelper from "./TitleCaseHelper.js";
10
+
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
+ replaceTermsList = wordReplacementsList
31
+ } = this.options;
32
+ const ignoreList = ["nl2br", ...neverCapitalize];
33
+ const {
34
+ articlesList,
35
+ shortConjunctionsList,
36
+ shortPrepositionsList,
37
+ neverCapitalizedList,
38
+ replaceTerms
39
+ } = TitleCaseHelper.getTitleCaseOptions(this.options, commonAbbreviationList, wordReplacementsList);
40
+
41
+ // Prerocess the replaceTerms array to make it easier to search for.
42
+ const replaceTermsArray = replaceTermsList.map(term => Object.keys(term)[0].toLowerCase());
43
+ // Create an object from the replaceTerms array to make it easier to search for.
44
+ const replaceTermsObj = Object.fromEntries(replaceTermsList.map(
45
+ term => [Object.keys(term)[0].toLowerCase(), Object.values(term)[0]]
46
+ ));
47
+
48
+
49
+ // Remove extra spaces and replace <br> tags with a placeholder.
50
+ let inputString = str.trim();
51
+
52
+ // Remove extra spaces and replace <br> tags with a placeholder.
53
+ inputString = inputString.replace(/ {2,}/g, (match) => match.slice(0, 1));
54
+
55
+ // Replace <br> tags with a placeholder.
56
+ inputString = inputString.replace(/<br\s*[\/]?>/gi, "nl2br ");
57
+
58
+ // Split the string into an array of words.
59
+ const words = inputString.split(" ");
60
+
61
+ const wordsInTitleCase = words.map((word, i) => {
62
+ switch (true) {
63
+ case TitleCaseHelper.hasHtmlBreak(word):
64
+ // If the word is a <br> tag, return it as is.
65
+ return word;
66
+ case TitleCaseHelper.isWordIgnored(word, ignoreList):
67
+ // If the word is in the ignore list, return it as is.
68
+ return word;
69
+ case replaceTermsArray.includes(word.toLowerCase()):
70
+ // If the word is in the replaceTerms array, return the replacement.
71
+ return replaceTermsObj[word.toLowerCase()];
72
+ case TitleCaseHelper.isWordInArray(word, correctTitleCasingList):
73
+ // If the word is in the correctTitleCasingList array, return the correct casing.
74
+ return TitleCaseHelper.correctTerm(word, correctTitleCasingList);
75
+ case TitleCaseHelper.hasSuffix(word, style):
76
+ // If the word has a suffix, return the correct casing.
77
+ return TitleCaseHelper.correctSuffix(word, correctTitleCasingList);
78
+ case TitleCaseHelper.hasHyphen(word):
79
+ // If the word has a hyphen, return the correct casing.
80
+ return TitleCaseHelper.correctTermHyphenated(word, style);
81
+ case TitleCaseHelper.hasUppercaseIntentional(word):
82
+ // If the word has an intentional uppercase letter, return the correct casing.
83
+ return word;
84
+ case TitleCaseHelper.isShortWord(word, style) && i !== 0:
85
+ // If the word is a short word, return the correct casing.
86
+ return (i > 0 && TitleCaseHelper.endsWithSymbol(words[i - 1],
87
+ [':', '?', '!', '.'])) ? word.charAt(0).toUpperCase() + word.slice(1) : word.toLowerCase();
88
+ case TitleCaseHelper.endsWithSymbol(word):
89
+ // If the word ends with a symbol, return the correct casing.
90
+ const splitWord = word.split(/([.,\/#!$%\^&\*;:{}=\-_`~()])/g);
91
+ const processedWords = splitWord.map((splitWord, j) => {
92
+ // If the word is in the correctTitleCasingList array, return the correct casing.
93
+ if (TitleCaseHelper.isWordInArray(splitWord, correctTitleCasingList)) return TitleCaseHelper.correctTerm(splitWord, correctTitleCasingList);
94
+ // Else return the word with the correct casing.
95
+ else return (j > 0 && TitleCaseHelper.endsWithSymbol(splitWord)) ? splitWord.charAt(0)
96
+ .toUpperCase() + splitWord.slice(1) : splitWord.charAt(0)
97
+ .toUpperCase() + splitWord.slice(1);
98
+ });
99
+ // Join the processed words and return them.
100
+ return processedWords.join("");
101
+ case TitleCaseHelper.startsWithSymbol(word):
102
+ // If the word starts with a symbol, return the correct casing.
103
+ return !TitleCaseHelper.isWordInArray(word, correctTitleCasingList) ? word : TitleCaseHelper.correctTerm(word);
104
+ case TitleCaseHelper.hasRomanNumeral(word):
105
+ // If the word has a roman numeral, return the correct casing.
106
+ return word.toUpperCase();
107
+ case TitleCaseHelper.hasNumbers(word):
108
+ // If the word has numbers, return the correct casing.
109
+ return word;
110
+ default:
111
+ // Default to returning the word with the correct casing.
112
+ return word.charAt(0)
113
+ .toUpperCase() + word.slice(1)
114
+ .toLowerCase();
115
+ }
116
+ });
117
+
118
+ // Join the words in the array into a string.
119
+ inputString = wordsInTitleCase.join(" ");
120
+
121
+ for (const phrase of correctPhraseCasingList) {
122
+ // If the phrase is in the input string, replace it with the correct casing.
123
+ if (inputString.toLowerCase()
124
+ .includes(phrase.toLowerCase())) {
125
+ inputString = inputString.replace(new RegExp(phrase, 'gi'), phrase);
126
+ }
127
+ }
128
+
129
+ // Replace the nl2br placeholder with <br> tags.
130
+ inputString = inputString.replace(/nl2br /gi, "<br />");
131
+
132
+ // Return the string.
133
+ return inputString;
134
+ }
135
+ catch (error) {
136
+ throw new Error(error);
137
+ }
138
+ }
139
+
140
+ setReplaceTerms(terms) {
141
+ if (typeof terms !== 'object') {
142
+ throw new TypeError('Invalid argument: replace terms must be an object.');
143
+ }
144
+
145
+ // Add the new replace terms to the wordReplacementsList array
146
+ Object.entries(terms).forEach(([term, replacement]) => {
147
+ const index = wordReplacementsList.findIndex(obj => obj[term]);
148
+ if (index !== -1) {
149
+ // If the term already exists in the array, update the replacement value
150
+ wordReplacementsList[index][term] = replacement;
151
+ } else {
152
+ // If the term doesn't exist in the array, add a new object with the term and replacement
153
+ wordReplacementsList.push({ [term]: replacement });
154
+ }
155
+ });
156
+
157
+ // Log the updated wordReplacementsList array
158
+ // console.log(wordReplacementsList);
159
+
160
+ // Update the replace terms option
161
+ this.options.wordReplacementsList = wordReplacementsList;
162
+ }
163
+
164
+ addReplaceTerm(term, replacement) {
165
+ if (typeof term !== 'string' || typeof replacement !== 'string') {
166
+ throw new TypeError('Invalid argument: term and replacement must be strings.');
167
+ }
168
+
169
+ const index = this.wordReplacementsList.findIndex(obj => obj[term]);
170
+
171
+ if (index !== -1) {
172
+ // If the term already exists in the array, update the replacement value
173
+ this.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
+ this.wordReplacementsList.push({ [term]: replacement });
177
+ }
178
+
179
+ // Update the replace terms option
180
+ this.options.wordReplacementsList = this.wordReplacementsList;
181
+ }
182
+
183
+ removeReplaceTerm(term) {
184
+ if (typeof term !== 'string') {
185
+ throw new TypeError('Invalid argument: term must be a string.');
186
+ }
187
+
188
+ // Find the index of the term in the wordReplacementsList array
189
+ const index = this.wordReplacementsList.findIndex(obj => Object.keys(obj)[0] === term);
190
+
191
+ // If the term is not found in the array, throw an error
192
+ if (index === -1) {
193
+ throw new Error(`Term '${term}' not found in word replacements list.`);
194
+ }
195
+
196
+ // Remove the term from the array
197
+ this.wordReplacementsList.splice(index, 1);
198
+
199
+ // Log the updated wordReplacementsList array
200
+ // console.log(this.wordReplacementsList);
201
+
202
+ // Update the replace terms option
203
+ this.options.wordReplacementsList = this.wordReplacementsList;
204
+ }
205
+
206
+ setStyle(style) {
207
+ if (typeof style !== 'string') {
208
+ throw new TypeError('Invalid argument: style must be a string.');
209
+ }
210
+
211
+ this.options.style = style;
212
+ }
213
+ }
214
+
215
+ // If the module is being used in a Node environment, export the module.
216
+ if (typeof module === 'object' && module.exports) {
217
+ module.exports = { TitleCaser };
218
+ }
219
+
220
+
package/src/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { TitleCaser } from './TitleCaser.js';
2
+
3
+ if (typeof String.prototype.toTitleCase !== 'function') {
4
+ String.prototype.toTitleCase = function (options) {
5
+ const titleCaser = new TitleCaser(options);
6
+ return titleCaser.toTitleCase(this);
7
+ };
8
+ }
9
+
10
+ export { TitleCaser };
11
+
12
+ if (typeof window === 'object') {
13
+ window.TitleCaser = TitleCaser;
14
+ }