package/package.json000644 000770 000024 0000002374 13007614464013024 0ustar00000000 000000 { "name": "dot", "description": "Concise and fast javascript templating compatible with nodejs and other javascript environments", "keywords": [ "template", "fast", "simple", "templating" ], "version": "1.1.1", "main": "index", "bin": { "dottojs": "./bin/dot-packer" }, "homepage": "http://github.com/olado/doT", "repository": "git://github.com/olado/doT.git", "author": "Laura Doktorova ", "engines": [ "node >=0.2.6" ], "license": "MIT", "scripts": { "eslint": "if-node-version '>=4' eslint *.js --ignore-pattern *.min.js", "test-cov": "nyc mocha test/*.test.js", "test": "npm run eslint && npm run test-cov", "bundle": "uglifyjs doT.js -o doT.min.js -c -m --preamble '/* Laura Doktorova https://github.com/olado/doT */'", "prepublish": "npm run bundle" }, "dependencies": {}, "devDependencies": { "commander": "*", "coveralls": "^2.11.14", "eslint": "^3.9.1", "if-node-version": "^1.1.0", "jshint": "*", "mkdirp": "*", "mocha": "*", "nyc": "^8.3.2", "pre-commit": "^1.1.3", "uglify-js": "*" }, "nyc": { "exclude": [ "test", "node_modules" ], "reporter": [ "lcov", "text-summary" ] } } package/.npmignore000644 000770 000024 0000001102 13006720616012515 0ustar00000000 000000 # Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules # MacOS directory info files .DS_Store package/README.md000644 000770 000024 0000006333 13006720616012010 0ustar00000000 000000 # doT Created in search of the fastest and concise JavaScript templating function with emphasis on performance under V8 and nodejs. It shows great performance for both nodejs and browsers. doT.js is fast, small and has no dependencies. [![Build Status](https://travis-ci.org/olado/doT.svg?branch=master)](https://travis-ci.org/olado/doT) [![npm version](https://badge.fury.io/js/dot.svg)](https://www.npmjs.com/package/dot) [![Coverage Status](https://coveralls.io/repos/github/olado/doT/badge.svg?branch=master)](https://coveralls.io/github/olado/doT?branch=master) ## Features custom delimiters runtime evaluation runtime interpolation compile-time evaluation partials support conditionals support array iterators encoding control whitespace - strip or preserve streaming friendly use it as logic-less or with logic, it is up to you ## Docs, live playground and samples http://olado.github.com/doT (todo: update docs with new features added in version 1.0.0) ## New in version 1.0.0 ####Added parameters support in partials {{##def.macro:param:
{{=param.foo}}
#}} {{#def.macro:myvariable}} ####Node module now supports auto-compilation of dot templates from specified path var dots = require("dot").process({ path: "./views"}); This will compile .def, .dot, .jst files found under the specified path. Details * It ignores sub-directories. * Template files can have multiple extensions at the same time. * Files with .def extension can be included in other files via {{#def.name}} * Files with .dot extension are compiled into functions with the same name and can be accessed as renderer.filename * Files with .jst extension are compiled into .js files. Produced .js file can be loaded as a commonJS, AMD module, or just installed into a global variable (default is set to window.render) * All inline defines defined in the .jst file are compiled into separate functions and are available via _render.filename.definename Basic usage: ``` var dots = require("dot").process({path: "./views"}); dots.mytemplate({foo:"hello world"}); ``` The above snippet will: * Compile all templates in views folder (.dot, .def, .jst) * Place .js files compiled from .jst templates into the same folder These files can be used with require, i.e. require("./views/mytemplate") * Return an object with functions compiled from .dot templates as its properties * Render mytemplate template ####CLI tool to compile dot templates into js files ./bin/dot-packer -s examples/views -d out/views ## Example for express Many people are using doT with express. I added an example of the best way of doing it examples/express: [doT with express](examples/express) ## Notes doU.js is here only so that legacy external tests do not break. Use doT.js. doT.js with doT.templateSettings.append=false provides the same performance as doU.js. ## Author Laura Doktorova [@olado](http://twitter.com/olado) ## License doT is licensed under the MIT License. (See LICENSE-DOT)

logo by Kevin Kirchner

