vendor: 20,063 bytes, lines 1-630
1/*! 2 * mustache.js - Logic-less {{mustache}} templates with JavaScript 3 * http://github.com/janl/mustache.js 4 */ 5 6/*global define: false Mustache: true*/ 7 8(function defineMustache (global, factory) { 9 if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') { 10 factory(exports); // CommonJS 11 } else if (typeof define === 'function' && define.amd) { 12 define(['exports'], factory); // AMD 13 } else { 14 global.Mustache = {}; 15 factory(global.Mustache); // script, wsh, asp 16 } 17}(this, function mustacheFactory (mustache) { 18 19 var objectToString = Object.prototype.toString; 20 var isArray = Array.isArray || function isArrayPolyfill (object) { 21 return objectToString.call(object) === '[object Array]'; 22 }; 23 24 function isFunction (object) { 25 return typeof object === 'function'; 26 } 27 28 /** 29 * More correct typeof string handling array 30 * which normally returns typeof 'object' 31 */ 32 function typeStr (obj) { 33 return isArray(obj) ? 'array' : typeof obj; 34 } 35 36 function escapeRegExp (string) { 37 return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); 38 } 39 40 /** 41 * Null safe way of checking whether or not an object, 42 * including its prototype, has a given property 43 */ 44 function hasProperty (obj, propName) { 45 return obj != null && typeof obj === 'object' && (propName in obj); 46 } 47 48 // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577 49 // See https://github.com/janl/mustache.js/issues/189 50 var regExpTest = RegExp.prototype.test; 51 function testRegExp (re, string) { 52 return regExpTest.call(re, string); 53 } 54 55 var nonSpaceRe = /\S/; 56 function isWhitespace (string) { 57 return !testRegExp(nonSpaceRe, string); 58 } 59 60 var entityMap = { 61 '&': '&', 62 '<': '<', 63 '>': '>', 64 '"': '"', 65 "'": ''', 66 '/': '/', 67 '`': '`', 68 '=': '=' 69 }; 70 71 function escapeHtml (string) { 72 return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) { 73 return entityMap[s]; 74 }); 75 } 76 77 var whiteRe = /\s*/; 78 var spaceRe = /\s+/; 79 var equalsRe = /\s*=/; 80 var curlyRe = /\s*\}/; 81 var tagRe = /#|\^|\/|>|\{|&|=|!/; 82 83 /** 84 * Breaks up the given `template` string into a tree of tokens. If the `tags` 85 * argument is given here it must be an array with two string values: the 86 * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of 87 * course, the default is to use mustaches (i.e. mustache.tags). 88 * 89 * A token is an array with at least 4 elements. The first element is the 90 * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag 91 * did not contain a symbol (i.e. {{myValue}}) this element is "name". For 92 * all text that appears outside a symbol this element is "text". 93 * 94 * The second element of a token is its "value". For mustache tags this is 95 * whatever else was inside the tag besides the opening symbol. For text tokens 96 * this is the text itself. 97 * 98 * The third and fourth elements of the token are the start and end indices, 99 * respectively, of the token in the original template. 100 * 101 * Tokens that are the root node of a subtree contain two more elements: 1) an 102 * array of tokens in the subtree and 2) the index in the original template at 103 * which the closing tag for that section begins. 104 */ 105 function parseTemplate (template, tags) { 106 if (!template) 107 return []; 108 109 var sections = []; // Stack to hold section tokens 110 var tokens = []; // Buffer to hold the tokens 111 var spaces = []; // Indices of whitespace tokens on the current line 112 var hasTag = false; // Is there a {{tag}} on the current line? 113 var nonSpace = false; // Is there a non-space char on the current line? 114 115 // Strips all whitespace tokens array for the current line 116 // if there was a {{#tag}} on it and otherwise only space. 117 function stripSpace () { 118 if (hasTag && !nonSpace) { 119 while (spaces.length) 120 delete tokens[spaces.pop()]; 121 } else { 122 spaces = []; 123 } 124 125 hasTag = false; 126 nonSpace = false; 127 } 128 129 var openingTagRe, closingTagRe, closingCurlyRe; 130 function compileTags (tagsToCompile) { 131 if (typeof tagsToCompile === 'string') 132 tagsToCompile = tagsToCompile.split(spaceRe, 2); 133 134 if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) 135 throw new Error('Invalid tags: ' + tagsToCompile); 136 137 openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*'); 138 closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1])); 139 closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1])); 140 } 141 142 compileTags(tags || mustache.tags); 143 144 var scanner = new Scanner(template); 145 146 var start, type, value, chr, token, openSection; 147 while (!scanner.eos()) { 148 start = scanner.pos; 149 150 // Match any text between tags. 151 value = scanner.scanUntil(openingTagRe); 152 153 if (value) { 154 for (var i = 0, valueLength = value.length; i < valueLength; ++i) { 155 chr = value.charAt(i); 156 157 if (isWhitespace(chr)) { 158 spaces.push(tokens.length); 159 } else { 160 nonSpace = true; 161 } 162 163 tokens.push([ 'text', chr, start, start + 1 ]); 164 start += 1; 165 166 // Check for whitespace on the current line. 167 if (chr === '\n') 168 stripSpace(); 169 } 170 } 171 172 // Match the opening tag. 173 if (!scanner.scan(openingTagRe)) 174 break; 175 176 hasTag = true; 177 178 // Get the tag type. 179 type = scanner.scan(tagRe) || 'name'; 180 scanner.scan(whiteRe); 181 182 // Get the tag value. 183 if (type === '=') { 184 value = scanner.scanUntil(equalsRe); 185 scanner.scan(equalsRe); 186 scanner.scanUntil(closingTagRe); 187 } else if (type === '{') { 188 value = scanner.scanUntil(closingCurlyRe); 189 scanner.scan(curlyRe); 190 scanner.scanUntil(closingTagRe); 191 type = '&'; 192 } else { 193 value = scanner.scanUntil(closingTagRe); 194 } 195 196 // Match the closing tag. 197 if (!scanner.scan(closingTagRe)) 198 throw new Error('Unclosed tag at ' + scanner.pos); 199 200 token = [ type, value, start, scanner.pos ]; 201 tokens.push(token); 202 203 if (type === '#' || type === '^') { 204 sections.push(token); 205 } else if (type === '/') { 206 // Check section nesting. 207 openSection = sections.pop(); 208 209 if (!openSection) 210 throw new Error('Unopened section "' + value + '" at ' + start); 211 212 if (openSection[1] !== value) 213 throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); 214 } else if (type === 'name' || type === '{' || type === '&') { 215 nonSpace = true; 216 } else if (type === '=') { 217 // Set the tags for the next time around. 218 compileTags(value); 219 } 220 } 221 222 // Make sure there are no open sections when we're done. 223 openSection = sections.pop(); 224 225 if (openSection) 226 throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); 227 228 return nestTokens(squashTokens(tokens)); 229 } 230 231 /** 232 * Combines the values of consecutive text tokens in the given `tokens` array 233 * to a single token. 234 */ 235 function squashTokens (tokens) { 236 var squashedTokens = []; 237 238 var token, lastToken; 239 for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 240 token = tokens[i]; 241 242 if (token) { 243 if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { 244 lastToken[1] += token[1]; 245 lastToken[3] = token[3]; 246 } else { 247 squashedTokens.push(token); 248 lastToken = token; 249 } 250 } 251 } 252 253 return squashedTokens; 254 } 255 256 /** 257 * Forms the given array of `tokens` into a nested tree structure where 258 * tokens that represent a section have two additional items: 1) an array of 259 * all tokens that appear in that section and 2) the index in the original 260 * template that represents the end of that section. 261 */ 262 function nestTokens (tokens) { 263 var nestedTokens = []; 264 var collector = nestedTokens; 265 var sections = []; 266 267 var token, section; 268 for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 269 token = tokens[i]; 270 271 switch (token[0]) { 272 case '#': 273 case '^': 274 collector.push(token); 275 sections.push(token); 276 collector = token[4] = []; 277 break; 278 case '/': 279 section = sections.pop(); 280 section[5] = token[2]; 281 collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens; 282 break; 283 default: 284 collector.push(token); 285 } 286 } 287 288 return nestedTokens; 289 } 290 291 /** 292 * A simple string scanner that is used by the template parser to find 293 * tokens in template strings. 294 */ 295 function Scanner (string) { 296 this.string = string; 297 this.tail = string; 298 this.pos = 0; 299 } 300 301 /** 302 * Returns `true` if the tail is empty (end of string). 303 */ 304 Scanner.prototype.eos = function eos () { 305 return this.tail === ''; 306 }; 307 308 /** 309 * Tries to match the given regular expression at the current position. 310 * Returns the matched text if it can match, the empty string otherwise. 311 */ 312 Scanner.prototype.scan = function scan (re) { 313 var match = this.tail.match(re); 314 315 if (!match || match.index !== 0) 316 return ''; 317 318 var string = match[0]; 319 320 this.tail = this.tail.substring(string.length); 321 this.pos += string.length; 322 323 return string; 324 }; 325 326 /** 327 * Skips all text until the given regular expression can be matched. Returns 328 * the skipped string, which is the entire tail if no match can be made. 329 */ 330 Scanner.prototype.scanUntil = function scanUntil (re) { 331 var index = this.tail.search(re), match; 332 333 switch (index) { 334 case -1: 335 match = this.tail; 336 this.tail = ''; 337 break; 338 case 0: 339 match = ''; 340 break; 341 default: 342 match = this.tail.substring(0, index); 343 this.tail = this.tail.substring(index); 344 } 345 346 this.pos += match.length; 347 348 return match; 349 }; 350 351 /** 352 * Represents a rendering context by wrapping a view object and 353 * maintaining a reference to the parent context. 354 */ 355 function Context (view, parentContext) { 356 this.view = view; 357 this.cache = { '.': this.view }; 358 this.parent = parentContext; 359 } 360 361 /** 362 * Creates a new context using the given view with this context 363 * as the parent. 364 */ 365 Context.prototype.push = function push (view) { 366 return new Context(view, this); 367 }; 368 369 /** 370 * Returns the value of the given name in this context, traversing 371 * up the context hierarchy if the value is absent in this context's view. 372 */ 373 Context.prototype.lookup = function lookup (name) { 374 var cache = this.cache; 375 376 var value; 377 if (cache.hasOwnProperty(name)) { 378 value = cache[name]; 379 } else { 380 var context = this, names, index, lookupHit = false; 381 382 while (context) { 383 if (name.indexOf('.') > 0) { 384 value = context.view; 385 names = name.split('.'); 386 index = 0; 387 388 /** 389 * Using the dot notion path in `name`, we descend through the 390 * nested objects. 391 * 392 * To be certain that the lookup has been successful, we have to 393 * check if the last object in the path actually has the property 394 * we are looking for. We store the result in `lookupHit`. 395 * 396 * This is specially necessary for when the value has been set to 397 * `undefined` and we want to avoid looking up parent contexts. 398 **/ 399 while (value != null && index < names.length) { 400 if (index === names.length - 1) 401 lookupHit = hasProperty(value, names[index]); 402 403 value = value[names[index++]]; 404 } 405 } else { 406 value = context.view[name]; 407 lookupHit = hasProperty(context.view, name); 408 } 409 410 if (lookupHit) 411 break; 412 413 context = context.parent; 414 } 415 416 cache[name] = value; 417 } 418 419 if (isFunction(value)) 420 value = value.call(this.view); 421 422 return value; 423 }; 424 425 /** 426 * A Writer knows how to take a stream of tokens and render them to a 427 * string, given a context. It also maintains a cache of templates to 428 * avoid the need to parse the same template twice. 429 */ 430 function Writer () { 431 this.cache = {}; 432 } 433 434 /** 435 * Clears all cached templates in this writer. 436 */ 437 Writer.prototype.clearCache = function clearCache () { 438 this.cache = {}; 439 }; 440 441 /** 442 * Parses and caches the given `template` and returns the array of tokens 443 * that is generated from the parse. 444 */ 445 Writer.prototype.parse = function parse (template, tags) { 446 var cache = this.cache; 447 var tokens = cache[template]; 448 449 if (tokens == null) 450 tokens = cache[template + ':' + (tags || mustache.tags).join(':')] = parseTemplate(template, tags); 451 452 return tokens; 453 }; 454 455 /** 456 * High-level method that is used to render the given `template` with 457 * the given `view`. 458 * 459 * The optional `partials` argument may be an object that contains the 460 * names and templates of partials that are used in the template. It may 461 * also be a function that is used to load partial templates on the fly 462 * that takes a single argument: the name of the partial. 463 */ 464 Writer.prototype.render = function render (template, view, partials) { 465 var tokens = this.parse(template); 466 var context = (view instanceof Context) ? view : new Context(view); 467 return this.renderTokens(tokens, context, partials, template); 468 }; 469 470 /** 471 * Low-level method that renders the given array of `tokens` using 472 * the given `context` and `partials`. 473 * 474 * Note: The `originalTemplate` is only ever used to extract the portion 475 * of the original template that was contained in a higher-order section. 476 * If the template doesn't use higher-order sections, this argument may 477 * be omitted. 478 */ 479 Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) { 480 var buffer = ''; 481 482 var token, symbol, value; 483 for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { 484 value = undefined; 485 token = tokens[i]; 486 symbol = token[0]; 487 488 if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate); 489 else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate); 490 else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate); 491 else if (symbol === '&') value = this.unescapedValue(token, context); 492 else if (symbol === 'name') value = this.escapedValue(token, context); 493 else if (symbol === 'text') value = this.rawValue(token); 494 495 if (value !== undefined) 496 buffer += value; 497 } 498 499 return buffer; 500 }; 501 502 Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) { 503 var self = this; 504 var buffer = ''; 505 var value = context.lookup(token[1]); 506 507 // This function is used to render an arbitrary template 508 // in the current context by higher-order sections. 509 function subRender (template) { 510 return self.render(template, context, partials); 511 } 512 513 if (!value) return; 514 515 if (isArray(value)) { 516 for (var j = 0, valueLength = value.length; j < valueLength; ++j) { 517 buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate); 518 } 519 } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') { 520 buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate); 521 } else if (isFunction(value)) { 522 if (typeof originalTemplate !== 'string') 523 throw new Error('Cannot use higher-order sections without the original template'); 524 525 // Extract the portion of the original template that the section contains. 526 value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); 527 528 if (value != null) 529 buffer += value; 530 } else { 531 buffer += this.renderTokens(token[4], context, partials, originalTemplate); 532 } 533 return buffer; 534 }; 535 536 Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) { 537 var value = context.lookup(token[1]); 538 539 // Use JavaScript's definition of falsy. Include empty arrays. 540 // See https://github.com/janl/mustache.js/issues/186 541 if (!value || (isArray(value) && value.length === 0)) 542 return this.renderTokens(token[4], context, partials, originalTemplate); 543 }; 544 545 Writer.prototype.renderPartial = function renderPartial (token, context, partials) { 546 if (!partials) return; 547 548 var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; 549 if (value != null) 550 return this.renderTokens(this.parse(value), context, partials, value); 551 }; 552 553 Writer.prototype.unescapedValue = function unescapedValue (token, context) { 554 var value = context.lookup(token[1]); 555 if (value != null) 556 return value; 557 }; 558 559 Writer.prototype.escapedValue = function escapedValue (token, context) { 560 var value = context.lookup(token[1]); 561 if (value != null) 562 return mustache.escape(value); 563 }; 564 565 Writer.prototype.rawValue = function rawValue (token) { 566 return token[1]; 567 }; 568 569 mustache.name = 'mustache.js'; 570 mustache.version = '2.3.0'; 571 mustache.tags = [ '{{', '}}' ]; 572 573 // All high-level mustache.* functions use this writer. 574 var defaultWriter = new Writer(); 575 576 /** 577 * Clears all cached templates in the default writer. 578 */ 579 mustache.clearCache = function clearCache () { 580 return defaultWriter.clearCache(); 581 }; 582 583 /** 584 * Parses and caches the given template in the default writer and returns the 585 * array of tokens it contains. Doing this ahead of time avoids the need to 586 * parse templates on the fly as they are rendered. 587 */ 588 mustache.parse = function parse (template, tags) { 589 return defaultWriter.parse(template, tags); 590 }; 591 592 /** 593 * Renders the `template` with the given `view` and `partials` using the 594 * default writer. 595 */ 596 mustache.render = function render (template, view, partials) { 597 if (typeof template !== 'string') { 598 throw new TypeError('Invalid template! Template should be a "string" ' + 599 'but "' + typeStr(template) + '" was given as the first ' + 600 'argument for mustache#render(template, view, partials)'); 601 } 602 603 return defaultWriter.render(template, view, partials); 604 }; 605 606 // This is here for backwards compatibility with 0.4.x., 607 /*eslint-disable */ // eslint wants camel cased function name 608 mustache.to_html = function to_html (template, view, partials, send) { 609 /*eslint-enable*/ 610 611 var result = mustache.render(template, view, partials); 612 613 if (isFunction(send)) { 614 send(result); 615 } else { 616 return result; 617 } 618 }; 619 620 // Export the escaping function so that the user may override it. 621 // See https://github.com/janl/mustache.js/issues/244 622 mustache.escape = escapeHtml; 623 624 // Export these mainly for testing, but also for advanced usage. 625 mustache.Scanner = Scanner; 626 mustache.Context = Context; 627 mustache.Writer = Writer; 628 629 return mustache; 630}));
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.