1 | var Inflector;
|
2 |
|
3 | module.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 | })();
|