1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | var p = console.log;
|
8 | var format = require('util').format;
|
9 |
|
10 | var assert = require('assert-plus');
|
11 |
|
12 | var DEBUG = true;
|
13 | if (DEBUG) {
|
14 | var debug = console.warn;
|
15 | } else {
|
16 | var debug = function () {};
|
17 | }
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | function shallowCopy(obj) {
|
27 | if (!obj) {
|
28 | return (obj);
|
29 | }
|
30 | var copy = {};
|
31 | Object.keys(obj).forEach(function (k) {
|
32 | copy[k] = obj[k];
|
33 | });
|
34 | return (copy);
|
35 | }
|
36 |
|
37 |
|
38 | function space(n) {
|
39 | var s = '';
|
40 | for (var i = 0; i < n; i++) {
|
41 | s += ' ';
|
42 | }
|
43 | return s;
|
44 | }
|
45 |
|
46 |
|
47 | function makeIndent(arg, deflen, name) {
|
48 | if (arg === null || arg === undefined)
|
49 | return space(deflen);
|
50 | else if (typeof (arg) === 'number')
|
51 | return space(arg);
|
52 | else if (typeof (arg) === 'string')
|
53 | return arg;
|
54 | else
|
55 | assert.fail('invalid "' + name + '": not a string or number: ' + arg);
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | function textwrap(s, width) {
|
65 | var words = s.trim().split(/\s+/);
|
66 | var lines = [];
|
67 | var line = '';
|
68 | words.forEach(function (w) {
|
69 | var newLength = line.length + w.length;
|
70 | if (line.length > 0)
|
71 | newLength += 1;
|
72 | if (newLength > width) {
|
73 | lines.push(line);
|
74 | line = '';
|
75 | }
|
76 | if (line.length > 0)
|
77 | line += ' ';
|
78 | line += w;
|
79 | });
|
80 | lines.push(line);
|
81 | return lines;
|
82 | }
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | function optionKeyFromName(name) {
|
95 | return name.replace(/-/g, '_');
|
96 | }
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | function parseBool(option, optstr, arg) {
|
103 | return Boolean(arg);
|
104 | }
|
105 |
|
106 | function parseString(option, optstr, arg) {
|
107 | assert.string(arg, 'arg');
|
108 | return arg;
|
109 | }
|
110 |
|
111 | function parseNumber(option, optstr, arg) {
|
112 | assert.string(arg, 'arg');
|
113 | var num = Number(arg);
|
114 | if (isNaN(num)) {
|
115 | throw new Error(format('arg for "%s" is not a number: "%s"',
|
116 | optstr, arg));
|
117 | }
|
118 | return num;
|
119 | }
|
120 |
|
121 | function parseInteger(option, optstr, arg) {
|
122 | assert.string(arg, 'arg');
|
123 | var num = Number(arg);
|
124 | if (!/^[0-9-]+$/.test(arg) || isNaN(num)) {
|
125 | throw new Error(format('arg for "%s" is not an integer: "%s"',
|
126 | optstr, arg));
|
127 | }
|
128 | return num;
|
129 | }
|
130 |
|
131 | function parsePositiveInteger(option, optstr, arg) {
|
132 | assert.string(arg, 'arg');
|
133 | var num = Number(arg);
|
134 | if (!/^[0-9]+$/.test(arg) || isNaN(num)) {
|
135 | throw new Error(format('arg for "%s" is not a positive integer: "%s"',
|
136 | optstr, arg));
|
137 | }
|
138 | return num;
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 | function parseDate(option, optstr, arg) {
|
152 | assert.string(arg, 'arg');
|
153 | var date;
|
154 | if (/^\d+$/.test(arg)) {
|
155 |
|
156 | date = new Date(Number(arg) * 1000);
|
157 |
|
158 | } else if (/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?Z?)?$/i.test(arg)) {
|
159 |
|
160 | date = new Date(arg);
|
161 | } else {
|
162 | throw new Error(format('arg for "%s" is not a valid date format: "%s"',
|
163 | optstr, arg));
|
164 | }
|
165 | if (date.toString() === 'Invalid Date') {
|
166 | throw new Error(format('arg for "%s" is an invalid date: "%s"',
|
167 | optstr, arg));
|
168 | }
|
169 | return date;
|
170 | }
|
171 |
|
172 | var optionTypes = {
|
173 | bool: {
|
174 | takesArg: false,
|
175 | parseArg: parseBool
|
176 | },
|
177 | string: {
|
178 | takesArg: true,
|
179 | helpArg: 'ARG',
|
180 | parseArg: parseString
|
181 | },
|
182 | number: {
|
183 | takesArg: true,
|
184 | helpArg: 'NUM',
|
185 | parseArg: parseNumber
|
186 | },
|
187 | integer: {
|
188 | takesArg: true,
|
189 | helpArg: 'INT',
|
190 | parseArg: parseInteger
|
191 | },
|
192 | positiveInteger: {
|
193 | takesArg: true,
|
194 | helpArg: 'INT',
|
195 | parseArg: parsePositiveInteger
|
196 | },
|
197 | date: {
|
198 | takesArg: true,
|
199 | helpArg: 'DATE',
|
200 | parseArg: parseDate
|
201 | },
|
202 | arrayOfBool: {
|
203 | takesArg: false,
|
204 | array: true,
|
205 | parseArg: parseBool
|
206 | },
|
207 | arrayOfString: {
|
208 | takesArg: true,
|
209 | helpArg: 'ARG',
|
210 | array: true,
|
211 | parseArg: parseString
|
212 | },
|
213 | arrayOfNumber: {
|
214 | takesArg: true,
|
215 | helpArg: 'NUM',
|
216 | array: true,
|
217 | parseArg: parseNumber
|
218 | },
|
219 | arrayOfInteger: {
|
220 | takesArg: true,
|
221 | helpArg: 'INT',
|
222 | array: true,
|
223 | parseArg: parseInteger
|
224 | },
|
225 | arrayOfPositiveInteger: {
|
226 | takesArg: true,
|
227 | helpArg: 'INT',
|
228 | array: true,
|
229 | parseArg: parsePositiveInteger
|
230 | },
|
231 | arrayOfDate: {
|
232 | takesArg: true,
|
233 | helpArg: 'INT',
|
234 | array: true,
|
235 | parseArg: parseDate
|
236 | },
|
237 | };
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | function Parser(config) {
|
260 | assert.object(config, 'config');
|
261 | assert.arrayOfObject(config.options, 'config.options');
|
262 | assert.optionalBool(config.interspersed, 'config.interspersed');
|
263 | var self = this;
|
264 |
|
265 |
|
266 | this.interspersed = (config.interspersed !== undefined
|
267 | ? config.interspersed : true);
|
268 |
|
269 |
|
270 | this.allowUnknown = (config.allowUnknown !== undefined
|
271 | ? config.allowUnknown : false);
|
272 |
|
273 | this.options = config.options.map(function (o) { return shallowCopy(o); });
|
274 | this.optionFromName = {};
|
275 | this.optionFromEnv = {};
|
276 | for (var i = 0; i < this.options.length; i++) {
|
277 | var o = this.options[i];
|
278 | if (o.group !== undefined && o.group !== null) {
|
279 | assert.optionalString(o.group,
|
280 | format('config.options.%d.group', i));
|
281 | continue;
|
282 | }
|
283 | assert.ok(optionTypes[o.type],
|
284 | format('invalid config.options.%d.type: "%s" in %j',
|
285 | i, o.type, o));
|
286 | assert.optionalString(o.name, format('config.options.%d.name', i));
|
287 | assert.optionalArrayOfString(o.names,
|
288 | format('config.options.%d.names', i));
|
289 | assert.ok((o.name || o.names) && !(o.name && o.names),
|
290 | format('exactly one of "name" or "names" required: %j', o));
|
291 | assert.optionalString(o.help, format('config.options.%d.help', i));
|
292 | var env = o.env || [];
|
293 | if (typeof (env) === 'string') {
|
294 | env = [env];
|
295 | }
|
296 | assert.optionalArrayOfString(env, format('config.options.%d.env', i));
|
297 | assert.optionalString(o.helpGroup,
|
298 | format('config.options.%d.helpGroup', i));
|
299 | assert.optionalBool(o.helpWrap,
|
300 | format('config.options.%d.helpWrap', i));
|
301 |
|
302 | if (o.name) {
|
303 | o.names = [o.name];
|
304 | } else {
|
305 | assert.string(o.names[0],
|
306 | format('config.options.%d.names is empty', i));
|
307 | }
|
308 | o.key = optionKeyFromName(o.names[0]);
|
309 | o.names.forEach(function (n) {
|
310 | if (self.optionFromName[n]) {
|
311 | throw new Error(format(
|
312 | 'option name collision: "%s" used in %j and %j',
|
313 | n, self.optionFromName[n], o));
|
314 | }
|
315 | self.optionFromName[n] = o;
|
316 | });
|
317 | env.forEach(function (n) {
|
318 | if (self.optionFromEnv[n]) {
|
319 | throw new Error(format(
|
320 | 'option env collision: "%s" used in %j and %j',
|
321 | n, self.optionFromEnv[n], o));
|
322 | }
|
323 | self.optionFromEnv[n] = o;
|
324 | });
|
325 | }
|
326 | }
|
327 |
|
328 | Parser.prototype.optionTakesArg = function optionTakesArg(option) {
|
329 | return optionTypes[option.type].takesArg;
|
330 | };
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | Parser.prototype.parse = function parse(inputs) {
|
347 | var self = this;
|
348 |
|
349 |
|
350 | if (Array.isArray(arguments[0])) {
|
351 | inputs = {argv: arguments[0], slice: arguments[1]};
|
352 | }
|
353 |
|
354 | assert.optionalObject(inputs, 'inputs');
|
355 | if (!inputs) {
|
356 | inputs = {};
|
357 | }
|
358 | assert.optionalArrayOfString(inputs.argv, 'inputs.argv');
|
359 |
|
360 | var argv = inputs.argv || process.argv;
|
361 | var slice = inputs.slice !== undefined ? inputs.slice : 2;
|
362 | var args = argv.slice(slice);
|
363 | var env = inputs.env || process.env;
|
364 | var opts = {};
|
365 | var _order = [];
|
366 |
|
367 | function addOpt(option, optstr, key, val, from) {
|
368 | var type = optionTypes[option.type];
|
369 | var parsedVal = type.parseArg(option, optstr, val);
|
370 | if (type.array) {
|
371 | if (!opts[key]) {
|
372 | opts[key] = [];
|
373 | }
|
374 | opts[key].push(parsedVal);
|
375 | } else {
|
376 | opts[key] = parsedVal;
|
377 | }
|
378 | var item = { key: key, value: parsedVal, from: from };
|
379 | _order.push(item);
|
380 | }
|
381 |
|
382 |
|
383 | var _args = [];
|
384 | var i = 0;
|
385 | outer: while (i < args.length) {
|
386 | var arg = args[i];
|
387 |
|
388 |
|
389 | if (arg === '--') {
|
390 | i++;
|
391 | break;
|
392 |
|
393 |
|
394 | } else if (arg.slice(0, 2) === '--') {
|
395 | var name = arg.slice(2);
|
396 | var val = null;
|
397 | var idx = name.indexOf('=');
|
398 | if (idx !== -1) {
|
399 | val = name.slice(idx + 1);
|
400 | name = name.slice(0, idx);
|
401 | }
|
402 | var option = this.optionFromName[name];
|
403 | if (!option) {
|
404 | if (!this.allowUnknown)
|
405 | throw new Error(format('unknown option: "--%s"', name));
|
406 | else if (this.interspersed)
|
407 | _args.push(arg);
|
408 | else
|
409 | break outer;
|
410 | } else {
|
411 | var takesArg = this.optionTakesArg(option);
|
412 | if (val !== null && !takesArg) {
|
413 | throw new Error(format('argument given to "--%s" option '
|
414 | + 'that does not take one: "%s"', name, arg));
|
415 | }
|
416 | if (!takesArg) {
|
417 | addOpt(option, '--'+name, option.key, true, 'argv');
|
418 | } else if (val !== null) {
|
419 | addOpt(option, '--'+name, option.key, val, 'argv');
|
420 | } else if (i + 1 >= args.length) {
|
421 | throw new Error(format('do not have enough args for "--%s" '
|
422 | + 'option', name));
|
423 | } else {
|
424 | addOpt(option, '--'+name, option.key, args[i + 1], 'argv');
|
425 | i++;
|
426 | }
|
427 | }
|
428 |
|
429 |
|
430 | } else if (arg[0] === '-' && arg.length > 1) {
|
431 | var j = 1;
|
432 | var allFound = true;
|
433 | while (j < arg.length) {
|
434 | var name = arg[j];
|
435 |
|
436 | var option = this.optionFromName[name];
|
437 | if (!option) {
|
438 | allFound = false;
|
439 | if (this.allowUnknown) {
|
440 | if (this.interspersed) {
|
441 | _args.push(arg);
|
442 | break;
|
443 | } else
|
444 | break outer;
|
445 | } else if (arg.length > 2) {
|
446 | throw new Error(format(
|
447 | 'unknown option: "-%s" in "%s" group',
|
448 | name, arg));
|
449 | } else {
|
450 | throw new Error(format('unknown option: "-%s"', name));
|
451 | }
|
452 | } else if (this.optionTakesArg(option)) {
|
453 | break;
|
454 | }
|
455 | j++;
|
456 | }
|
457 |
|
458 | j = 1;
|
459 | while (allFound && j < arg.length) {
|
460 | var name = arg[j];
|
461 | var val = arg.slice(j + 1);
|
462 | var takesArg = this.optionTakesArg(option);
|
463 | var option = this.optionFromName[name];
|
464 | if (!takesArg) {
|
465 | addOpt(option, '-'+name, option.key, true, 'argv');
|
466 | } else if (val) {
|
467 | addOpt(option, '-'+name, option.key, val, 'argv');
|
468 | break;
|
469 | } else {
|
470 | if (i + 1 >= args.length) {
|
471 | throw new Error(format('do not have enough args '
|
472 | + 'for "-%s" option', name));
|
473 | }
|
474 | addOpt(option, '-'+name, option.key, args[i + 1], 'argv');
|
475 | i++;
|
476 | break;
|
477 | }
|
478 | j++;
|
479 | }
|
480 |
|
481 |
|
482 | } else if (this.interspersed) {
|
483 | _args.push(arg);
|
484 |
|
485 |
|
486 | } else {
|
487 | break outer;
|
488 | }
|
489 | i++;
|
490 | }
|
491 | _args = _args.concat(args.slice(i));
|
492 |
|
493 |
|
494 | Object.keys(this.optionFromEnv).forEach(function (envname) {
|
495 | var val = env[envname];
|
496 | if (val === undefined)
|
497 | return;
|
498 | var option = self.optionFromEnv[envname];
|
499 | if (opts[option.key] !== undefined)
|
500 | return;
|
501 | var takesArg = self.optionTakesArg(option);
|
502 | if (takesArg) {
|
503 | addOpt(option, envname, option.key, val, 'env');
|
504 | } else if (val !== '') {
|
505 |
|
506 |
|
507 |
|
508 |
|
509 | addOpt(option, envname, option.key, (val !== '0'), 'env');
|
510 | }
|
511 | });
|
512 |
|
513 |
|
514 | this.options.forEach(function (o) {
|
515 | if (o.default !== undefined && opts[o.key] === undefined) {
|
516 | opts[o.key] = o.default;
|
517 | }
|
518 | });
|
519 |
|
520 | opts._order = _order;
|
521 | opts._args = _args;
|
522 | return opts;
|
523 | };
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | Parser.prototype.help = function help(config) {
|
553 | config = config || {};
|
554 | assert.object(config, 'config');
|
555 |
|
556 | var indent = makeIndent(config.indent, 4, 'config.indent');
|
557 | var headingIndent = makeIndent(config.headingIndent,
|
558 | Math.round(indent.length / 2), 'config.headingIndent');
|
559 |
|
560 | assert.optionalString(config.nameSort, 'config.nameSort');
|
561 | var nameSort = config.nameSort || 'length';
|
562 | assert.ok(~['length', 'none'].indexOf(nameSort),
|
563 | 'invalid "config.nameSort"');
|
564 | assert.optionalNumber(config.maxCol, 'config.maxCol');
|
565 | assert.optionalNumber(config.maxHelpCol, 'config.maxHelpCol');
|
566 | assert.optionalNumber(config.minHelpCol, 'config.minHelpCol');
|
567 | assert.optionalNumber(config.helpCol, 'config.helpCol');
|
568 | assert.optionalBool(config.includeEnv, 'config.includeEnv');
|
569 | assert.optionalBool(config.helpWrap, 'config.helpWrap');
|
570 | var maxCol = config.maxCol || 80;
|
571 | var minHelpCol = config.minHelpCol || 20;
|
572 | var maxHelpCol = config.maxHelpCol || 40;
|
573 |
|
574 | var lines = [];
|
575 | var maxWidth = 0;
|
576 | this.options.forEach(function (o) {
|
577 | if (o.group !== undefined && o.group !== null) {
|
578 |
|
579 | lines.push(null);
|
580 | return;
|
581 | }
|
582 | var type = optionTypes[o.type];
|
583 | var arg = o.helpArg || type.helpArg || 'ARG';
|
584 | var line = '';
|
585 | var names = o.names.slice();
|
586 | if (nameSort === 'length') {
|
587 | names.sort(function (a, b) {
|
588 | if (a.length < b.length)
|
589 | return -1;
|
590 | else if (b.length < a.length)
|
591 | return 1;
|
592 | else
|
593 | return 0;
|
594 | })
|
595 | }
|
596 | names.forEach(function (name, i) {
|
597 | if (i > 0)
|
598 | line += ', ';
|
599 | if (name.length === 1) {
|
600 | line += '-' + name
|
601 | if (type.takesArg)
|
602 | line += ' ' + arg;
|
603 | } else {
|
604 | line += '--' + name
|
605 | if (type.takesArg)
|
606 | line += '=' + arg;
|
607 | }
|
608 | });
|
609 | maxWidth = Math.max(maxWidth, line.length);
|
610 | lines.push(line);
|
611 | });
|
612 |
|
613 |
|
614 | var helpCol = config.helpCol;
|
615 | if (!helpCol) {
|
616 | helpCol = maxWidth + indent.length + 2;
|
617 | helpCol = Math.min(Math.max(helpCol, minHelpCol), maxHelpCol);
|
618 | }
|
619 | this.options.forEach(function (o, i) {
|
620 | if (o.group !== undefined && o.group !== null) {
|
621 | if (o.group === '') {
|
622 |
|
623 |
|
624 | lines[i] = '';
|
625 | } else {
|
626 |
|
627 | lines[i] = (i === 0 ? '' : '\n') + headingIndent +
|
628 | o.group + ':';
|
629 | }
|
630 | return;
|
631 | }
|
632 |
|
633 | var line = lines[i] = indent + lines[i];
|
634 | if (!o.help && !(config.includeEnv && o.env)) {
|
635 | return;
|
636 | }
|
637 | var n = helpCol - line.length;
|
638 | if (n >= 0) {
|
639 | line += space(n);
|
640 | } else {
|
641 | line += '\n' + space(helpCol);
|
642 | }
|
643 |
|
644 | var helpEnv = '';
|
645 | if (o.env && o.env.length && config.includeEnv) {
|
646 | helpEnv += 'Environment: ';
|
647 | var type = optionTypes[o.type];
|
648 | var arg = o.helpArg || type.helpArg || 'ARG';
|
649 | var envs = (Array.isArray(o.env) ? o.env : [o.env]).map(
|
650 | function (e) {
|
651 | if (type.takesArg) {
|
652 | return e + '=' + arg;
|
653 | } else {
|
654 | return e + '=1';
|
655 | }
|
656 | }
|
657 | );
|
658 | helpEnv += envs.join(', ');
|
659 | }
|
660 | var help = (o.help || '').trim();
|
661 | if (o.helpWrap !== false && config.helpWrap !== false) {
|
662 |
|
663 | if (help.length && !~'.!?'.indexOf(help.slice(-1))) {
|
664 | help += '.';
|
665 | }
|
666 | if (help.length) {
|
667 | help += ' ';
|
668 | }
|
669 | help += helpEnv;
|
670 | line += textwrap(help, maxCol - helpCol).join(
|
671 | '\n' + space(helpCol));
|
672 | } else {
|
673 |
|
674 | var helpLines = help.split('\n').filter(
|
675 | function (ln) { return ln.length });
|
676 | if (helpEnv !== '') {
|
677 | helpLines.push(helpEnv);
|
678 | }
|
679 | line += helpLines.join('\n' + space(helpCol));
|
680 | }
|
681 |
|
682 | lines[i] = line;
|
683 | });
|
684 |
|
685 | var rv = '';
|
686 | if (lines.length > 0) {
|
687 | rv = lines.join('\n') + '\n';
|
688 | }
|
689 | return rv;
|
690 | };
|
691 |
|
692 |
|
693 |
|
694 |
|
695 |
|
696 | function createParser(config) {
|
697 | return new Parser(config);
|
698 | }
|
699 |
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 | function parse(config) {
|
708 | assert.object(config, 'config');
|
709 | assert.optionalArrayOfString(config.argv, 'config.argv');
|
710 | assert.optionalObject(config.env, 'config.env');
|
711 | var config = shallowCopy(config);
|
712 | var argv = config.argv;
|
713 | delete config.argv;
|
714 | var env = config.env;
|
715 | delete config.env;
|
716 |
|
717 | var parser = new Parser(config);
|
718 | return parser.parse({argv: argv, env: env});
|
719 | }
|
720 |
|
721 |
|
722 |
|
723 |
|
724 |
|
725 |
|
726 |
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 | function addOptionType(optionType) {
|
740 | assert.object(optionType, 'optionType');
|
741 | assert.string(optionType.name, 'optionType.name');
|
742 | assert.bool(optionType.takesArg, 'optionType.takesArg');
|
743 | if (optionType.takesArg) {
|
744 | assert.string(optionType.helpArg, 'optionType.helpArg');
|
745 | }
|
746 | assert.func(optionType.parseArg, 'optionType.parseArg');
|
747 | assert.optionalBool(optionType.array, 'optionType.array');
|
748 |
|
749 | optionTypes[optionType.name] = {
|
750 | takesArg: optionType.takesArg,
|
751 | helpArg: optionType.helpArg,
|
752 | parseArg: optionType.parseArg,
|
753 | array: optionType.array
|
754 | }
|
755 |
|
756 |
|
757 | }
|
758 |
|
759 | module.exports = {
|
760 | createParser: createParser,
|
761 | Parser: Parser,
|
762 | parse: parse,
|
763 | addOptionType: addOptionType,
|
764 |
|
765 |
|
766 |
|
767 | parseBool: parseBool,
|
768 | parseString: parseString,
|
769 | parseNumber: parseNumber,
|
770 | parseInteger: parseInteger,
|
771 | parsePositiveInteger: parsePositiveInteger,
|
772 | parseDate: parseDate
|
773 | };
|