├── .gitignore ├── README.md ├── lib └── sprintf.js ├── package.json └── test └── function-export.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sprintf() for node 2 | 3 | ## Disclaimer 4 | 5 | This was originally just an adoption of a browser library to node.js. 6 | Since that library is now itself a node.js module, you should use that one: [alexei/sprintf.js](https://github.com/alexei/sprintf.js) 7 | 8 | Or just install it via npm: 9 | 10 | npm install sprintf-js 11 | 12 | 13 | There is also a [built-in util.format](http://nodejs.org/api/util.html#util_util_format_format). 14 | 15 | 16 | 17 | 18 | ## Install 19 | 20 | **This package is not maintained anymore. See Disclaimer above.** 21 | 22 | npm install sprintf 23 | 24 | 25 | ## How to 26 | 27 | Works exactly like http://www.diveintojavascript.com/projects/javascript-sprintf, except that it exports those two functions: 28 | 29 | sprintf = require('sprintf').sprintf; 30 | vsprintf = require('sprintf').vsprintf; 31 | 32 | Have fun! 33 | 34 | 35 | ## Copyright/License 36 | 37 | Copyright (c) 2007-2013, Alexandru Marasteanu 38 | All rights reserved. 39 | 40 | Redistribution and use in source and binary forms, with or without 41 | modification, are permitted provided that the following conditions are met: 42 | * Redistributions of source code must retain the above copyright 43 | notice, this list of conditions and the following disclaimer. 44 | * Redistributions in binary form must reproduce the above copyright 45 | notice, this list of conditions and the following disclaimer in the 46 | documentation and/or other materials provided with the distribution. 47 | * Neither the name of this software nor the names of its contributors may be 48 | used to endorse or promote products derived from this software without 49 | specific prior written permission. 50 | 51 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 52 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 53 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 54 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 55 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 56 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 57 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 58 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 59 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 | -------------------------------------------------------------------------------- /lib/sprintf.js: -------------------------------------------------------------------------------- 1 | /** 2 | sprintf() for JavaScript 0.7-beta1 3 | http://www.diveintojavascript.com/projects/javascript-sprintf 4 | 5 | Copyright (c) Alexandru Marasteanu 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of sprintf() for JavaScript nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | Changelog: 32 | 2010.11.07 - 0.7-beta1-node 33 | - converted it to a node.js compatible module 34 | 35 | 2010.09.06 - 0.7-beta1 36 | - features: vsprintf, support for named placeholders 37 | - enhancements: format cache, reduced global namespace pollution 38 | 39 | 2010.05.22 - 0.6: 40 | - reverted to 0.4 and fixed the bug regarding the sign of the number 0 41 | Note: 42 | Thanks to Raphael Pigulla (http://www.n3rd.org/) 43 | who warned me about a bug in 0.5, I discovered that the last update was 44 | a regress. I appologize for that. 45 | 46 | 2010.05.09 - 0.5: 47 | - bug fix: 0 is now preceeded with a + sign 48 | - bug fix: the sign was not at the right position on padded results (Kamal Abdali) 49 | - switched from GPL to BSD license 50 | 51 | 2007.10.21 - 0.4: 52 | - unit test and patch (David Baird) 53 | 54 | 2007.09.17 - 0.3: 55 | - bug fix: no longer throws exception on empty paramenters (Hans Pufal) 56 | 57 | 2007.09.11 - 0.2: 58 | - feature: added argument swapping 59 | 60 | 2007.04.03 - 0.1: 61 | - initial release 62 | **/ 63 | 64 | var sprintf = (function() { 65 | function get_type(variable) { 66 | return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); 67 | } 68 | function str_repeat(input, multiplier) { 69 | for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} 70 | return output.join(''); 71 | } 72 | 73 | var str_format = function() { 74 | if (!str_format.cache.hasOwnProperty(arguments[0])) { 75 | str_format.cache[arguments[0]] = str_format.parse(arguments[0]); 76 | } 77 | return str_format.format.call(null, str_format.cache[arguments[0]], arguments); 78 | }; 79 | 80 | // convert object to simple one line string without indentation or 81 | // newlines. Note that this implementation does not print array 82 | // values to their actual place for sparse arrays. 83 | // 84 | // For example sparse array like this 85 | // l = [] 86 | // l[4] = 1 87 | // Would be printed as "[1]" instead of "[, , , , 1]" 88 | // 89 | // If argument 'seen' is not null and array the function will check for 90 | // circular object references from argument. 91 | str_format.object_stringify = function(obj, depth, maxdepth, seen) { 92 | var str = ''; 93 | if (obj != null) { 94 | switch( typeof(obj) ) { 95 | case 'function': 96 | return '[Function' + (obj.name ? ': '+obj.name : '') + ']'; 97 | break; 98 | case 'object': 99 | if ( obj instanceof Error) { return '[' + obj.toString() + ']' }; 100 | if (depth >= maxdepth) return '[Object]' 101 | if (seen) { 102 | // add object to seen list 103 | seen = seen.slice(0) 104 | seen.push(obj); 105 | } 106 | if (obj.length != null) { //array 107 | str += '['; 108 | var arr = [] 109 | for (var i in obj) { 110 | if (seen && seen.indexOf(obj[i]) >= 0) arr.push('[Circular]'); 111 | else arr.push(str_format.object_stringify(obj[i], depth+1, maxdepth, seen)); 112 | } 113 | str += arr.join(', ') + ']'; 114 | } else if ('getMonth' in obj) { // date 115 | return 'Date(' + obj + ')'; 116 | } else { // object 117 | str += '{'; 118 | var arr = [] 119 | for (var k in obj) { 120 | if(obj.hasOwnProperty(k)) { 121 | if (seen && seen.indexOf(obj[k]) >= 0) arr.push(k + ': [Circular]'); 122 | else arr.push(k +': ' +str_format.object_stringify(obj[k], depth+1, maxdepth, seen)); 123 | } 124 | } 125 | str += arr.join(', ') + '}'; 126 | } 127 | return str; 128 | break; 129 | case 'string': 130 | return '"' + obj + '"'; 131 | break 132 | } 133 | } 134 | return '' + obj; 135 | } 136 | 137 | str_format.format = function(parse_tree, argv) { 138 | var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; 139 | for (i = 0; i < tree_length; i++) { 140 | node_type = get_type(parse_tree[i]); 141 | if (node_type === 'string') { 142 | output.push(parse_tree[i]); 143 | } 144 | else if (node_type === 'array') { 145 | match = parse_tree[i]; // convenience purposes only 146 | if (match[2]) { // keyword argument 147 | arg = argv[cursor]; 148 | for (k = 0; k < match[2].length; k++) { 149 | if (!arg.hasOwnProperty(match[2][k])) { 150 | throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k])); 151 | } 152 | arg = arg[match[2][k]]; 153 | } 154 | } 155 | else if (match[1]) { // positional argument (explicit) 156 | arg = argv[match[1]]; 157 | } 158 | else { // positional argument (implicit) 159 | arg = argv[cursor++]; 160 | } 161 | 162 | if (/[^sO]/.test(match[8]) && (get_type(arg) != 'number')) { 163 | throw new Error(sprintf('[sprintf] expecting number but found %s "' + arg + '"', get_type(arg))); 164 | } 165 | switch (match[8]) { 166 | case 'b': arg = arg.toString(2); break; 167 | case 'c': arg = String.fromCharCode(arg); break; 168 | case 'd': arg = parseInt(arg, 10); break; 169 | case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; 170 | case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; 171 | case 'O': arg = str_format.object_stringify(arg, 0, parseInt(match[7]) || 5); break; 172 | case 'o': arg = arg.toString(8); break; 173 | case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; 174 | case 'u': arg = Math.abs(arg); break; 175 | case 'x': arg = arg.toString(16); break; 176 | case 'X': arg = arg.toString(16).toUpperCase(); break; 177 | } 178 | arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg); 179 | pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' '; 180 | pad_length = match[6] - String(arg).length; 181 | pad = match[6] ? str_repeat(pad_character, pad_length) : ''; 182 | output.push(match[5] ? arg + pad : pad + arg); 183 | } 184 | } 185 | return output.join(''); 186 | }; 187 | 188 | str_format.cache = {}; 189 | 190 | str_format.parse = function(fmt) { 191 | var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; 192 | while (_fmt) { 193 | if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { 194 | parse_tree.push(match[0]); 195 | } 196 | else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { 197 | parse_tree.push('%'); 198 | } 199 | else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosOuxX])/.exec(_fmt)) !== null) { 200 | if (match[2]) { 201 | arg_names |= 1; 202 | var field_list = [], replacement_field = match[2], field_match = []; 203 | if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { 204 | field_list.push(field_match[1]); 205 | while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { 206 | if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { 207 | field_list.push(field_match[1]); 208 | } 209 | else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) { 210 | field_list.push(field_match[1]); 211 | } 212 | else { 213 | throw new Error('[sprintf] ' + replacement_field); 214 | } 215 | } 216 | } 217 | else { 218 | throw new Error('[sprintf] ' + replacement_field); 219 | } 220 | match[2] = field_list; 221 | } 222 | else { 223 | arg_names |= 2; 224 | } 225 | if (arg_names === 3) { 226 | throw new Error('[sprintf] mixing positional and named placeholders is not (yet) supported'); 227 | } 228 | parse_tree.push(match); 229 | } 230 | else { 231 | throw new Error('[sprintf] ' + _fmt); 232 | } 233 | _fmt = _fmt.substring(match[0].length); 234 | } 235 | return parse_tree; 236 | }; 237 | 238 | return str_format; 239 | })(); 240 | 241 | var vsprintf = function(fmt, argv) { 242 | var argvClone = argv.slice(); 243 | argvClone.unshift(fmt); 244 | return sprintf.apply(null, argvClone); 245 | }; 246 | 247 | module.exports = sprintf; 248 | sprintf.sprintf = sprintf; 249 | sprintf.vsprintf = vsprintf; 250 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sprintf", 3 | "version": "0.1.5", 4 | "engines": { 5 | "node" : ">=0.2.4" 6 | }, 7 | "author": "Moritz Peters", 8 | "directories": { 9 | "lib": "./lib" 10 | }, 11 | "description": "Sprintf() for node.js", 12 | "main": "./lib/sprintf", 13 | "homepage": "https://github.com/maritz/node-sprintf", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/maritz/node-sprintf.git" 17 | }, 18 | "license": "BSD-3-Clause", 19 | "scripts": { 20 | "test": "nodeunit test/function-export.js" 21 | }, 22 | "devDependencies": { 23 | "nodeunit": "0.8.0" 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /test/function-export.js: -------------------------------------------------------------------------------- 1 | var sprintf = require(__dirname + '/..'); 2 | 3 | exports['sprintf() export works'] = function(test) { 4 | test.equal(sprintf('Hallo %s!', 'Welt'), 'Hallo Welt!'); 5 | test.equal(sprintf.sprintf('Hallo %s!', 'Welt'), 'Hallo Welt!'); 6 | test.equal(sprintf.vsprintf('Hallo %s!', ['Welt']), 'Hallo Welt!'); 7 | 8 | test.done(); 9 | }; 10 | --------------------------------------------------------------------------------