/ * !
* mustache . js - Logic - less { { mustache } } templates with JavaScript
* http : //github.com/janl/mustache.js
* /
var Mustache = ( typeof module !== "undefined" && module . exports ) || { } ;
( function ( exports ) {
exports . name = "mustache.js" ;
exports . version = "0.5.0-dev" ;
exports . tags = [ "{{" , "}}" ] ;
exports . parse = parse ;
exports . compile = compile ;
exports . render = render ;
exports . clearCache = clearCache ;
// This is here for backwards compatibility with 0.4.x.
exports . to _html = function ( template , view , partials , send ) {
var result = render ( template , view , partials ) ;
if ( typeof send === "function" ) {
send ( result ) ;
} else {
return result ;
}
} ;
var _toString = Object . prototype . toString ;
var _isArray = Array . isArray ;
var _forEach = Array . prototype . forEach ;
var _trim = String . prototype . trim ;
var isArray ;
if ( _isArray ) {
isArray = _isArray ;
} else {
isArray = function ( obj ) {
return _toString . call ( obj ) === "[object Array]" ;
} ;
}
var forEach ;
if ( _forEach ) {
forEach = function ( obj , callback , scope ) {
return _forEach . call ( obj , callback , scope ) ;
} ;
} else {
forEach = function ( obj , callback , scope ) {
for ( var i = 0 , len = obj . length ; i < len ; ++ i ) {
callback . call ( scope , obj [ i ] , i , obj ) ;
}
} ;
}
var spaceRe = /^\s*$/ ;
function isWhitespace ( string ) {
return spaceRe . test ( string ) ;
}
var trim ;
if ( _trim ) {
trim = function ( string ) {
return string == null ? "" : _trim . call ( string ) ;
} ;
} else {
var trimLeft , trimRight ;
if ( isWhitespace ( "\xA0" ) ) {
trimLeft = /^\s+/ ;
trimRight = /\s+$/ ;
} else {
// IE doesn't match non-breaking spaces with \s, thanks jQuery.
trimLeft = /^[\s\xA0]+/ ;
trimRight = /[\s\xA0]+$/ ;
}
trim = function ( string ) {
return string == null ? "" :
String ( string ) . replace ( trimLeft , "" ) . replace ( trimRight , "" ) ;
} ;
}
var escapeMap = {
"&" : "&" ,
"<" : "<" ,
">" : ">" ,
'"' : '"' ,
"'" : '''
} ;
function escapeHTML ( string ) {
return String ( string ) . replace ( /&(?!\w+;)|[<>"']/g , function ( s ) {
return escapeMap [ s ] || s ;
} ) ;
}
/ * *
* Adds the ` template ` , ` line ` , and ` file ` properties to the given error
* object and alters the message to provide more useful debugging information .
* /
function debug ( e , template , line , file ) {
file = file || "<template>" ;
var lines = template . split ( "\n" ) ,
start = Math . max ( line - 3 , 0 ) ,
end = Math . min ( lines . length , line + 3 ) ,
context = lines . slice ( start , end ) ;
var c ;
for ( var i = 0 , len = context . length ; i < len ; ++ i ) {
c = i + start + 1 ;
context [ i ] = ( c === line ? " >> " : " " ) + context [ i ] ;
}
e . template = template ;
e . line = line ;
e . file = file ;
e . message = [ file + ":" + line , context . join ( "\n" ) , "" , e . message ] . join ( "\n" ) ;
return e ;
}
/ * *
* Looks up the value of the given ` name ` in the given context ` stack ` .
* /
function lookup ( name , stack , defaultValue ) {
if ( name === "." ) {
return stack [ stack . length - 1 ] ;
}
var names = name . split ( "." ) ;
var lastIndex = names . length - 1 ;
var target = names [ lastIndex ] ;
var value , context , i = stack . length , j , localStack ;
while ( i ) {
localStack = stack . slice ( 0 ) ;
context = stack [ -- i ] ;
j = 0 ;
while ( j < lastIndex ) {
context = context [ names [ j ++ ] ] ;
if ( context == null ) {
break ;
}
localStack . push ( context ) ;
}
if ( context && target in context ) {
value = context [ target ] ;
break ;
}
}
// If the value is a function, call it in the current context.
if ( typeof value === "function" ) {
value = value . call ( localStack [ localStack . length - 1 ] ) ;
}
if ( value == null ) {
return defaultValue ;
}
return value ;
}
function renderSection ( name , stack , callback , inverted ) {
var buffer = "" ;
var value = lookup ( name , stack ) ;
if ( inverted ) {
// From the spec: inverted sections may render text once based on the
// inverse value of the key. That is, they will be rendered if the key
// doesn't exist, is false, or is an empty list.
if ( value == null || value === false || ( isArray ( value ) && value . length === 0 ) ) {
buffer += callback ( ) ;
}
} else if ( isArray ( value ) ) {
forEach ( value , function ( value ) {
stack . push ( value ) ;
buffer += callback ( ) ;
stack . pop ( ) ;
} ) ;
} else if ( typeof value === "object" ) {
stack . push ( value ) ;
buffer += callback ( ) ;
stack . pop ( ) ;
} else if ( typeof value === "function" ) {
var scope = stack [ stack . length - 1 ] ;
var scopedRender = function ( template ) {
return render ( template , scope ) ;
} ;
buffer += value . call ( scope , callback ( ) , scopedRender ) || "" ;
} else if ( value ) {
buffer += callback ( ) ;
}
return buffer ;
}
/ * *
* Parses the given ` template ` and returns the source of a function that ,
* with the proper arguments , will render the template . Recognized options
* include the following :
*
* - file The name of the file the template comes from ( displayed in
* error messages )
* - tags An array of open and close tags the ` template ` uses . Defaults
* to the value of Mustache . tags
* - debug Set ` true ` to log the body of the generated function to the
* console
* - space Set ` true ` to preserve whitespace from lines that otherwise
* contain only a { { tag } } . Defaults to ` false `
* /
function parse ( template , options ) {
options = options || { } ;
var tags = options . tags || exports . tags ,
openTag = tags [ 0 ] ,
closeTag = tags [ tags . length - 1 ] ;
var code = [
'var buffer = "";' , // output buffer
"\nvar line = 1;" , // keep track of source line number
"\ntry {" ,
'\nbuffer += "'
] ;
var spaces = [ ] , // indices of whitespace in code on the current line
hasTag = false , // is there a {{tag}} on the current line?
nonSpace = false ; // is there a non-space char on the current line?
// Strips all space characters from the code array for the current line
// if there was a {{tag}} on it and otherwise only spaces.
var stripSpace = function ( ) {
if ( hasTag && ! nonSpace && ! options . space ) {
while ( spaces . length ) {
code . splice ( spaces . pop ( ) , 1 ) ;
}
} else {
spaces = [ ] ;
}
hasTag = false ;
nonSpace = false ;
} ;
var sectionStack = [ ] , updateLine , nextOpenTag , nextCloseTag ;
var setTags = function ( source ) {
tags = trim ( source ) . split ( /\s+/ ) ;
nextOpenTag = tags [ 0 ] ;
nextCloseTag = tags [ tags . length - 1 ] ;
} ;
var includePartial = function ( source ) {
code . push (
'";' ,
updateLine ,
'\nvar partial = partials["' + trim ( source ) + '"];' ,
'\nif (partial) {' ,
'\n buffer += render(partial,stack[stack.length - 1],partials);' ,
'\n}' ,
'\nbuffer += "'
) ;
} ;
var openSection = function ( source , inverted ) {
var name = trim ( source ) ;
if ( name === "" ) {
throw debug ( new Error ( "Section name may not be empty" ) , template , line , options . file ) ;
}
sectionStack . push ( { name : name , inverted : inverted } ) ;
code . push (
'";' ,
updateLine ,
'\nvar name = "' + name + '";' ,
'\nvar callback = (function () {' ,
'\n return function () {' ,
'\n var buffer = "";' ,
'\nbuffer += "'
) ;
} ;
var openInvertedSection = function ( source ) {
openSection ( source , true ) ;
} ;
var closeSection = function ( source ) {
var name = trim ( source ) ;
var openName = sectionStack . length != 0 && sectionStack [ sectionStack . length - 1 ] . name ;
if ( ! openName || name != openName ) {
throw debug ( new Error ( 'Section named "' + name + '" was never opened' ) , template , line , options . file ) ;
}
var section = sectionStack . pop ( ) ;
code . push (
'";' ,
'\n return buffer;' ,
'\n };' ,
'\n})();'
) ;
if ( section . inverted ) {
code . push ( "\nbuffer += renderSection(name,stack,callback,true);" ) ;
} else {
code . push ( "\nbuffer += renderSection(name,stack,callback);" ) ;
}
code . push ( '\nbuffer += "' ) ;
} ;
var sendPlain = function ( source ) {
code . push (
'";' ,
updateLine ,
'\nbuffer += lookup("' + trim ( source ) + '",stack,"");' ,
'\nbuffer += "'
) ;
} ;
var sendEscaped = function ( source ) {
code . push (
'";' ,
updateLine ,
'\nbuffer += escapeHTML(lookup("' + trim ( source ) + '",stack,""));' ,
'\nbuffer += "'
) ;
} ;
var line = 1 , c , callback ;
for ( var i = 0 , len = template . length ; i < len ; ++ i ) {
if ( template . slice ( i , i + openTag . length ) === openTag ) {
i += openTag . length ;
c = template . substr ( i , 1 ) ;
updateLine = '\nline = ' + line + ';' ;
nextOpenTag = openTag ;
nextCloseTag = closeTag ;
hasTag = true ;
switch ( c ) {
case "!" : // comment
i ++ ;
callback = null ;
break ;
case "=" : // change open/close tags, e.g. {{=<% %>=}}
i ++ ;
closeTag = "=" + closeTag ;
callback = setTags ;
break ;
case ">" : // include partial
i ++ ;
callback = includePartial ;
break ;
case "#" : // start section
i ++ ;
callback = openSection ;
break ;
case "^" : // start inverted section
i ++ ;
callback = openInvertedSection ;
break ;
case "/" : // end section
i ++ ;
callback = closeSection ;
break ;
case "{" : // plain variable
closeTag = "}" + closeTag ;
// fall through
case "&" : // plain variable
i ++ ;
nonSpace = true ;
callback = sendPlain ;
break ;
default : // escaped variable
nonSpace = true ;
callback = sendEscaped ;
}
var end = template . indexOf ( closeTag , i ) ;
if ( end === - 1 ) {
throw debug ( new Error ( 'Tag "' + openTag + '" was not closed properly' ) , template , line , options . file ) ;
}
var source = template . substring ( i , end ) ;
if ( callback ) {
callback ( source ) ;
}
// Maintain line count for \n in source.
var n = 0 ;
while ( ~ ( n = source . indexOf ( "\n" , n ) ) ) {
line ++ ;
n ++ ;
}
i = end + closeTag . length - 1 ;
openTag = nextOpenTag ;
closeTag = nextCloseTag ;
} else {
c = template . substr ( i , 1 ) ;
switch ( c ) {
case '"' :
case "\\" :
nonSpace = true ;
code . push ( "\\" + c ) ;
break ;
case "\r" :
// Ignore carriage returns.
break ;
case "\n" :
spaces . push ( code . length ) ;
code . push ( "\\n" ) ;
stripSpace ( ) ; // Check for whitespace on the current line.
line ++ ;
break ;
default :
if ( isWhitespace ( c ) ) {
spaces . push ( code . length ) ;
} else {
nonSpace = true ;
}
code . push ( c ) ;
}
}
}
if ( sectionStack . length != 0 ) {
throw debug ( new Error ( 'Section "' + sectionStack [ sectionStack . length - 1 ] . name + '" was not closed properly' ) , template , line , options . file ) ;
}
// Clean up any whitespace from a closing {{tag}} that was at the end
// of the template without a trailing \n.
stripSpace ( ) ;
code . push (
'";' ,
"\nreturn buffer;" ,
"\n} catch (e) { throw {error: e, line: line}; }"
) ;
// Ignore `buffer += "";` statements.
var body = code . join ( "" ) . replace ( /buffer \+= "";\n/g , "" ) ;
if ( options . debug ) {
if ( typeof console != "undefined" && console . log ) {
console . log ( body ) ;
} else if ( typeof print === "function" ) {
print ( body ) ;
}
}
return body ;
}
/ * *
* Used by ` compile ` to generate a reusable function for the given ` template ` .
* /
function _compile ( template , options ) {
var args = "view,partials,stack,lookup,escapeHTML,renderSection,render" ;
var body = parse ( template , options ) ;
var fn = new Function ( args , body ) ;
// This anonymous function wraps the generated function so we can do
// argument coercion, setup some variables, and handle any errors
// encountered while executing it.
return function ( view , partials ) {
partials = partials || { } ;
var stack = [ view ] ; // context stack
try {
return fn ( view , partials , stack , lookup , escapeHTML , renderSection , render ) ;
} catch ( e ) {
throw debug ( e . error , template , e . line , options . file ) ;
}
} ;
}
// Cache of pre-compiled templates.
var _cache = { } ;
/ * *
* Clear the cache of compiled templates .
* /
function clearCache ( ) {
_cache = { } ;
}
/ * *
* Compiles the given ` template ` into a reusable function using the given
* ` options ` . In addition to the options accepted by Mustache . parse ,
* recognized options include the following :
*
* - cache Set ` false ` to bypass any pre - compiled version of the given
* template . Otherwise , a given ` template ` string will be cached
* the first time it is parsed
* /
function compile ( template , options ) {
options = options || { } ;
// Use a pre-compiled version from the cache if we have one.
if ( options . cache !== false ) {
if ( ! _cache [ template ] ) {
_cache [ template ] = _compile ( template , options ) ;
}
return _cache [ template ] ;
}
return _compile ( template , options ) ;
}
/ * *
* High - level function that renders the given ` template ` using the given
* ` view ` and ` partials ` . If you need to use any of the template options ( see
* ` compile ` above ) , you must compile in a separate step , and then call that
* compiled function .
* /
function render ( template , view , partials ) {
return compile ( template ) ( view , partials ) ;
}
} ) ( Mustache ) ;