Source: src/sprintf.js

"use strict";
var slice = [].slice;
/**
 * @fileOverview The sprintf for js script from Alexandru Marasteanu at diveintojavascript.com
 * @module ink/strings/sprintf
 *
 * @copyright Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com> All rights reserved.
 * @see http://www.diveintojavascript.com/projects/javascript-sprintf
 * @author Alexandru Marasteanu <hello at alexei dot ro>
 *
 */
var sprintf = ( function() {
	function get_type( variable ) {
		return Object.prototype.toString.call( variable ).slice( 8, -1 ).toLowerCase();
	}

	function str_repeat( input, multiplier ) {
		for ( var output = []; multiplier > 0; output[ --multiplier ] = input ) {/* do nothing */
		}
		//noinspection JSHint
		return output.join( '' );
	}

	var str_format = function() {
		if ( !str_format.cache.hasOwnProperty( arguments[ 0 ] ) ) {
			str_format.cache[ arguments[ 0 ] ] = str_format.parse( arguments[ 0 ] );
		}
		return str_format.format.call( null, str_format.cache[ arguments[ 0 ] ], arguments );
	};

	str_format.format = function( parse_tree, argv ) {
		var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
		for ( i = 0; i < tree_length; i++ ) {
			node_type = get_type( parse_tree[ i ] );
			if ( node_type === 'string' ) {
				output.push( parse_tree[ i ] );
			} else if ( node_type === 'array' ) {
				match = parse_tree[ i ]; // convenience purposes only
				if ( match[ 2 ] ) { // keyword argument
					arg = argv[ cursor ];
					for ( k = 0; k < match[ 2 ].length; k++ ) {
						if ( !arg.hasOwnProperty( match[ 2 ][ k ] ) ) { throw ( sprintf(
								'[sprintf] property "%s" does not exist', match[ 2 ][ k ] ) ); }
						arg = arg[ match[ 2 ][ k ] ];
					}
				} else if ( match[ 1 ] ) { // positional argument (explicit)
					arg = argv[ match[ 1 ] ];
				} else { // positional argument (implicit)
					arg = argv[ cursor++ ];
				}

				if ( /[^s]/.test( match[ 8 ] ) && ( get_type( arg ) !== 'number' ) ) { throw ( sprintf(
						'[sprintf] expecting number but found %s', get_type( arg ) ) ); }
				switch ( match[ 8 ] ) {
					case 'b':
						arg = arg.toString( 2 );
						break;
					case 'c':
						arg = String.fromCharCode( arg );
						break;
					case 'd':
						arg = parseInt( arg, 10 );
						break;
					case 'e':
						arg = match[ 7 ] ? arg.toExponential( match[ 7 ] ) : arg.toExponential();
						break;
					case 'f':
						arg = match[ 7 ] ? parseFloat( arg ).toFixed( match[ 7 ] ) : parseFloat( arg );
						break;
					case 'o':
						arg = arg.toString( 8 );
						break;
					case 's':
						arg = ( ( arg = String( arg ) ) && match[ 7 ] ? arg.substring( 0, match[ 7 ] ) : arg );
						break;
					case 'u':
						arg = Math.abs( arg );
						break;
					case 'x':
						arg = arg.toString( 16 );
						break;
					case 'X':
						arg = arg.toString( 16 ).toUpperCase();
						break;
				}
				arg = ( /[def]/.test( match[ 8 ] ) && match[ 3 ] && arg >= 0 ? '+' + arg : arg );
				pad_character = match[ 4 ] ? match[ 4 ] === '0' ? '0' : match[ 4 ].charAt( 1 ) : ' ';
				pad_length = match[ 6 ] - String( arg ).length;
				pad = match[ 6 ] ? str_repeat( pad_character, pad_length ) : '';
				output.push( match[ 5 ] ? arg + pad : pad + arg );
			}
		}
		return output.join( '' );
	};

	str_format.cache = {};

	str_format.parse = function( fmt ) {
		var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
		while ( _fmt ) {
			if ( ( match = /^[^\x25]+/.exec( _fmt ) ) !== null ) {
				parse_tree.push( match[ 0 ] );
			} else if ( ( match = /^\x25{2}/.exec( _fmt ) ) !== null ) {
				parse_tree.push( '%' );
			} else if ( ( match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/
					.exec( _fmt ) ) !== null ) {
				if ( match[ 2 ] ) { // noinspection JSDeclarationsAtScopeStart
					{

						var field_list = [], replacement_field = match[ 2 ], field_match = [];
						if ( ( field_match = /^([a-z_][a-z_\d]*)/i.exec( replacement_field ) ) !== null ) {
							field_list.push( field_match[ 1 ] );
							while ( ( replacement_field = replacement_field.substring( field_match[ 0 ].length ) ) !== '' ) {
								if ( ( field_match = /^\.([a-z_][a-z_\d]*)/i.exec( replacement_field ) ) !== null ) {
									field_list.push( field_match[ 1 ] );
								} else if ( ( field_match = /^\[(\d+)\]/.exec( replacement_field ) ) !== null ) {
									field_list.push( field_match[ 1 ] );
								} else {
									throw ( '[sprintf] huh?' );
								}
							}
						} else {
							throw ( '[sprintf] huh?' );
						}
						match[ 2 ] = field_list;
					}
				} else {
					//noinspection JSHint
					arg_names |= 2;
				}
				if ( arg_names === 3 ) { throw ( '[sprintf] mixing positional and named placeholders is not (yet) supported' ); }
				parse_tree.push( match );
			} else {
				throw ( '[sprintf] huh?' );
			}
			_fmt = _fmt.substring( match[ 0 ].length );
		}
		return parse_tree;
	};

	return str_format;
} )();

