1/*! 2* mustache.js - Logic-less {{mustache}} templates with JavaScript 3* http://github.com/janl/mustache.js 4*/ 5var Mustache = (typeof module !== "undefined" && module.exports) || {}; 6 7(function (exports) { 8 9 exports.name = "mustache.js"; 10 exports.version = "0.5.0-dev"; 11 exports.tags = ["{{", "}}"]; 12 exports.parse = parse; 13 exports.compile = compile; 14 exports.render = render; 15 exports.clearCache = clearCache; 16 17 // This is here for backwards compatibility with 0.4.x. 18 exports.to_html = function (template, view, partials, send) { 19 var result = render(template, view, partials); 20 21 if (typeof send === "function") { 22 send(result); 23 } else { 24 return result; 25 } 26 }; 27 28 var _toString = Object.prototype.toString; 29 var _isArray = Array.isArray; 30 var _forEach = Array.prototype.forEach; 31 var _trim = String.prototype.trim; 32 33 var isArray; 34 if (_isArray) { 35 isArray = _isArray; 36 } else { 37 isArray = function (obj) { 38 return _toString.call(obj) === "[object Array]"; 39 }; 40 } 41 42 var forEach; 43 if (_forEach) { 44 forEach = function (obj, callback, scope) { 45 return _forEach.call(obj, callback, scope); 46 }; 47 } else { 48 forEach = function (obj, callback, scope) { 49 for (var i = 0, len = obj.length; i < len; ++i) { 50 callback.call(scope, obj[i], i, obj); 51 } 52 }; 53 } 54 55 var spaceRe = /^\s*$/; 56 57 function isWhitespace(string) { 58 return spaceRe.test(string); 59 } 60 61 var trim; 62 if (_trim) { 63 trim = function (string) { 64 return string == null ? "" : _trim.call(string); 65 }; 66 } else { 67 var trimLeft, trimRight; 68 69 if (isWhitespace("\xA0")) { 70 trimLeft = /^\s+/; 71 trimRight = /\s+$/; 72 } else { 73 // IE doesn't match non-breaking spaces with \s, thanks jQuery. 74 trimLeft = /^[\s\xA0]+/; 75 trimRight = /[\s\xA0]+$/; 76 } 77 78 trim = function (string) { 79 return string == null ? "" : 80 String(string).replace(trimLeft, "").replace(trimRight, ""); 81 }; 82 } 83 84 var escapeMap = { 85 "&": "&", 86 "<": "<", 87 ">": ">", 88 '"': '"', 89 "'": ''' 90 }; 91 92 function escapeHTML(string) { 93 return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { 94 return escapeMap[s] || s; 95 }); 96 } 97 98 /** 99 * Adds the `template`, `line`, and `file` properties to the given error 100 * object and alters the message to provide more useful debugging information. 101 */ 102 function debug(e, template, line, file) { 103 file = file || "<template>"; 104 105 var lines = template.split("\n"), 106 start = Math.max(line - 3, 0), 107 end = Math.min(lines.length, line + 3), 108 context = lines.slice(start, end); 109 110 var c; 111 for (var i = 0, len = context.length; i < len; ++i) { 112 c = i + start + 1; 113 context[i] = (c === line ? " >> " : " ") + context[i]; 114 } 115 116 e.template = template; 117 e.line = line; 118 e.file = file; 119 e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); 120 121 return e; 122 } 123 124 /** 125 * Looks up the value of the given `name` in the given context `stack`. 126 */ 127 function lookup(name, stack, defaultValue) { 128 if (name === ".") { 129 return stack[stack.length - 1]; 130 } 131 132 var names = name.split("."); 133 var lastIndex = names.length - 1; 134 var target = names[lastIndex]; 135 136 var value, context, i = stack.length, j, localStack; 137 while (i) { 138 localStack = stack.slice(0); 139 context = stack[--i]; 140 141 j = 0; 142 while (j < lastIndex) { 143 context = context[names[j++]]; 144 145 if (context == null) { 146 break; 147 } 148 149 localStack.push(context); 150 } 151 152 if (context && typeof context === "object" && target in context) { 153 value = context[target]; 154 break; 155 } 156 } 157 158 // If the value is a function, call it in the current context. 159 if (typeof value === "function") { 160 value = value.call(localStack[localStack.length - 1]); 161 } 162 163 if (value == null) { 164 return defaultValue; 165 } 166 167 return value; 168 } 169
170 function renderSection(name, stack, callback, inverted) { 171 var buffer = ""; 172 var value = lookup(name, stack); 173 174 if (inverted) { 175 // From the spec: inverted sections may render text once based on the 176 // inverse value of the key. That is, they will be rendered if the key 177 // doesn't exist, is false, or is an empty list. 178 if (value == null || value === false || (isArray(value) && value.length === 0)) { 179 buffer += callback(); 180 } 181 } else if (isArray(value)) { 182 forEach(value, function (value) { 183 stack.push(value); 184 buffer += callback(); 185 stack.pop(); 186 }); 187 } else if (typeof value === "object") { 188 stack.push(value); 189 buffer += callback(); 190 stack.pop(); 191 } else if (typeof value === "function") { 192 var scope = stack[stack.length - 1]; 193 var scopedRender = function (template) { 194 return render(template, scope); 195 }; 196 buffer += value.call(scope, callback(), scopedRender) || ""; 197 } else if (value) { 198 buffer += callback(); 199 } 200 201 return buffer; 202 } 203 204 /** 205 * Parses the given `template` and returns the source of a function that, 206 * with the proper arguments, will render the template. Recognized options 207 * include the following: 208 * 209 * - file The name of the file the template comes from (displayed in 210 * error messages) 211 * - tags An array of open and close tags the `template` uses. Defaults 212 * to the value of Mustache.tags 213 * - debug Set `true` to log the body of the generated function to the 214 * console 215 * - space Set `true` to preserve whitespace from lines that otherwise 216 * contain only a {{tag}}. Defaults to `false` 217 */ 218 function parse(template, options) { 219 options = options || {}; 220 221 var tags = options.tags || exports.tags, 222 openTag = tags[0], 223 closeTag = tags[tags.length - 1]; 224 225 var code = [ 226 'var buffer = "";', // output buffer 227 "\nvar line = 1;", // keep track of source line number 228 "\ntry {", 229 '\nbuffer += "' 230 ]; 231 232 var spaces = [], // indices of whitespace in code on the current line 233 hasTag = false, // is there a {{tag}} on the current line? 234 nonSpace = false; // is there a non-space char on the current line? 235 236 // Strips all space characters from the code array for the current line 237 // if there was a {{tag}} on it and otherwise only spaces. 238 var stripSpace = function () { 239 if (hasTag && !nonSpace && !options.space) { 240 while (spaces.length) { 241 code.splice(spaces.pop(), 1); 242 } 243 } else { 244 spaces = []; 245 } 246 247 hasTag = false; 248 nonSpace = false; 249 }; 250 251 var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; 252 253 var setTags = function (source) { 254 tags = trim(source).split(/\s+/); 255 nextOpenTag = tags[0]; 256 nextCloseTag = tags[tags.length - 1]; 257 }; 258 259 var includePartial = function (source) { 260 code.push( 261 '";', 262 updateLine, 263 '\nvar partial = partials["' + trim(source) + '"];', 264 '\nif (partial) {', 265 '\n buffer += render(partial,stack[stack.length - 1],partials);', 266 '\n}', 267 '\nbuffer += "' 268 ); 269 }; 270 271 var openSection = function (source, inverted) { 272 var name = trim(source); 273 274 if (name === "") { 275 throw debug(new Error("Section name may not be empty"), template, line, options.file); 276 } 277 278 sectionStack.push({ name: name, inverted: inverted }); 279 280 code.push( 281 '";', 282 updateLine,
283 '\nvar name = "' + name + '";', 284 '\nvar callback = (function () {', 285 '\n return function () {', 286 '\n var buffer = "";', 287 '\nbuffer += "' 288 ); 289 }; 290 291 var openInvertedSection = function (source) { 292 openSection(source, true); 293 }; 294 295 var closeSection = function (source) { 296 var name = trim(source); 297 var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; 298 299 if (!openName || name != openName) { 300 throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); 301 } 302 303 var section = sectionStack.pop(); 304 305 code.push( 306 '";', 307 '\n return buffer;', 308 '\n };', 309 '\n})();' 310 ); 311 312 if (section.inverted) { 313 code.push("\nbuffer += renderSection(name,stack,callback,true);"); 314 } else { 315 code.push("\nbuffer += render
315Section(name,stack,callback);"); 316 } 317 318 code.push('\nbuffer += "'); 319 }; 320 321 var sendPlain = function (source) { 322 code.push( 323 '";', 324 updateLine, 325 '\nbuffer += lookup("' + trim(source) + '",stack,"");', 326 '\nbuffer += "' 327 ); 328 }; 329 330 var sendEscaped = function (source) { 331 code.push( 332 '";', 333 updateLine, 334 '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', 335 '\nbuffer += "' 336 ); 337 }; 338 339 var line = 1, c, callback; 340 for (var i = 0, len = template.length; i < len; ++i) { 341 if (template.slice(i, i + openTag.length) === openTag) { 342 i += openTag.length; 343 c = template.substr(i, 1); 344 updateLine = '\nline = ' + line + ';'; 345 nextOpenTag = openTag; 346 nextCloseTag = closeTag; 347 hasTag = true; 348 349 switch (c) { 350 case "!": // comment 351 i++; 352 callback = null; 353 break; 354 case "=": // change open/close tags, e.g. {{=<% %>=}} 355 i++; 356 closeTag = "=" + closeTag; 357 callback = setTags; 358 break; 359 case ">": // include partial 360 i++; 361 callback = includePartial; 362 break; 363 case "#": // start section 364 i++; 365 callback = openSection; 366 break; 367 case "^": // start inverted section 368 i++; 369 callback = openInvertedSection; 370 break; 371 case "/": // end section 372 i++; 373 callback = closeSection; 374 break; 375 case "{": // plain variable 376 closeTag = "}" + closeTag; 377 // fall through 378 case "&": // plain variable 379 i++; 380 nonSpace = true; 381 callback = sendPlain; 382 break; 383 default: // escaped variable 384 nonSpace = true; 385 callback = sendEscaped; 386 } 387 388 var end = template.indexOf(closeTag, i); 389 390 if (end === -1) { 391 throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); 392 } 393 394 var source = template.substring(i, end); 395 396 if (callback) { 397 callback(source); 398 } 399 400 // Maintain line count for \n in source. 401 var n = 0; 402 while (~(n = source.indexOf("\n", n))) { 403 line++; 404 n++; 405 } 406 407 i = end + closeTag.length - 1; 408 openTag = nextOpenTag; 409 closeTag = nextCloseTag; 410 } else { 411 c = template.substr(i, 1); 412 413 switch (c) { 414 case '"': 415 case "\\": 416 nonSpace = true; 417 code.push("\\" + c); 418 break; 419 case "\r": 420 // Ignore carriage returns. 421 break; 422 case "\n": 423 spaces.push(code.length); 424 code.push("\\n"); 425 stripSpace(); // Check for whitespace on the current line. 426 line++; 427 break; 428 default: 429 if (isWhitespace(c)) { 430 spaces.push(code.length); 431 } else { 432 nonSpace = true; 433 } 434 435 code.push(c); 436 } 437 } 438 } 439 440 if (sectionStack.length != 0) { 441 throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); 442 } 443 444 // Clean up any whitespace from a closing {{tag}} that was at the end 445 // of the template without a trailing \n.
446 stripSpace(); 447 448 code.push( 449 '";', 450 "\nreturn buffer;", 451 "\n} catch (e) { throw {error: e, line: line}; }" 452 ); 453 454 // Ignore `buffer += "";` statements. 455 var body = code.join("").replace(/buffer \+= "";\n/g, ""); 456 457 if (options.debug) { 458 if (typeof console != "undefined" && console.log) { 459 console.log(body); 460 } else if (typeof print === "function") { 461 print(body); 462 } 463 } 464 465 return body; 466 } 467 468 /** 469 * Used by `compile` to generate a reusable function for the given `template`. 470 */ 471 function _compile(template, options) { 472 var args = "view,partials,stack,lookup,escapeHTML,renderSection,render"; 473 var body = parse(template, options); 474 var fn = new Function(args, body); 475 476 // This anonymous function wraps the generated function so we can do 477 // argument coercion, setup some variables, and handle any errors 478 // encountered while executing it. 479 return function (view, partials) { 480 partials = partials || {}; 481 482 var stack = [view]; // context stack 483 484 try { 485 return fn(view, partials, stack, lookup, escapeHTML, renderSection, render); 486 } catch (e) { 487 throw debug(e.error, template, e.line, options.file); 488 } 489 }; 490 } 491 492 // Cache of pre-compiled templates. 493 var _cache = {}; 494 495 /** 496 * Clear the cache of compiled templates. 497 */ 498 function clearCache() { 499 _cache = {}; 500 } 501 502 /** 503 * Compiles the given `template` into a reusable function using the given 504 * `options`. In addition to the options accepted by Mustache.parse, 505 * recognized options include the following: 506 * 507 * - cache Set `false` to bypass any pre-compiled version of the given 508 * template. Otherwise, a given `template` string will be cached 509 * the first time it is parsed 510 */ 511 function compile(template, options) { 512 options = options || {}; 513 514 // Use a pre-compiled version from the cache if we have one. 515 if (options.cache !== false) { 516 if (!_cache[template]) { 517 _cache[template] = _compile(template, options); 518 } 519 520 return _cache[template]; 521 } 522 523 return _compile(template, options); 524 } 525 526 /** 527 * High-level function that renders the given `template` using the given 528 * `view` and `partials`. If you need to use any of the template options (see 529 * `compile` above), you must compile in a separate step, and then call that 530 * compiled function. 531 */ 532 function render(template, view, partials) { 533 return compile(template)(view, partials); 534 } 535 536})(Mustache); 537 538 539;(function($) { 540 $.mustache = function (template, view, partials) { 541 return Mustache.render(template, view, partials); 542 }; 543 544 $.fn.mustache = function (view, partials) { 545 return $(this).map(function (i, elm) { 546 var template = $(elm).html().trim(); 547 var output = $.mustache(template, view, partials); 548 return $(output).get(); 549 }); 550 }; 551 552})(jQuery);
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.