1 // ==========================================================================
  2 // Project:   SproutCore Costello - Property Observing Library
  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 /**
  9   @class
 10 
 11   Implements support methods useful when working with strings in SproutCore
 12   applications.
 13 */
 14 SC.String = /** @scope SC.String.prototype */ {
 15 
 16   /**
 17     This finds the value for a key in a formatting string.
 18 
 19     Keys take the form:
 20 
 21         key[:argument to formatter]
 22   */
 23   _scs_valueForKey: function(key, data, /* for debugging purposes: */ string) {
 24     var arg, value, formatter, argsplit = key.indexOf(':');
 25     if (argsplit > -1) {
 26       arg = key.substr(argsplit + 1);
 27       key = key.substr(0, argsplit);
 28     }
 29 
 30     value = data.get ? data.get(key): data[key];
 31     formatter = data[key + 'Formatter'];
 32 
 33     // formatters are optional
 34     if (formatter) value = formatter(value, arg);
 35     else if (arg) {
 36       throw new Error("String.fmt was given a formatting string, but key `" + key + "` has no formatter! String: " + string);
 37     }
 38 
 39     return value;
 40   },
 41 
 42   /**
 43     Formats a string. You can format either with named parameters or
 44     indexed, but not both.
 45 
 46     Indexed Parameters
 47     --------------------
 48     Indexed parameters are just arguments you pass into format. For example:
 49 
 50         "%@1 %@3 %@2".fmt(1, 2, 3)
 51 
 52         // -> "1 3 2"
 53 
 54     If you don't supply a number, it will use them in the order you supplied. For example:
 55 
 56         "%@, %@".fmt("Iskander", "Alex")
 57 
 58         // -> "Iskander, Alex"
 59 
 60     Named Paramters
 61     --------------------
 62     You can use named parameters like this:
 63 
 64         "Value: %{key_name}".fmt({ key_name: "A Value" })
 65 
 66         // -> "Value: A Value"
 67 
 68     You can supply formatters for each field. A formatter is a method to get applied
 69     to the parameter:
 70 
 71         Currency = function(v) { return "$" + v; };
 72         "Value: %{val}".fmt({ val: 12.00, valFormatter: Currency })
 73 
 74         // -> $12.00
 75 
 76     Formatters can also use arguments:
 77 
 78         Currency = function(v, sign) { return sign + v; };
 79         "Value: %{val:£}".fmt({ val: 12.00, valFormatter: Currency })
 80 
 81         // -> £12.00
 82 
 83     You can supply a different formatter for each named parameter. Formatters can ONLY be
 84     used with named parameters (not indexed parameters).
 85 
 86   */
 87   fmt: function(string, args) {
 88     var i = 0, data, hasHadNamedArguments;
 89     if (args) {
 90       data = args[0];
 91     }
 92 
 93     return string.replace(/%\{(.*?)\}/g, function(match, propertyPath) {
 94       hasHadNamedArguments = YES;
 95       if (!data) {
 96         throw new Error("Cannot use named parameters with `fmt` without a data hash. String: '" + string + "'");
 97       }
 98 
 99       var ret = SC.String._scs_valueForKey(propertyPath, data, string);
100       // If a value was found, return that; otherwise return the original matched text to retain it in the string
101       // for future formatting.
102       if (!SC.none(ret)) { return ret; }
103       else { return match; }
104     }).replace(/%@([0-9]+)?/g, function(match, index) {
105       if (hasHadNamedArguments) {
106         throw new Error("Invalid attempt to use both named parameters and indexed parameters. String: '" + string + "'");
107       }
108       index = index ? parseInt(index, 10) - 1 : i++;
109       if(args[index]!==undefined) return args[index];
110       else return "";
111     });
112   },
113 
114   /**
115     Splits the string into words, separated by spaces. Empty strings are
116     removed from the results.
117 
118     @returns {Array} An array of non-empty strings
119   */
120   w: function(str) {
121     var ary = [], ary2 = str.split(' '), len = ary2.length, string, idx=0;
122     for (idx=0; idx<len; ++idx) {
123       string = ary2[idx] ;
124       if (string.length !== 0) ary.push(string) ; // skip empty strings
125     }
126     return ary ;
127   }
128 };
129