var vsprintf = function( fmt, argv ) {
	argv.unshift( fmt );
	return sprintf.apply( null, argv );
};
/**
 * sprintf() for JavaScript 0.7-beta1 sprintf() for JavaScript is a complete open source JavaScript sprintf
 * implementation.
 *
 * It's prototype is simple:
 *
 *      string sprintf(string format , [mixed arg1 [, mixed arg2 [ ,...]]]);
 *
 * The placeholders in the format string are marked by "%" and are followed by one or more of these elements, in this order:
 *
 * + An optional "+" sign that forces to preceed the result with a plus or minus sign on numeric values. By default,
 * only the "-" sign is used on negative numbers.
 * + An optional padding specifier that says what character to use for padding (if specified). Possible values are 0
 * or any other character
 * precedeed by a '. The default is to pad with spaces.
 * + An optional "-" sign, that causes sprintf to left-align the result of this placeholder. The default is to
 * right-align the result.
 * + An optional number, that says how many characters the result should have. If the value to be returned is shorter
 * than this number, the result will be padded.
 * + An optional precision modifier, consisting of a "." (dot) followed by a number, that says how many digits should
 * be displayed for floating point numbers. When used on a string, it causes the result to be truncated.
 * + A type specifier that can be any of:
 *
 *      + % — print a literal "%" character
 *      + b — print an integer as a binary number
 *      + c — print an integer as the character with that ASCII value
 *      + d — print an integer as a signed decimal number
 *      + e — print a float as scientific notation
 *      + u — print an integer as an unsigned decimal number
 *      + f — print a float as is
 *      + o — print an integer as an octal number
 *      + s — print a string as is
 *      + x — print an integer as a hexadecimal number (lower-case)
 *      + X — print an integer as a hexadecimal number (upper-case)
 *

 * You can also swap the arguments. That is, the order of the placeholders doesn't have to match the order of the
 * arguments. You can do that by simply indicating in the format string which arguments the placeholders refer to:

 *      sprintf('%2$s %3$s a %1$s', 'cracker', 'Polly', 'wants');
 *
 * And, of course, you can repeat the placeholders without having to increase the number of arguments.
 *
 *
 * Format strings may contain replacement fields rather than positional placeholders. Instead of referring to a certain
 * argument, you can now refer to a certain key within an object. Replacement fields are surrounded by rounded
 * parentheses () and begin with a keyword that refers to a key:

 *      var user = {
 *          name : 'Dolly'
 *      };
 *      sprintf( 'Hello %(name)s', user ); // Hello Dolly

 * Keywords in replacement fields can be optionally followed by any number of keywords or indexes:

 *      var users = [ {
            name : 'Dolly'
 *      }, {
            name : 'Molly'
 *      }, {
 *          name : 'Polly'
*       } ];
 *
*       sprintf( 'Hello %(users[0].name)s, %(users[1].name)s and %(users[2].name)s', {
			users : users
 *      } ); // Hello Dolly, Molly and Polly

    @param {string}
 *            fmt The format string
 * @param {...*} argv The formatters
 * @returns {string}
 * @method
 * @author Alexandru Marasteanu
 * @copyright Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>. All rights reserved.
 * @note mixing positional and named placeholders is not (yet) supported
 * @see http://www.diveintojavascript.com/projects/javascript-sprintf
 * @example
 *      var strings = require("ink-strings");
 *      strings.sprintf( "Hello %1s, it is me, %2s", "Doctor", "Dalek Caan" );
 *      -> "Hello Doctor, it is me, Dalek Caan"
 *
 *      strings.sprintf( "%1b", 24 );
 *      -> "11000"
 *
 *      strings.sprintf( "%1c", 74 );
 *      -> "J"
 *
 *      strings.sprintf( "%1d", 40 );
 *      -> "40"
 *
 *      strings.sprintf( "%1d", 40 * -1 );
 *      -> "-40"
 *
 *      strings.sprintf( "%1e", 40 * 1000000 );
 *      -> "4e+7"
 *
 *      strings.sprintf( "%1u", 40 * -1 );
 *      -> "40"
 *
 *      strings.sprintf( "%1f", 40.23498765 );
 *      -> "40.23498765"
 *
 *      strings.sprintf( "%1o", 24 );
 *      -> "30"
 *
 *      strings.sprintf( "%(name)s %(occupation)s", {
			name       : "doctor",
			occupation : "timelord"
		} );
        -> "doctor timelord"

 *
 */
exports.sprintf = sprintf;
/**
 * vsprintf() is the same as sprintf() except that it accepts an array of arguments, rather than a variable number of
 * arguments
 * @method
 * @author Alexandru Marasteanu
 * @copyright Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>. All rights reserved.
 * @param {string}
 *            fmt The format string
 * @param {array.<string>} argv The formatters
 * @returns {string}
 */
exports.vsprintf = vsprintf;

See NOTICE.md for licenses
ink-strings Copyright contributers to ink-strings, base64, Helma, RingoJS, sprintf projects.
Documentation generated by JSDoc 3.2.0-dev on Sun Jun 09 2013 11:56:07 GMT-0400 (EDT) using the DocStrap template.