1 /** 2 * @fileOverview This file contains new methods for the String class 3 */ 4 /** 5 * @name String 6 * @class A built-in class for sequences of UTF-16 characters forming a "string" of textual data. 7 */ 8 9 /** 10 * Repeat a string a number of times. 11 * 12 * @param {Number} num The number of times to repeat 13 * @param {String} separator An optional separator to insert in between the strings 14 * @return {String} the repeated string 15 */ 16 String.prototype.repeat = function repeat(num, separator) { 17 return Array.fill(num||1, this).join(separator||''); 18 }; 19 20 /** 21 * Expand a string repeating it up to a certain length. 22 * 23 * @param {Number} len The length of the string to expand to 24 * @return {String} the expanded string 25 */ 26 String.prototype.expand = function expand(len) { 27 return this.repeat(Math.ceil(len / this.length)).substr(0, len); 28 }; 29 30 /** 31 * Return a version of this string with the first character in upper case 32 * 33 * @return {String} The string with the modified case 34 */ 35 String.prototype.toFirstUpperCase = function toFirstUpperCase() { 36 return this.charAt(0).toUpperCase() + this.substr(1); 37 }; 38 39 /** 40 * Return a version of this string with the first character in lower case 41 * 42 * @return {String} The string with the modified case 43 */ 44 String.prototype.toFirstLowerCase = function toFirstLowerCase() { 45 return this.charAt(0).toLowerCase() + this.substr(1); 46 }; 47 48 /** 49 * Return a version fo this string with all words matched by \w given an upper 50 * case first character. 51 * 52 * @return {String} The string with the modified case 53 */ 54 String.prototype.toTitleCase = function toTitleCase() { 55 return this.replace(/\w+/g, function(m) { return m.toFirstUpperCase(); }); 56 }; 57 58 /** 59 * Check and see if this string starts with another 60 * 61 * @return {Boolean} A boolean indicating if this string starts with another 62 */ 63 String.prototype.startsWith = function startsWith(other) { 64 return this.substr(0, other.length) === other; 65 }; 66 67 /** 68 * Check and see if this string ends with another 69 * 70 * @return {Boolean} A boolean indicating if this string ends with another 71 */ 72 String.prototype.endsWith = function endsWith(other) { 73 return this.substr(-other.length) === other; 74 }; 75 76 /** 77 * Check and see if this string contains another 78 * 79 * @return {Boolean} A boolean indicating if this string contains another 80 */ 81 String.prototype.contains = function contains(other) { 82 return this.indexOf(other) > -1; 83 }; 84 85 /** 86 * Count the number of times a substring is found within this string 87 * 88 * @param {String} other The substring to search for 89 * @param {Number} [offset=0] The offset from the start of the string for the search 90 * @return {Number} An integer indicating how many times the substring is found 91 */ 92 String.prototype.numberOf = function numberOf(other, offset) { 93 offset = offset || 0; 94 var i, c = 0; 95 while( (i = this.indexOf(other, offset)) && i >= 0 ) { 96 c++; 97 offset = i + other.length; 98 } 99 return c; 100 }; 101 102 /** 103 * Reverse the order of characters in this string 104 * 105 * @return {String} A new string with characters in the reverse order 106 */ 107 String.prototype.reverse = function reverse() { 108 return this.split('').reverse().join(''); 109 }; 110 /** 111 * Trim all whitespace from the left side of the string 112 * 113 * @return {String} The new trimmed string 114 */ 115 116 /** 117 * Trim all whitespace from the left side of the string 118 * 119 * @return {String} The new trimmed string 120 */ 121 if ( !String.prototype.trimLeft ) 122 String.prototype.trimLeft = function trimLeft() { 123 return this.replace(/^\s\s*/, ''); 124 }; 125 126 /** 127 * Trim all whitespace from the right side of the string 128 * 129 * @return {String} The new trimmed string 130 */ 131 if ( !String.prototype.trimRight ) 132 String.prototype.trimRight = function trimRight() { 133 return this.replace(/\s*\s*$/, ''); 134 }; 135 136 /** 137 * Trim all whitespace from both sides of the string 138 * 139 * @return {String} The new trimmed string 140 */ 141 if ( !String.prototype.trim ) 142 String.prototype.trim = function trim() { 143 return this.trimLeft().trimRight(); 144 }; 145 146 147 /** 148 * Strip characters from both sides of the string 149 * 150 * @param {String} chars The characters to remove 151 * @return {String} The new stripped string 152 */ 153 String.prototype.strip = function strip(chars, internal) { 154 if(!chars) throw new TypeError("Stripping requires a list of characters to strip"); 155 internal = internal || 3; 156 // This creates a table where chars[char] will be truthy/falsey for inclusion 157 var chars = Object.invert(typeof chars === 'string' ? chars.split('') : chars); 158 159 var start = 0, end = this.length; 160 161 if ( internal & 1 ) { // Left 162 while( this.charAt(start) in chars ) 163 start++; 164 } 165 if ( internal & 2 ) { // Right 166 while( this.charAt(end-1) in chars && end > start ) 167 end--; 168 } 169 170 return this.substring(start, end); 171 }; 172 173 /** 174 * Strip characters from the left side of the string 175 * 176 * @param {String} chars The characters to remove 177 * @return {String} The new stripped string 178 */ 179 String.prototype.stripLeft = function stripLeft(chars) { 180 return this.strip(chars, 1); 181 }; 182 183 /** 184 * Strip characters from the right side of the string 185 * 186 * @param {String} chars The characters to remove 187 * @return {String} The new stripped string 188 */ 189 String.prototype.stripRight = function stripRight(chars) { 190 return this.strip(chars, 2); 191 }; 192 193 /** 194 * Pad a string on both sides to a certain length 195 * If the string is equal to or larger than that length then it's size will be left alone 196 * 197 * @param {Number} len The length to pad the string to 198 * @param {String} [chars=" "] The characters to pad the string with 199 */ 200 String.prototype.pad = function pad(len, chars) { 201 return this.padLeft(Math.floor(len/2), chars).padRight(Math.ceil(len/2), chars); 202 }; 203 204 /** 205 * Pad a string on the left side to a certain length 206 * If the string is equal to or larger than that length then it's size will be left alone 207 * 208 * @param {Number} len The length to pad the string to 209 * @param {String} [chars=" "] The characters to pad the string with 210 */ 211 String.prototype.padLeft = function padLeft(len, chars) { 212 chars = chars || ' '; 213 return chars.expand(Math.max(0, len - this.length)) + this; 214 }; 215 216 /** 217 * Pad a string on the right side to a certain length 218 * If the string is equal to or larger than that length then it's size will be left alone 219 * 220 * @param {Number} len The length to pad the string to 221 * @param {String} [chars=" "] The characters to pad the string with 222 */ 223 String.prototype.padRight = function padRight() { 224 chars = chars || ' '; 225 return this + chars.expand(Math.max(0, len - this.length)); 226 }; 227 228 /** 229 * Partition a string. This breaks up a string by the first occurence of a separator 230 * and returns a 3 item array containing the part before the separator, 231 * the separator itself, and the part after the separator. 232 * If the separator is not found then the returned array will contain the string 233 * followed by two empty strings. 234 * @see http://docs.python.org/library/stdtypes.html#str.partition 235 * 236 * @param {String|RegExp} sep The separator to split by 237 * @return {Array} The three item partitioned array 238 */ 239 String.prototype.partition = function partition(sep) { 240 if ( sep instanceof RegExp ) { 241 var m = this.match(m); 242 var i = m ? m.index : -1; 243 sep = m[0]; 244 } else { 245 var i = this.indexOf(sep); 246 } 247 var l = sep.length; 248 249 return i > -1 ? 250 [ this.substr(0, i), sep, this.substr(i+l) ] : 251 [ this, '', '' ]; 252 }; 253 254 /** 255 * Same as string.partition but from the rightmost occurence of the separator 256 * @see String#partition 257 * 258 * @param {String} sep The separator to split by 259 * @return {Array} The three item partitioned array 260 */ 261 String.prototype.partitionRight = function partitionRight(sep) { 262 var i = this.lastIndexOf(sep); 263 return i > -1 ? 264 [ this.substr(0, i), sep, this.substr(i+sep.length) ] : 265 [ '', '', this ]; 266 }; 267 268 String.prototype.explode = function explode(sep, limit) { 269 270 }; 271 272 String.prototype.scan = function scan(regex, offset) { 273 var m, list = []; 274 offset = offset || 0; 275 if ( regex.global ) { 276 var str = this.substr(offset); 277 while( m = regex.exec(str) ) 278 list.push( m.length > 1 ? m.slice(1) : m[0] ); 279 } else { 280 while( m = this.substr(offset).match(regex) ) { 281 offset = m.index + m[0].length; 282 list.push( m.length > 1 ? m.slice(1) : m[0] ); 283 } 284 } 285 286 return list; 287 }; 288 289 /** 290 * Converts a dash (foo-bar) or underscore (foo_bar) style name into a 291 * cammel case (fooBar) name. 292 * 293 * @return {String} The new cammel case style string 294 */ 295 String.prototype.toCamelCase = function toCamelCase() { 296 return this.replace(/[-_][a-z]/g, function(i) { return i[1].toUpperCase(); }); 297 }; 298 299 /** 300 * Converts a cammel case (fooBar) name into an underscore (foo_bar) style name. 301 * 302 * @return {String} The new underscore style string 303 */ 304 String.prototype.toUnderscore = function toUnderscore() { 305 return this.replace(/[A-Z]/, function(i) { return '_' + i.toLowerCase(); }); 306 }; 307 308 /** 309 * Converts a cammel case (fooBar) name into a dash (foo-bar) style name. 310 * 311 * @return {String} The new dash style string 312 */ 313 String.prototype.toDash = function toDash() { 314 return this.replace(/[A-Z]/, function(i) { return '-' + i.toLowerCase(); }); 315 }; 316 317