1/**
2 *  $Id: stringextensions.js 147 2006-12-20 22:53:19Z wingedfox $
3 *  $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/tags/BrowserExtensions.003/stringextensions.js $
4 *
5 *  @author Ildar Shaimordanov
6 *  @author Ilya Lebedev
7 *  @license LGPL
8 *  @version $Rev: 147 $
9 */
10/**
11 *  Method is used to trim specified chars from the left of the string
12 *
13 *  @param {String, Array} c char or char list to be trimmed, default is \s
14 *  @return {String}
15 *  @scope public
16 */
17String.prototype.ltrim = function(c) {
18    if (isString(c)) c=c.split("");
19    if (isArray(c) || isUndefined(c)) {
20        c = isEmpty(c)?"\\s":RegExp.escape(c);
21        c = new RegExp("^(?:"+c+")+", "g");
22        return this.replace(c, "");
23    }
24    return this;
25}
26/**
27 *  Method is used to trim specified list from the right of the string
28 *
29 *  @param {String, Array} c char or char sequence to be trimmed, default is \s
30 *  @return {String}
31 *  @scope public
32 */
33String.prototype.rtrim = function(c) {
34    if (isString(c)) c=c.split("");
35    if (isArray(c) || isUndefined(c)) {
36        c = isEmpty(c)?"\\s":RegExp.escape(c);
37        c = new RegExp("(?:"+c+")+$", "g");
38        return this.replace(c, "");
39    }
40    return this;
41}
42/**
43 *  Method is used to trim specified chars from the left and the right of the string
44 *
45 *  @param {String, Array} c char or char list to be trimmed, default is \s
46 *  @return {String}
47 *  @scope public
48 */
49String.prototype.trim = function(c) {
50    if (isString(c)) c=c.split("");
51    if (isArray(c) || isUndefined(c)) {
52        c = isEmpty(c)?"\\s":RegExp.escape(c);
53        c = new RegExp("^(?:"+c+")+|(?:"+c+")+$", "g");
54        return this.replace(c, "");
55    }
56    return this;
57}
58
59/**
60 *  Duplicates the string
61 *
62 *  @return {String}
63 *  @scope public
64 */
65String.prototype.dup = function() {
66    var val = this.valueOf();
67    return [val,val].join("");
68}
69/**
70 *  Repeats string specified number of times
71 *
72 *  @param {Number} n number of times to repeat the string
73 *  @return {String}
74 *  @scope public
75 */
76String.prototype.repeat = function(n /* :Number */) /* :String */ {
77    if (isNaN(n=parseInt(n)) || n<0) return "";
78    return Array(n+1).join(this.valueOf());
79}
80/**
81 *  Pads the string to the specified length
82 *
83 *  @param {Number} n number of times to repeat c
84 *                 positive - on the right side
85 *                 negative - on the left side
86 *  @param {String} c fill char, space is default
87 *  @return {String}
88 *  @scope public
89 */
90String.prototype.padding = function(n, c) {
91    var val = this.valueOf();
92    n = parseInt(n);
93    if (!n) return val;
94    if (isUndefined(c)) c = " ";
95    var pad = String(c).charAt(0).repeat(Math.abs(n) - this.length);
96    return (n < 0) ? pad + val : val + pad;
97}
98/**
99 *  Pads the string on the right side
100 *
101 *  @param {Number} n number of times to repeat c
102 *  @param {String} c fill char
103 *  @return {String}
104 *  @scope public
105 */
106String.prototype.padLeft = function(n, c) {
107    return this.padding(-Math.abs(n), c);
108}
109/**
110 *  Pads the string on the left side
111 *
112 *  @param {Number} n number of times to repeat c
113 *  @param {String} c fill char
114 *  @return {String}
115 *  @scope public
116 */
117String.prototype.padRight = function(n, c) {
118    return this.padding(Math.abs(n), c);
119}
120
121/**
122 * sprintf(format, argument_list)
123 *
124 * The string format function like the one in C/C++, PHP, Perl
125 * Each conversion specification is defined as below:
126 *
127 * %[index][alignment][padding][width][precision]type
128 *
129 * index         An optional index specifier that changes the order of the
130 *               arguments in the list to be displayed.
131 * alignment     An optional alignment specifier that says if the result should be
132 *               left-justified or right-justified. The default is
133 *               right-justified; a "-" character here will make it left-justified.
134 * padding       An optional padding specifier that says what character will be
135 *               used for padding the results to the right string size. This may
136 *               be a space character or a "0" (zero character). The default is to
137 *               pad with spaces. An alternate padding character can be specified
138 *               by prefixing it with a single quote ('). See the examples below.
139 * width         An optional number, a width specifier that says how many
140 *               characters (minimum) this conversion should result in.
141 * precision     An optional precision specifier that says how many decimal digits
142 *               should be displayed for floating-point numbers. This option has
143 *               no effect for other types than float.
144 * type          A type specifier that says what type the argument data should be
145 *               treated as. Possible types:
146 *
147 * % - a literal percent character. No argument is required.
148 * b - the argument is treated as an integer, and presented as a binary number.
149 * c - the argument is treated as an integer, and presented as the character
150 *       with that ASCII value.
151 * d - the argument is treated as an integer, and presented as a decimal number.
152 * u - the same as "d".
153 * f - the argument is treated as a float, and presented as a floating-point.
154 * o - the argument is treated as an integer, and presented as an octal number.
155 * s - the argument is treated as and presented as a string.
156 * x - the argument is treated as an integer and presented as a hexadecimal
157 *        number (with lowercase letters).
158 * X - the argument is treated as an integer and presented as a hexadecimal
159 *        number (with uppercase letters).
160 *
161 *  @return {String}
162 *  @scope public
163 */
164String.prototype.sprintf = function() {
165    var args = isArray(arguments[0])?arguments[0]:arguments
166       ,index = 0
167       ,frmt = this.replace(/%%/g, "\0\0")
168       ,re = /%((?:\d+\$)?)((?:[-0+# ])?)((?:\d+|\*(?:\d+\$)?)?)((?:.(?:\d+|\*(?:\d+\$)?))?)([bcdeEfosuxX])/g;
169    /*
170    * The re.exec() method returns the array with the following properties
171    * wich are used in this function
172    *       x.index contains the substring position found at the origin string
173    *       x[0] contains the found substring
174    *       x[1] contains the explicit parameter number
175    *       x[2] contains the flags
176    *       x[3] contains the minwidth
177    *       x[4] contains the precision
178    *       x[5] contains the type specifier (as [bcdfosuxX])
179    */
180    frmt = frmt.replace(re, function() {
181        var x = arguments
182           ,sign = false
183           ,ins;
184
185        /*
186        *  calculate min width
187        */
188        if (!isUndefined(x[3]) && x[3].indexOf("*")==0) {
189            x[3] = parseInt(x[3].replace(/\D/g,""))
190            if (isNaN(x[3])) {
191              x[3] = args[index];
192              /*
193              *  increment
194              */
195              index++;
196            } else {
197              x[3] = args[x[3]]
198            }
199        }
200        /*
201        *  calculate precision
202        */
203        if ("" != x[4]) {
204            if (x[4].indexOf("*")==1) {
205                x[4] = parseInt(x[4].replace(/\D/g,""))
206                if (isNaN(x[4])) {
207                  x[4] = args[index];
208                  /*
209                  *  increment
210                  */
211                  index++;
212                } else {
213                  x[4] = args[x[4]]
214                }
215            } else {
216                x[4] = x[4].replace(/\D/,"")
217            }
218            x[4] = Math.abs(x[4]);
219        }
220        /*
221        *  calculate insertion value
222        */
223        x[1] = parseInt(x[1]);
224        var ins;
225        if (isNumeric(x[1])) {
226            ins = args[x[1]];
227        } else {
228            ins = args[index];
229            /*
230            *  index should be incremented only when no explicit parameter number is specified
231            */
232            index++;
233        }
234        switch (x[5]) {
235        case "b":
236            if (ins<0) ins = 0x10000000000000000+parseInt(ins);
237            ins = Number(ins).bin(x[4]);
238            if (x[4]) ins = ins.substr(0,x[4]);
239            if (x[2]=='#') ins = '0b'+ins;
240            break;
241        case "c":
242            ins = String.fromCharCode(ins);
243            break;
244        case "u":
245            ins = Math.abs(ins);
246        case "d":
247            ins = Math.round(ins);
248            if (ins<0) {
249                ins = "-"+Math.abs(ins).dec(x[4]);
250            } else {
251                ins = Number(ins).dec(x[4]);
252                sign = (x[2] == ' ' || x[2] == '+');
253            }
254            break;
255        case "e":
256        case "E":
257            if (ins>0) {
258                sign = (x[2] == ' ' || x[2] == '+');
259            }
260            ins = Number(ins).toExponential(x[4]?x[4]:6);
261            if (x[5]=='E') ins=ins.toUpperCase();
262            break;
263        case "f":
264            if (ins>0) {
265                sign = (x[2] == ' ' || x[2] == '+');
266            }
267            ins = Number(ins).toFixed(isNumeric(x[4])?x[4]:6);
268            break;
269        case "o":
270            if (ins<0) ins = 0x10000000000000000+parseInt(ins);
271            ins = Number(ins).toString(8);
272            if (x[4]) ins = ins.substr(0,x[4]);
273            if (x[2]=='#' && ins != 0) ins = '0'+ins;
274            break;
275        case "s":
276            ins = String(ins);
277            if (x[4]) ins = ins.substr(0,x[4]);
278            break;
279        case "x":
280        case "X":
281            if (ins<0) ins = 0x10000000000000000+parseInt(ins);
282            ins = Number(ins).hex(-x[4]);
283            if (x[4]) ins = ins.substr(0,x[4]);
284            if (x[2]=='#') ins = '0x'+ins;
285            if (x[5]=='X') ins = ins.toUpperCase();
286            break;
287        }
288        if (sign) ins = x[2]+ins;
289        if (x[3]) ins = (x[2]=='-' || x[3]<0)?ins.padRight(x[3]):ins.padLeft(x[3],x[2]=='0'?0:" ");
290        return ins;
291    })
292    return frmt.replace(/\0\0/g, "%");
293}
294