Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojox.string.sprintf"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.string.sprintf"] = true;
3
dojo.provide("dojox.string.sprintf");
4
 
5
dojo.require("dojox.string.tokenize");
6
 
7
dojox.string.sprintf = function(/*String*/ format, /*mixed...*/ filler){
8
	for(var args = [], i = 1; i < arguments.length; i++){
9
		args.push(arguments[i]);
10
	}
11
	var formatter = new dojox.string.sprintf.Formatter(format);
12
	return formatter.format.apply(formatter, args);
13
}
14
 
15
dojox.string.sprintf.Formatter = function(/*String*/ format){
16
	var tokens = [];
17
	this._mapped = false;
18
	this._format = format;
19
	this._tokens = dojox.string.tokenize(format, this._re, this._parseDelim, this);
20
}
21
dojo.extend(dojox.string.sprintf.Formatter, {
22
	_re: /\%(?:\(([\w_]+)\)|([1-9]\d*)\$)?([0 +\-\#]*)(\*|\d+)?(\.)?(\*|\d+)?[hlL]?([\%scdeEfFgGiouxX])/g,
23
	_parseDelim: function(mapping, intmapping, flags, minWidth, period, precision, specifier){
24
		if(mapping){
25
			this._mapped = true;
26
		}
27
		return {
28
			mapping: mapping,
29
			intmapping: intmapping,
30
			flags: flags,
31
			_minWidth: minWidth, // May be dependent on parameters
32
			period: period,
33
			_precision: precision, // May be dependent on parameters
34
			specifier: specifier
35
		};
36
	},
37
	_specifiers: {
38
		b: {
39
			base: 2,
40
			isInt: true
41
		},
42
		o: {
43
			base: 8,
44
			isInt: true
45
		},
46
		x: {
47
			base: 16,
48
			isInt: true
49
		},
50
		X: {
51
			extend: ["x"],
52
			toUpper: true
53
		},
54
		d: {
55
			base: 10,
56
			isInt: true
57
		},
58
		i: {
59
			extend: ["d"]
60
		},
61
		u: {
62
			extend: ["d"],
63
			isUnsigned: true
64
		},
65
		c: {
66
			setArg: function(token){
67
				if(!isNaN(token.arg)){
68
					var num = parseInt(token.arg);
69
					if(num < 0 || num > 127){
70
						throw new Error("invalid character code passed to %c in sprintf");
71
					}
72
					token.arg = isNaN(num) ? "" + num : String.fromCharCode(num);
73
				}
74
			}
75
		},
76
		s: {
77
			setMaxWidth: function(token){
78
				token.maxWidth = (token.period == ".") ? token.precision : -1;
79
			}
80
		},
81
		e: {
82
			isDouble: true,
83
			doubleNotation: "e"
84
		},
85
		E: {
86
			extend: ["e"],
87
			toUpper: true
88
		},
89
		f: {
90
			isDouble: true,
91
			doubleNotation: "f"
92
		},
93
		F: {
94
			extend: ["f"]
95
		},
96
		g: {
97
			isDouble: true,
98
			doubleNotation: "g"
99
		},
100
		G: {
101
			extend: ["g"],
102
			toUpper: true
103
		}
104
	},
105
	format: function(/*mixed...*/ filler){
106
		if(this._mapped && typeof filler != "object"){
107
			throw new Error("format requires a mapping");
108
		}
109
 
110
		var str = "";
111
		var position = 0;
112
		for(var i = 0, token; i < this._tokens.length; i++){
113
			token = this._tokens[i];
114
			if(typeof token == "string"){
115
				str += token;
116
			}else{
117
				if(this._mapped){
118
					if(typeof filler[token.mapping] == "undefined"){
119
						throw new Error("missing key " + token.mapping);
120
					}
121
					token.arg = filler[token.mapping];
122
				}else{
123
					if(token.intmapping){
124
						var position = parseInt(token.intmapping) - 1;
125
					}
126
					if(position >= arguments.length){
127
						throw new Error("got " + arguments.length + " printf arguments, insufficient for '" + this._format + "'");
128
					}
129
					token.arg = arguments[position++];
130
				}
131
 
132
				if(!token.compiled){
133
					token.compiled = true;
134
					token.sign = "";
135
					token.zeroPad = false;
136
					token.rightJustify = false;
137
					token.alternative = false;
138
 
139
					var flags = {};
140
					for(var fi = token.flags.length; fi--;){
141
						var flag = token.flags.charAt(fi);
142
						flags[flag] = true;
143
						switch(flag){
144
							case " ":
145
								token.sign = " ";
146
								break;
147
							case "+":
148
								token.sign = "+";
149
								break;
150
							case "0":
151
								token.zeroPad = (flags["-"]) ? false : true;
152
								break;
153
							case "-":
154
								token.rightJustify = true;
155
								token.zeroPad = false;
156
								break;
157
							case "\#":
158
								token.alternative = true;
159
								break;
160
							default:
161
								throw Error("bad formatting flag '" + token.flags.charAt(fi) + "'");
162
						}
163
					}
164
 
165
					token.minWidth = (token._minWidth) ? parseInt(token._minWidth) : 0;
166
					token.maxWidth = -1;
167
					token.toUpper = false;
168
					token.isUnsigned = false;
169
					token.isInt = false;
170
					token.isDouble = false;
171
					token.precision = 1;
172
					if(token.period == '.'){
173
						if(token._precision){
174
							token.precision = parseInt(token._precision);
175
						}else{
176
							token.precision = 0;
177
						}
178
					}
179
 
180
					var mixins = this._specifiers[token.specifier];
181
					if(typeof mixins == "undefined"){
182
						throw new Error("unexpected specifier '" + token.specifier + "'");
183
					}
184
					if(mixins.extend){
185
						dojo.mixin(mixins, this._specifiers[mixins.extend]);
186
						delete mixins.extend;
187
					}
188
					dojo.mixin(token, mixins);
189
				}
190
 
191
				if(typeof token.setArg == "function"){
192
					token.setArg(token);
193
				}
194
 
195
				if(typeof token.setMaxWidth == "function"){
196
					token.setMaxWidth(token);
197
				}
198
 
199
				if(token._minWidth == "*"){
200
					if(this._mapped){
201
						throw new Error("* width not supported in mapped formats");
202
					}
203
					token.minWidth = parseInt(arguments[position++]);
204
					if(isNaN(token.minWidth)){
205
						throw new Error("the argument for * width at position " + position + " is not a number in " + this._format);
206
					}
207
					// negative width means rightJustify
208
					if (token.minWidth < 0) {
209
						token.rightJustify = true;
210
						token.minWidth = -token.minWidth;
211
					}
212
				}
213
 
214
				if(token._precision == "*" && token.period == "."){
215
					if(this._mapped){
216
						throw new Error("* precision not supported in mapped formats");
217
					}
218
					token.precision = parseInt(arguments[position++]);
219
					if(isNaN(token.precision)){
220
						throw Error("the argument for * precision at position " + position + " is not a number in " + this._format);
221
					}
222
					// negative precision means unspecified
223
					if (token.precision < 0) {
224
						token.precision = 1;
225
						token.period = '';
226
					}
227
				}
228
 
229
				if(token.isInt){
230
					// a specified precision means no zero padding
231
					if(token.period == '.'){
232
						token.zeroPad = false;
233
					}
234
					this.formatInt(token);
235
				}else if(token.isDouble){
236
					if(token.period != '.'){
237
						token.precision = 6;
238
					}
239
					this.formatDouble(token);
240
				}
241
				this.fitField(token);
242
 
243
				str += "" + token.arg;
244
			}
245
		}
246
 
247
		return str;
248
	},
249
	_zeros10: '0000000000',
250
	_spaces10: '          ',
251
	formatInt: function(token) {
252
		var i = parseInt(token.arg);
253
		if(!isFinite(i)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
254
			// allow this only if arg is number
255
			if(typeof token.arg != "number"){
256
				throw new Error("format argument '" + token.arg + "' not an integer; parseInt returned " + i);
257
			}
258
			//return '' + i;
259
			i = 0;
260
		}
261
 
262
		// if not base 10, make negatives be positive
263
		// otherwise, (-10).toString(16) is '-a' instead of 'fffffff6'
264
		if(i < 0 && (token.isUnsigned || token.base != 10)){
265
			i = 0xffffffff + i + 1;
266
		}
267
 
268
		if(i < 0){
269
			token.arg = (- i).toString(token.base);
270
			this.zeroPad(token);
271
			token.arg = "-" + token.arg;
272
		}else{
273
			token.arg = i.toString(token.base);
274
			// need to make sure that argument 0 with precision==0 is formatted as ''
275
			if(!i && !token.precision){
276
				token.arg = "";
277
			}else{
278
				this.zeroPad(token);
279
			}
280
			if(token.sign){
281
				token.arg = token.sign + token.arg;
282
			}
283
		}
284
		if(token.base == 16){
285
			if(token.alternative){
286
				token.arg = '0x' + token.arg;
287
			}
288
			toke.art = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
289
		}
290
		if(token.base == 8){
291
			if(token.alternative && token.arg.charAt(0) != '0'){
292
				token.arg = '0' + token.arg;
293
			}
294
		}
295
	},
296
	formatDouble: function(token) {
297
		var f = parseFloat(token.arg);
298
		if(!isFinite(f)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
299
			// allow this only if arg is number
300
			if(typeof token.arg != "number"){
301
				throw new Error("format argument '" + token.arg + "' not a float; parseFloat returned " + f);
302
			}
303
			// C99 says that for 'f':
304
			//   infinity -> '[-]inf' or '[-]infinity' ('[-]INF' or '[-]INFINITY' for 'F')
305
			//   NaN -> a string  starting with 'nan' ('NAN' for 'F')
306
			// this is not commonly implemented though.
307
			//return '' + f;
308
			f = 0;
309
		}
310
 
311
		switch(token.doubleNotation) {
312
			case 'e': {
313
				token.arg = f.toExponential(token.precision);
314
				break;
315
			}
316
			case 'f': {
317
				token.arg = f.toFixed(token.precision);
318
				break;
319
			}
320
			case 'g': {
321
				// C says use 'e' notation if exponent is < -4 or is >= prec
322
				// ECMAScript for toPrecision says use exponential notation if exponent is >= prec,
323
				// though step 17 of toPrecision indicates a test for < -6 to force exponential.
324
				if(Math.abs(f) < 0.0001){
325
					//print("forcing exponential notation for f=" + f);
326
					token.arg = f.toExponential(token.precision > 0 ? token.precision - 1 : token.precision);
327
				}else{
328
					token.arg = f.toPrecision(token.precision);
329
				}
330
 
331
				// In C, unlike 'f', 'gG' removes trailing 0s from fractional part, unless alternative format flag ("#").
332
				// But ECMAScript formats toPrecision as 0.00100000. So remove trailing 0s.
333
				if(!token.alternative){
334
					//print("replacing trailing 0 in '" + s + "'");
335
					token.arg = token.arg.replace(/(\..*[^0])0*/, "$1");
336
					// if fractional part is entirely 0, remove it and decimal point
337
					token.arg = token.arg.replace(/\.0*e/, 'e').replace(/\.0$/,'');
338
				}
339
				break;
340
			}
341
			default: throw new Error("unexpected double notation '" + token.doubleNotation + "'");
342
		}
343
 
344
		// C says that exponent must have at least two digits.
345
		// But ECMAScript does not; toExponential results in things like "1.000000e-8" and "1.000000e+8".
346
		// Note that s.replace(/e([\+\-])(\d)/, "e$10$2") won't work because of the "$10" instead of "$1".
347
		// And replace(re, func) isn't supported on IE50 or Safari1.
348
		token.arg = token.arg.replace(/e\+(\d)$/, "e+0$1").replace(/e\-(\d)$/, "e-0$1");
349
 
350
		// Ensure a '0' before the period.
351
		// Opera implements (0.001).toString() as '0.001', but (0.001).toFixed(1) is '.001'
352
		if(dojo.isOpera){
353
			token.arg = token.arg.replace(/^\./, '0.');
354
		}
355
 
356
		// if alt, ensure a decimal point
357
		if(token.alternative){
358
			token.arg = token.arg.replace(/^(\d+)$/,"$1.");
359
			token.arg = token.arg.replace(/^(\d+)e/,"$1.e");
360
		}
361
 
362
		if(f >= 0 && token.sign){
363
			token.arg = token.sign + token.arg;
364
		}
365
 
366
		token.arg = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
367
	},
368
	zeroPad: function(token, /*Int*/ length) {
369
		length = (arguments.length == 2) ? length : token.precision;
370
		if(typeof token.arg != "string"){
371
			token.arg = "" + token.arg;
372
		}
373
 
374
		var tenless = length - 10;
375
		while(token.arg.length < tenless){
376
			token.arg = (token.rightJustify) ? token.arg + this._zeros10 : this._zeros10 + token.arg;
377
		}
378
		var pad = length - token.arg.length;
379
		token.arg = (token.rightJustify) ? token.arg + this._zeros10.substring(0, pad) : this._zeros10.substring(0, pad) + token.arg;
380
	},
381
	fitField: function(token) {
382
		if(token.maxWidth >= 0 && token.arg.length > token.maxWidth){
383
			return token.arg.substring(0, token.maxWidth);
384
		}
385
		if(token.zeroPad){
386
			this.zeroPad(token, token.minWidth);
387
			return;
388
		}
389
		this.spacePad(token);
390
	},
391
	spacePad: function(token, /*Int*/ length) {
392
		length = (arguments.length == 2) ? length : token.minWidth;
393
		if(typeof token.arg != 'string'){
394
			token.arg = '' + token.arg;
395
		}
396
 
397
		var tenless = length - 10;
398
		while(token.arg.length < tenless){
399
			token.arg = (token.rightJustify) ? token.arg + this._spaces10 : this._spaces10 + token.arg;
400
		}
401
		var pad = length - token.arg.length;
402
		token.arg = (token.rightJustify) ? token.arg + this._spaces10.substring(0, pad) : this._spaces10.substring(0, pad) + token.arg;
403
	}
404
});
405
 
406
}