1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 sc_require('system/locale'); 9 10 // These are basic enhancements to the string class used throughout 11 // SproutCore. 12 /** @private */ 13 SC.STRING_TITLEIZE_REGEXP = (/([\s|\-|\_|\n])([^\s|\-|\_|\n]?)/g); 14 SC.STRING_DECAMELIZE_REGEXP = (/([a-z])([A-Z])/g); 15 SC.STRING_DASHERIZE_REGEXP = (/[ _]/g); 16 SC.STRING_DASHERIZE_CACHE = {}; 17 SC.STRING_TRIM_LEFT_REGEXP = (/^\s+/g); 18 SC.STRING_TRIM_RIGHT_REGEXP = (/\s+$/g); 19 SC.STRING_CSS_ESCAPED_REGEXP = (/(:|\.|\[|\])/g); 20 21 /** 22 @namespace 23 24 SproutCore implements a variety of enhancements to the built-in String 25 object that make it easy to perform common substitutions and conversions. 26 27 Most of the utility methods defined here mirror those found in Prototype 28 1.6. 29 30 @since SproutCore 1.0 31 @lends String.prototype 32 */ 33 SC.mixin(SC.String, { 34 35 /** 36 Capitalizes a string. 37 38 ## Examples 39 40 capitalize('my favorite items') // 'My favorite items' 41 capitalize('css-class-name') // 'Css-class-name' 42 capitalize('action_name') // 'Action_name' 43 capitalize('innerHTML') // 'InnerHTML' 44 45 @return {String} capitalized string 46 */ 47 capitalize: function(str) { 48 return str.charAt(0).toUpperCase() + str.slice(1); 49 }, 50 51 /** 52 Camelizes a string. This will take any words separated by spaces, dashes 53 or underscores and convert them into camelCase. 54 55 ## Examples 56 57 camelize('my favorite items') // 'myFavoriteItems' 58 camelize('css-class-name') // 'cssClassName' 59 camelize('action_name') // 'actionName' 60 camelize('innerHTML') // 'innerHTML' 61 62 @returns {String} camelized string 63 */ 64 camelize: function(str) { 65 var ret = str.replace(SC.STRING_TITLEIZE_REGEXP, function(str, separater, character) { 66 return character ? character.toUpperCase() : ''; 67 }); 68 69 var first = ret.charAt(0), 70 lower = first.toLowerCase(); 71 72 return first !== lower ? lower + ret.slice(1) : ret; 73 }, 74 75 /** 76 Converts a camelized string into all lower case separated by underscores. 77 78 ## Examples 79 80 decamelize('my favorite items') // 'my favorite items' 81 decamelize('css-class-name') // 'css-class-name' 82 decamelize('action_name') // 'action_name' 83 decamelize('innerHTML') // 'inner_html' 84 85 @returns {String} the decamelized string. 86 */ 87 decamelize: function(str) { 88 return str.replace(SC.STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); 89 }, 90 91 /** 92 Converts a camelized string or a string with spaces or underscores into 93 a string with components separated by dashes. 94 95 ## Examples 96 97 | *Input String* | *Output String* | 98 dasherize('my favorite items') // 'my-favorite-items' 99 dasherize('css-class-name') // 'css-class-name' 100 dasherize('action_name') // 'action-name' 101 dasherize('innerHTML') // 'inner-html' 102 103 @returns {String} the dasherized string. 104 */ 105 dasherize: function(str) { 106 var cache = SC.STRING_DASHERIZE_CACHE, 107 ret = cache[str]; 108 109 if (ret) { 110 return ret; 111 } else { 112 ret = SC.String.decamelize(str).replace(SC.STRING_DASHERIZE_REGEXP,'-'); 113 cache[str] = ret; 114 } 115 116 return ret; 117 }, 118 119 /** 120 Escapes the given string to make it safe to use as a jQuery selector. 121 jQuery will interpret '.' and ':' as class and pseudo-class indicators. 122 123 @see http://learn.jquery.com/using-jquery-core/faq/how-do-i-select-an-element-by-an-id-that-has-characters-used-in-css-notation/ 124 125 @param {String} str the string to escape 126 @returns {String} the escaped string 127 */ 128 escapeCssIdForSelector: function (str) { 129 return str.replace(SC.STRING_CSS_ESCAPED_REGEXP, '\\$1'); 130 }, 131 132 /** 133 Localizes the string. This will look up the receiver string as a key 134 in the current Strings hash. If the key matches, the loc'd value will be 135 used. The resulting string will also be passed through fmt() to insert 136 any variables. 137 138 @param str {String} String to localize 139 @param args {Object...} optional arguments to interpolate also 140 @returns {String} the localized and formatted string. 141 */ 142 loc: function(str) { 143 // NB: This could be implemented as a wrapper to locWithDefault() but 144 // it would add some overhead to deal with the arguments and adds stack 145 // frames, so we are keeping the implementation separate. 146 if (!SC.Locale.currentLocale) { SC.Locale.createCurrentLocale(); } 147 148 var localized = SC.Locale.currentLocale.locWithDefault(str); 149 if (SC.typeOf(localized) !== SC.T_STRING) { localized = str; } 150 151 var args = SC.$A(arguments); 152 args.shift(); // remove str param 153 //to extend String.prototype 154 if (args.length > 0 && args[0] && args[0].isSCArray) { args = args[0]; } 155 156 // I looked up the performance of try/catch. IE and FF do not care so 157 // long as the catch never happens. Safari and Chrome are affected rather 158 // severely (10x), but this is a one-time cost per loc (the code being 159 // executed is likely as expensive as this try/catch cost). 160 // 161 // Also, .loc() is not called SO much to begin with. So, the error handling 162 // that this gives us is worth it. 163 try { 164 return SC.String.fmt(localized, args); 165 } catch (e) { 166 SC.error("Error processing string with key: " + str); 167 SC.error("Localized String: " + localized); 168 SC.error("Error: " + e); 169 } 170 171 }, 172 173 /** 174 Returns the localized metric value for the specified key. A metric is a 175 single value intended to be used in your interface’s layout, such as 176 "Button.Confirm.Width" = 100. 177 178 If you would like to return a set of metrics for use in a layout hash, you 179 may prefer to use the locLayout() method instead. 180 181 @param str {String} key 182 @returns {Number} the localized metric 183 */ 184 locMetric: function(key) { 185 var K = SC.Locale, 186 currentLocale = K.currentLocale; 187 188 if (!currentLocale) { 189 K.createCurrentLocale(); 190 currentLocale = K.currentLocale; 191 } 192 return currentLocale.locMetric(key); 193 }, 194 195 /** 196 Creates and returns a new hash suitable for use as an SC.View’s 'layout' 197 hash. This hash will be created by looking for localized metrics following 198 a pattern based on the “base key” you specify. 199 200 For example, if you specify "Button.Confirm", the following metrics will be 201 used if they are defined: 202 203 Button.Confirm.left 204 Button.Confirm.top 205 Button.Confirm.right 206 Button.Confirm.bottom 207 Button.Confirm.width 208 Button.Confirm.height 209 Button.Confirm.midWidth 210 Button.Confirm.minHeight 211 Button.Confirm.centerX 212 Button.Confirm.centerY 213 214 Additionally, you can optionally specify a hash which will be merged on top 215 of the returned hash. For example, if you wish to allow a button’s width 216 to be configurable per-locale, but always wish for it to be centered 217 vertically and horizontally, you can call: 218 219 locLayout("Button.Confirm", {centerX:0, centerY:0}) 220 221 …so that you can combine both localized and non-localized elements in the 222 returned hash. (An exception will be thrown if there is a locale-specific 223 key that matches a key specific in this hash.) 224 225 226 For example, if your locale defines: 227 228 Button.Confirm.left 229 Button.Confirm.top 230 Button.Confirm.right 231 Button.Confirm.bottom 232 233 234 …then these two code snippets will produce the same result: 235 236 layout: { 237 left: "Button.Confirm.left".locMetric(), 238 top: "Button.Confirm.top".locMetric(), 239 right: "Button.Confirm.right".locMetric(), 240 bottom: "Button.Confirm.bottom".locMetric() 241 } 242 243 layout: "Button.Confirm".locLayout() 244 245 The former is slightly more efficient because it doesn’t have to iterate 246 through the possible localized layout keys, but in virtually all situations 247 you will likely wish to use the latter. 248 249 @param str {String} key 250 @param {str} (optional) additionalHash 251 @param {String} (optional) additionalHash 252 @returns {Number} the localized metric 253 */ 254 locLayout: function(key, additionalHash) { 255 var K = SC.Locale, 256 currentLocale = K.currentLocale; 257 258 if (!currentLocale) { 259 K.createCurrentLocale(); 260 currentLocale = K.currentLocale; 261 } 262 return currentLocale.locLayout(key, additionalHash); 263 }, 264 265 /** 266 Works just like loc() except that it will return the passed default 267 string if a matching key is not found. 268 269 @param {String} str the string to localize 270 @param {String} def the default to return 271 @param {Object...} args optional formatting arguments 272 @returns {String} localized and formatted string 273 */ 274 locWithDefault: function(str, def) { 275 if (!SC.Locale.currentLocale) { SC.Locale.createCurrentLocale(); } 276 277 var localized = SC.Locale.currentLocale.locWithDefault(str, def); 278 if (SC.typeOf(localized) !== SC.T_STRING) { localized = str; } 279 280 var args = SC.$A(arguments); 281 args.shift(); // remove str param 282 args.shift(); // remove def param 283 284 return SC.String.fmt(localized, args); 285 }, 286 287 /** 288 Removes any extra whitespace from the edges of the string. This method is 289 also aliased as strip(). 290 291 @returns {String} the trimmed string 292 */ 293 trim: jQuery.trim, 294 295 /** 296 Removes any extra whitespace from the left edge of the string. 297 298 @returns {String} the trimmed string 299 */ 300 trimLeft: function (str) { 301 return str.replace(SC.STRING_TRIM_LEFT_REGEXP,""); 302 }, 303 304 /** 305 Removes any extra whitespace from the right edge of the string. 306 307 @returns {String} the trimmed string 308 */ 309 trimRight: function (str) { 310 return str.replace(SC.STRING_TRIM_RIGHT_REGEXP,""); 311 }, 312 313 /** 314 Mulitplies a given string. For instance if you have a string "xyz" 315 and multiply it by 2 the result is "xyzxyz". 316 317 @param {String} str the string to multiply 318 @param {Number} value the number of times to multiply the string 319 @returns {String} the mulitiplied string 320 */ 321 mult: function(str, value) { 322 if (SC.typeOf(value) !== SC.T_NUMBER || value < 1) return null; 323 324 var ret = ""; 325 for (var i = 0; i < value; i += 1) { 326 ret += str; 327 } 328 329 return ret; 330 } 331 332 }); 333 334 335 // IE doesn't support string trimming 336 if(String.prototype.trim) { 337 SC.supplement(String.prototype, 338 /** @scope String.prototype */ { 339 340 trim: function() { 341 return SC.String.trim(this, arguments); 342 }, 343 344 trimLeft: function() { 345 return SC.String.trimLeft(this, arguments); 346 }, 347 348 trimRight: function() { 349 return SC.String.trimRight(this, arguments); 350 } 351 }); 352 } 353 354 // We want the version defined here, not in Runtime 355 SC.mixin(String.prototype, 356 /** @scope String.prototype */ { 357 358 loc: function() { 359 return SC.String.loc(this.toString(), SC.$A(arguments)); 360 }, 361 362 locMetric: function() { 363 return SC.String.locMetric(this.toString()); 364 }, 365 366 locLayout: function(additionalHash) { 367 return SC.String.locLayout(this.toString(), additionalHash); 368 } 369 370 }); 371 372