1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | var sprintf = (function() {
|
65 | function get_type(variable) {
|
66 | return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
|
67 | }
|
68 | function str_repeat(input, multiplier) {
|
69 | for (var output = []; multiplier > 0; output[--multiplier] = input) {}
|
70 | return output.join('');
|
71 | }
|
72 |
|
73 | var str_format = function() {
|
74 | if (!str_format.cache.hasOwnProperty(arguments[0])) {
|
75 | str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
|
76 | }
|
77 | return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
|
78 | };
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 | str_format.object_stringify = function(obj, depth, maxdepth, seen) {
|
92 | var str = '';
|
93 | if (obj != null) {
|
94 | switch( typeof(obj) ) {
|
95 | case 'function':
|
96 | return '[Function' + (obj.name ? ': '+obj.name : '') + ']';
|
97 | break;
|
98 | case 'object':
|
99 | if ( obj instanceof Error) { return '[' + obj.toString() + ']' };
|
100 | if (depth >= maxdepth) return '[Object]'
|
101 | if (seen) {
|
102 |
|
103 | seen = seen.slice(0)
|
104 | seen.push(obj);
|
105 | }
|
106 | if (obj.length != null) {
|
107 | str += '[';
|
108 | var arr = []
|
109 | for (var i in obj) {
|
110 | if (seen && seen.indexOf(obj[i]) >= 0) arr.push('[Circular]');
|
111 | else arr.push(str_format.object_stringify(obj[i], depth+1, maxdepth, seen));
|
112 | }
|
113 | str += arr.join(', ') + ']';
|
114 | } else if ('getMonth' in obj) {
|
115 | return 'Date(' + obj + ')';
|
116 | } else {
|
117 | str += '{';
|
118 | var arr = []
|
119 | for (var k in obj) {
|
120 | if(obj.hasOwnProperty(k)) {
|
121 | if (seen && seen.indexOf(obj[k]) >= 0) arr.push(k + ': [Circular]');
|
122 | else arr.push(k +': ' +str_format.object_stringify(obj[k], depth+1, maxdepth, seen));
|
123 | }
|
124 | }
|
125 | str += arr.join(', ') + '}';
|
126 | }
|
127 | return str;
|
128 | break;
|
129 | case 'string':
|
130 | return '"' + obj + '"';
|
131 | break
|
132 | }
|
133 | }
|
134 | return '' + obj;
|
135 | }
|
136 |
|
137 | str_format.format = function(parse_tree, argv) {
|
138 | var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
|
139 | for (i = 0; i < tree_length; i++) {
|
140 | node_type = get_type(parse_tree[i]);
|
141 | if (node_type === 'string') {
|
142 | output.push(parse_tree[i]);
|
143 | }
|
144 | else if (node_type === 'array') {
|
145 | match = parse_tree[i];
|
146 | if (match[2]) {
|
147 | arg = argv[cursor];
|
148 | for (k = 0; k < match[2].length; k++) {
|
149 | if (!arg.hasOwnProperty(match[2][k])) {
|
150 | throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
|
151 | }
|
152 | arg = arg[match[2][k]];
|
153 | }
|
154 | }
|
155 | else if (match[1]) {
|
156 | arg = argv[match[1]];
|
157 | }
|
158 | else {
|
159 | arg = argv[cursor++];
|
160 | }
|
161 |
|
162 | if (/[^sO]/.test(match[8]) && (get_type(arg) != 'number')) {
|
163 | throw new Error(sprintf('[sprintf] expecting number but found %s "' + arg + '"', get_type(arg)));
|
164 | }
|
165 | switch (match[8]) {
|
166 | case 'b': arg = arg.toString(2); break;
|
167 | case 'c': arg = String.fromCharCode(arg); break;
|
168 | case 'd': arg = parseInt(arg, 10); break;
|
169 | case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
|
170 | case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
|
171 | case 'O': arg = str_format.object_stringify(arg, 0, parseInt(match[7]) || 5); break;
|
172 | case 'o': arg = arg.toString(8); break;
|
173 | case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
|
174 | case 'u': arg = Math.abs(arg); break;
|
175 | case 'x': arg = arg.toString(16); break;
|
176 | case 'X': arg = arg.toString(16).toUpperCase(); break;
|
177 | }
|
178 | arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
|
179 | pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
|
180 | pad_length = match[6] - String(arg).length;
|
181 | pad = match[6] ? str_repeat(pad_character, pad_length) : '';
|
182 | output.push(match[5] ? arg + pad : pad + arg);
|
183 | }
|
184 | }
|
185 | return output.join('');
|
186 | };
|
187 |
|
188 | str_format.cache = {};
|
189 |
|
190 | str_format.parse = function(fmt) {
|
191 | var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
|
192 | while (_fmt) {
|
193 | if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
|
194 | parse_tree.push(match[0]);
|
195 | }
|
196 | else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
|
197 | parse_tree.push('%');
|
198 | }
|
199 | else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosOuxX])/.exec(_fmt)) !== null) {
|
200 | if (match[2]) {
|
201 | arg_names |= 1;
|
202 | var field_list = [], replacement_field = match[2], field_match = [];
|
203 | if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
204 | field_list.push(field_match[1]);
|
205 | while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
|
206 | if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
207 | field_list.push(field_match[1]);
|
208 | }
|
209 | else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
|
210 | field_list.push(field_match[1]);
|
211 | }
|
212 | else {
|
213 | throw new Error('[sprintf] ' + replacement_field);
|
214 | }
|
215 | }
|
216 | }
|
217 | else {
|
218 | throw new Error('[sprintf] ' + replacement_field);
|
219 | }
|
220 | match[2] = field_list;
|
221 | }
|
222 | else {
|
223 | arg_names |= 2;
|
224 | }
|
225 | if (arg_names === 3) {
|
226 | throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported');
|
227 | }
|
228 | parse_tree.push(match);
|
229 | }
|
230 | else {
|
231 | throw new Error('[sprintf] ' + _fmt);
|
232 | }
|
233 | _fmt = _fmt.substring(match[0].length);
|
234 | }
|
235 | return parse_tree;
|
236 | };
|
237 |
|
238 | return str_format;
|
239 | })();
|
240 |
|
241 | var vsprintf = function(fmt, argv) {
|
242 | var argvClone = argv.slice();
|
243 | argvClone.unshift(fmt);
|
244 | return sprintf.apply(null, argvClone);
|
245 | };
|
246 |
|
247 | module.exports = sprintf;
|
248 | sprintf.sprintf = sprintf;
|
249 | sprintf.vsprintf = vsprintf;
|