222 lines
8.0 KiB
JavaScript
222 lines
8.0 KiB
JavaScript
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Implementation of sprintf-like, python-%-operator-like,
|
|
* .NET-String.Format-like functionality. Uses JS string's replace method to
|
|
* extract format specifiers and sends those specifiers to a handler function,
|
|
* which then, based on conversion type part of the specifier, calls the
|
|
* appropriate function to handle the specific conversion.
|
|
* For specific functionality implemented, look at formatRe below, or look
|
|
* at the tests.
|
|
*/
|
|
|
|
goog.provide('goog.string.format');
|
|
|
|
goog.require('goog.string');
|
|
|
|
|
|
/**
|
|
* Performs sprintf-like conversion, i.e. puts the values in a template.
|
|
* DO NOT use it instead of built-in conversions in simple cases such as
|
|
* 'Cost: %.2f' as it would introduce unnecessary latency opposed to
|
|
* 'Cost: ' + cost.toFixed(2).
|
|
* @param {string} formatString Template string containing % specifiers.
|
|
* @param {...string|number} var_args Values formatString is to be filled with.
|
|
* @return {string} Formatted string.
|
|
*/
|
|
goog.string.format = function(formatString, var_args) {
|
|
|
|
// Convert the arguments to an array (MDC recommended way).
|
|
var args = Array.prototype.slice.call(arguments);
|
|
|
|
// Try to get the template.
|
|
var template = args.shift();
|
|
if (typeof template == 'undefined') {
|
|
throw Error('[goog.string.format] Template required');
|
|
}
|
|
|
|
// This re is used for matching, it also defines what is supported.
|
|
var formatRe = /%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g;
|
|
|
|
/**
|
|
* Chooses which conversion function to call based on type conversion
|
|
* specifier.
|
|
* @param {string} match Contains the re matched string.
|
|
* @param {string} flags Formatting flags.
|
|
* @param {string} width Replacement string minimum width.
|
|
* @param {string} dotp Matched precision including a dot.
|
|
* @param {string} precision Specifies floating point precision.
|
|
* @param {string} type Type conversion specifier.
|
|
* @param {string} offset Matching location in the original string.
|
|
* @param {string} wholeString Has the actualString being searched.
|
|
* @return {string} Formatted parameter.
|
|
*/
|
|
function replacerDemuxer(
|
|
match, flags, width, dotp, precision, type, offset, wholeString) {
|
|
// The % is too simple and doesn't take an argument.
|
|
if (type == '%') {
|
|
return '%';
|
|
}
|
|
|
|
// Try to get the actual value from parent function.
|
|
var value = args.shift();
|
|
|
|
// If we didn't get any arguments, fail.
|
|
if (typeof value == 'undefined') {
|
|
throw Error('[goog.string.format] Not enough arguments');
|
|
}
|
|
|
|
// Patch the value argument to the beginning of our type specific call.
|
|
arguments[0] = value;
|
|
|
|
return goog.string.format.demuxes_[type].apply(null, arguments);
|
|
}
|
|
|
|
return template.replace(formatRe, replacerDemuxer);
|
|
};
|
|
|
|
|
|
/**
|
|
* Contains various conversion functions (to be filled in later on).
|
|
* @private {!Object}
|
|
*/
|
|
goog.string.format.demuxes_ = {};
|
|
|
|
|
|
/**
|
|
* Processes %s conversion specifier.
|
|
* @param {string} value Contains the formatRe matched string.
|
|
* @param {string} flags Formatting flags.
|
|
* @param {string} width Replacement string minimum width.
|
|
* @param {string} dotp Matched precision including a dot.
|
|
* @param {string} precision Specifies floating point precision.
|
|
* @param {string} type Type conversion specifier.
|
|
* @param {string} offset Matching location in the original string.
|
|
* @param {string} wholeString Has the actualString being searched.
|
|
* @return {string} Replacement string.
|
|
*/
|
|
goog.string.format.demuxes_['s'] = function(
|
|
value, flags, width, dotp, precision, type, offset, wholeString) {
|
|
var replacement = value;
|
|
// If no padding is necessary we're done.
|
|
// The check for '' is necessary because Firefox incorrectly provides the
|
|
// empty string instead of undefined for non-participating capture groups,
|
|
// and isNaN('') == false.
|
|
if (isNaN(width) || width == '' || replacement.length >= Number(width)) {
|
|
return replacement;
|
|
}
|
|
|
|
// Otherwise we should find out where to put spaces.
|
|
if (flags.indexOf('-', 0) > -1) {
|
|
replacement = replacement +
|
|
goog.string.repeat(' ', Number(width) - replacement.length);
|
|
} else {
|
|
replacement = goog.string.repeat(' ', Number(width) - replacement.length) +
|
|
replacement;
|
|
}
|
|
return replacement;
|
|
};
|
|
|
|
|
|
/**
|
|
* Processes %f conversion specifier.
|
|
* @param {string} value Contains the formatRe matched string.
|
|
* @param {string} flags Formatting flags.
|
|
* @param {string} width Replacement string minimum width.
|
|
* @param {string} dotp Matched precision including a dot.
|
|
* @param {string} precision Specifies floating point precision.
|
|
* @param {string} type Type conversion specifier.
|
|
* @param {string} offset Matching location in the original string.
|
|
* @param {string} wholeString Has the actualString being searched.
|
|
* @return {string} Replacement string.
|
|
*/
|
|
goog.string.format.demuxes_['f'] = function(
|
|
value, flags, width, dotp, precision, type, offset, wholeString) {
|
|
|
|
var replacement = value.toString();
|
|
|
|
// The check for '' is necessary because Firefox incorrectly provides the
|
|
// empty string instead of undefined for non-participating capture groups,
|
|
// and isNaN('') == false.
|
|
if (!(isNaN(precision) || precision == '')) {
|
|
replacement = parseFloat(value).toFixed(precision);
|
|
}
|
|
|
|
// Generates sign string that will be attached to the replacement.
|
|
var sign;
|
|
if (Number(value) < 0) {
|
|
sign = '-';
|
|
} else if (flags.indexOf('+') >= 0) {
|
|
sign = '+';
|
|
} else if (flags.indexOf(' ') >= 0) {
|
|
sign = ' ';
|
|
} else {
|
|
sign = '';
|
|
}
|
|
|
|
if (Number(value) >= 0) {
|
|
replacement = sign + replacement;
|
|
}
|
|
|
|
// If no padding is necessary we're done.
|
|
if (isNaN(width) || replacement.length >= Number(width)) {
|
|
return replacement;
|
|
}
|
|
|
|
// We need a clean signless replacement to start with
|
|
replacement = isNaN(precision) ? Math.abs(Number(value)).toString() :
|
|
Math.abs(Number(value)).toFixed(precision);
|
|
|
|
var padCount = Number(width) - replacement.length - sign.length;
|
|
|
|
// Find out which side to pad, and if it's left side, then which character to
|
|
// pad, and set the sign on the left and padding in the middle.
|
|
if (flags.indexOf('-', 0) >= 0) {
|
|
replacement = sign + replacement + goog.string.repeat(' ', padCount);
|
|
} else {
|
|
// Decides which character to pad.
|
|
var paddingChar = (flags.indexOf('0', 0) >= 0) ? '0' : ' ';
|
|
replacement =
|
|
sign + goog.string.repeat(paddingChar, padCount) + replacement;
|
|
}
|
|
|
|
return replacement;
|
|
};
|
|
|
|
|
|
/**
|
|
* Processes %d conversion specifier.
|
|
* @param {string} value Contains the formatRe matched string.
|
|
* @param {string} flags Formatting flags.
|
|
* @param {string} width Replacement string minimum width.
|
|
* @param {string} dotp Matched precision including a dot.
|
|
* @param {string} precision Specifies floating point precision.
|
|
* @param {string} type Type conversion specifier.
|
|
* @param {string} offset Matching location in the original string.
|
|
* @param {string} wholeString Has the actualString being searched.
|
|
* @return {string} Replacement string.
|
|
*/
|
|
goog.string.format.demuxes_['d'] = function(
|
|
value, flags, width, dotp, precision, type, offset, wholeString) {
|
|
return goog.string.format.demuxes_['f'](
|
|
parseInt(value, 10) /* value */, flags, width, dotp, 0 /* precision */,
|
|
type, offset, wholeString);
|
|
};
|
|
|
|
|
|
// These are additional aliases, for integer conversion.
|
|
goog.string.format.demuxes_['i'] = goog.string.format.demuxes_['d'];
|
|
goog.string.format.demuxes_['u'] = goog.string.format.demuxes_['d'];
|