UNPKG

21.3 kBJavaScriptView Raw
1var Inflector;
2
3module.exports = Inflector = (function() {
4 var inflection;
5 var __slice = Array.prototype.slice;
6 /*
7 Copyright (c) 2010 Ryan Schuft (ryan.schuft@gmail.com)
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 */
27
28 /*
29 This code is based in part on the work done in Ruby to support
30 infection as part of Ruby on Rails in the ActiveSupport's Inflector
31 and Inflections classes. It was initally ported to Javascript by
32 Ryan Schuft (ryan.schuft@gmail.com) in 2007.
33
34 The code is available at http://code.google.com/p/inflection-js/
35
36 The basic usage is:
37 1. Include this script on your web page.
38 2. Call functions on any String object in Javascript
39
40 Currently implemented functions:
41
42 String.pluralize(plural) == String
43 renders a singular English language noun into its plural form
44 normal results can be overridden by passing in an alternative
45
46 String.singularize(singular) == String
47 renders a plural English language noun into its singular form
48 normal results can be overridden by passing in an alterative
49
50 String.camelize(lowFirstLetter) == String
51 renders a lower case underscored word into camel case
52 the first letter of the result will be upper case unless you pass true
53 also translates "/" into "::" (underscore does the opposite)
54
55 String.underscore() == String
56 renders a camel cased word into words seperated by underscores
57 also translates "::" back into "/" (camelize does the opposite)
58
59 String.humanize(lowFirstLetter) == String
60 renders a lower case and underscored word into human readable form
61 defaults to making the first letter capitalized unless you pass true
62
63 String.capitalize() == String
64 renders all characters to lower case and then makes the first upper
65
66 String.dasherize() == String
67 renders all underbars and spaces as dashes
68
69 String.titleize() == String
70 renders words into title casing (as for book titles)
71
72 String.demodulize() == String
73 renders class names that are prepended by modules into just the class
74
75 String.tableize() == String
76 renders camel cased singular words into their underscored plural form
77
78 String.classify() == String
79 renders an underscored plural word into its camel cased singular form
80
81 String.foreign_key(dropIdUbar) == String
82 renders a class name (camel cased singular noun) into a foreign key
83 defaults to seperating the class from the id with an underbar unless
84 you pass true
85
86 String.ordinalize() == String
87 renders all numbers found in the string into their sequence like "22nd"
88 */
89
90 var InflectionJS;
91
92 /*
93 This sets up some constants for later use
94 This should use the window namespace variable if available
95 */
96 InflectionJS = {
97 /*
98 This is a list of nouns that use the same form for both singular and plural.
99 This list should remain entirely in lower case to correctly match Strings.
100 */
101 uncountable_words: [
102 'equipment', 'information', 'rice', 'money', 'species', 'series',
103 'fish', 'sheep', 'moose', 'deer', 'news'
104 ],
105
106 /*
107 These rules translate from the singular form of a noun to its plural form.
108 */
109 plural_rules: [
110 [new RegExp('(m)an$', 'gi'), '$1en'],
111 [new RegExp('(pe)rson$', 'gi'), '$1ople'],
112 [new RegExp('(child)$', 'gi'), '$1ren'],
113 [new RegExp('^(ox)$', 'gi'), '$1en'],
114 [new RegExp('(ax|test)is$', 'gi'), '$1es'],
115 [new RegExp('(octop|vir)us$', 'gi'), '$1i'],
116 [new RegExp('(alias|status|by)$', 'gi'), '$1es'],
117 [new RegExp('(bu)s$', 'gi'), '$1ses'],
118 [new RegExp('(buffal|tomat|potat)o$', 'gi'), '$1oes'],
119 [new RegExp('([ti])um$', 'gi'), '$1a'],
120 [new RegExp('sis$', 'gi'), 'ses'],
121 [new RegExp('(?:([^f])fe|([lr])f)$', 'gi'), '$1$2ves'],
122 [new RegExp('(hive)$', 'gi'), '$1s'],
123 [new RegExp('([^aeiouy]|qu)y$', 'gi'), '$1ies'],
124 [new RegExp('(x|ch|ss|sh)$', 'gi'), '$1es'],
125 [new RegExp('(matr|vert|ind)ix|ex$', 'gi'), '$1ices'],
126 [new RegExp('([m|l])ouse$', 'gi'), '$1ice'],
127 [new RegExp('(quiz)$', 'gi'), '$1zes'],
128 [new RegExp('s$', 'gi'), 's'],
129 [new RegExp('$', 'gi'), 's']
130 ],
131
132 /*
133 These rules translate from the plural form of a noun to its singular form.
134 */
135 singular_rules: [
136 [new RegExp('(m)en$', 'gi'), '$1an'],
137 [new RegExp('(pe)ople$', 'gi'), '$1rson'],
138 [new RegExp('(child)ren$', 'gi'), '$1'],
139 [new RegExp('([ti])a$', 'gi'), '$1um'],
140 [new RegExp('((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$','gi'), '$1$2sis'],
141 [new RegExp('(hive)s$', 'gi'), '$1'],
142 [new RegExp('(tive)s$', 'gi'), '$1'],
143 [new RegExp('(curve)s$', 'gi'), '$1'],
144 [new RegExp('([lr])ves$', 'gi'), '$1f'],
145 [new RegExp('([^fo])ves$', 'gi'), '$1fe'],
146 [new RegExp('([^aeiouy]|qu)ies$', 'gi'), '$1y'],
147 [new RegExp('(s)eries$', 'gi'), '$1eries'],
148 [new RegExp('(m)ovies$', 'gi'), '$1ovie'],
149 [new RegExp('(x|ch|ss|sh)es$', 'gi'), '$1'],
150 [new RegExp('([m|l])ice$', 'gi'), '$1ouse'],
151 [new RegExp('(bus)es$', 'gi'), '$1'],
152 [new RegExp('(o)es$', 'gi'), '$1'],
153 [new RegExp('(shoe)s$', 'gi'), '$1'],
154 [new RegExp('(cris|ax|test)es$', 'gi'), '$1is'],
155 [new RegExp('(octop|vir)i$', 'gi'), '$1us'],
156 [new RegExp('(alias|status)es$', 'gi'), '$1'],
157 [new RegExp('^(ox)en', 'gi'), '$1'],
158 [new RegExp('(vert|ind)ices$', 'gi'), '$1ex'],
159 [new RegExp('(matr)ices$', 'gi'), '$1ix'],
160 [new RegExp('(quiz)zes$', 'gi'), '$1'],
161 [new RegExp('s$', 'gi'), '']
162 ],
163
164 /*
165 This is a list of words that should not be capitalized for title case
166 */
167 non_titlecased_words: [
168 'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
169 'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
170 'with', 'for'
171 ],
172
173 /*
174 These are regular expressions used for converting between String formats
175 */
176 id_suffix: new RegExp('(_ids|_id)$', 'g'),
177 underbar: new RegExp('_', 'g'),
178 space_or_underbar: new RegExp('[\ _]', 'g'),
179 uppercase: new RegExp('([A-Z])', 'g'),
180 underbar_prefix: new RegExp('^_'),
181
182 /*
183 This is a helper method that applies rules based replacement to a String
184 Signature:
185 InflectionJS.apply_rules(str, rules, skip, override) == String
186 Arguments:
187 str - String - String to modify and return based on the passed rules
188 rules - Array: [RegExp, String] - Regexp to match paired with String to use for replacement
189 skip - Array: [String] - Strings to skip if they match
190 override - String (optional) - String to return as though this method succeeded (used to conform to APIs)
191 Returns:
192 String - passed String modified by passed rules
193 Examples:
194 InflectionJS.apply_rules("cows", InflectionJs.singular_rules) === 'cow'
195 */
196 apply_rules: function(str, rules, skip, override) {
197 if (override) {
198 str = override;
199 }
200 else {
201 var ignore = (skip.indexOf(str.toLowerCase()) > -1);
202 if (!ignore) {
203 for (var x = 0; x < rules.length; x++) {
204 if (str.match(rules[x][0])) {
205 str = str.replace(rules[x][0], rules[x][1]);
206 break;
207 }
208 }
209 }
210 }
211 return str;
212 }
213 };
214
215 /*
216 This function adds plurilization support to every String object
217 Signature:
218 String.pluralize(plural) == String
219 Arguments:
220 plural - String (optional) - overrides normal output with said String
221 Returns:
222 String - singular English language nouns are returned in plural form
223 Examples:
224 "person".pluralize() == "people"
225 "octopus".pluralize() == "octopi"
226 "Hat".pluralize() == "Hats"
227 "person".pluralize("guys") == "guys"
228 */
229 InflectionJS.pluralize = function(string, plural) {
230 return this.apply_rules(
231 string,
232 this.plural_rules,
233 this.uncountable_words,
234 plural
235 );
236 };
237
238 /*
239 This function adds singularization support to every String object
240 Signature:
241 String.singularize(singular) == String
242 Arguments:
243 singular - String (optional) - overrides normal output with said String
244 Returns:
245 String - plural English language nouns are returned in singular form
246 Examples:
247 "people".singularize() == "person"
248 "octopi".singularize() == "octopus"
249 "Hats".singularize() == "Hat"
250 "guys".singularize("person") == "person"
251 */
252 InflectionJS.singularize = function(string, singular) {
253 return this.apply_rules(
254 string,
255 this.singular_rules,
256 this.uncountable_words,
257 singular
258 );
259 };
260
261 /*
262 This function adds camelization support to every String object
263 Signature:
264 String.camelize(lowFirstLetter) == String
265 Arguments:
266 lowFirstLetter - boolean (optional) - default is to capitalize the first
267 letter of the results... passing true will lowercase it
268 Returns:
269 String - lower case underscored words will be returned in camel case
270 additionally '/' is translated to '::'
271 Examples:
272 "message_properties".camelize() == "MessageProperties"
273 "message_properties".camelize(true) == "messageProperties"
274 */
275 InflectionJS.camelize = function(string, lowFirstLetter, dontLowercaseBefore) {
276 if (!dontLowercaseBefore) {
277 string = string.toLowerCase();
278 }
279 var str_path = string.split('/');
280 for (var i = 0; i < str_path.length; i++) {
281 var str_arr = str_path[i].split('_');
282 var initX = ((lowFirstLetter && i + 1 === str_path.length) ? (1) : (0));
283 for (var x = initX; x < str_arr.length; x++) {
284 str_arr[x] = str_arr[x].charAt(0).toUpperCase() + str_arr[x].substring(1);
285 }
286 str_path[i] = str_arr.join('');
287 }
288 string = str_path.join('::');
289 return string;
290 };
291
292 /*
293 This function adds underscore support to every String object
294 Signature:
295 String.underscore() == String
296 Arguments:
297 N/A
298 Returns:
299 String - camel cased words are returned as lower cased and underscored
300 additionally '::' is translated to '/'
301 Examples:
302 "MessageProperties".camelize() == "message_properties"
303 "messageProperties".underscore() == "message_properties"
304 */
305 InflectionJS.underscore = function(string) {
306 var str_path = string.split('::');
307 for (var i = 0; i < str_path.length; i++) {
308 str_path[i] = str_path[i].replace(this.uppercase, '_$1');
309 str_path[i] = str_path[i].replace(this.underbar_prefix, '');
310 }
311 string = str_path.join('/').toLowerCase();
312 return string;
313 };
314
315 /*
316 This function adds humanize support to every String object
317 Signature:
318 String.humanize(lowFirstLetter) == String
319 Arguments:
320 lowFirstLetter - boolean (optional) - default is to capitalize the first
321 letter of the results... passing true will lowercase it
322 Returns:
323 String - lower case underscored words will be returned in humanized form
324 Examples:
325 "message_properties".humanize() == "Message properties"
326 "message_properties".humanize(true) == "message properties"
327 */
328 InflectionJS.humanize = function(string, lowFirstLetter) {
329 string = string.toLowerCase();
330 string = string.replace(this.id_suffix, '');
331 string = string.replace(this.underbar, ' ');
332 if (!lowFirstLetter) {
333 string = this.capitalize(string);
334 }
335 return string;
336 };
337
338 /*
339 This function adds capitalization support to every String object
340 Signature:
341 String.capitalize() == String
342 Arguments:
343 N/A
344 Returns:
345 String - all characters will be lower case and the first will be upper
346 Examples:
347 "message_properties".capitalize() == "Message_properties"
348 "message properties".capitalize() == "Message properties"
349 */
350 InflectionJS.capitalize = function(string, dontLowercaseFirst) {
351 if (!dontLowercaseFirst) {
352 string = string.toLowerCase();
353 }
354 string = string.substring(0, 1).toUpperCase() + string.substring(1);
355 return string;
356 };
357
358 /*
359 This function adds decapitalization support to every String object
360 Signature:
361 String.decapitalize() == String
362 Arguments:
363 N/A
364 Returns:
365 String - all characters will be lower case and the first will be upper
366 Examples:
367 "Message_properties".capitalize() == "message_properties"
368 "Message properties".capitalize() == "message properties"
369 */
370 InflectionJS.decapitalize = function(string) {
371 string = string.substring(0, 1).toLowerCase() + string.substring(1);
372 return string;
373 };
374
375 /*
376 This function adds dasherization support to every String object
377 Signature:
378 String.dasherize() == String
379 Arguments:
380 N/A
381 Returns:
382 String - replaces all spaces or underbars with dashes
383 Examples:
384 "message_properties".capitalize() == "message-properties"
385 "Message Properties".capitalize() == "Message-Properties"
386 */
387 InflectionJS.dasherize = function(string) {
388 string = string.replace(this.space_or_underbar, '-');
389 return string;
390 };
391
392 /*
393 This function adds titleize support to every String object
394 Signature:
395 String.titleize() == String
396 Arguments:
397 N/A
398 Returns:
399 String - capitalizes words as you would for a book title
400 Examples:
401 "message_properties".titleize() == "Message Properties"
402 "message properties to keep".titleize() == "Message Properties to Keep"
403 */
404 InflectionJS.titleize = function(string) {
405 string = string.toLowerCase();
406 string = string.replace(this.underbar, ' ');
407 var str_arr = string.split(' ');
408 for (var x = 0; x < str_arr.length; x++) {
409 var d = str_arr[x].split('-');
410 for (var i = 0; i < d.length; i++) {
411 if (this.non_titlecased_words.indexOf(d[i].toLowerCase()) < 0) {
412 d[i] = this.capitalize(d[i]);
413 }
414 }
415 str_arr[x] = d.join('-');
416 }
417 string = str_arr.join(' ');
418 string = string.substring(0, 1).toUpperCase() + string.substring(1);
419 return string;
420 };
421
422 /*
423 This function adds demodulize support to every String object
424 Signature:
425 String.demodulize() == String
426 Arguments:
427 N/A
428 Returns:
429 String - removes module names leaving only class names (Ruby style)
430 Examples:
431 "Message::Bus::Properties".demodulize() == "Properties"
432 */
433 InflectionJS.demodulize = function(string) {
434 var str_arr = string.split('::');
435 string = str_arr[str_arr.length - 1];
436 return string;
437 };
438
439 /*
440 This function adds tableize support to every String object
441 Signature:
442 String.tableize() == String
443 Arguments:
444 N/A
445 Returns:
446 String - renders camel cased words into their underscored plural form
447 Examples:
448 "MessageBusProperty".tableize() == "message_bus_properties"
449 */
450 InflectionJS.tableize = function(string) {
451 string = this.pluralize(this.underscore(string));
452 return string;
453 };
454
455 /*
456 This function adds classification support to every String object
457 Signature:
458 String.classify() == String
459 Arguments:
460 N/A
461 Returns:
462 String - underscored plural nouns become the camel cased singular form
463 Examples:
464 "message_bus_properties".classify() == "MessageBusProperty"
465 */
466 InflectionJS.classify = function(string) {
467 string = this.singularize(this.camelize(string));
468 return string;
469 };
470
471 /*
472 This function adds foreign key support to every String object
473 Signature:
474 String.foreign_key(dropIdUbar) == String
475 Arguments:
476 dropIdUbar - boolean (optional) - default is to seperate id with an
477 underbar at the end of the class name, you can pass true to skip it
478 Returns:
479 String - camel cased singular class names become underscored with id
480 Examples:
481 "MessageBusProperty".foreign_key() == "message_bus_property_id"
482 "MessageBusProperty".foreign_key(true) == "message_bus_propertyid"
483 */
484 InflectionJS.foreign_key = function(string, dropIdUbar) {
485 string = this.underscore(this.demodulize(string)) + ((dropIdUbar) ? ('') : ('_')) + 'id';
486 return string;
487 };
488
489 /*
490 This function adds ordinalize support to every String object
491 Signature:
492 String.ordinalize() == String
493 Arguments:
494 N/A
495 Returns:
496 String - renders all found numbers their sequence like "22nd"
497 Examples:
498 "the 1 pitch".ordinalize() == "the 1st pitch"
499 */
500 InflectionJS.ordinalize = function(string) {
501 var str_arr = string.split(' ');
502 for (var x = 0; x < str_arr.length; x++) {
503 var i = parseInt(str_arr[x]);
504 if (i === NaN) {
505 var ltd = str_arr[x].substring(str_arr[x].length - 2);
506 var ld = str_arr[x].substring(str_arr[x].length - 1);
507 var suf = "th";
508 if (ltd != "11" && ltd != "12" && ltd != "13") {
509 if (ld === "1") {
510 suf = "st";
511 }
512 else if (ld === "2") {
513 suf = "nd";
514 }
515 else if (ld === "3") {
516 suf = "rd";
517 }
518 }
519 str_arr[x] += suf;
520 }
521 }
522 string = str_arr.join(' ');
523 return string;
524 };
525
526 inflection = InflectionJS;
527
528 Object.keys(inflection).forEach(function(key) {
529 Inflector[key] = inflection[key];
530 return Inflector.prototype[key] = function() {
531 this.value = Inflector[key].apply(Inflector, [this.value].concat(__slice.call(arguments)));
532 return this;
533 };
534 });
535
536 function Inflector(value) {
537 if (!(this instanceof Inflector)) {
538 return new Inflector(value);
539 } else {
540 this.value = value;
541 }
542 }
543
544 Inflector.prototype.tap = function(callback) {
545 callback(this.value);
546 return this;
547 };
548
549 Inflector.prototype.inspect = function() {
550 return this + '';
551 };
552
553 Inflector.prototype.toString = function() {
554 return this.value;
555 };
556
557 Inflector.prototype.valueOf = function() {
558 return this.value;
559 };
560
561 return Inflector;
562
563})();