Thank you [@KevinKirchner](https://twitter.com/kevinkirchner) for the logo. package/doT.js000644 000770 000024 0000012161 13007611554011612 0ustar00000000 000000 // doT.js // 2011-2014, Laura Doktorova, https://github.com/olado/doT // Licensed under the MIT license. (function() { "use strict"; var doT = { version: "1.1.1", templateSettings: { evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g, interpolate: /\{\{=([\s\S]+?)\}\}/g, encode: /\{\{!([\s\S]+?)\}\}/g, use: /\{\{#([\s\S]+?)\}\}/g, useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g, define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, defineParams:/^\s*([\w$]+):([\s\S]+)/, conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, varname: "it", strip: true, append: true, selfcontained: false, doNotSkipEncoded: false }, template: undefined, //fn, compile template compile: undefined, //fn, for express log: true }, _globals; doT.encodeHTMLSource = function(doNotSkipEncoded) { var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }, matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g; return function(code) { return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : ""; }; }; _globals = (function(){ return this || (0,eval)("this"); }()); /* istanbul ignore else */ if (typeof module !== "undefined" && module.exports) { module.exports = doT; } else if (typeof define === "function" && define.amd) { define(function(){return doT;}); } else { _globals.doT = doT; } var startend = { append: { start: "'+(", end: ")+'", startencode: "'+encodeHTML(" }, split: { start: "';out+=(", end: ");out+='", startencode: "';out+=encodeHTML(" } }, skip = /$^/; function resolveDefs(c, block, def) { return ((typeof block === "string") ? block : block.toString()) .replace(c.define || skip, function(m, code, assign, value) { if (code.indexOf("def.") === 0) { code = code.substring(4); } if (!(code in def)) { if (assign === ":") { if (c.defineParams) value.replace(c.defineParams, function(m, param, v) { def[code] = {arg: param, text: v}; }); if (!(code in def)) def[code]= value; } else { new Function("def", "def['"+code+"']=" + value)(def); } } return ""; }) .replace(c.use || skip, function(m, code) { if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) { if (def[d] && def[d].arg && param) { var rw = (d+":"+param).replace(/'|\\/g, "_"); def.__exp = def.__exp || {}; def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2"); return s + "def.__exp['"+rw+"']"; } }); var v = new Function("def", "return " + code)(def); return v ? resolveDefs(c, v, def) : v; }); } function unescape(code) { return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " "); } doT.template = function(tmpl, c, def) { c = c || doT.templateSettings; var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv, str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl; str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ") .replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""): str) .replace(/'|\\/g, "\\$&") .replace(c.interpolate || skip, function(m, code) { return cse.start + unescape(code) + cse.end; }) .replace(c.encode || skip, function(m, code) { needhtmlencode = true; return cse.startencode + unescape(code) + cse.end; }) .replace(c.conditional || skip, function(m, elsecase, code) { return elsecase ? (code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") : (code ? "';if(" + unescape(code) + "){out+='" : "';}out+='"); }) .replace(c.iterate || skip, function(m, iterate, vname, iname) { if (!iterate) return "';} } out+='"; sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate); return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"":">",'"':""","'":"'","/":"/"},t=e?/[&<>"'\/]/g:/&(?!#?\w+;)|<|>|"|'|\//g;return function(e){return e?e.toString().replace(t,function(e){return n[e]||e}):""}},t=function(){return this||(0,eval)("this")}(),"undefined"!=typeof module&&module.exports?module.exports=r:"function"==typeof define&&define.amd?define(function(){return r}):t.doT=r;var o={append:{start:"'+(",end:")+'",startencode:"'+encodeHTML("},split:{start:"';out+=(",end:");out+='",startencode:"';out+=encodeHTML("}},a=/$^/;r.template=function(c,i,u){i=i||r.templateSettings;var d,s,p=i.append?o.append:o.split,l=0,f=i.use||i.define?e(i,c,u||{}):c;f=("var out='"+(i.strip?f.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ").replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""):f).replace(/'|\\/g,"\\$&").replace(i.interpolate||a,function(e,t){return p.start+n(t)+p.end}).replace(i.encode||a,function(e,t){return d=!0,p.startencode+n(t)+p.end}).replace(i.conditional||a,function(e,t,r){return t?r?"';}else if("+n(r)+"){out+='":"';}else{out+='":r?"';if("+n(r)+"){out+='":"';}out+='"}).replace(i.iterate||a,function(e,t,r,o){return t?(l+=1,s=o||"i"+l,t=n(t),"';var arr"+l+"="+t+";if(arr"+l+"){var "+r+","+s+"=-1,l"+l+"=arr"+l+".length-1;while("+s+"\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '') .split(tb).join(te +'\x1b') .split(te); for (m=0,l=arr.length; m < l; m++) { str += arr[m].charAt(0) !== '\x1b' ? "out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ? ';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ? ';out+=(' + arr[m].substr(2) + ").toString().replace(/&(?!\\w+;)/g, '&').split('<').join('<').split('>').join('>').split('" + '"' + "').join('"').split(" + '"' + "'" + '"' + ").join(''').split('/').join('/');" : ';' + arr[m].substr(1))); } str = ('var out="";'+str+';return out;') .split("out+='';").join('') .split('var out="";out+=').join('var out='); try { return new Function(conf.varname, str); } catch (e) { if (typeof console !== 'undefined') console.log("Could not create a template function: " + str); throw e; } }; }()); package/index.js000644 000770 000024 0000012114 13007426343012171 0ustar00000000 000000 /* doT + auto-compilation of doT templates * * 2012, Laura Doktorova, https://github.com/olado/doT * Licensed under the MIT license * * Compiles .def, .dot, .jst files found under the specified path. * It ignores sub-directories. * Template files can have multiple extensions at the same time. * Files with .def extension can be included in other files via {{#def.name}} * Files with .dot extension are compiled into functions with the same name and * can be accessed as renderer.filename * Files with .jst extension are compiled into .js files. Produced .js file can be * loaded as a commonJS, AMD module, or just installed into a global variable * (default is set to window.render). * All inline defines defined in the .jst file are * compiled into separate functions and are available via _render.filename.definename * * Basic usage: * var dots = require("dot").process({path: "./views"}); * dots.mytemplate({foo:"hello world"}); * * The above snippet will: * 1. Compile all templates in views folder (.dot, .def, .jst) * 2. Place .js files compiled from .jst templates into the same folder. * These files can be used with require, i.e. require("./views/mytemplate"). * 3. Return an object with functions compiled from .dot templates as its properties. * 4. Render mytemplate template. */ var fs = require("fs"), doT = module.exports = require("./doT"); doT.process = function(options) { //path, destination, global, rendermodule, templateSettings return new InstallDots(options).compileAll(); }; function InstallDots(o) { this.__path = o.path || "./"; if (this.__path[this.__path.length-1] !== '/') this.__path += '/'; this.__destination = o.destination || this.__path; if (this.__destination[this.__destination.length-1] !== '/') this.__destination += '/'; this.__global = o.global || "window.render"; this.__rendermodule = o.rendermodule || {}; this.__settings = o.templateSettings ? copy(o.templateSettings, copy(doT.templateSettings)) : undefined; this.__includes = {}; } InstallDots.prototype.compileToFile = function(path, template, def) { def = def || {}; var modulename = path.substring(path.lastIndexOf("/")+1, path.lastIndexOf(".")) , defs = copy(this.__includes, copy(def)) , settings = this.__settings || doT.templateSettings , compileoptions = copy(settings) , defaultcompiled = doT.template(template, settings, defs) , exports = [] , compiled = "" , fn; for (var property in defs) { if (defs[property] !== def[property] && defs[property] !== this.__includes[property]) { fn = undefined; if (typeof defs[property] === 'string') { fn = doT.template(defs[property], settings, defs); } else if (typeof defs[property] === 'function') { fn = defs[property]; } else if (defs[property].arg) { compileoptions.varname = defs[property].arg; fn = doT.template(defs[property].text, compileoptions, defs); } if (fn) { compiled += fn.toString().replace('anonymous', property); exports.push(property); } } } compiled += defaultcompiled.toString().replace('anonymous', modulename); fs.writeFileSync(path, "(function(){" + compiled + "var itself=" + modulename + ", _encodeHTML=(" + doT.encodeHTMLSource.toString() + "(" + (settings.doNotSkipEncoded || '') + "));" + addexports(exports) + "if(typeof module!=='undefined' && module.exports) module.exports=itself;else if(typeof define==='function')define(function(){return itself;});else {" + this.__global + "=" + this.__global + "||{};" + this.__global + "['" + modulename + "']=itself;}}());"); }; function addexports(exports) { var ret = ''; for (var i=0; i< exports.length; i++) { ret += "itself." + exports[i]+ "=" + exports[i]+";"; } return ret; } function copy(o, to) { to = to || {}; for (var property in o) { to[property] = o[property]; } return to; } function readdata(path) { var data = fs.readFileSync(path); if (data) return data.toString(); console.log("problems with " + path); } InstallDots.prototype.compilePath = function(path) { var data = readdata(path); if (data) { return doT.template(data, this.__settings || doT.templateSettings, copy(this.__includes)); } }; InstallDots.prototype.compileAll = function() { if (doT.log) console.log("Compiling all doT templates..."); var defFolder = this.__path, sources = fs.readdirSync(defFolder), k, l, name; for( k = 0, l = sources.length; k < l; k++) { name = sources[k]; if (/\.def(\.dot|\.jst)?$/.test(name)) { if (doT.log) console.log("Loaded def " + name); this.__includes[name.substring(0, name.indexOf('.'))] = readdata(defFolder + name); } } for( k = 0, l = sources.length; k < l; k++) { name = sources[k]; if (/\.dot(\.def|\.jst)?$/.test(name)) { if (doT.log) console.log("Compiling " + name + " to function"); this.__rendermodule[name.substring(0, name.indexOf('.'))] = this.compilePath(defFolder + name); } if (/\.jst(\.dot|\.def)?$/.test(name)) { if (doT.log) console.log("Compiling " + name + " to file"); this.compileToFile(this.__destination + name.substring(0, name.indexOf('.')) + '.js', readdata(defFolder + name)); } } return this.__rendermodule; }; package/bower.json000644 000770 000024 0000000671 13007424531012537 0ustar00000000 000000 { "name": "dot", "description": "Concise and fast javascript templating compatible with nodejs and other javascript environments", "main": "doT.js", "authors": [ "Laura Doktorova " ], "license": "MIT", "keywords": [ "template", "fast", "simple", "templating" ], "homepage": "https://github.com/olado/doT", "ignore": [ "node_modules", "bower_components", "test" ] } package/bin/dot-packer000755 000770 000024 0000002642 13006720616013257 0ustar00000000 000000 #!/usr/bin/env node /* Continuation of https://github.com/Katahdin/dot-packer */ var program = require('commander'), dot = require('../'); program .version('0.0.1') .usage('dottojs') .option('-s, --source [value]', 'source folder/file path') .option('-d, --dest [value]', 'destination folder') .option('-g, --global [value]', 'the global variable to install the templates in',"window.render") .option('-p, --package [value]', 'if specified, package all templates from destination folder into specified file') .parse(process.argv); if (program.dest) mkdirordie(program.dest); if (program.package) { var li = program.package.lastIndexOf('/'); if (li>0) mkdirordie(program.package.substring(0, li)); } function mkdirordie(path) { require("mkdirp")(path, function (err) { if (err) { console.error(err); process.exit(1); } }); } var render = dot.process({ path: program.source, destination: program.dest, global: program.global }); if (program.package) { console.log("Packaging all files into " + program.package); var fs = require("fs"); var files = []; var dest = program.dest || './'; if (dest[dest.length-1] !== '/') dest += '/'; var sources = fs.readdirSync(dest); for(k = 0; k < sources.length; k++) { name = sources[k]; if (/\.js$/.test(name)) { files.push(dest + name); } } var result = require("uglify-js").minify(files); fs.writeFileSync(program.package, result.code); } package/.travis.yml000644 000770 000024 0000000157 13007614551012641 0ustar00000000 000000 language: node_js node_js: - "0.12" - "4" - "6" - "7" after_script: - coveralls < coverage/lcov.info package/LICENSE-DOT.txt000644 000770 000024 0000002230 13006720616012770 0ustar00000000 000000 Copyright (c) 2011 Laura Doktorova Software includes portions from jQote2 Copyright (c) 2010 aefxx, http://aefxx.com/ licensed under the MIT license. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package/examples/customdoT.js000644 000770 000024 0000001222 13006720616014656 0ustar00000000 000000 (function() { var doT = require('../doT.js'), data = { f1: 1, f2: 2, f3: 3}, snippet = '

Just static text

Here is a simple case

Next we will use a JavaScript block:
'; doT.templateSettings = { evaluate : /\<\?([\s\S]+?)\?\>/g, interpolate : /\<\?=([\s\S]+?)\?\>/g, varname : 'it', strip: true }; var doTCompiled = doT.template(snippet); console.log("Generated function: \n" + doTCompiled.toString()); console.log("Result of calling with " + JSON.stringify(data) + " :\n" + doTCompiled(data)); }()); package/examples/withdoT.js000644 000770 000024 0000001560 13006720616014324 0ustar00000000 000000 (function() { var doT = require('../doT.js'), fs = require('fs'), data = { name: "Foo", f1: 1, f2: 2, f3: 3, altEmail: "conditional works", farray:[{farray:[1,2,3,[11,22,33]],person:{name:'Ell',age:23}},{farray:{how:'really'}}, {farray:[5,6,7,8]}]}, defs = { a: 100, b: 200}; defs.loadfile = function(path) { return fs.readFileSync(process.argv[1].replace(/\/[^\/]*$/,path)); }; defs.externalsnippet = defs.loadfile('/snippet.txt'); fs.readFile(process.argv[1].replace(/\/[^\/]*$/,'/advancedsnippet.txt'), function (err, snippet) { if (err) { console.log("Error reading advancedsnippet.txt " + err); } else { var doTCompiled = doT.template(snippet.toString(), undefined, defs); console.log("Generated function: \n" + doTCompiled.toString()); console.log("Result of calling with " + JSON.stringify(data) + " :\n" + doTCompiled(data)); } }); }()); package/examples/advancedsnippet.txt000644 000770 000024 0000005024 13006720616016254 0ustar00000000 000000 Advanced templating: illustrates defines and includes. Include external snippet defined in a variable: {{#def.externalsnippet}} Load external template from a file: {{#def.loadfile('/snippet.txt')}} Load external template from a file and cache in a variable: {{#def['snippet.txt'] || (def['snippet.txt'] = def.loadfile('/snippet.txt'))}} Use cached file again: {{#def['snippet.txt']}} Here is a def block that will be used later. This snippet can be referenced from external templates too: {{##def.snippet1: Some snippet that will be included {{#def.a}} later {{=it.f1}} #}} First use of snippet1: {{#def.snippet1}} Second use of snippet1: {{#def.snippet1}} Include snippet1 if true: {{# true && def.snippet1 }} Runtime and Compile time evaluation used together: {{= it.f3 + {{#def.a + def.b}} }} Include xyz or insert 'not found': {{#def.xyz || 'not found'}} Set xyz to 1 and exclude result from output: {{##def.xyz=1#}} is identical to {{#(def.xyz=1) && ""}} Compare xyz to 1, show 'xyz is not 1' if false: {{#def.xyz === 1 || 'xyz is not 1'}} {{ if ({{#!def.abc}}) { }} {{#def.abc}} is falsy {{ } }} {{ if ({{#def.xyz === 1}}) { }} if(true) block {{ } }} {{##def.fntest = function() { return "Function test worked!"; } #}} {{#def.fntest()}} Conditionals: {{? !it.altEmail }}

second email: {{= it.altEmail }}

{{?? true }} else case worked {{?}} Array iterators {{~ it.farray :p }}

{{=p.farray}}

{{~ p.farray :value:i }}

{{=i}}: {{=value}}

{{~ value :w }}

{{=w}}

{{~}} {{~}} {{~}} {{~ ["apple", "banana", "orange"] :k}} {{=k}} {{~}} {{~ (function(){ return [1,2,3]})() :k}} {{=k}} {{~}} {{ function children(it) { }} {{?it.Nodes.length}}
    {{~ it.Nodes :p}}
  • {{=p.title}} {{children(p);}}
  • {{~}}
{{?}} {{ } }} {{ children( {Nodes:[ {title:"1.1", Nodes:[ {title:"1.1.1", Nodes:[]}, {title:"1.1.2", Nodes:[]}] }, { title:"1.2", Nodes:[]}, { title:"1.3", Nodes:[]}], title:"1" } ); }} {{##def.block:param:
{{=param}}
#}} {{##def.block1:param:
{{=param.a}}
#}} {{#(def.block:'text' || '') + def.block:5}} {{#def.block:it.f3 || ''}} {{#def.block:"lala tralala" || ''}} {{#def.block1:{a:1, b:2} || ''}} {{##def.testFunctionWithParam = function(str) { return "My name is: " + str; } #}} {{##def.mytestparam: {{=it.name}} #}} {{#def.testFunctionWithParam(def.mytestparam)}} {{#def.testFunctionWithParam("\{\{=it.name\}\}")}} {{##def.testParamDef:myparam: My name is: {{=myparam}} #}} {{#def.testParamDef:it.name}} The end package/examples/browsersample.html000644 000770 000024 0000002603 13006720616016116 0ustar00000000 000000
package/examples/express/package.json000644 000770 000024 0000000263 13006720616016322 0ustar00000000 000000 { "name": "dot-express-example", "description": "doT express example", "version": "0.0.1", "private": true, "dependencies": { "express": "4.x", "dot": "*" } } package/examples/express/index.js000644 000770 000024 0000000026 13006720616015476 0ustar00000000 000000 require("./lib/app"); package/examples/express/lib/app.js000644 000770 000024 0000001124 13006720616015715 0ustar00000000 000000 require("dot").process({ global: "_page.render" , destination: __dirname + "/render/" , path: (__dirname + "/../templates") }); var express = require('express') , http = require('http') , app = express() , render = require('./render') ; app.get('/', function(req, res){ res.send(render.dashboard({text:"Good morning!"})); }); app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }); var httpServer = http.createServer(app); httpServer.listen(3000, function() { console.log('Listening on port %d', httpServer.address().port); }); package/examples/express/lib/render/index.js000644 000770 000024 0000001045 13006720616017525 0ustar00000000 000000 var path = require('path') , fs = require('fs'); function req(name) { var module = require("./" + name); delete exports[name]; return exports[name] = module; } fs.readdirSync(__dirname).forEach(function(file) { if ((file === 'index.js') || (file[0] === '_')) { return; } var ext = path.extname(file); var stats = fs.statSync(__dirname + '/' + file); if (stats.isFile() && !(ext in require.extensions)) { return; } var basename = path.basename(file, '.js'); exports.__defineGetter__(basename, function(){ return req(basename); }); }); package/examples/express/templates/dashboard.jst000644 000770 000024 0000000057 13006720616020504 0ustar00000000 000000 {{##def.test: {{=it.text}} #}} {{#def.test}} package/examples/express/templates/login.jst000644 000770 000024 0000001412 13006720616017661 0ustar00000000 000000
Signin

Signup

package/examples/snippet.txt000644 000770 000024 0000000433 13006720616014565 0ustar00000000 000000 Core templating

Just static text

Interpolation {{=it.f1 + it.f3}}

JavaScript block evaluation {{ for(var i=0; i < it.f2; i++) { console.log("Pass\t" + i); }}
{{=it.f3}}
{{ } }}
Encoding {{!''}}
package/examples/views/multidef.def.jst000644 000770 000024 0000000303 13006720616016563 0ustar00000000 000000 {{##def.template1:
{{=it.bar}}
/* Including one.def file here */ {{#def.one}} #}} {{##def.template2:param:
{{=param.bar}}
#}} package/examples/views/one.def000644 000770 000024 0000000110 13006720616014730 0ustar00000000 000000 /* This file can be inserted into other files */
{{=it.foo}}
package/examples/views/two.dot.jst000644 000770 000024 0000000517 13006720616015622 0ustar00000000 000000 /* Including multidef file */ {{#def.multidef}}
/* Including one.def file */ {{#def.one}} /* Using sub-define from multidef */ {{#def.template2:{bar:"hello", class:"hidden"} }} /* Using sub-define from multidef */ {{ var data = {bar:"world", class:"visible"}; }} {{#def.template2:data}}
package/.eslintrc.yml000644 000770 000024 0000000764 13007426603013157 0ustar00000000 000000 env: node: true extends: 'eslint:recommended' globals: define: false parserOptions: ecmaVersion: 5 rules: no-trailing-spaces: 2 linebreak-style: [ 2, unix ] semi: [ 2, always ] valid-jsdoc: [ 2, { requireReturn: false } ] no-unused-vars: [ 2, { args: none } ] no-console: 0 block-scoped-var: 2 dot-location: [ 2, property ] dot-notation: 2 no-else-return: 2 no-eq-null: 2 no-fallthrough: 2 no-return-assign: 2 no-use-before-define: [ 2, nofunc ] no-path-concat: 2 package/benchmarks/compileBench.js000644 000770 000024 0000005627 13006720616015601 0ustar00000000 000000 (function() { var jslitmus, _, doU, doT, data = { f1: 1, f2: 2, f3: 3, f4: "http://bebedo.com/laura"}, snippet = "

Just static text

\

Here is a simple {{=it.f1}}

\
test {{=it.f2}}\
{{=it.f3}}
\
{{!it.f4}}
\
"; if (typeof module !== 'undefined' && module.exports) { runTests(); } else { window.onload = runTestsInBrowser; } function testsetup(snippet) { jslitmus.test('doU.js', function() { doU.template(snippet); }); jslitmus.test('doU.js - looping', function(count) { while (count--) { doU.template(snippet); } }); jslitmus.test('doT.js - using this', function() { doT.template(snippet); }); jslitmus.test('doT.js - using this - looping', function(count) { while (count--) { doT.template(snippet); } }); } function runTests() { //var util = require('util'); jslitmus = require('./jslitmus.js'); doU = require('./templating/doU.js'); doT = require('./templating/doT.js'); var passOne = 0; console.log("*** Compilation speed test"); console.log("*** Small template length: " + snippet.length); testsetup(snippet); // Log the test results jslitmus.on('complete', function(test) { //console.log(util.inspect(process.memoryUsage())); console.log(test.toString()); }); // 'all_complete' fires when all tests have finished. jslitmus.on('all_complete', function() { switch (passOne) { case 0: passOne++; for(var i=0; i<5; i++) { snippet += snippet; } console.log("*** Medium template length: " + snippet.length); break; case 1: passOne++; for(var i=0; i<3; i++) { snippet += snippet; } console.log("*** Large template length: " + snippet.length); break; default: return; } jslitmus.clearAll(); testsetup(snippet); jslitmus.runAll(); }); // Run it! jslitmus.runAll(); } function runTestsInBrowser() { jslitmus = window.jslitmus;doU = window.doU;doT = window.doT; var resultTmpl = doT.template("

Template length : {{=it.size}}

"); var currentSet = document.getElementById('small'); testsetup(snippet); // 'complete' fires for each test when it finishes. jslitmus.on('complete', function(test) { // Output test results currentSet.innerHTML += test + '
'; }); // 'all_complete' fires when all tests have finished. jslitmus.on('all_complete', function() { // Get the results image URL var url = jslitmus.getGoogleChart(); if (currentSet.id === 'small') { currentSet.innerHTML += resultTmpl({size: snippet.length, url: url}); setTimeout(function() { jslitmus.clearAll(); currentSet = document.getElementById('large'); for(var i=0; i<8; i++) { snippet += snippet; } testsetup(snippet); jslitmus.runAll(); }, 10); } else { currentSet.innerHTML += resultTmpl({size: snippet.length, url: url}); } }); // Run it! jslitmus.runAll(); } })(); package/benchmarks/jslitmus.js000644 000770 000024 0000036223 13006720616015057 0ustar00000000 000000 // jslitmus.js // // Copyright (c) 2010, Robert Kieffer, http://broofa.com // Available under MIT license (http://en.wikipedia.org/wiki/MIT_License) (function() { var root = this; // // Platform detect // var platform = (function() { // Platform info object var p = { name: null, version: null, os: null, description: 'unknown platform', toString: function() {return this.description;} }; if (root.navigator) { var ua = navigator.userAgent; // Detect OS var oses = 'Windows|iPhone OS|(?:Intel |PPC )?Mac OS X|Linux'; p.os = new RegExp('((' + oses + ') +[^ \);]*)').test(ua) ? RegExp.$1.replace(/_/g, '.') : null; // Detect expected names p.name = /(Chrome|MSIE|Safari|Opera|Firefox|Minefield)/.test(ua) ? RegExp.$1 : null; // Detect version if (p.name == 'Opera') { p.version = opera.name; } else if (p.name) { var vre = new RegExp('(Version|' + p.name + ')[ \/]([^ ;]*)'); p.version = vre.test(ua) ? RegExp.$2 : null; } } else if (root.process && process.platform) { // Support node.js (see http://nodejs.org) p.name = 'node'; p.version = process.version; p.os = process.platform; } // Set the description var d = []; if (p.name) d.push(p.name); if (p.version) d.push(' ' + p.version); if (p.os) d.push(' on ' + p.os); if (d.length) p.description = d.join(''); return p; })(); // // Context-specific initialization // var sys = null, querystring = null; if (platform.name == 'node') { util = require('util'); querystring = require('querystring'); } // // Misc convenience methods // function log(msg) { if (typeof(console) != 'undefined') { console.log(msg); } else if (sys) { util.log(msg); } } // nil function function nilf(x) { return x; } // Copy properties function extend(dst, src) { for (var k in src) { dst[k] = src[k]; } return dst; } // Array: apply f to each item in a function forEach(a, f) { for (var i = 0, il = (a && a.length); i < il; i++) { var o = a[i]; f(o, i); } } // Array: return array of all results of f(item) function map(a, f) { var o, res = []; for (var i = 0, il = (a && a.length); i < il; i++) { var o = a[i]; res.push(f(o, i)); } return res; } // Array: filter out items for which f(item) is falsy function filter(a, f) { var o, res = []; for (var i = 0, il = (a && a.length); i < il; i++) { var o = a[i]; if (f(o, i)) res.push(o); } return res; } // Array: IE doesn't have indexOf in some cases function indexOf(a, o) { if (a.indexOf) return a.indexOf(o); for (var i = 0, l = a.length; i < l; i++) if (a[i] === o) return i; return -1; } // Enhanced escape() function escape2(s) { s = s.replace(/,/g, '\\,'); s = querystring ? querystring.escape(s) : escape(s); s = s.replace(/\+/g, '%2b'); s = s.replace(/ /g, '+'); return s; } // join(), for objects. Creates url query param-style strings by default function join(o, delimit1, delimit2) { var asQuery = !delimit1 && !delimit2; if (asQuery) { delimit1 = '&'; delimit2 = '='; } var pairs = []; for (var key in o) { var value = o[key]; if (asQuery) value = escape2(value); pairs.push(key + delimit2 + o[key]); } return pairs.join(delimit1); } // split(), for object strings. Parses url query param strings by default function split(s, delimit1, delimit2) { var asQuery = !delimit1 && !delimit2; if (asQuery) { s = s.replace(/.*[?#]/, ''); delimit1 = '&'; delimit2 = '='; } if (match) { var o = query.split(delimit1); for (var i = 0; i < o.length; i++) { var pair = o[i].split(new RegExp(delimit2 + '+')); var key = pair.shift(); var value = (asQuery && pair.length > 1) ? pair.join(delimit2) : pair[0]; o[key] = value; } } return o; } // Round x to d significant digits function sig(x, d) { var exp = Math.ceil(Math.log(Math.abs(x))/Math.log(10)), f = Math.pow(10, exp-d); return Math.round(x/f)*f; } // Convert x to a readable string version function humanize(x, sd) { var ax = Math.abs(x), res; sd = sd | 4; // significant digits if (ax == Infinity) { res = ax > 0 ? 'Infinity' : '-Infinity'; } else if (ax > 1e9) { res = sig(x/1e9, sd) + 'G'; } else if (ax > 1e6) { res = sig(x/1e6, sd) + 'M'; } else if (ax > 1e3) { res = sig(x/1e3, sd) + 'k'; } else if (ax > .01) { res = sig(x, sd); } else if (ax > 1e-3) { res = sig(x/1e-3, sd) + 'm'; } else if (ax > 1e-6) { res = sig(x/1e-6, sd) + '\u00b5'; // Greek mu } else if (ax > 1e-9) { res = sig(x/1e-9, sd) + 'n'; } else { res = x ? sig(x, sd) : 0; } // Turn values like "1.1000000000005" -> "1.1" res = (res + '').replace(/0{5,}\d*/, ''); return res; } // Node.js-inspired event emitter API, with some enhancements. function EventEmitter() { var ee = this; var listeners = {}; extend(ee, { on: function(e, f) { if (!listeners[e]) listeners[e] = []; listeners[e].push(f); }, removeListener: function(e, f) { listeners[e] = filter(listeners[e], function(l) { return l != f; }); }, removeAllListeners: function(e) { listeners[e] = []; }, emit: function(e) { var args = Array.prototype.slice.call(arguments, 1); forEach([].concat(listeners[e], listeners['*']), function(l) { ee._emitting = e; if (l) l.apply(ee, args); }); delete ee._emitting; } }); } // // Test class // /** * Test manages a single test (created with JSLitmus.test()) */ function Test(name, f) { var test = this; // Test instances get EventEmitter API EventEmitter.call(test); if (!f) throw new Error('Undefined test function'); if (!/function[^\(]*\(([^,\)]*)/.test(f)) { throw new Error('"' + name + '" test: Invalid test function'); } // If the test function takes an argument, we assume it does the iteration // for us var isLoop = !!RegExp.$1; /** * Reset test state */ function reset() { delete test.count; delete test.time; delete test.running; test.emit('reset', test); return test; } function clone() { var test = extend(new Test(name, f), test); return test.reset(); } /** * Run the test n times, and use the best results */ function bestOf(n) { var best = null; while (n--) { var t = clone(); t.run(null, true); if (!best || t.period < best.period) { best = t; } } extend(test, best); } /** * Start running a test. Default is to run the test asynchronously (via * setTimeout). Can be made synchronous by passing true for 2nd param */ function run(count, synchronous) { count = count || test.INIT_COUNT; test.running = true; if (synchronous) { _run(count, synchronous); } else { setTimeout(function() { _run(count); }, 1); } return test; } /** * Run, for real */ function _run(count, noTimeout) { try { var start, f = test.f, now, i = count; // Start the timer start = new Date(); // Run the test code test.count = count; test.time = 0; test.period = 0; test.emit('start', test); if (isLoop) { // Test code does it's own iteration f(count); } else { // Do the iteration ourselves while (i--) f(); } // Get time test took (in secs) test.time = Math.max(1,new Date() - start)/1000; // Store iteration count and per-operation time taken test.count = count; test.period = test.time/count; // Do we need to keep running? test.running = test.time < test.MIN_TIME; // Publish results test.emit('results', test); // Set up for another run, if needed if (test.running) { // Use an iteration count that will (we hope) get us close to the // MAX_COUNT time. var x = test.MIN_TIME/test.time; var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2)))); count *= pow; if (count > test.MAX_COUNT) { throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.'); } if (noTimeout) { _run(count, noTimeout); } else { run(count); } } else { test.emit('complete', test); } } catch (err) { log(err); // Exceptions are caught and displayed in the test UI test.emit('error', err); } return test; } /** * Get the number of operations per second for this test. * * @param normalize if true, iteration loop overhead taken into account. * Note that normalized tests may return Infinity if the * test time is of the same order as the calibration time. */ function getHz(normalize) { var p = test.period; // Adjust period based on the calibration test time if (normalize) { var cal = test.isLoop ? Test.LOOP_CAL : Test.NOLOOP_CAL; if (!cal.period) { // Run calibration if needed cal.MIN_TIME = .3; cal.bestOf(3); } // Subtract off calibration time. In theory this should never be // negative, but in practice the calibration times are affected by a // variety of factors so just clip to zero and let users test for // getHz() == Infinity p = Math.max(0, p - cal.period); } return sig(1/p, 4); } // Set properties that are specific to this instance extend(test, { // Test name name: name, // Test function f: f, // True if the test function does it's own looping (i.e. takes an arg) isLoop: isLoop, clone: clone, run: run, bestOf: bestOf, getHz: getHz, reset: reset }); // IE7 doesn't do 'toString' or 'toValue' in object enumerations, so set // it explicitely here. test.toString = function() { if (this.time) { return this.name + ', f = ' + humanize(this.getHz()) + 'hz (' + humanize(this.count) + '/' + humanize(this.time) + 'secs)'; } else { return this.name + ', count = ' + humanize(this.count); } }; }; // Set static properties extend(Test, { LOOP_CAL: new Test('loop cal', function(count) {while (count--) {}}), NOLOOP_CAL: new Test('noloop cal', nilf) }); // Set default property values extend(Test.prototype, { // Initial number of iterations INIT_COUNT: 10, // Max iterations allowed (used to detect bad looping functions) MAX_COUNT: 1e9, // Minimum time test should take to get valid results (secs) MIN_TIME: 1.0 }); // // jslitmus // // Set up jslitmus context var jslitmus; if (platform.name == 'node') { jslitmus = exports; } else { jslitmus = root.jslitmus = {}; } var tests = [], // test store (all tests added w/ jslitmus.test()) queue = [], // test queue (to be run) currentTest; // currently runnning test // jslitmus gets EventEmitter API EventEmitter.call(jslitmus); /** * Create a new test */ function test(name, f) { // Create the Test object var test = new Test(name, f); tests.push(test); // Run the next test if this one finished test.on('*', function() { // Forward test events to jslitmus listeners var args = Array.prototype.slice.call(arguments); args.unshift(test._emitting); jslitmus.emit.apply(jslitmus, args); // Auto-run the next test if (test._emitting == 'complete') { currentTest = null; _nextTest(); } }); jslitmus.emit('added', test); return test; } /** * Add all tests to the run queue */ function runAll(e) { forEach(tests, _queueTest); } /** * Remove all tests from the run queue. The current test has to finish on * it's own though */ function stop() { while (queue.length) { var test = queue.shift(); } } /** * Run the next test in the run queue */ function _nextTest() { if (!currentTest) { var test = queue.shift(); if (test) { currentTest = test; test.run(); } else { jslitmus.emit('all_complete'); } } } /** * Add a test to the run queue */ function _queueTest(test) { if (indexOf(queue, test) >= 0) return; queue.push(test); _nextTest(); } function clearAll() { tests = []; } /** * Generate a Google Chart URL that shows the data for all tests */ function getGoogleChart(normalize) { var chart_title = [ 'Operations/second on ' + platform.name, '(' + platform.version + ' / ' + platform.os + ')' ]; var n = tests.length, markers = [], data = []; var d, min = 0, max = -1e10; // Gather test data var markers = map(tests, function(test, i) { if (test.count) { var hz = test.getHz(); var v = hz != Infinity ? hz : 0; data.push(v); var label = test.name + '(' + humanize(hz)+ ')'; var marker = 't' + escape2(label) + ',000000,0,' + i + ',10'; max = Math.max(v, max); return marker; } }); if (markers.length <= 0) return null; // Build labels var labels = [humanize(min), humanize(max)]; var w = 250, bw = 15; var bs = 5; var h = markers.length*(bw + bs) + 30 + chart_title.length*20; var params = { chtt: escape(chart_title.join('|')), chts: '000000,10', cht: 'bhg', // chart type chd: 't:' + data.join(','), // data set chds: min + ',' + max, // max/min of data chxt: 'x', // label axes chxl: '0:|' + labels.join('|'), // labels chsp: '0,1', chm: markers.join('|'), // test names chbh: [bw, 0, bs].join(','), // bar widths // chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient chs: w + 'x' + h }; var url = 'http://chart.apis.google.com/chart?' + join(params); return url; } // Public API extend(jslitmus, { Test: Test, platform: platform, test: test, runAll: runAll, getGoogleChart: getGoogleChart, clearAll: clearAll }); // Expose code goodness we've got here, since it's useful, but do so in a way // that doesn't commit us to supporting it in future versions. jslitmus.unsupported = { nilf: nilf, log: log, extend: extend, forEach: forEach, filter: filter, map: map, indexOf: indexOf, escape2: escape2, join: join, split: split, sig: sig, humanize: humanize }; })(); package/benchmarks/templatesBench.js000644 000770 000024 0000007216 13006720616016143 0ustar00000000 000000 (function() { var jslitmus, _, doU, doT, data = { f1: 1, f2: 2, f3: 3, f4: "http://bebedo.com/laura"}, snippet = "

Just static text

\

Here is a simple {{=it.f1}}

\
test {{=it.f2}}\
{{=it.f3}}
\
{{!it.f4}}
\
"; if (typeof module !== 'undefined' && module.exports) { runTests(); } else { window.onload = runTestsInBrowser; } function testsetup(snippet) { // doU with 'it' var doUCompiled = doU.template(snippet); // doT with 'it' var doTCompiledParam = doT.template(snippet); // doT with 'this' var doTCompiled = doT.template(snippet.replace(/=it\./g, '=this.').replace(/{{!it\./g, '{{!this.')); // doT with 'it' and append = false doT.templateSettings.append = false; var doTCompiledNoAppend = doT.template(snippet); jslitmus.test('doU.js', function() { doUCompiled(data); }); jslitmus.test('doU.js - looping', function(count) { while (count--) { doUCompiled(data); } }); jslitmus.test('doT.js - using this', function() { doTCompiled.call(data); }); jslitmus.test('doT.js - using this - looping', function(count) { while (count--) { doTCompiled.call(data); } }); jslitmus.test('doT.js - using it', function() { doTCompiledParam(data); }); jslitmus.test('doT.js - using it - looping', function(count) { while (count--) { doTCompiledParam(data); } }); jslitmus.test('doT.js - append off', function() { doTCompiledNoAppend(data); }); jslitmus.test('doT.js - append off - looping', function(count) { while (count--) { doTCompiledNoAppend(data); } }); } function runTests() { //var util = require('util'); jslitmus = require('./jslitmus.js'); doU = require('./templating/doU.js'); doT = require('./templating/doT.js'); var passOne = 0; console.log("*** Small template length: " + snippet.length); testsetup(snippet); // Log the test results jslitmus.on('complete', function(test) { //console.log(util.inspect(process.memoryUsage())); console.log(test.toString()); }); // 'all_complete' fires when all tests have finished. jslitmus.on('all_complete', function() { switch (passOne) { case 0: passOne++; for(var i=0; i<5; i++) { snippet += snippet; } console.log("*** Medium template length: " + snippet.length); break; case 1: passOne++; for(var i=0; i<3; i++) { snippet += snippet; } console.log("*** Large template length: " + snippet.length); break; default: return; } jslitmus.clearAll(); testsetup(snippet); jslitmus.runAll(); }); // Run it! jslitmus.runAll(); } function runTestsInBrowser() { jslitmus = window.jslitmus;doU = window.doU;doT = window.doT; var resultTmpl = doT.template("

Template length : {{=it.size}}

"); var currentSet = document.getElementById('small'); testsetup(snippet); // 'complete' fires for each test when it finishes. jslitmus.on('complete', function(test) { // Output test results currentSet.innerHTML += test + '
'; }); // 'all_complete' fires when all tests have finished. jslitmus.on('all_complete', function() { // Get the results image URL var url = jslitmus.getGoogleChart(); if (currentSet.id === 'small') { currentSet.innerHTML += resultTmpl({size: snippet.length, url: url}); setTimeout(function() { jslitmus.clearAll(); currentSet = document.getElementById('large'); for(var i=0; i<8; i++) { snippet += snippet; } testsetup(snippet); jslitmus.runAll(); }, 10); } else { currentSet.innerHTML += resultTmpl({size: snippet.length, url: url}); } }); // Run it! jslitmus.runAll(); } })(); package/benchmarks/genspeed.html000644 000770 000024 0000001443 13006720616015323 0ustar00000000 000000 Templating Test Suite

Comparing doU.js and doT.js compilation speed

Small template

Large template

package/benchmarks/index.html000644 000770 000024 0000001423 13006720616014636 0ustar00000000 000000 Templating Test Suite

Comparing doU.js and doT.js

Small template

Large template

package/benchmarks/templating/doT.js000644 000770 000024 0000012053 13006720616016072 0ustar00000000 000000 // doT.js // 2011-2014, Laura Doktorova, https://github.com/olado/doT // Licensed under the MIT license. (function() { "use strict"; var doT = { version: "1.0.3", templateSettings: { evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g, interpolate: /\{\{=([\s\S]+?)\}\}/g, encode: /\{\{!([\s\S]+?)\}\}/g, use: /\{\{#([\s\S]+?)\}\}/g, useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g, define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, defineParams:/^\s*([\w$]+):([\s\S]+)/, conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, varname: "it", strip: true, append: true, selfcontained: false, doNotSkipEncoded: false }, template: undefined, //fn, compile template compile: undefined //fn, for express }, _globals; doT.encodeHTMLSource = function(doNotSkipEncoded) { var encodeHTMLRules = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }, matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g; return function(code) { return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : ""; }; }; _globals = (function(){ return this || (0,eval)("this"); }()); if (typeof module !== "undefined" && module.exports) { module.exports = doT; } else if (typeof define === "function" && define.amd) { define(function(){return doT;}); } else { _globals.doT = doT; } var startend = { append: { start: "'+(", end: ")+'", startencode: "'+encodeHTML(" }, split: { start: "';out+=(", end: ");out+='", startencode: "';out+=encodeHTML(" } }, skip = /$^/; function resolveDefs(c, block, def) { return ((typeof block === "string") ? block : block.toString()) .replace(c.define || skip, function(m, code, assign, value) { if (code.indexOf("def.") === 0) { code = code.substring(4); } if (!(code in def)) { if (assign === ":") { if (c.defineParams) value.replace(c.defineParams, function(m, param, v) { def[code] = {arg: param, text: v}; }); if (!(code in def)) def[code]= value; } else { new Function("def", "def['"+code+"']=" + value)(def); } } return ""; }) .replace(c.use || skip, function(m, code) { if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) { if (def[d] && def[d].arg && param) { var rw = (d+":"+param).replace(/'|\\/g, "_"); def.__exp = def.__exp || {}; def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2"); return s + "def.__exp['"+rw+"']"; } }); var v = new Function("def", "return " + code)(def); return v ? resolveDefs(c, v, def) : v; }); } function unescape(code) { return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " "); } doT.template = function(tmpl, c, def) { c = c || doT.templateSettings; var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv, str = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl; str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ") .replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""): str) .replace(/'|\\/g, "\\$&") .replace(c.interpolate || skip, function(m, code) { return cse.start + unescape(code) + cse.end; }) .replace(c.encode || skip, function(m, code) { needhtmlencode = true; return cse.startencode + unescape(code) + cse.end; }) .replace(c.conditional || skip, function(m, elsecase, code) { return elsecase ? (code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") : (code ? "';if(" + unescape(code) + "){out+='" : "';}out+='"); }) .replace(c.iterate || skip, function(m, iterate, vname, iname) { if (!iterate) return "';} } out+='"; sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate); return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '') .split(tb).join(te +'\x1b') .split(te); for (m=0,l=arr.length; m < l; m++) { str += arr[m].charAt(0) !== '\x1b' ? "out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ? ';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ? ';out+=(' + arr[m].substr(2) + ").toString().replace(/&(?!\\w+;)/g, '&').split('<').join('<').split('>').join('>').split('" + '"' + "').join('"').split(" + '"' + "'" + '"' + ").join(''').split('/').join('/');" : ';' + arr[m].substr(1))); } str = ('var out="";'+str+';return out;') .split("out+='';").join('') .split('var out="";out+=').join('var out='); try { return new Function(conf.varname, str); } catch (e) { if (typeof console !== 'undefined') console.log("Could not create a template function: " + str); throw e; } }; }()); package/test/conditionals.test.js000644 000770 000024 0000003612 13007424545015512 0ustar00000000 000000 'use strict'; var test = require('./util').test; describe('conditionals', function() { describe('without else', function() { var templates = [ '{{?it.one < 2}}{{=it.one}}{{?}}{{=it.two}}', '{{? it.one < 2 }}{{= it.one }}{{?}}{{= it.two }}' ]; it('should evaluate condition and include template if valid', function() { test(templates, {one: 1, two: 2}, '12') }); it('should evaluate condition and do NOT include template if invalid', function() { test(templates, {one: 3, two: 2}, '2') }); }); describe('with else', function() { var templates = [ '{{?it.one < 2}}{{=it.one}}{{??}}{{=it.two}}{{?}}', '{{? it.one < 2 }}{{= it.one }}{{??}}{{= it.two }}{{?}}' ]; it('should evaluate condition and include "if" template if valid', function() { test(templates, {one: 1, two: 2}, '1') }); it('should evaluate condition and include "else" template if invalid', function() { test(templates, {one: 3, two: 2}, '2') }); }); describe('with else if', function() { var templates = [ '{{?it.one < 2}}{{=it.one}}{{??it.two < 3}}{{=it.two}}{{??}}{{=it.three}}{{?}}', '{{? it.one < 2 }}{{= it.one }}{{?? it.two < 3 }}{{= it.two }}{{??}}{{= it.three }}{{?}}' ]; it('should evaluate condition and include "if" template if valid', function() { test(templates, {one: 1, two: 2, three: 3}, '1') }); it('should evaluate condition and include "else if" template if second condition valid', function() { test(templates, {one: 10, two: 2, three: 3}, '2') }); it('should evaluate condition and include "else" template if invalid', function() { test(templates, {one: 10, two: 20, three: 3}, '3') }); }); }); package/test/defines.test.js000644 000770 000024 0000001745 13007424545014446 0ustar00000000 000000 'use strict'; var test = require('./util').test; var doT = require('../doT'); var assert = require('assert'); describe('defines', function() { describe('without parameters', function() { it('should render define', function(){ testDef('{{##def.tmp:
{{!it.foo}}
#}}{{#def.tmp}}'); }); it('should render define if it is passed to doT.compile', function() { testDef('{{#def.tmp}}', {tmp: '
{{!it.foo}}
'}); }); }); describe('with parameters', function() { it('should render define', function(){ testDef('{{##def.tmp:foo:
{{!foo}}
#}}{{ var bar = it.foo; }}{{# def.tmp:bar }}'); }); }); function testDef(tmpl, defines) { var fn = doT.compile(tmpl, defines); assert.equal(fn({foo:'http'}), '
http
'); assert.equal(fn({foo:'http://abc.com'}), '
http://abc.com
'); assert.equal(fn({}), '
'); } });package/test/dot.test.js000644 000770 000024 0000004143 13007611377013613 0ustar00000000 000000 'use strict'; var test = require('./util').test; var assert = require("assert") var doT = require("../doT"); describe('doT', function(){ var basictemplate = "
{{!it.foo}}
"; var basiccompiled = doT.template(basictemplate); describe('#template()', function(){ it('should return a function', function(){ assert.equal(typeof basiccompiled, "function"); }); }); describe('#()', function(){ it('should render the template', function(){ assert.equal(basiccompiled({foo:"http"}), "
http
"); assert.equal(basiccompiled({foo:"http://abc.com"}), "
http://abc.com
"); assert.equal(basiccompiled({}), "
"); }); }); describe('encoding with doNotSkipEncoded=false', function() { it('should not replace &', function() { global._encodeHTML = undefined; doT.templateSettings.doNotSkipEncoded = false; var fn = doT.template('
{{!it.foo}}
'); assert.equal(fn({foo:"&"}), "
&
"); }); }); describe('interpolate 2 numbers', function() { it('should print numbers next to each other', function() { test([ '{{=it.one}}{{=it.two}}', '{{= it.one}}{{= it.two}}', '{{= it.one }}{{= it.two }}' ], {one:1, two: 2}, '12'); }); }); describe('evaluate JavaScript', function() { it('should print numbers next to each other', function() { test([ '{{ it.one = 1; it.two = 2; }}{{= it.one }}{{= it.two }}', ], {}, '12'); }); }); describe('encoding with doNotSkipEncoded=true', function() { it('should replace &', function() { global._encodeHTML = undefined; doT.templateSettings.doNotSkipEncoded = true; assert.equal(doT.template('
{{!it.foo}}
')({foo:"&"}), "
&amp;
"); assert.equal(doT.template('{{!it.a}}')({a:"& < > / ' \""}), "& < > / ' ""); assert.equal(doT.template('{{!"& < > / \' \\""}}')(), "& < > / ' ""); }); }); describe('invalid JS in templates', function() { it('should throw exception', function() { assert.throws(function() { var fn = doT.template('
{{= foo + }}
'); }); }); }); }); package/test/iteration.test.js000644 000770 000024 0000002243 13007424545015021 0ustar00000000 000000 'use strict'; var test = require('./util').test; describe('iteration', function() { describe('without index', function() { it('should repeat string N times', function() { test([ '{{~it.arr:x}}*{{~}}', '{{~ it.arr:x }}*{{~}}', '{{~ it.arr: x }}*{{~}}', '{{~ it.arr :x }}*{{~}}' ], {arr: Array(3)}, '***'); }); it('should concatenate items', function() { test(['{{~it.arr:x}}{{=x}}{{~}}'], {arr: [1,2,3]}, '123'); }); }); describe('with index', function() { it('should repeat string N times', function() { test([ '{{~it.arr:x:i}}*{{~}}', '{{~ it.arr : x : i }}*{{~}}' ], {arr: Array(3)}, '***'); }); it('should concatenate indices', function() { test(['{{~it.arr:x:i}}{{=i}}{{~}}'], {arr: Array(3)}, '012'); }); it('should concatenate indices and items', function() { test([ '{{~it.arr:x:i}}{{?i}}, {{?}}{{=i}}:{{=x}}{{~}}' ], {arr: [10,20,30]}, '0:10, 1:20, 2:30'); }); }); }); package/test/util.js000644 000770 000024 0000000406 13007424545013021 0ustar00000000 000000 'use strict'; var assert = require('assert') var doT = require('../doT'); exports.test = function (templates, data, result) { templates.forEach(function (tmpl) { var fn = doT.template(tmpl); assert.strictEqual(fn(data), result); }); };