1void function(global) { 2 3 'use strict'; 4 5 // ValueError :: String -> Error 6 function ValueError(message) { 7 var err = new Error (message); 8 err.name = 'ValueError'; 9 return err; 10 } 11 12 // create :: Object -> String,*... -> String 13 function create(transformers) { 14 return function(template) { 15 var args = Array.prototype.slice.call (arguments, 1); 16 var idx = 0; 17 var state = 'UNDEFINED'; 18 19 return template.replace ( 20 /([{}])\1|[{](.*?)(?:!(.+?))?[}]/g, 21 function(match, literal, _key, xf) { 22 if (literal != null) { 23 return literal; 24 } 25 var key = _key; 26 if (key.length > 0) { 27 if (state === 'IMPLICIT') { 28 throw ValueError ('cannot switch from ' + 29 'implicit to explicit numbering'); 30 } 31 state = 'EXPLICIT'; 32 } else { 33 if (state === 'EXPLICIT') { 34 throw ValueError ('cannot switch from ' + 35 'explicit to implicit numbering'); 36 } 37 state = 'IMPLICIT'; 38 key = String (idx); 39 idx += 1; 40 } 41 42 // 1. Split the key into a lookup path. 43 // 2. If the first path component is not an index, prepend '0'. 44 // 3. Reduce the lookup path to a single result. If the lookup 45 // succeeds the result is a singleton array containing the 46 // value at the lookup path; otherwise the result is []. 47 // 4. Unwrap the result by reducing with '' as the default value. 48 var path = key.split ('.'); 49 var value = (/^\d+$/.test (path[0]) ? path : ['0'].concat (path)) 50 .reduce (function(maybe, key) { 51 return maybe.reduce (function(_, x) { 52 return x != null && key in Object (x) ? 53 [typeof x[key] === 'function' ? x[key] () : x[key]] : 54 []; 55 }, []); 56 }, [args]) 57 .reduce (function(_, x) { return x; }, ''); 58 59 if (xf == null) { 60 return value; 61 } else if (Object.prototype.hasOwnProperty.call (transformers, xf)) { 62 return transformers[xf] (value); 63 } else { 64 throw ValueError ('no transformer named "' + xf + '"'); 65 } 66 } 67 ); 68 }; 69 } 70 71 // format :: String,*... -> String 72 var format = create ({}); 73 74 // format.create :: Object -> String,*... -> String 75 format.create = create; 76 77 // format.extend :: Object,Object -> () 78 format.extend = function(prototype, transformers) { 79 var $format = create (transformers); 80 prototype.format = function() { 81 var args = Array.prototype.slice.call (arguments); 82 args.unshift (this); 83 return $format.apply (global, args); 84 }; 85 }; 86 87 /* istanbul ignore else */ 88 if (typeof module !== 'undefined') { 89 module.exports = format; 90 } else if (typeof define === 'function' && define.amd) { 91 define (function() { return format; }); 92 } else { 93 global.format = format; 94 } 95 96 /* istanbul ignore if */ 97 if (typeof __doctest !== 'undefined') { 98 format.extend (String.prototype, {}); 99 } 100 101 //. # string-format 102 //. 103 //. string-format is a small JavaScript library for formatting strings, 104 //. based on Python's [`str.format()`][1]. For example: 105 //. 106 //. ```javascript 107 //. > const user = { 108 //. . firstName: 'Jane', 109 //. . lastName: 'Smith', 110 //. . email: '[email protected]', 111 //. . } 112 //. ``` 113 //. 114 //. ```javascript 115 //. > '"{firstName} {lastName}" <{email}>'.format (user) 116 //. '"Jane Smith" <[email protected]>' 117 //. ``` 118 //. 119 //. The equivalent concatenation: 120 //. 121 //. ```javascript 122 //. > '"' + user.firstName + ' ' + user.lastName + '" <' + user.email + '>' 123 //. '"Jane Smith" <[email protected]>' 124 //. ``` 125 //. 126 //. ### Installation 127 //. 128 //. #### Node 129 //. 130 //. 1. Install: 131 //. 132 //. ```console 133 //. $ npm install string-format 134 //. ``` 135 //. 136 //. 2. Require: 137 //. 138 //. ```javascript 139 //. const format = require ('string-format') 140 //. ``` 141 //. 142 //. #### Browser 143 //. 144 //. 1. Define `window.format`: 145 //. 146 //. ```html 147 //. <script src="path/to/string-format.js"></script> 148 //. ``` 149 //. 150 //. ### Modes 151 //. 152 //. string-format can be used in two modes: [function mode](#function-mode) 153 //. and [method mode](#method-mode). 154 //. 155 //. #### Function mode 156 //. 157 //. ```javascript 158 //. > format ('Hello, {}!', 'Alice') 159 //. 'Hello, Alice!' 160 //. ``` 161 //. 162 //. In this mode the first argument is a template string and the remaining 163 //. arguments are values to be interpolated. 164 //. 165 //. #### Method mode 166 //. 167 //. ```javascript 168 //. > 'Hello, {}!'.format ('Alice') 169 //. 'Hello, Alice!' 170 //. ``` 171 //. 172 //. In this mode values to be interpolated are supplied to the `format` 173 //. method of a template string. This mode is not enabled by default. 174 //. The method must first be defined via [`format.extend`](#format.extend): 175 //. 176 //. ```javascript 177 //. > format.extend (String.prototype, {}) 178 //. ``` 179 //. 180 //. `format (template, $0, $1, â¦, $N)` and `template.format ($0, $1, â¦, $N)` 181 //. can then be used interchangeably. 182 //. 183 //. <a name="format"></a> 184 //. 185 //. ### `format (template, $0, $1, â¦, $N)` 186 //. 187 //. Returns the result of replacing each `{â¦}` placeholder in the template 188 //. string with its corresponding replacement. 189 //. 190 //. Placeholders may contain numbers which refer to positional arguments: 191 //. 192 //. ```javascript 193 //. > '{0}
193, you have {1} unread message{2}'.format ('Holly', 2, 's') 194 //. 'Holly, you have 2 unread messages' 195 //. ``` 196 //. 197 //. Unmatched placeholders produce no output: 198 //. 199 //. ```javascript 200 //. > '{0}, you have {1} unread message{2}'.format ('Steve', 1) 201 //. 'Steve, you have 1 unread message' 202 //. ``` 203 //. 204 //. A format string may reference a positional argument multiple times: 205 //. 206 //. ```javascript 207 //. > "The name's {1}. {0} {1}.".format ('James', 'Bond') 208 //. "The name's Bond. James Bond." 209 //. ``` 210 //. 211 //. Positional arguments may be referenced implicitly: 212 //. 213 //. ```javascript 214 //. > '{}, you have {} unread message{}'.format ('Steve', 1) 215 //. 'Steve, you have 1 unread message' 216 //. ``` 217 //. 218 //. A format string must not contain both implicit and explicit references: 219 //. 220 //. ```javascript 221 //. > 'My name is {} {}. Do you like the name {0}?'.format ('Lemony', 'Snicket') 222 //. ! ValueError: cannot switch from implicit to explicit numbering 223 //. ``` 224 //. 225 //. `{{` and `}}` in format strings produce `{` and `}`: 226 //. 227 //. ```javascript 228 //. > '{{}} creates an empty {} in {}'.format ('dictionary', 'Python') 229 //. '{} creates an empty dictionary in Python' 230 //. ``` 231 //. 232 //. Dot notation may be used to reference object properties: 233 //. 234 //. ```javascript 235 //. > const bobby = {firstName: 'Bobby', lastName: 'Fischer'} 236 //. > const garry = {firstName: 'Garry', lastName: 'Kasparov'} 237 //. 238 //. > '{0.firstName} {0.lastName} vs. {1.firstName} {1.lastName}'.format (bobby, garry) 239 //. 'Bobby Fischer vs. Garry Kasparov' 240 //. ``` 241 //. 242 //. `0.` may be omitted when referencing a property of `{0}`: 243 //. 244 //. ```javascript 245 //. > const repo = {owner: 'davidchambers', slug: 'string-format'} 246 //. 247 //. > 'https://github.com/{owner}/{slug}'.format (repo) 248 //. 'https://github.com/davidchambers/string-format' 249 //. ``` 250 //. 251 //. If the referenced property is a method, it is invoked with no arguments 252 //. to determine the replacement: 253 //. 254 //. ```javascript 255 //. > const sheldon = { 256 //. . firstName: 'Sheldon', 257 //. . lastName: 'Cooper', 258 //. . dob: new Date ('1970-01-01'), 259 //. . fullName: function() { return this.firstName + ' ' + this.lastName }, 260 //. . quip: function() { return 'Bazinga!' }, 261 //. . } 262 //. 263 //. > '{fullName} was born at precisely {dob.toISOString}'.format (sheldon) 264 //. 'Sheldon Cooper was born at precisely 1970-01-01T00:00:00.000Z' 265 //. 266 //. > "I've always wanted to go to a goth club. {quip.toUpperCase}".format (sheldon) 267 //. "I've always wanted to go to a goth club. BAZINGA!" 268 //. ``` 269 //. 270 //. <a name="format.create"></a> 271 //. 272 //. ### `format.create (transformers)` 273 //. 274 //. This function takes an object mapping names to transformers and returns 275 //. a formatting function. A transformer is applied if its name appears, 276 //. prefixed with `!`, after a field name in a template string. 277 //. 278 //. ```javascript 279 //. > const fmt = format.create ({ 280 //. . escape: s => 281 //. . s.replace (/[&<>"'`]/g, c => '&#' + c.charCodeAt (0) + ';'), 282 //. . upper: s => 283 //. . s.toUpperCase (), 284 //. . }) 285 //. 286 //. > fmt ('Hello, {!upper}!', 'Alice') 287 //. 'Hello, ALICE!' 288 //. 289 //. > fmt ('<a href="{url!escape}">{name!escape}</a>', { 290 //. . name: 'Anchor & Hope', 291 //. . url: 'http://anchorandhopesf.com/', 292 //. . }) 293 //. '<a href="http://anchorandhopesf.com/">Anchor & Hope</a>' 294 //. ``` 295 //. 296 //. <a name="format.extend"></a> 297 //. 298 //. ### `format.extend (prototype, transformers)` 299 //. 300 //. This function takes a prototype (presumably `String.prototype`) and an 301 //. object mapping names to transformers, and defines a `format` method on 302 //. the prototype. A transformer is applied if its name appears, prefixed 303 //. with `!`, after a field name in a template string. 304 //. 305 //. ```javascript 306 //. > format.extend (String.prototype, { 307 //. . escape: s => 308 //. . s.replace (/[&<>"'`]/g, c => '&#' + c.charCodeAt (0) + ';'), 309 //. . upper: s => 310 //. . s.toUpperCase (), 311 //. . }) 312 //. 313 //. > 'Hello, {!upper}!'.format ('Alice') 314 //. 'Hello, ALICE!' 315 //. 316 //. > '<a href="{url!escape}">{name!escape}</a>'.format ({ 317 //. . name: 'Anchor & Hope', 318 //. . url: 'http://anchorandhopesf.com/', 319 //. . }) 320 //. '<a href="http://anchorandhopesf.com/">Anchor & Hope</a>' 321 //. ``` 322 //. 323 //. ### Running the test suite 324 //. 325 //. ```console 326 //. $ npm install 327 //. $ npm test 328 //. ``` 329 //. 330 //. [1]: http://docs.python.org/library/stdtypes.html#str.format 331 332 }.call (this, this);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.