You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
7.2 KiB
217 lines
7.2 KiB
/** |
|
* Copyright (c) 2007-2016 Kevin van Zonneveld (https://kvz.io) |
|
* and Contributors (https://locutus.io/authors) |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
* this software and associated documentation files (the "Software"), to deal in |
|
* the Software without restriction, including without limitation the rights to |
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
|
* of the Software, and to permit persons to whom the Software is furnished to do |
|
* so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included in all |
|
* copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
* SOFTWARE. |
|
*/ |
|
function sprintf () { |
|
// discuss at: https://locutus.io/php/sprintf/ |
|
// original by: Ash Searle (https://hexmen.com/blog/) |
|
// improved by: Michael White (https://getsprink.com) |
|
// improved by: Jack |
|
// improved by: Kevin van Zonneveld (https://kvz.io) |
|
// improved by: Kevin van Zonneveld (https://kvz.io) |
|
// improved by: Kevin van Zonneveld (https://kvz.io) |
|
// improved by: Dj |
|
// improved by: Allidylls |
|
// input by: Paulo Freitas |
|
// input by: Brett Zamir (https://brett-zamir.me) |
|
// improved by: Rafał Kukawski (https://kukawski.pl) |
|
// example 1: sprintf("%01.2f", 123.1) |
|
// returns 1: '123.10' |
|
// example 2: sprintf("[%10s]", 'monkey') |
|
// returns 2: '[ monkey]' |
|
// example 3: sprintf("[%'#10s]", 'monkey') |
|
// returns 3: '[####monkey]' |
|
// example 4: sprintf("%d", 123456789012345) |
|
// returns 4: '123456789012345' |
|
// example 5: sprintf('%-03s', 'E') |
|
// returns 5: 'E00' |
|
// example 6: sprintf('%+010d', 9) |
|
// returns 6: '+000000009' |
|
// example 7: sprintf('%+0\'@10d', 9) |
|
// returns 7: '@@@@@@@@+9' |
|
// example 8: sprintf('%.f', 3.14) |
|
// returns 8: '3.140000' |
|
// example 9: sprintf('%% %2$d', 1, 2) |
|
// returns 9: '% 2' |
|
|
|
var regex = /%%|%(?:(\d+)\$)?((?:[-+#0 ]|'[\s\S])*)(\d+)?(?:\.(\d*))?([\s\S])/g |
|
var args = arguments |
|
var i = 0 |
|
var format = args[i++] |
|
|
|
var _pad = function (str, len, chr, leftJustify) { |
|
if (!chr) { |
|
chr = ' ' |
|
} |
|
var padding = (str.length >= len) ? '' : new Array(1 + len - str.length >>> 0).join(chr) |
|
return leftJustify ? str + padding : padding + str |
|
} |
|
|
|
var justify = function (value, prefix, leftJustify, minWidth, padChar) { |
|
var diff = minWidth - value.length |
|
if (diff > 0) { |
|
// when padding with zeros |
|
// on the left side |
|
// keep sign (+ or -) in front |
|
if (!leftJustify && padChar === '0') { |
|
value = [ |
|
value.slice(0, prefix.length), |
|
_pad('', diff, '0', true), |
|
value.slice(prefix.length) |
|
].join('') |
|
} else { |
|
value = _pad(value, minWidth, padChar, leftJustify) |
|
} |
|
} |
|
return value |
|
} |
|
|
|
var _formatBaseX = function (value, base, leftJustify, minWidth, precision, padChar) { |
|
// Note: casts negative numbers to positive ones |
|
var number = value >>> 0 |
|
value = _pad(number.toString(base), precision || 0, '0', false) |
|
return justify(value, '', leftJustify, minWidth, padChar) |
|
} |
|
|
|
// _formatString() |
|
var _formatString = function (value, leftJustify, minWidth, precision, customPadChar) { |
|
if (precision !== null && precision !== undefined) { |
|
value = value.slice(0, precision) |
|
} |
|
return justify(value, '', leftJustify, minWidth, customPadChar) |
|
} |
|
|
|
// doFormat() |
|
var doFormat = function (substring, argIndex, modifiers, minWidth, precision, specifier) { |
|
var number, prefix, method, textTransform, value |
|
|
|
if (substring === '%%') { |
|
return '%' |
|
} |
|
|
|
// parse modifiers |
|
var padChar = ' ' // pad with spaces by default |
|
var leftJustify = false |
|
var positiveNumberPrefix = '' |
|
var j, l |
|
|
|
for (j = 0, l = modifiers.length; j < l; j++) { |
|
switch (modifiers.charAt(j)) { |
|
case ' ': |
|
case '0': |
|
padChar = modifiers.charAt(j) |
|
break |
|
case '+': |
|
positiveNumberPrefix = '+' |
|
break |
|
case '-': |
|
leftJustify = true |
|
break |
|
case "'": |
|
if (j + 1 < l) { |
|
padChar = modifiers.charAt(j + 1) |
|
j++ |
|
} |
|
break |
|
} |
|
} |
|
|
|
if (!minWidth) { |
|
minWidth = 0 |
|
} else { |
|
minWidth = +minWidth |
|
} |
|
|
|
if (!isFinite(minWidth)) { |
|
throw new Error('Width must be finite') |
|
} |
|
|
|
if (!precision) { |
|
precision = (specifier === 'd') ? 0 : 'fFeE'.indexOf(specifier) > -1 ? 6 : undefined |
|
} else { |
|
precision = +precision |
|
} |
|
|
|
if (argIndex && +argIndex === 0) { |
|
throw new Error('Argument number must be greater than zero') |
|
} |
|
|
|
if (argIndex && +argIndex >= args.length) { |
|
throw new Error('Too few arguments') |
|
} |
|
|
|
value = argIndex ? args[+argIndex] : args[i++] |
|
|
|
switch (specifier) { |
|
case '%': |
|
return '%' |
|
case 's': |
|
return _formatString(value + '', leftJustify, minWidth, precision, padChar) |
|
case 'c': |
|
return _formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, padChar) |
|
case 'b': |
|
return _formatBaseX(value, 2, leftJustify, minWidth, precision, padChar) |
|
case 'o': |
|
return _formatBaseX(value, 8, leftJustify, minWidth, precision, padChar) |
|
case 'x': |
|
return _formatBaseX(value, 16, leftJustify, minWidth, precision, padChar) |
|
case 'X': |
|
return _formatBaseX(value, 16, leftJustify, minWidth, precision, padChar) |
|
.toUpperCase() |
|
case 'u': |
|
return _formatBaseX(value, 10, leftJustify, minWidth, precision, padChar) |
|
case 'i': |
|
case 'd': |
|
number = +value || 0 |
|
// Plain Math.round doesn't just truncate |
|
number = Math.round(number - number % 1) |
|
prefix = number < 0 ? '-' : positiveNumberPrefix |
|
value = prefix + _pad(String(Math.abs(number)), precision, '0', false) |
|
|
|
if (leftJustify && padChar === '0') { |
|
// can't right-pad 0s on integers |
|
padChar = ' ' |
|
} |
|
return justify(value, prefix, leftJustify, minWidth, padChar) |
|
case 'e': |
|
case 'E': |
|
case 'f': // @todo: Should handle locales (as per setlocale) |
|
case 'F': |
|
case 'g': |
|
case 'G': |
|
number = +value |
|
prefix = number < 0 ? '-' : positiveNumberPrefix |
|
method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(specifier.toLowerCase())] |
|
textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(specifier) % 2] |
|
value = prefix + Math.abs(number)[method](precision) |
|
return justify(value, prefix, leftJustify, minWidth, padChar)[textTransform]() |
|
default: |
|
// unknown specifier, consume that char and return empty |
|
return '' |
|
} |
|
} |
|
|
|
try { |
|
return format.replace(regex, doFormat) |
|
} catch (err) { |
|
return false |
|
} |
|
}
|
|
|