/ Published in: JavaScript
URL: http://it.toolbox.com/blogs/macsploitation/titlecase-in-javascript-24824
Transforms a string to CamelCase. Slightly modified from source's version to accept all-uppercase strings. Update: added matches for some portuguese small words.
Expand |
Embed | Plain Text
/* String.prototype.titleCase 1.0 An implementation of John Gruber's TitleCase.pl in JavaScript. (http://daringfireball.net/projects/titlecase/TitleCase.pl) TitleCase.pl Documentation: # This filter changes all words to Title Caps, and attempts to be clever # about *un*capitalizing small words like a/an/the in the input. # # The list of "small words" which are not capped comes from # the New York Times Manual of Style, plus 'vs' and 'v'. # # John Gruber # http://daringfireball.net/ # 10 May 2008 # # License: http://www.opensource.org/licenses/mit-license.php # Usage: js> "Sub-Phrase With a Small Word in Quotes: 'a Trick, Perhaps?'".toTitleCase() "Sub-Phrase With a Small Word in Quotes: 'A Trick, Perhaps?'" Uses Steven Levithan's Cross-Browser Split (note: this replaces your hosts's native split method). By Jon Hohle <http://hohle.net> 21.May.2008 MIT Licence <http://www.opensource.org/licenses/mit-license.php> */ /* Cross-Browser Split 0.2.1 By Steven Levithan <http://stevenlevithan.com> MIT license */ var nativeSplit = nativeSplit || String.prototype.split; String.prototype.split = function (s /* separator */, limit) { // If separator is not a regex, use the native split method if (!(s instanceof RegExp)) return nativeSplit.apply(this, arguments); /* Behavior for limit: If it's... - Undefined: No limit - NaN or zero: Return an empty array - A positive number: Use limit after dropping any decimal - A negative number: No limit - Other: Type-convert, then use the above rules */ if (limit === undefined || +limit < 0) { limit = false; } else { limit = Math.floor(+limit); if (!limit) return []; } var flags = (s.global ? "g" : "") + (s.ignoreCase ? "i" : "") + (s.multiline ? "m" : ""), s2 = new RegExp("^" + s.source + "$", flags), output = [], lastLastIndex = 0, i = 0, match; if (!s.global) s = new RegExp(s.source, "g" + flags); while ((!limit || i++ <= limit) && (match = s.exec(this))) { var zeroLengthMatch = !match[0].length; // Fix IE's infinite-loop-resistant but incorrect lastIndex if (zeroLengthMatch && s.lastIndex > match.index) s.lastIndex = match.index; // The same as s.lastIndex-- if (s.lastIndex > lastLastIndex) { // Fix browsers whose exec methods don't consistently // return undefined for non-participating capturing groups if (match.length > 1) { match[0].replace(s2, function () { for (var j = 1; j < arguments.length - 2; j++) { if (arguments[j] === undefined) match[j] = undefined; } }); } output = output.concat( this.slice(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1))); lastLastIndex = s.lastIndex; } if (zeroLengthMatch) s.lastIndex++; } return (lastLastIndex === this.length) ? (s.test("") ? output : output.concat("")) : (limit ? output : output.concat(this.slice(lastLastIndex))); }; // give a hoot, don't pollute var __TitleCase = { __smallWords: ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'en', 'for', 'if', 'in', 'of', 'on', 'or', 'the', 'to', 'v[.]?', 'via', 'vs[.]?', 'e', 'o[s]?', 'na[s]?', 'no[s]?', 'em', 'de', 'do[s]?', 'da[s]?' ], init: function() { this.__smallRE = this.__smallWords.join('|'); this.__lowerCaseWordsRE = new RegExp( '\\b(' + this.__smallRE + ')\\b', 'gi'); this.__firstWordRE = new RegExp( '^([^a-zA-Z0-9 \\r\\n\\t]*)(' + this.__smallRE + ')\\b', 'gi'); this.__lastWordRE = new RegExp( '\\b(' + this.__smallRE + ')([^a-zA-Z0-9 \\r\\n\\t]*)$', 'gi'); }, toTitleCase: function(string) { var line = ''; var split = string.split(/([:.;?!][ ]|(?:[ ]|^)["“])/); for (var i = 0; i < split.length; ++i) { var s = split[i]; s = s.replace( /\b([a-zA-Z][a-z.'’]*)\b/g, this.__titleCaseDottedWordReplacer); // lowercase the list of small words s = s.replace(this.__lowerCaseWordsRE, this.__lowerReplacer); // if the first word in the title is a small word then capitalize it s = s.replace(this.__firstWordRE, this.__firstToUpperCase); // if the last word in the title is a small word, then capitalize it s = s.replace(this.__lastWordRE, this.__firstToUpperCase); line += s; } // special cases line = line.replace(/ V(s?)\. /g, ' v$1. '); line = line.replace(/(['’])S\b/g, '$1s'); line = line.replace(/\b(AT&T|Q&A)\b/ig, this.__upperReplacer); return line; }, __titleCaseDottedWordReplacer: function (w) { return (w.match(/[a-zA-Z][.][a-zA-Z]/)) ? w : __TitleCase.__firstToUpperCase(w); }, __lowerReplacer: function (w) { return w.toLowerCase() }, __upperReplacer: function (w) { return w.toUpperCase() }, __firstToUpperCase: function (w) { var split = w.split(/(^[^a-zA-Z0-9]*[a-zA-Z0-9])(.*)$/); split[1] = split[1].toUpperCase(); return split.join(''); }, test: function() { var testStrings = [ "Q&A With Steve Jobs: 'That's What Happens In Technology'", "What Is AT&T's Problem?", "Apple Deal With AT&T Falls Through", "this v that", "this vs that", "this v. that", "this vs. that", "The SEC's Apple Probe: What You Need to Know", "'by the Way, small word at the start but within quotes.'", "Small word at end is nothing to be afraid of", "Starting Sub-Phrase With a Small Word: a Trick, Perhaps?", "Sub-Phrase With a Small Word in Quotes: 'a Trick, Perhaps?'", 'Sub-Phrase With a Small Word in Quotes: "a Trick, Perhaps?"', '"Nothing to Be Afraid of?"', '"Nothing to Be Afraid Of?"', 'a thing']; var validStrings = [ "Q&A With Steve Jobs: 'That's What Happens in Technology'", "What Is AT&T's Problem?", "Apple Deal With AT&T Falls Through", "This v That", "This vs That", "This v. That", "This vs. That", "The SEC's Apple Probe: What You Need to Know", "'By the Way, Small Word at the Start but Within Quotes.'", "Small Word at End Is Nothing to Be Afraid Of", "Starting Sub-Phrase With a Small Word: A Trick, Perhaps?", "Sub-Phrase With a Small Word in Quotes: 'A Trick, Perhaps?'", 'Sub-Phrase With a Small Word in Quotes: "A Trick, Perhaps?"', '"Nothing to Be Afraid Of?"', '"Nothing to Be Afraid Of?"', 'A Thing']; for (var i = 0; i < testStrings.length ; ++i) { var s = testStrings[i].toTitleCase(); if (s != validStrings[i]) { alert(s + '\ndoes not match\n' + validStrings[i]); return false; break; } } return true; } }; __TitleCase.init(); function toTitleCase(string) { return __TitleCase.toTitleCase(string); } String.prototype.toTitleCase = function() { return toTitleCase(this.toLowerCase()); }
You need to login to post a comment.
