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