From d2ba92f23eb124afe62b31b5c86f9f826d46be2e Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Wed, 13 Sep 2017 12:21:20 +0200 Subject: [PATCH] Update sprintf.js to 1.1.1 --- jed.js | 341 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 190 insertions(+), 151 deletions(-) diff --git a/jed.js b/jed.js index bda163b..106d626 100644 --- a/jed.js +++ b/jed.js @@ -328,184 +328,223 @@ in order to offer easy upgrades -- jsgettext.berlios.de // We _slightly_ modify the normal sprintf behavior to more gracefully handle // undefined values. - - /** - sprintf() for JavaScript 0.7-beta1 - http://www.diveintojavascript.com/projects/javascript-sprintf - - Copyright (c) Alexandru Marasteanu - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of sprintf() for JavaScript nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - var sprintf = (function() { - function get_type(variable) { - return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); + !function() { + /*! sprintf-js v1.1.1 | Copyright (c) 2007-present, Alexandru Mărășteanu | BSD-3-Clause */ + /*! https://github.com/alexei/sprintf.js */ + var re = { + not_string: /[^s]/, + not_bool: /[^t]/, + not_type: /[^T]/, + not_primitive: /[^v]/, + number: /[diefg]/, + numeric_arg: /[bcdiefguxX]/, + json: /[j]/, + not_json: /[^j]/, + text: /^[^\x25]+/, + modulo: /^\x25{2}/, + placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/, + key: /^([a-z_][a-z_\d]*)/i, + key_access: /^\.([a-z_][a-z_\d]*)/i, + index_access: /^\[(\d+)\]/, + sign: /^[\+\-]/ } - function str_repeat(input, multiplier) { - for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} - return output.join(''); + + function sprintf(key) { + // `arguments` is not an array, but should be fine for this call + return sprintf_format(sprintf_parse(key), arguments) } - 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); - }; + function vsprintf(fmt, argv) { + return sprintf.apply(null, [fmt].concat(argv || [])) + } - 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]]; + function sprintf_format(parse_tree, argv) { + var cursor = 1, tree_length = parse_tree.length, arg, output = '', i, k, match, pad, pad_character, pad_length, is_positive, sign + for (i = 0; i < tree_length; i++) { + if (typeof parse_tree[i] === 'string') { + output += parse_tree[i] } - } - else if (match[1]) { // positional argument (explicit) - arg = argv[match[1]]; - } - else { // positional argument (implicit) - arg = argv[cursor++]; - } + else if (Array.isArray(parse_tree[i])) { + 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 new Error(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))); - } + if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && arg instanceof Function) { + arg = arg() + } - // Jed EDIT - if ( typeof arg == 'undefined' || arg === null ) { - arg = ''; - } - // Jed EDIT - - 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(''); - }; + if (re.numeric_arg.test(match[8]) && (typeof arg !== 'number' && isNaN(arg))) { + throw new TypeError(sprintf('[sprintf] expecting number but found %T', arg)) + } - str_format.cache = {}; + if (re.number.test(match[8])) { + is_positive = arg >= 0 + } - 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]); + // Jed EDIT + if ( typeof arg == 'undefined' || arg === null ) { + arg = ''; + } + // Jed EDIT + + switch (match[8]) { + case 'b': + arg = parseInt(arg, 10).toString(2) + break + case 'c': + arg = String.fromCharCode(parseInt(arg, 10)) + break + case 'd': + case 'i': + arg = parseInt(arg, 10) + break + case 'j': + arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0) + break + case 'e': + arg = match[7] ? parseFloat(arg).toExponential(match[7]) : parseFloat(arg).toExponential() + break + case 'f': + arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg) + break + case 'g': + arg = match[7] ? String(Number(arg.toPrecision(match[7]))) : parseFloat(arg) + break + case 'o': + arg = (parseInt(arg, 10) >>> 0).toString(8) + break + case 's': + arg = String(arg) + arg = (match[7] ? arg.substring(0, match[7]) : arg) + break + case 't': + arg = String(!!arg) + arg = (match[7] ? arg.substring(0, match[7]) : arg) + break + case 'T': + arg = Object.prototype.toString.call(arg).slice(8, -1).toLowerCase() + arg = (match[7] ? arg.substring(0, match[7]) : arg) + break + case 'u': + arg = parseInt(arg, 10) >>> 0 + break + case 'v': + arg = arg.valueOf() + arg = (match[7] ? arg.substring(0, match[7]) : arg) + break + case 'x': + arg = (parseInt(arg, 10) >>> 0).toString(16) + break + case 'X': + arg = (parseInt(arg, 10) >>> 0).toString(16).toUpperCase() + break + } + if (re.json.test(match[8])) { + output += arg + } + else { + if (re.number.test(match[8]) && (!is_positive || match[3])) { + sign = is_positive ? '+' : '-' + arg = arg.toString().replace(re.sign, '') + } + else { + sign = '' + } + pad_character = match[4] ? match[4] === '0' ? '0' : match[4].charAt(1) : ' ' + pad_length = match[6] - (sign + arg).length + pad = match[6] ? (pad_length > 0 ? pad_character.repeat(pad_length) : '') : '' + output += match[5] ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg) + } + } } - else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { - parse_tree.push('%'); + return output + } + + var sprintf_cache = Object.create(null) + + function sprintf_parse(fmt) { + if (sprintf_cache[fmt]) { + return sprintf_cache[fmt] } - else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) { - if (match[2]) { - arg_names |= 1; - 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]); + + var _fmt = fmt, match, parse_tree = [], arg_names = 0 + while (_fmt) { + if ((match = re.text.exec(_fmt)) !== null) { + parse_tree.push(match[0]) + } + else if ((match = re.modulo.exec(_fmt)) !== null) { + parse_tree.push('%') + } + else if ((match = re.placeholder.exec(_fmt)) !== null) { + if (match[2]) { + arg_names |= 1 + var field_list = [], replacement_field = match[2], field_match = [] + if ((field_match = re.key.exec(replacement_field)) !== null) { + field_list.push(field_match[1]) + while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { + if ((field_match = re.key_access.exec(replacement_field)) !== null) { + field_list.push(field_match[1]) + } + else if ((field_match = re.index_access.exec(replacement_field)) !== null) { + field_list.push(field_match[1]) + } + else { + throw new SyntaxError('[sprintf] failed to parse named argument key') + } + } + } + else { + throw new SyntaxError('[sprintf] failed to parse named argument key') + } + match[2] = field_list } else { - throw('[sprintf] huh?'); + arg_names |= 2 + } + if (arg_names === 3) { + throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported') } - } + parse_tree.push(match) } else { - throw('[sprintf] huh?'); + throw new SyntaxError('[sprintf] unexpected placeholder') } - match[2] = field_list; - } - else { - 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) } - _fmt = _fmt.substring(match[0].length); + return sprintf_cache[fmt] = parse_tree + } + + Jed.sprintf = function ( fmt, args ) { + if ( {}.toString.call( args ) == '[object Array]' ) { + return vsprintf( fmt, [].slice.call(args) ); } - return parse_tree; + return sprintf.apply(this, [].slice.call(arguments) ); }; - return str_format; - })(); - - var vsprintf = function(fmt, argv) { - argv.unshift(fmt); - return sprintf.apply(null, argv); - }; + Jed.prototype.sprintf = function () { + return Jed.sprintf.apply(this, arguments); + }; + }(); + // END sprintf Implementation Jed.parse_plural = function ( plural_forms, n ) { plural_forms = plural_forms.replace(/n/g, n); return Jed.parse_expression(plural_forms); }; - Jed.sprintf = function ( fmt, args ) { - if ( {}.toString.call( args ) == '[object Array]' ) { - return vsprintf( fmt, [].slice.call(args) ); - } - return sprintf.apply(this, [].slice.call(arguments) ); - }; - - Jed.prototype.sprintf = function () { - return Jed.sprintf.apply(this, arguments); - }; - // END sprintf Implementation - // Start the Plural forms section // This is a full plural form expression parser. It is used to avoid // running 'eval' or 'new Function' directly against the plural