1 /* 2 * A port of the Rails/Sequel inflections class 3 * http://sequel.rubyforge.org/rdoc/classes/Sequel/Inflections.html 4 */ 5 6 var array = require("./array").array, misc = require("./misc"); 7 8 var comb = exports; 9 10 var CAMELIZE_CONVERT_REGEXP = /_(.)/g; 11 var DASH = '-'; 12 var UNDERSCORE = '_'; 13 var UNDERSCORE_CONVERT_REGEXP1 = /([A-Z]+)([A-Z][a-z])/g; 14 var UNDERSCORE_CONVERT_REGEXP2 = /([a-z\d])([A-Z])/g; 15 var UNDERSCORE_CONVERT_REPLACE = '$1_$2'; 16 17 var PLURALS = [], SINGULARS = [], UNCOUNTABLES = []; 18 19 20 var _plural = function (rule, replacement) { 21 PLURALS.unshift([rule, replacement]) 22 }; 23 var _singular = function (rule, replacement) { 24 SINGULARS.unshift([rule, replacement]) 25 }; 26 27 /** 28 * Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used 29 # for strings, not regular expressions. You simply pass the irregular in singular and plural form. 30 # 31 # Examples: 32 # irregular 'octopus', 'octopi' 33 # irregular 'person', 'people' 34 * @param singular the singular version 35 * @param plural the plural version 36 */ 37 var _irregular = function (singular, plural) { 38 _plural(new RegExp("(" + singular.substr(0, 1) + ")" + singular.substr(1) + "$"), "$1" + plural.substr(1)); 39 _singular(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$"), "$1" + singular.substr(1)); 40 }; 41 var _uncountable = function (words) { 42 UNCOUNTABLES.push(misc.argsToArray(arguments)) 43 UNCOUNTABLES = array.flatten(UNCOUNTABLES); 44 }; 45 46 _plural(/$/, 's'); 47 _plural(/s$/i, 's'); 48 _plural(/(alias|(?:stat|octop|vir|b)us)$/i, '$1es'); 49 _plural(/(buffal|tomat)o$/i, '$1oes'); 50 _plural(/([ti])um$/i, '$1a'); 51 _plural(/sis$/i, 'ses'); 52 _plural(/(?:([^f])fe|([lr])f)$/i, '$1$2ves'); 53 _plural(/(hive)$/i, '$1s'); 54 _plural(/([^aeiouy]|qu)y$/i, '$1ies'); 55 _plural(/(x|ch|ss|sh)$/i, '$1es'); 56 _plural(/(matr|vert|ind)ix|ex$/i, '$1ices'); 57 _plural(/([m|l])ouse$/i, '$1ice'); 58 _plural(/^(ox)$/i, "$1en"); 59 60 _singular(/s$/i, ''); 61 _singular(/([ti])a$/i, '$1um'); 62 _singular(/(analy|ba|cri|diagno|parenthe|progno|synop|the)ses$/i, '$1sis'); 63 _singular(/([^f])ves$/i, '$1fe'); 64 _singular(/([h|t]ive)s$/i, '$1'); 65 _singular(/([lr])ves$/i, '$1f'); 66 _singular(/([^aeiouy]|qu)ies$/i, '$1y'); 67 _singular(/(m)ovies$/i, '$1ovie'); 68 _singular(/(x|ch|ss|sh)es$/i, '$1'); 69 _singular(/([m|l])ice$/i, '$1ouse'); 70 _singular(/buses$/i, 'bus'); 71 _singular(/oes$/i, 'o'); 72 _singular(/shoes$/i, 'shoe'); 73 _singular(/(alias|(?:stat|octop|vir|b)us)es$/i, '$1'); 74 _singular(/(vert|ind)ices$/i, '$1ex'); 75 _singular(/matrices$/i, 'matrix'); 76 77 _irregular('person', 'people'); 78 _irregular('man', 'men'); 79 _irregular('child', 'children'); 80 _irregular('sex', 'sexes'); 81 _irregular('move', 'moves'); 82 _irregular('quiz', 'quizzes'); 83 _irregular('testis', 'testes'); 84 85 _uncountable("equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "news"); 86 87 exports.singular = _singular; 88 exports.plural = _plural; 89 exports.uncountable = _uncountable; 90 91 /** 92 * Converts a string to camelcase 93 * 94 * @example 95 * comb.camelize('hello_world') => helloWorld 96 * comb.camelize('column_name') => columnName 97 * comb.camelize('columnName') => columnName 98 * comb.camelize(null) => null 99 * comb.camelize() => undefined 100 * 101 * @param {String} str the string to camelize 102 * @memberOf comb 103 * @returns {String} the camelized version of the string 104 */ 105 comb.camelize = function (str) { 106 var ret = str; 107 if (!misc.isUndefinedOrNull(str)) { 108 ret = str.replace(CAMELIZE_CONVERT_REGEXP, function (a, b) { 109 return b.toUpperCase(); 110 }); 111 } 112 return ret; 113 }; 114 115 /** 116 * The reverse of camelize. Makes an underscored form from the expression in the string. 117 * 118 * @example 119 * comb.underscore('helloWorld') => hello_world 120 * comb.underscore('column_name') => column_name 121 * comb.underscore('columnName') => column_name 122 * comb.underscore(null) => null 123 * comb.underscore() => undefined 124 * @param {String} str The string to underscore 125 * @memberOf comb 126 * @returns {String} the underscored version of the string 127 * */ 128 comb.underscore = function (str) { 129 var ret = str; 130 if (!misc.isUndefinedOrNull(str)) { 131 ret = str.replace(UNDERSCORE_CONVERT_REGEXP1, UNDERSCORE_CONVERT_REPLACE) 132 .replace(UNDERSCORE_CONVERT_REGEXP2, UNDERSCORE_CONVERT_REPLACE) 133 .replace(DASH, UNDERSCORE).toLowerCase(); 134 } 135 return ret; 136 }; 137 138 /** 139 * Singularizes and camelizes the string. Also strips out all characters preceding 140 * and including a period ("."). 141 * 142 * @example 143 * comb.classify('egg_and_hams') => "eggAndHam" 144 * comb.classify('post') => "post" 145 * comb.classify('schema.post') => "post" 146 * 147 * @param {String} str the string to classify 148 * @memberOf comb 149 * @returns {String} the classified version of the string 150 **/ 151 comb.classify = function (str) { 152 var ret = str; 153 if (!misc.isUndefinedOrNull(str)) { 154 ret = comb.camelize(comb.singularize(str.replace(/.*\./g, ''))); 155 } 156 return ret; 157 }; 158 /** 159 * Returns the plural form of the word in the string. 160 * 161 * @example 162 * comb.pluralize("post") => "posts" 163 * comb.pluralize("octopus") => "octopi" 164 * comb.pluralize("sheep") => "sheep" 165 * comb.pluralize("words") => "words" 166 * comb.pluralize("the blue mailman") => "the blue mailmen" 167 * comb.pluralize("CamelOctopus") => "CamelOctopi" 168 * 169 * @param {String} str the string to pluralize 170 * @memberOf comb 171 * @returns {String} the pluralized version of the string 172 **/ 173 comb.pluralize = function (str) { 174 var ret = str; 175 if (!misc.isUndefinedOrNull(str)) { 176 if (UNCOUNTABLES.indexOf(str) == -1) { 177 for (var i in PLURALS) { 178 var s = PLURALS[i], rule = s[0], replacement = s[1]; 179 if ((ret = ret.replace(rule, replacement)) != str) { 180 break; 181 } 182 } 183 } 184 } 185 return ret; 186 }; 187 /** 188 * The reverse of pluralize, returns the singular form of a word in a string. 189 * 190 * @example 191 * comb.singularize("posts") => "post" 192 * comb.singularize("octopi")=> "octopus" 193 * comb.singularize("sheep") => "sheep" 194 * comb.singularize("word") => "word" 195 * comb.singularize("the blue mailmen") => "the blue mailman" 196 * comb.singularize("CamelOctopi") => "CamelOctopus" 197 * 198 * @param {String} str the string to singularize 199 * @memberOf comb 200 * @returns {String} the singularized version of the string 201 * */ 202 comb.singularize = function (str) { 203 var ret = str; 204 if (!misc.isUndefinedOrNull(str)) { 205 if (UNCOUNTABLES.indexOf(str) == -1) { 206 for (var i in SINGULARS) { 207 var s = SINGULARS[i], rule = s[0], replacement = s[1]; 208 if ((ret = ret.replace(rule, replacement)) != str) { 209 break; 210 } 211 } 212 } 213 } 214 return ret; 215 }; 216