├── articles.js ├── conjunctions.js ├── bin.js ├── package.json ├── test ├── index.html └── tests.json ├── LICENCE.md ├── to-title-case.js ├── prepositions.js └── README.md /articles.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 'the', 'a', 'an', 'some' ] 2 | -------------------------------------------------------------------------------- /conjunctions.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'as' 3 | , 'because' 4 | , 'for' 5 | , 'and' 6 | , 'nor' 7 | , 'but' 8 | , 'or' 9 | , 'yet' 10 | , 'so' 11 | ] 12 | -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var toTitleCase = require('./to-title-case') 4 | 5 | process.argv.slice(2).forEach(function (a) { 6 | console.log(toTitleCase(a)) 7 | }) 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "titlecase", 3 | "version": "1.1.2", 4 | "description": "Intelligently converting strings to title case (an enhanced fork of David Gouch's library)", 5 | "main": "to-title-case.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/rvagg/titlecase.git" 9 | }, 10 | "keywords": [ 11 | "title-case", 12 | "title", 13 | "case", 14 | "gouch", 15 | "to-title-case" 16 | ], 17 | "author": "Rod Vagg ", 18 | "license": "MIT", 19 | "bin": { 20 | "to-title-case": "./bin.js" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | To Title Case 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2008–2013 David Gouch. Licensed under the MIT License. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | 22 | -------------------------------------------------------------------------------- /to-title-case.js: -------------------------------------------------------------------------------- 1 | /* 2 | * To Title Case 2.1 – http://individed.com/code/to-title-case/ 3 | * Copyright © 2008–2013 David Gouch. Licensed under the MIT License. 4 | * 5 | * modifications by @rvagg Apr-2014 6 | */ 7 | 8 | //String.prototype.toTitleCase = function(){ 9 | 10 | 11 | var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i; 12 | 13 | 14 | module.exports = function toTitleCase(str){ 15 | return titleCase(str, smallWords) 16 | } 17 | 18 | 19 | module.exports.toTitleCase = module.exports 20 | 21 | 22 | var laxWords = require('./articles').concat(require('./prepositions')).concat(require('./conjunctions')) 23 | .concat(smallWords.source.replace(/(^\^\(|\)\$$)/g, '').split('|')) 24 | .concat(['is']) // a personal preference 25 | , laxWordsRe = new RegExp('^(' + laxWords.join('|') + ')$', 'i') 26 | 27 | 28 | module.exports.toLaxTitleCase = function toLaxTitleCase(str){ 29 | return titleCase(str, laxWordsRe) 30 | } 31 | 32 | 33 | function titleCase (str, smallWords) { 34 | if (!str) 35 | return str 36 | return str.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function(match, index, title){ 37 | if (index > 0 && index + match.length !== title.length && 38 | match.search(smallWords) > -1 && title.charAt(index - 2) !== ':' && 39 | (title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') && 40 | title.charAt(index - 1).search(/[^\s-]/) < 0) { 41 | return match.toLowerCase(); 42 | } 43 | 44 | if (match.substr(1).search(/[A-Z]|\../) > -1) { 45 | return match; 46 | } 47 | 48 | return match.charAt(0).toUpperCase() + match.substr(1); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /prepositions.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'a' 3 | , 'abaft' 4 | , 'aboard' 5 | , 'about' 6 | , 'above' 7 | , 'absent' 8 | , 'across' 9 | , 'afore' 10 | , 'after' 11 | , 'against' 12 | , 'along' 13 | , 'alongside' 14 | , 'amid' 15 | , 'amidst' 16 | , 'among' 17 | , 'amongst' 18 | , 'an' 19 | , 'apropos' 20 | , 'apud' 21 | , 'around' 22 | , 'as' 23 | , 'aside' 24 | , 'astride' 25 | , 'at' 26 | , 'athwart' 27 | , 'atop' 28 | , 'barring' 29 | , 'before' 30 | , 'behind' 31 | , 'below' 32 | , 'beneath' 33 | , 'beside' 34 | , 'besides' 35 | , 'between' 36 | , 'beyond' 37 | , 'but' 38 | , 'by' 39 | , 'circa' 40 | , 'concerning' 41 | , 'despite' 42 | , 'down' 43 | , 'during' 44 | , 'except' 45 | , 'excluding' 46 | , 'failing' 47 | , 'following' 48 | , 'for' 49 | , 'forenenst' 50 | , 'from' 51 | , 'given' 52 | , 'in' 53 | , 'including' 54 | , 'inside' 55 | , 'into' 56 | , 'like' 57 | , 'mid' 58 | , 'midst' 59 | , 'minus' 60 | , 'modulo' 61 | , 'near' 62 | , 'next' 63 | , 'notwithstanding' 64 | , 'o\'' 65 | , 'of' 66 | , 'off' 67 | , 'on' 68 | , 'onto' 69 | , 'opposite' 70 | , 'out' 71 | , 'outside' 72 | , 'over' 73 | , 'pace' 74 | , 'past' 75 | , 'per' 76 | , 'plus' 77 | , 'pro' 78 | , 'qua' 79 | , 'regarding' 80 | , 'round' 81 | , 'sans' 82 | , 'save' 83 | , 'since' 84 | , 'than' 85 | , 'through' 86 | , 'throughout' 87 | , 'thru' 88 | , 'thruout' 89 | , 'till' 90 | , 'times' 91 | , 'to' 92 | , 'toward' 93 | , 'towards' 94 | , 'under' 95 | , 'underneath' 96 | , 'unlike' 97 | , 'until' 98 | , 'unto' 99 | , 'up' 100 | , 'upon' 101 | , 'versus' 102 | , 'via' 103 | , 'vice' 104 | , 'vis-à-vis' 105 | , 'with' 106 | , 'within' 107 | , 'without' 108 | , 'worth' 109 | ] 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # David Gouch's String#toTitleCase() for Node.js 2 | 3 | This is a fork of David Gouch's excellent [`String#toTitleCase()`](https://github.com/gouch/to-title-case) method inspired by John Gruber's [post on the topic](http://daringfireball.net/2008/08/title_case_update) 4 | 5 | I've simply taken it off the `String` prototype and exported it from a simple module. 6 | 7 | ```js 8 | var toTitleCase = require('titlecase') 9 | 10 | console.log(toTitleCase('foo bar baz')) 11 | ``` 12 | 13 | See [Gouch's page](http://individed.com/code/to-title-case/) with inline converter. Also see this excellent **[table of test case results](http://individed.com/code/to-title-case/tests.html)** for different converters. 14 | 15 | In addition, I've added a more comprehensive list of words to not capitalise that includes articles, prepositions and conjunctions (see source files for lists), I'm calling this "lax title case", use it like so: 16 | 17 | 18 | ```js 19 | var toLaxTitleCase = require('titlecase').toLaxTitleCase 20 | 21 | console.log(toLaxTitleCase('foo bar baz')) 22 | ``` 23 | 24 | **Using as an executable** 25 | 26 | Install with `npm install titlecase -g` and you'll get a `to-title-case` executable that you can run to titlecase strings: 27 | 28 | ``` 29 | $ to-title-case "what is this thing?" 30 | What Is This Thing? 31 | ``` 32 | 33 | *Original README:* 34 | 35 | # To Title Case for JavaScript 36 | 37 | Instructions: Include the to-title-case.js script and use the new .toTitleCase() method on the string you want converted. 38 | 39 | ## History 40 | ### 2.1 / 2013-11-03 41 | - Acknowledge characters outside of US-ASCII 42 | - Fix bug related to hyphenated small words 43 | - Replace baby's first testing script with the QUnit framework 44 | 45 | ### 2.0.1 / 2012-01-06 46 | 47 | - Fixed IE 7 breakage introduced in 2.0. Don't treat strings like character arrays. 48 | 49 | ### 2.0 / 2012-01-06 50 | 51 | - 15% less code and 35% easier to understand. 52 | - Small words list moved to variable for easy modification. 53 | - Test titles rewritten to focus on a single issue per title. 54 | - More braces to make style guides and JSLint happier. 55 | 56 | ## License 57 | 58 | Copyright © 2008–2013 David Gouch. Licensed under the MIT License. 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in 68 | all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | THE SOFTWARE. 77 | 78 | -------------------------------------------------------------------------------- /test/tests.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "input": "follow step-by-step instructions", 4 | "expect": "Follow Step-by-Step Instructions" 5 | }, 6 | { 7 | "input": "this sub-phrase is nice", 8 | "expect": "This Sub-Phrase Is Nice" 9 | }, 10 | { 11 | "input": "catchy title: a subtitle", 12 | "expect": "Catchy Title: A Subtitle" 13 | }, 14 | { 15 | "input": "catchy title: \"a quoted subtitle\"", 16 | "expect": "Catchy Title: \"A Quoted Subtitle\"" 17 | }, 18 | { 19 | "input": "catchy title: “‘a twice quoted subtitle’”", 20 | "expect": "Catchy Title: “‘A Twice Quoted Subtitle’”" 21 | }, 22 | { 23 | "input": "\"a title inside double quotes\"", 24 | "expect": "\"A Title Inside Double Quotes\"" 25 | }, 26 | { 27 | "input": "all words capitalized", 28 | "expect": "All Words Capitalized" 29 | }, 30 | { 31 | "input": "small words are for by and of lowercase", 32 | "expect": "Small Words Are for by and of Lowercase" 33 | }, 34 | { 35 | "input": "a small word starts", 36 | "expect": "A Small Word Starts" 37 | }, 38 | { 39 | "input": "a small word it ends on", 40 | "expect": "A Small Word It Ends On" 41 | }, 42 | { 43 | "input": "do questions work?", 44 | "expect": "Do Questions Work?" 45 | }, 46 | { 47 | "input": "multiple sentences. more than one.", 48 | "expect": "Multiple Sentences. More Than One." 49 | }, 50 | { 51 | "input": "Ends with small word of", 52 | "expect": "Ends With Small Word Of" 53 | }, 54 | { 55 | "input": "double quoted \"inner\" word", 56 | "expect": "Double Quoted \"Inner\" Word" 57 | }, 58 | { 59 | "input": "single quoted 'inner' word", 60 | "expect": "Single Quoted 'Inner' Word" 61 | }, 62 | { 63 | "input": "fancy double quoted “inner” word", 64 | "expect": "Fancy Double Quoted “Inner” Word" 65 | }, 66 | { 67 | "input": "fancy single quoted ‘inner’ word", 68 | "expect": "Fancy Single Quoted ‘Inner’ Word" 69 | }, 70 | { 71 | "input": "this vs. that", 72 | "expect": "This vs. That" 73 | }, 74 | { 75 | "input": "this vs that", 76 | "expect": "This vs That" 77 | }, 78 | { 79 | "input": "this v. that", 80 | "expect": "This v. That" 81 | }, 82 | { 83 | "input": "this v that", 84 | "expect": "This v That" 85 | }, 86 | { 87 | "input": "address email@example.com titles", 88 | "expect": "Address email@example.com Titles" 89 | }, 90 | { 91 | "input": "pass camelCase through", 92 | "expect": "Pass camelCase Through" 93 | }, 94 | { 95 | "input": "don't break", 96 | "expect": "Don't Break" 97 | }, 98 | { 99 | "input": "catchy title: substance subtitle", 100 | "expect": "Catchy Title: Substance Subtitle" 101 | }, 102 | { 103 | "input": "we keep NASA capitalized", 104 | "expect": "We Keep NASA Capitalized" 105 | }, 106 | { 107 | "input": "leave Q&A unscathed", 108 | "expect": "Leave Q&A Unscathed" 109 | }, 110 | { 111 | "input": "Scott Moritz and TheStreet.com’s million iPhone la-la land", 112 | "expect": "Scott Moritz and TheStreet.com’s Million iPhone La-La Land" 113 | }, 114 | { 115 | "input": "you have a http://example.com/foo/ title", 116 | "expect": "You Have a http://example.com/foo/ Title" 117 | }, 118 | { 119 | "input": "your hair[cut] looks (nice)", 120 | "expect": "Your Hair[cut] Looks (Nice)" 121 | }, 122 | { 123 | "input": "keep that colo(u)r", 124 | "expect": "Keep That Colo(u)r" 125 | }, 126 | { 127 | "input": "have you read “The Lottery”?", 128 | "expect": "Have You Read “The Lottery”?" 129 | }, 130 | { 131 | "input": "Read markdown_rules.txt to find out how _underscores around words_ will be interpreted", 132 | "expect": "Read markdown_rules.txt to Find Out How _Underscores Around Words_ Will Be Interpreted" 133 | }, 134 | { 135 | "input": "Read markdown_rules.txt to find out how *asterisks around words* will be interpreted", 136 | "expect": "Read markdown_rules.txt to Find Out How *Asterisks Around Words* Will Be Interpreted" 137 | }, 138 | { 139 | "input": "Notes and observations regarding Apple’s announcements from ‘The Beat Goes On’ special event", 140 | "expect": "Notes and Observations Regarding Apple’s Announcements From ‘The Beat Goes On’ Special Event" 141 | }, 142 | { 143 | "input": "Drink this piña colada while you listen to ænima", 144 | "expect": "Drink This Piña Colada While You Listen to Ænima" 145 | }, 146 | { 147 | "input": "capitalize hyphenated words on-demand", 148 | "expect": "Capitalize Hyphenated Words On-Demand" 149 | }, 150 | { 151 | "input": "take them on: special lower cases", 152 | "expect": "Take Them On: Special Lower Cases" 153 | } 154 | ] 155 | --------------------------------------------------------------------------------