├── .gitignore ├── ex.nim ├── examples ├── dbdriven │ ├── ex.nim │ └── templates │ │ ├── base.css │ │ ├── base.html │ │ ├── dark.css │ │ ├── detail.html │ │ ├── foo.html │ │ ├── index.html │ │ ├── light.css │ │ ├── list.html │ │ ├── menu.html │ │ └── numbertest.html ├── homepage │ ├── README.md │ └── templates │ │ ├── base.css │ │ ├── base.html │ │ ├── dark.css │ │ ├── foo.html │ │ ├── index.html │ │ ├── light.css │ │ ├── menu.html │ │ ├── t.html │ │ └── t2.html ├── jestr.nim ├── nimhack │ ├── nimhack.nim │ └── templates │ │ ├── base.nim │ │ ├── base2.nim │ │ └── kunde1.nim ├── nojester │ ├── nojester.nim │ ├── public │ │ └── style.css │ └── templates │ │ ├── about.html │ │ ├── base.html │ │ ├── downloads.html │ │ ├── footer.html │ │ ├── index.html │ │ └── menu.html └── simple │ ├── example.nim │ └── templates │ ├── about.html │ ├── base.html │ ├── index.html │ ├── menu.html │ └── stats.html ├── future ├── tfilter.nwt ├── tfor.nwt └── tif.nwt ├── licence ├── nwt.nimble ├── readme.md ├── src ├── nwt.nim ├── nwtTokenizer.nim └── stack.nim ├── t ├── t1.nim ├── t2.nim └── testsuit.nim └── templates ├── 2blocks.html ├── ass.html ├── base.html ├── block.html ├── block2.html ├── extendsImportsTwoBlocksFromOneFile.html ├── for.html ├── for2.html ├── import.html ├── importsTwoBlocksFromOneFile.html ├── index.html ├── ji.html ├── multi.html ├── one.html ├── self.html ├── selfT.html ├── setter.html ├── styleBase.css ├── styleDark.css ├── styleLight.css ├── t01.html ├── two.html ├── ugga.html ├── vars.html ├── vars2.html └── vars3.html /.gitignore: -------------------------------------------------------------------------------- 1 | **nimcache** 2 | **.exe 3 | **git_broken** 4 | **freezed** 5 | *.7z 6 | *.dll 7 | *.exe 8 | *.db -------------------------------------------------------------------------------- /ex.nim: -------------------------------------------------------------------------------- 1 | import jester 2 | import nwt 3 | import times 4 | import asyncdispatch 5 | import json 6 | 7 | var t = newNwt("templates/*") 8 | 9 | echo "FREEZING TEMPLATES (JUST FOR FUN)" 10 | t.freeze() 11 | 12 | routes: 13 | get "/": 14 | resp t.renderTemplate("index.html", %*{"content": $epochTime()}) 15 | get "/@name":#& ".html" 16 | resp t.renderTemplate(@"name" , %*{"content": $epochTime(), "items": @["one", "two", "three", "four"]}) 17 | runForever() 18 | -------------------------------------------------------------------------------- /examples/dbdriven/ex.nim: -------------------------------------------------------------------------------- 1 | import jester 2 | # import nwt 3 | import ../../nwt 4 | import times 5 | import asyncdispatch 6 | import """D:\flatdb\flatdb.nim""" 7 | import json 8 | # import tables 9 | 10 | var t = newNwt("templates/*") 11 | var db = newFlatDb("tst.db", false) 12 | discard db.load() 13 | 14 | ## Testdata 15 | if db.nodes.len == 0: 16 | ## If the db is empty we fill some default values 17 | discard db.append(%* {"title": "HALLO!!", "link": "hallo", "content": "ich bin ein bisschen content", "author": "enthus1ast"}) 18 | discard db.append(%* {"title": "foo :)", "link": "foo", "content": "i am some conten about foo", "author": "klauspeter"}) 19 | 20 | 21 | routes: 22 | get "/": 23 | resp t.renderTemplate("list.html", %* {"entries": $db.nodes, "userid": 10} ) 24 | 25 | get "/numbertest": 26 | resp t.renderTemplate("numbertest.html", %* {"float": 123.99, "int": 8080} ) 27 | 28 | get "/@link": 29 | # entry{"float", "int"} = #{"float": 123.99, "int": 8080} 30 | resp t.renderTemplate("detail.html", db.queryOne equal("link", @"link") ) 31 | runForever() 32 | -------------------------------------------------------------------------------- /examples/dbdriven/templates/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: {{bg}}; 3 | color: {{fg}}; 4 | } 5 | 6 | #menu { 7 | position: absolute; 8 | top:0px; 9 | right: 0px; 10 | left:0px; 11 | height:50px; 12 | background-color: "red"; 13 | } 14 | 15 | 16 | #content { 17 | position: absolute; 18 | top:50px; 19 | right: 0px; 20 | left:0px; 21 | bottom:0pc; 22 | /*height:50px;*/ 23 | /*background-color: "red";*/ 24 | } -------------------------------------------------------------------------------- /examples/dbdriven/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {%import "menu.html"%} 3 | 4 | 5 | myHomepage {{title}} 6 | 7 | 8 | 9 | 10 | 11 |
12 | {%block "content"%}{%endblock%} 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/dbdriven/templates/dark.css: -------------------------------------------------------------------------------- 1 | {%set bg "black"%} 2 | {%set fg "white"%} 3 | {%extends "base.css"%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/detail.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%block "content"%} 3 |

{{title}}

4 | {{content}} 5 |
6 | {{author}} 7 | {%endblock%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/foo.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%set style "light.css"%} 3 | {%set title "FOFOFOFOFOFOFOOFOF"%} 4 | {%block "content"%} 5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi accusamus dicta quis fugiat magni numquam, autem dolores quos laudantium asperiores voluptas hic perspiciatis aspernatur ratione, eius quas nam, optio distinctio? 6 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 9 | 10 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 11 | {%endblock%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%set style "dark.css"%} 3 | {%set title "some index"%} 4 | {%block "content"%}Some content for the index{%endblock%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/light.css: -------------------------------------------------------------------------------- 1 | {%extends "base.css"%} 2 | {%set bg "white"%} 3 | {%set fg "black"%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/list.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%block "content"%} 3 |

{{userid}}

4 | 5 | {{entries}} 6 | {%endblock%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/menu.html: -------------------------------------------------------------------------------- 1 | {%block "menu"%} 2 | index 3 | hallo 4 | foo 5 | numbertest 6 | 7 | {# Use jinja like comments to remove trails from html output #} 8 | {# bar #} 9 | {# baz #} 10 | {%endblock%} -------------------------------------------------------------------------------- /examples/dbdriven/templates/numbertest.html: -------------------------------------------------------------------------------- 1 | {{int}} 2 | {{float}} 3 | -------------------------------------------------------------------------------- /examples/homepage/README.md: -------------------------------------------------------------------------------- 1 | to run this example drop and compile the `ex.nim` from the main folder -------------------------------------------------------------------------------- /examples/homepage/templates/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: {{bg}}; 3 | color: {{fg}}; 4 | } 5 | 6 | #menu { 7 | position: absolute; 8 | top:0px; 9 | right: 0px; 10 | left:0px; 11 | height:50px; 12 | background-color: red; 13 | } 14 | 15 | 16 | #content { 17 | position: absolute; 18 | top:50px; 19 | right: 0px; 20 | left:0px; 21 | bottom:0pc; 22 | /*height:50px;*/ 23 | /*background-color: blue;*/ 24 | /*opacity: 0.1;*/ 25 | } -------------------------------------------------------------------------------- /examples/homepage/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {%import "menu.html"%} 3 | 4 | 5 | myHomepage {{title}} 6 | 7 | 8 | 9 | 10 |
{%block "content"%}{%endblock%}
11 | 12 | -------------------------------------------------------------------------------- /examples/homepage/templates/dark.css: -------------------------------------------------------------------------------- 1 | {%set bg "black"%} 2 | {%set fg "white"%} 3 | {%extends "base.css"%} -------------------------------------------------------------------------------- /examples/homepage/templates/foo.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%set style "light.css"%} 3 | {%set title "FOFOFOFOFOFOFOOFOF"%} 4 | {%block "content"%} 5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi accusamus dicta quis fugiat magni numquam, autem dolores quos laudantium asperiores voluptas hic perspiciatis aspernatur ratione, eius quas nam, optio distinctio? 6 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 9 | 10 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempora debitis illum minus corporis deleniti ullam iste, vitae odit ab sint blanditiis eveniet nihil recusandae, eaque architecto a explicabo suscipit numquam! 11 | {%endblock%} -------------------------------------------------------------------------------- /examples/homepage/templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%set style "dark.css"%} 3 | {%set title "some index"%} 4 | {%block "content"%}Some content for the index{%endblock%} -------------------------------------------------------------------------------- /examples/homepage/templates/light.css: -------------------------------------------------------------------------------- 1 | {%extends "base.css"%} 2 | {%set bg "white"%} 3 | {%set fg "black"%} -------------------------------------------------------------------------------- /examples/homepage/templates/menu.html: -------------------------------------------------------------------------------- 1 | {%block "menu"%} 2 | hallo 3 | foo 4 | {# bar #} 5 | {# baz #} 6 | {%endblock%} -------------------------------------------------------------------------------- /examples/homepage/templates/t.html: -------------------------------------------------------------------------------- 1 | {%import t2.html%} 2 | {%block foo%}asdf{%endblock%} 3 | {%block baa%}aaa{%endblock%} -------------------------------------------------------------------------------- /examples/homepage/templates/t2.html: -------------------------------------------------------------------------------- 1 | {%block "foo"%}peter{%endblock%} 2 | {%block "baa"%}peter2{%endblock%} -------------------------------------------------------------------------------- /examples/jestr.nim: -------------------------------------------------------------------------------- 1 | import jester 2 | import ../nwt 3 | # import nwt 4 | import times 5 | import asyncdispatch 6 | # import """D:\flatdb\flatdb.nim""" 7 | import json 8 | import tables 9 | 10 | var t = newNwt("../templates/*.html") 11 | # var db = newFlatDb("tst.db", false) 12 | # if db.nodes.len == 0: 13 | # discard db.append(%* {"site": "index.html", "content": "ich bin ein bisschen content"}) 14 | routes: 15 | get "/": 16 | resp t.renderTemplate("index.html", newStringTable({"content": $epochTime()})) 17 | get "/@name": 18 | resp t.renderTemplate(@"name", newStringTable({"content": $epochTime()})) 19 | # resp t.renderTemplate("index.html", newStringTable(db.queryOne equal("site","index.html")) )) 20 | # get "/ass": 21 | # resp t.renderTemplate("ass.html", newStringTable({"content": $epochTime()})) 22 | runForever() 23 | -------------------------------------------------------------------------------- /examples/nimhack/nimhack.nim: -------------------------------------------------------------------------------- 1 | ## what is is good for? :) 2 | 3 | import ../../nwt 4 | 5 | newNwt("templates/*.nim").freeze() 6 | 7 | -------------------------------------------------------------------------------- /examples/nimhack/templates/base.nim: -------------------------------------------------------------------------------- 1 | import asynchttpserver, asyncdispatch 2 | import ../../../nwt 3 | import strutils 4 | import tables 5 | import os 6 | 7 | 8 | const PUBLIC_DIR = "{{publicDir}}" 9 | var t = newNwt("{{templateDir}}") 10 | 11 | when not defined release: 12 | t.freeze(staticPath = PUBLIC_DIR, outputPath = "./freezed/") ## Example for "freezing" your templates into a static site. 13 | 14 | var server = newAsyncHttpServer() 15 | proc cb(req: Request) {.async.} = 16 | let res = req.url.path.strip(leading=true, trailing=false, {'/'}) 17 | 18 | case res 19 | # some routes! 20 | # be aware that freeze() only freezes the template and copies the public folder. 21 | # we do not visit your custom rules for now! 22 | 23 | 24 | {%block "routes"%}{%endblock%} 25 | 26 | else: 27 | if t.templates.contains(res): # when we have a template with this name, serve it 28 | await req.respond(Http200, t.renderTemplate(res)) 29 | elif fileExists(PUBLIC_DIR / res): # when there is a static file with this name, serve it 30 | await req.respond(Http200, open(PUBLIC_DIR / res,).readAll()) 31 | else: # unkown to us 32 | await req.respond(Http404, "404 not found") 33 | 34 | waitFor server.serve(Port(8080), cb) -------------------------------------------------------------------------------- /examples/nimhack/templates/base2.nim: -------------------------------------------------------------------------------- 1 | {%block content%} 2 | echo {{foo}} 3 | {%endblock%} -------------------------------------------------------------------------------- /examples/nimhack/templates/kunde1.nim: -------------------------------------------------------------------------------- 1 | {%extends base.nim%} 2 | {%set publicDir "./public/"%} 3 | {%set templateDir "templates/*.html"%} 4 | 5 | 6 | 7 | {%block "routes"%} 8 | of "": 9 | await req.respond(Http200, t.renderTemplate("index.html") ) 10 | of "logout": 11 | await req.respond(Http200, "TODO do something usefull") 12 | {%endblock%} -------------------------------------------------------------------------------- /examples/nojester/nojester.nim: -------------------------------------------------------------------------------- 1 | import asynchttpserver, asyncdispatch 2 | import ../../nwt 3 | import strutils 4 | import tables 5 | import os 6 | import asyncfile 7 | 8 | const PUBLIC_DIR = "./public/" 9 | var t = newNwt("templates/*.html") 10 | 11 | when not defined release: 12 | t.freeze(staticPath = PUBLIC_DIR, outputPath = "./freezed/") ## Example for "freezing" your templates into a static site. 13 | 14 | var server = newAsyncHttpServer() 15 | proc cb(req: Request) {.async.} = 16 | let res = req.url.path.strip(leading=true, trailing=false, {'/'}) 17 | 18 | case res 19 | # some routes! 20 | # be aware that freeze() only freezes the template, wich are loaded by the constructor, and copies the public folder. 21 | # we do not visit your custom routes for now! 22 | of "": 23 | await req.respond(Http200, t.renderTemplate("index.html") ) 24 | of "logout": 25 | await req.respond(Http200, "TODO do something usefull") 26 | 27 | 28 | else: 29 | if t.templates.contains(res): # when we have a template with this name, serve it 30 | await req.respond(Http200, t.renderTemplate(res)) 31 | elif fileExists(PUBLIC_DIR / res): # when there is a static file with this name, serve it 32 | let file = openAsync(PUBLIC_DIR / res , fmRead) 33 | # let data 34 | await req.respond(Http200, await file.readAll()) 35 | file.close() 36 | else: # unkown to us 37 | await req.respond(Http404, "404 not found") 38 | 39 | waitFor server.serve(Port(8080), cb) -------------------------------------------------------------------------------- /examples/nojester/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #171921; 3 | color:white; 4 | font-size: 22px; 5 | } 6 | 7 | a { 8 | color:#fff; 9 | } 10 | 11 | h1 { 12 | color:#ffe953; 13 | } 14 | 15 | #footer { 16 | color:darkgray; 17 | } 18 | 19 | #content { 20 | width: 80%; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/nojester/templates/about.html: -------------------------------------------------------------------------------- 1 | {%extends base.html%} 2 | {%set title "about"%} 3 | {%block content%} 4 |
5 |

{{title}}

6 |
7 | 8 |
9 | 18 |
19 | {%endblock%} 20 | -------------------------------------------------------------------------------- /examples/nojester/templates/base.html: -------------------------------------------------------------------------------- 1 | {%import footer.html%} 2 | {%import menu.html%} 3 | 4 | 5 | {{title}} 6 | 7 | 8 | 9 | 10 |

11 | 12 | {%block menu%}{%endblock%} 13 | 14 |

15 | 16 |
17 | {%block content%}{%endblock%} 18 |
19 | 20 |

21 | 22 | {%block footer%}{%endblock%} 23 | 24 |

25 | 26 | -------------------------------------------------------------------------------- /examples/nojester/templates/downloads.html: -------------------------------------------------------------------------------- 1 | {%extends base.html%} 2 | {%set title "some downloads"%} 3 | {%block content%} 4 |
5 |

{{title}}

6 |
7 | 8 |
9 | 14 |
15 | {%endblock%} 16 | -------------------------------------------------------------------------------- /examples/nojester/templates/footer.html: -------------------------------------------------------------------------------- 1 | {%block footer%} 2 | Impressum
3 | david@code0.xyz
4 | tel example: 1234-5678
5 | fax example: 1234-56789
6 |
7 |

build with nwt and nim

8 | {%endblock%} -------------------------------------------------------------------------------- /examples/nojester/templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends base.html%} 2 | {%set title "my fancy title"%} 3 | {%block content%} 4 |
5 |

I am the index

6 | 7 | 15 |
16 | {%endblock%} 17 | -------------------------------------------------------------------------------- /examples/nojester/templates/menu.html: -------------------------------------------------------------------------------- 1 | {%block menu%} 2 | index 3 | about 4 | downloads 5 | {%endblock%} -------------------------------------------------------------------------------- /examples/simple/example.nim: -------------------------------------------------------------------------------- 1 | import asynchttpserver, asyncdispatch 2 | import json 3 | import nwt 4 | 5 | var templates = newNwt("templates/*.html") # we have all the templates in a folder called "templates" 6 | 7 | var server = newAsyncHttpServer() 8 | proc cb(req: Request) {.async.} = 9 | let res = req.url.path 10 | 11 | case res 12 | of "/", "/index.html": 13 | await req.respond(Http200, templates.renderTemplate("index.html")) 14 | of "/about.html": 15 | await req.respond(Http200, templates.renderTemplate("about.html")) 16 | of "/stats.html": 17 | var context = %* {"title": "some variables from nim", "foo": "Foo!", "baa": "Baa!"} 18 | await req.respond(Http200, templates.renderTemplate("stats.html", context)) 19 | else: 20 | await req.respond(Http404, "not found") 21 | 22 | waitFor server.serve(Port(8080), cb) -------------------------------------------------------------------------------- /examples/simple/templates/about.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | {%set title "Some about me and stuff"%} 3 | 4 | {%block content%} 5 | 9 | {%endblock%} 10 | 11 | {# we not set any additional footer here, so we omit the block #} -------------------------------------------------------------------------------- /examples/simple/templates/base.html: -------------------------------------------------------------------------------- 1 | {%import menu.html%} 2 | 3 | 4 | {{title}} 5 | 6 | 7 | 8 |
9 | {{self.menu}} 10 |
11 | 12 |

{{title}}

13 | 14 |
15 | {%block "content"%} 16 | {# i will be replaced by my children #} 17 | {%endblock%} 18 |
19 | 20 |
21 |
22 | Made with nim
23 | {{self.footer}} 24 |
25 | 26 | -------------------------------------------------------------------------------- /examples/simple/templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | 3 | {%set title "Some index and more"%} 4 | 5 | {%block content%} 6 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. 7 | Ab, sint repellendus iure similique ipsa unde eos est nam numquam, laborum, sit ipsum voluptates modi impedit doloremque. 8 | Fugiat, obcaecati delectus accusantium. 9 | {%endblock%} 10 | 11 | {%block footer%} 12 | Some footer infos we only want to see on the index 13 | {%endblock%} 14 | -------------------------------------------------------------------------------- /examples/simple/templates/menu.html: -------------------------------------------------------------------------------- 1 | {%block menu%}index || about || stats{%endblock%} -------------------------------------------------------------------------------- /examples/simple/templates/stats.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | 3 | 4 | {%block content%} 5 | 9 | {%endblock%} 10 | 11 | {# we not set any additional footer here, so we omit the block #} -------------------------------------------------------------------------------- /future/tfilter.nwt: -------------------------------------------------------------------------------- 1 | {# filters could be self defined, somewhere in the nim code #} 2 | 3 | {# FOO #} 4 | {{ "foo" | toUpper }} 5 | 6 | {# f #} 7 | {{ "FOO" | toLower | trim(1) }} 8 | 9 | {# f #} 10 | {{ "FOO"[0] | toLower }} 11 | 12 | {# 1 #} 13 | {{ "FOO"[0] | len }} 14 | 15 | -------------------------------------------------------------------------------- /future/tfor.nwt: -------------------------------------------------------------------------------- 1 | {# users is a list of objects #} 2 | {% for user in users %} 3 |
  • {{ user.username }}
  • 4 | {% endfor %} 5 | 6 | {% for email in userOne.emails %} 7 | {{email.subject}} 8 | {{email.body}} 9 | {% endfor %} 10 | 11 | {% for i in [1,2,3,4] %} 12 | {{ i }} 13 | {% endfor %} 14 | 15 | {# same for tuples? #} 16 | {% for i in (1,2,3,4) %} 17 | {{ i }} 18 | {% endfor %} 19 | 20 | 21 | -------------------------------------------------------------------------------- /future/tif.nwt: -------------------------------------------------------------------------------- 1 | {% if true %} 2 | Some text 3 | {% endif %} 4 | 5 | {% if users %} 6 | Users contains list entries 7 | {% endif %} 8 | 9 | {% if 1 %} 10 | One is true 11 | {% endif %} 12 | 13 | {% if userOne.emails %} 14 | userOne has a filled emails list 15 | {% endif %} 16 | 17 | {% if true %} 18 | Some text 19 | {% else %} 20 | Never 21 | {% endif %} 22 | 23 | {% if users.mails | len == 1 %} 24 | mails is 1 25 | {% elif users.mails | len == 2 %} 26 | mails is 2 27 | {% elif users.mails | len == 3 %} 28 | mails is 2 29 | {% else %} 30 | mails is something else 31 | {% endif %} 32 | 33 | -------------------------------------------------------------------------------- /licence: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 David Krause 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nwt.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.7" 4 | author = "enthus1ast" 5 | description = "minimal jinja like experiment" 6 | license = "MIT" 7 | 8 | 9 | srcDir = "src" 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 0.17.0" 14 | requires "https://github.com/enthus1ast/nimCommandParser" 15 | 16 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | nimWebTemplates is ABADONNED and DEPRECATED 2 | ========== 3 | 4 | for a better template engine use: [enthus1ast/nimja](https://github.com/enthus1ast/nimja) 5 | 6 | 7 | readme 8 | ====== 9 | 10 | nwt is a little template engine inspired by jinja2. 11 | 12 | (warning: it lacks control statements like `if` `while` etc..) 13 | 14 | it do not enforce any web framework so use whatever you like! 15 | 16 | rudimentary freeze support with `freeze()` 17 | 18 | have a look at the other demos! 19 | 20 | if you feeling fancy today have a special look at the `dbdriven` database example and 21 | my little flatfile database [flatdb](https://github.com/enthus1ast/flatdb) for nim-lang 22 | 23 | 24 | usage 25 | ===== 26 | 27 | 28 | Directory structure 29 | ----------------------- 30 | 31 | To follow this example create this folder structure 32 | 33 | ``` 34 | ./example.nim 35 | ./templates/base.html 36 | ./templates/index.html 37 | ./templates/about.html 38 | ./templates/stats.html 39 | ``` 40 | 41 | 42 | 43 | menu.html 44 | ---------- 45 | 46 | ```jinja 47 | {%block menu%}index || about || stats{%endblock%} 48 | ``` 49 | 50 | base.html 51 | ---------- 52 | 53 | ```jinja 54 | {%import menu.html%} 55 | 56 | 57 | {{title}} 58 | 59 | 60 | 61 |
    62 | {{self.menu}} 63 | 64 |
    65 | 66 |

    {{title}}

    67 | 68 |
    69 | {%block "content"%} 70 | {# i will be replaced by my children #} 71 | {%endblock%} 72 |
    73 | 74 |
    75 |
    76 | Made with nim
    77 | {{self.footer}} 78 |
    79 | 80 | 81 | ``` 82 | 83 | 84 | index.html 85 | ----------- 86 | 87 | ```jinja 88 | {%extends "base.html"%} 89 | 90 | {%set title "Some index and more"%} 91 | 92 | {%block content%} 93 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. 94 | Ab, sint repellendus iure similique ipsa unde eos est nam numquam, laborum, sit ipsum voluptates modi impedit doloremque. 95 | Fugiat, obcaecati delectus accusantium. 96 | {%endblock%} 97 | 98 | {%block footer%} 99 | Some footer infos we only want to see on the index 100 | {%endblock%} 101 | ``` 102 | 103 | 104 | 105 | about.html 106 | ----------- 107 | 108 | ```jinja 109 | {%extends "base.html"%} 110 | {%set title "Some about me and stuff"%} 111 | 112 | {%block content%} 113 | 117 | {%endblock%} 118 | 119 | {# we not set any additional footer here, so we omit the block #} 120 | ``` 121 | 122 | stats.html 123 | ----------- 124 | 125 | ```jinja 126 | {%extends "base.html"%} 127 | 128 | {%block content%} 129 | 133 | {%endblock%} 134 | 135 | {# we not set any additional footer here, so we omit the block #} 136 | ``` 137 | 138 | 139 | example.nim 140 | ------------ 141 | 142 | ```nim 143 | import asynchttpserver, asyncdispatch 144 | import json 145 | import nwt 146 | 147 | var templates = newNwt("templates/*.html") # we have all the templates in a folder called "templates" 148 | 149 | var server = newAsyncHttpServer() 150 | proc cb(req: Request) {.async.} = 151 | let res = req.url.path 152 | 153 | case res 154 | of "/", "/index.html": 155 | await req.respond(Http200, templates.renderTemplate("index.html")) 156 | of "/about.html": 157 | await req.respond(Http200, templates.renderTemplate("about.html")) 158 | of "/stats.html": 159 | var context = %* {"title": "some variables from nim", "foo": "Foo!", "baa": "Baa!"} 160 | await req.respond(Http200, templates.renderTemplate("stats.html", context)) 161 | else: 162 | await req.respond(Http404, "not found") 163 | 164 | waitFor server.serve(Port(8080), cb) 165 | ``` 166 | -------------------------------------------------------------------------------- /src/nwt.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # nimWebTemplates 4 | # (c) Copyright 2017 David Krause 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | ## :Author: David Krause (enthus1ast) 10 | ## 11 | ## a jinja like template syntax parser 12 | ## 13 | ## Works: 14 | ## - comments {# comment #} 15 | ## - variable {{variable}} 16 | ## - extends [NON recursive ATM] 17 | ## NOT: 18 | ## - evaluation (for, while, etc) 19 | ## - everything else ;) 20 | ## Example usage with jester: 21 | ## routes: 22 | ## get "/": 23 | ## resp t.renderTemplate("index.html", newStringTable({"content": $epochTime()})) 24 | ## get "/ass": 25 | ## resp t.renderTemplate("ass.html", newStringTable({"content": $epochTime()})) 26 | ## runForever() 27 | import strtabs, strutils, parseutils, sequtils, os, tables, json, cgi, options 28 | import commandParser 29 | import nwtTokenizer 30 | import stack 31 | export options 32 | 33 | type 34 | TemplateIdent = string 35 | BlockIdent = string 36 | NwtTemplate = seq[Token] 37 | NwtTemplates = Table[TemplateIdent, NwtTemplate] 38 | Nwt* = ref object of RootObj 39 | templates*: NwtTemplates 40 | templatesDir*: string 41 | echoEmptyVars*: bool ## set this to true to let nwt print an empty variable like this '{{variable}}' or '{{self.variable}}' 42 | autoEscape*: bool ## automatically html escapes variables 43 | Block = tuple[name: string, cmd: ChatCommand, posStart: int, posEnd: int] 44 | Blocks = Table[BlockIdent, Block] 45 | Context = ref object 46 | variables: JsonNode 47 | nwtTemplates: NwtTemplates 48 | 49 | proc newNwtTemplate(): NwtTemplate = 50 | result = @[] 51 | 52 | proc newNwtTemplate(templateString: string): NwtTemplate = 53 | ## parses the templateString and creates a NwtTemplate from it. 54 | result = toSeq(nwtTokenize templateString) 55 | 56 | proc newNwtTemplates(): NwtTemplates = 57 | result = initTable[TemplateIdent,seq[Token]]() 58 | 59 | proc newBlocks(): Blocks = 60 | result = initTable[BlockIdent, Block]() 61 | 62 | proc newContext(variables = %* {}): Context = 63 | result = Context() 64 | result.variables = variables 65 | # result.blocks = newBlocks() 66 | result.nwtTemplates = newNwtTemplates() 67 | 68 | proc add*(nwtTemplates: var NwtTemplates, templateName: TemplateIdent, templateStr: string) = 69 | ## parses and adds/updates a template 70 | nwtTemplates[templateName] = newNwtTemplate(templateStr) 71 | 72 | proc addTemplate*(nwt: Nwt, templateName: TemplateIdent , templateStr: string) = 73 | ## parses and adds/updates a template, given by a string 74 | nwt.templates[templateName] = toSeq(nwtTokenize templateStr) 75 | 76 | proc loadTemplates*(nwt: Nwt, templatesDir: string) = 77 | ## loads and parses the templates from the filesystem. 78 | ## basic wildcards supported: 79 | ## /foo/baa/*.html 80 | ## call this for refreshing the templates 81 | for filename in walkFiles(templatesDir): 82 | var templateName = extractFilename(filename) 83 | # echo "Load: $1 as $2", % [filename, templateName] 84 | nwt.templates[templateName] = toSeq(nwtTokenize readFile(filename)) 85 | 86 | proc newNwt*(templatesDir: string = "./templates/*.html"): Nwt = 87 | ## this loads all templates from the template into memory 88 | ## if templatesDir == nil we do not load any templates 89 | ## we can add them later by `addTemplate("{%foo%}{%baa%}")` 90 | result = Nwt() 91 | result.templates = newNwtTemplates() 92 | result.templatesDir = templatesDir 93 | result.loadTemplates(templatesDir) 94 | result.echoEmptyVars = false 95 | result.autoEscape = false 96 | 97 | proc getBlocks*(nwtTemplate: NwtTemplate, starting="block", ending="endblock" ): Blocks = # TODO private 98 | # returns all {%block 'foo'%} statements as a Table of Block 99 | result = newBlocks() 100 | var stack = newSeq[(ChatCommand, int)]() 101 | 102 | var actual: Block = ("",ChatCommand(),0,0) 103 | for i, each in nwtTemplate: 104 | if each.tokenType == NwtEval and each.value.strip().startswith(starting): # block 105 | var cmd = newChatCommand(each.value) 106 | stack.pushl( (cmd, i )) 107 | elif each.tokenType == NwtEval and each.value.strip().startswith(ending): 108 | if stack.len == 0: 109 | raise newException(ValueError, "UNBALANCED BLOCKS too many closeing tags for: " & $each & " " & $nwtTemplate ) 110 | var cmd: ChatCommand 111 | (cmd, actual.posStart) = stack.popl() 112 | actual.name = cmd.params[0] 113 | actual.cmd = cmd 114 | actual.posEnd = i 115 | result.add(actual.name, actual) 116 | actual = ("", ChatCommand() ,0,0) 117 | if stack.len > 0: 118 | raise newException(ValueError, "UNBALANCED BLOCKS too many opening tags for: " & starting & "\nstack:\n" & $stack ) 119 | 120 | proc fillBlocks*(nwt: Nwt, baseTemplateTokens, nwtTemplate: NwtTemplate, ctx: Context): NwtTemplate = # TODO private 121 | ## This fills all the base template blocks with 122 | ## blocks from extending template 123 | # @[(name: content2, posStart: 2, posEnd: 4), (name: peter, posStart: 6, posEnd: 8)] 124 | # @[(name: content2, posStart: P, posEnd: 4), (name: peter, posStart: 6, posEnd: 8)] 125 | result = baseTemplateTokens 126 | var templateBlocks = getBlocks(nwtTemplate) 127 | var baseTemplateBlocks = getBlocks(baseTemplateTokens) 128 | 129 | ## To make {self.templateName} work 130 | for k,v in templateBlocks: 131 | ctx.nwtTemplates[k] = nwtTemplate[v.posStart .. v.posEnd] 132 | 133 | for baseBlock in baseTemplateBlocks.values: 134 | 135 | if templateBlocks.contains(baseBlock.name): 136 | var startp = templateBlocks[baseBlock.name].posStart 137 | var endp = templateBlocks[baseBlock.name].posEnd 138 | var blockTokens = nwtTemplate[startp .. endp] 139 | 140 | ctx.nwtTemplates[baseBlock.name] = blockTokens 141 | 142 | ## The main block replacement 143 | # we only do anything if we have that block in the extending template 144 | var inspos = baseTemplateBlocks[baseBlock.name].posStart 145 | result.delete(baseTemplateBlocks[baseBlock.name].posStart, baseTemplateBlocks[baseBlock.name].posEnd) 146 | result.insert(blockTokens , inspos) 147 | 148 | proc evalTemplate(nwt: Nwt, templateName: TemplateIdent, ctx: Context): NwtTemplate = 149 | result = @[] 150 | var baseTemplateTokens = newNwtTemplate() 151 | var importTemplateTokens = newNwtTemplate() 152 | var tokens = newNwtTemplate() 153 | var alreadyExtendet = false 154 | 155 | for idx, each in nwt.templates[templateName]: 156 | if each.tokenType == NwtEval and each.value.startswith("extends"): 157 | if alreadyExtendet: 158 | continue 159 | alreadyExtendet = true 160 | 161 | ## ONLY ONE BASE TEMPLATE IS SUPPORTED!! so only ONE {%extends%} __PER FILE__! 162 | baseTemplateTokens = evalTemplate(nwt, extractTemplateName(each.value), ctx) 163 | continue 164 | 165 | elif each.tokenType == NwtEval and each.value.startswith("set"): 166 | let setCmd = newChatCommand(each.value) 167 | ctx.variables[setCmd.params[0]] = %* setCmd.params[1] 168 | # echo "params[$1] = $2" % [setCmd.params[0], setCmd.params[1]] 169 | 170 | elif each.tokenType == NwtEval and each.value.startswith("if"): 171 | discard 172 | 173 | elif each.tokenType == NwtEval and each.value.startswith("for"): 174 | # for elem in 175 | discard 176 | 177 | elif each.tokenType == NwtEval and each.value.startswith("import"): 178 | let cmd = newChatCommand(each.value) 179 | let importTemplateName = cmd.params[0] 180 | if importTemplateName == templateName: 181 | raise newException(ValueError, "template $1 could not import itself" % templateName) 182 | for t in nwt.evalTemplate(importTemplateName, ctx): 183 | importTemplateTokens.add t 184 | continue 185 | else: # starwith checks passed 186 | tokens.add each 187 | 188 | if importTemplateTokens.len > 0: 189 | tokens = nwt.fillBlocks(tokens, importTemplateTokens, ctx) 190 | 191 | if baseTemplateTokens.len == 0: 192 | return tokens 193 | else: 194 | return nwt.fillBlocks(baseTemplateTokens, tokens, ctx) 195 | 196 | template decorateVariable(a: untyped): untyped = 197 | "{{" & a & "}}" 198 | 199 | proc toStr*(nwt: Nwt, token: Token, ctx: Context, autoEscape: Option[bool]): string = 200 | ## transforms the token to its string representation 201 | # TODO should this be `$`? 202 | var bufval = "" 203 | 204 | case token.tokenType 205 | of NwtString: 206 | return token.value 207 | of NwtComment: 208 | return "" 209 | of NwtVariable: 210 | if token.value == "debug": 211 | let dbg = """ 212 |
    213 |           PARAMS:
    214 |           $1
    215 | 
    216 |           BLOCKTABLE:
    217 |           $2
    218 |       
    219 | """ % @[ 220 | ctx.variables.pretty(), 221 | ($ctx.nwtTemplates) 222 | ] 223 | echo dbg 224 | return "
    " & dbg.xmlEncode & "
    " 225 | if token.value.startswith("self."): 226 | # echo "NODE STARTS WITH self. :: ", token.value 227 | var cmd = newChatCommand(token.value,false, @['.']) 228 | let templateName = cmd.params[1] 229 | bufval.setLen(0) 230 | if ctx.nwtTemplates.hasKey(templateName): 231 | for token in ctx.nwtTemplates[templateName]: 232 | bufval.add nwt.toStr(token, ctx, autoEscape) 233 | elif nwt.echoEmptyVars: 234 | bufval = decorateVariable(token.value) 235 | else: 236 | bufval = "" 237 | return bufval 238 | var node = ctx.variables.getOrDefault(token.value) 239 | if not node.isNil: 240 | case node.kind 241 | of JString: 242 | bufval = node.getStr() 243 | of JInt: 244 | bufval = $(node.getInt()) 245 | of JFloat: 246 | bufval = $(node.getFloat()) 247 | else: 248 | bufval = "" 249 | 250 | if (bufval == "") and (nwt.echoEmptyVars == true): 251 | return token.value.decorateVariable ## return the token when it could not be replaced 252 | else: 253 | 254 | if (autoEscape.isNone and nwt.autoEscape) or (autoEscape.isSome and autoEscape.get()): 255 | return bufval.xmlEncode 256 | else: 257 | return bufval 258 | 259 | 260 | else: 261 | return "" 262 | 263 | # proc evalScripts(nwt: Nwt, nwtTemplate: NwtTemplate , params: JsonNode = newJObject()): seq[Token] = 264 | # ## This evaluates the template logic. 265 | # ## After this the template is fully epanded an is ready to convert it to strings 266 | # ## TODO we should avoid looping multiple times.... 267 | # # var forBlocks = getBlocks(nwtTemplate, starting = "for", ending = "endfor") 268 | # # for token in tokens: 269 | # # for forBlock in forBlocks.values: 270 | # # echo forBlock ## (name: item, cmd: for --> @[item, in, items], posStart: 0, posEnd: 4) 271 | # # tokens.insert() 272 | # # for idx, each in params[forBlock.cmd.params[2]]: 273 | # # echo forBlock.cmd.params[0], "->", each # , "-> ", each , "[" , idx , "]" 274 | # return nwtTemplate 275 | 276 | proc renderTemplate*(nwt: Nwt, templateName: TemplateIdent, variables: JsonNode = newJObject(), autoEscape: Option[bool] = none(bool)): string = 277 | ## this returns the fully rendered template. 278 | ## all replacements are done. 279 | ## if the loaded template extends a base template, we parse this as well and fill all the blocks. 280 | ## ATM this is not recursively checking for extends on child templates! 281 | ## So only one 'extends' level is supported. 282 | var ctx = newContext(variables) 283 | when not defined release: 284 | echo "WARNING THIS IS AN DEBUG BUILD. NWT PARSES THE HTML ON EVERY GET; THIS IS SLOW" 285 | nwt.loadTemplates(nwt.templatesDir) 286 | result = "" 287 | var nwtTemplate = newNwtTemplate() 288 | # nwt.blockTable.clear() 289 | if not nwt.templates.hasKey(templateName): 290 | raise newException(ValueError, "Template '$1' not found." % [templateName]) # UnknownTemplate 291 | nwtTemplate = nwt.evalTemplate(templateName, ctx) 292 | # tokens = nwt.evalScripts(tokens, params) # expands `for` `if` etc 293 | for token in nwtTemplate: 294 | result.add nwt.toStr(token, ctx, autoEscape = autoEscape) 295 | 296 | proc freeze*(nwt: Nwt, params: JsonNode = newJObject(), outputPath: string = "./freezed/", staticPath = "./public/") = 297 | ## generates the html for each template. 298 | ## writes to the output path 299 | if not dirExists(outputPath): createDir(outputPath) 300 | if staticPath.len != 0 and dirExists(staticPath): 301 | copyDir( staticPath.strip(false, true, {'/'}) , outputPath) 302 | for name, tmpl in nwt.templates: 303 | let freezedFilePath = outputPath / name 304 | echo "Freezing: ", name, " to: ", freezedFilePath 305 | var fh = open( freezedFilePath, fmWrite ) 306 | fh.write(nwt.renderTemplate(name, params)) 307 | fh.close() 308 | 309 | when isMainModule: 310 | # var nwt = newNwt() 311 | # echo "Loaded $1 templates." % [$nwt.templates.len] 312 | # import t/testsuit 313 | block: 314 | var t = newNwt("") # "" do not read any templates 315 | # assert t.templates == initTable[system.string, seq[Token]]() 316 | assert t.templates == newNwtTemplates() 317 | 318 | t.addTemplate("foo.html","i am the {{faa}} template") 319 | # echo t.renderTemplate("foo.html",%*{"faa": "super"}) 320 | assert t.renderTemplate("foo.html", %*{"faa": "super"}) == "i am the super template" 321 | -------------------------------------------------------------------------------- /src/nwtTokenizer.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # nimWebTemplates 4 | # (c) Copyright 2017 David Krause 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | ## :Author: David Krause (enthus1ast) 10 | ## 11 | ## a jinja like template syntax parser 12 | ## 13 | ## This is the tokenizer of nwt. 14 | ## 15 | ## From this html: 16 | ## 17 | ## .. code-block:: 18 | ## 19 | ## 20 | ## 21 | ## {%block "title" %}BASE{%endblock%} 22 | ## 23 | ## 24 | ## 25 | ## 31 | ## 32 | ## 33 | ##

    Welcome from base

    34 | ##
    35 | ## {#

    {{content}}

    #} 36 | ##
    37 | ## {%block "content2" %}{%endblock%} 38 | ##
    39 | ## 40 | ##
    41 | ## 42 | ##
    43 | ## Hier beschreibe ich nwt's syntax: 44 | ## 45 | ## 46 | ##
    47 | ## 48 | ## 49 | 50 | ## 51 | ## This gets generated: 52 | ## 53 | ## .. code-block:: 54 | ## 55 | ## (tokenType: NwtString, value: 56 | ## 57 | ## ) 58 | ## (tokenType: NwtEval, value: block "title") 59 | ## (tokenType: NwtString, value: BASE) 60 | ## (tokenType: NwtEval, value: endblock) 61 | ## (tokenType: NwtString, value: 62 | ## 63 | ## 64 | ## 65 | ## 71 | ## 72 | ## 73 | ##

    Welcome from base

    74 | ##
    75 | ##

    ) 76 | ## (tokenType: NwtVariable, value: content) 77 | ## (tokenType: NwtString, value:

    78 | ##
    79 | ## ) 80 | ## (tokenType: NwtEval, value: block "content2") 81 | ## (tokenType: NwtEval, value: endblock) 82 | ## (tokenType: NwtString, value: 83 | ## 84 | ##
    85 | ## 86 | ##
    87 | ## 88 | ##
    89 | ## Hier beschreibe ich nwt's syntax: 90 | ##
    91 | ## 92 | ## ) 93 | 94 | import parseutils 95 | import sequtils 96 | import strutils 97 | import tables 98 | import strtabs 99 | 100 | proc debugPrint(buffer: string, pos: int) = 101 | var pointPos = if pos - 1 < 0: 0 else: pos - 1 102 | echo buffer 103 | echo '-'.repeat(pointPos) & "^" 104 | 105 | type 106 | TemplateSyntaxError* = ref object # of Exception 107 | UnknownTemplate* = ref object #of Exception 108 | 109 | NwtToken* = enum 110 | NwtString, # a string block 111 | NwtComment, 112 | NwtEval, 113 | NwtVariable 114 | 115 | Token* = object of RootObj 116 | tokenType*: NwtToken # the type of the token 117 | value*: string # the value 118 | 119 | Block* = tuple[name: string, posStart: int, posEnd: int] 120 | 121 | proc newToken*(tokenType:NwtToken, value: string): Token = 122 | result = Token() 123 | result.tokenType = tokenType 124 | result.value = value 125 | 126 | proc extractTemplateName*(raw: string): string = 127 | ## returns the template name from 128 | ## extends "base.html" 129 | ## returns "base.html" 130 | var parts = raw.strip().split(" ") 131 | if parts.len < 2: 132 | # TemplateSyntaxError 133 | raise newException(ValueError, "Could not extract template name from '$1'" % [raw]) 134 | # raise (OsError, "Could not extract template name from '$1'" % [raw]) 135 | result = parts[1].captureBetween('"', '"') 136 | if result != "": return 137 | 138 | result = parts[1].captureBetween('\'', '\'') 139 | if result != "": return 140 | 141 | result = parts[1] # " or ' are missing 142 | 143 | 144 | iterator nwtTokenize*(s: string): Token = 145 | ## transforms nwt templates into tokens 146 | var 147 | buffer: string = s 148 | pos = 0 149 | toyieldlater = "" # we use this to reconstruct a string whitch contains a "{" 150 | 151 | while true: 152 | var stringToken = "" 153 | pos = buffer.parseUntil(stringToken,'{',pos) + pos 154 | # buffer.debugPrint(pos) 155 | 156 | if buffer == "{": 157 | # echo "buffer ist just '{'" 158 | yield newToken(NwtString, "{") 159 | break 160 | 161 | if stringToken.len == buffer.len: 162 | # echo "we have read the string at once! no '{' found" 163 | yield newToken(NwtString, stringToken) 164 | break 165 | 166 | if stringToken != "" : 167 | toyieldlater.add stringToken 168 | pos.inc # skip "{" 169 | if buffer.continuesWith("{",pos): 170 | if toyieldlater != "": yield newToken(NwtString, toyieldlater); toyieldlater = "" 171 | pos.inc # skip { 172 | pos = buffer.parseUntil(stringToken,'}',pos) + pos 173 | yield newToken(NwtVariable, stringToken.strip()) 174 | pos.inc # skip } 175 | pos.inc # skip } 176 | elif buffer.continuesWith("#",pos): 177 | if toyieldlater != "": yield newToken(NwtString, toyieldlater); toyieldlater = "" 178 | pos.inc # skip # 179 | pos = buffer.parseUntil(stringToken,'#',pos) + pos 180 | pos.inc # skip end # 181 | if buffer.continuesWith("}", pos): 182 | pos.inc # skip } 183 | yield newToken(NwtComment, stringToken[0..^1].strip()) 184 | elif buffer.continuesWith("%",pos): 185 | if toyieldlater != "": yield newToken(NwtString, toyieldlater); toyieldlater = "" 186 | pos.inc # skip # 187 | pos = buffer.parseUntil(stringToken,'%',pos) + pos 188 | pos.inc # skip end # 189 | if buffer.continuesWith("}", pos): 190 | pos.inc # skip } 191 | yield newToken(NwtEval, stringToken[0..^1].strip()) 192 | else: 193 | if pos >= buffer.len: 194 | # echo "we have reached the end of buffer" 195 | yield newToken(NwtString, toyieldlater) 196 | else: 197 | # echo "we found a { somewhere so we have to prepend it" 198 | toyieldlater = toyieldlater & "{" 199 | discard 200 | 201 | if pos >= buffer.len: # TODO check if this has to be '>' 202 | ## Nothing to do for us here 203 | break 204 | 205 | 206 | when isMainModule: 207 | # var nwt = newNwt() 208 | # echo "Loaded $1 templates." % [$nwt.templates.len] 209 | 210 | # Tokenize tests 211 | assert toSeq(nwtTokenize("hello")) == @[newToken(NwtString, "hello")] 212 | assert toSeq(nwtTokenize("{{var}}")) == @[newToken(NwtVariable, "var")] 213 | assert toSeq(nwtTokenize("{{ var }}")) == @[newToken(NwtVariable, "var")] 214 | assert toSeq(nwtTokenize("{{var}}{{var}}")) == @[newToken(NwtVariable, "var"),newToken(NwtVariable, "var")] 215 | assert toSeq(nwtTokenize("{#i am a comment#}")) == @[newToken(NwtComment, "i am a comment")] 216 | assert toSeq(nwtTokenize("{# i am a comment #}")) == @[newToken(NwtComment, "i am a comment")] 217 | assert toSeq(nwtTokenize("{%raw%}")) == @[newToken(NwtEval, "raw")] 218 | assert toSeq(nwtTokenize("{% raw %}")) == @[newToken(NwtEval, "raw")] 219 | assert toSeq(nwtTokenize("{% for each in foo %}")) == @[newToken(NwtEval, "for each in foo")] 220 | 221 | assert toSeq(nwtTokenize("body { background-color: blue; }")) == 222 | @[newToken(NwtString, "body { background-color: blue; }")] 223 | 224 | assert toSeq(nwtTokenize("{ nope }")) == @[newToken(NwtString, "{ nope }")] 225 | assert toSeq(nwtTokenize("{nope}")) == @[newToken(NwtString, "{nope}")] 226 | assert toSeq(nwtTokenize("{nope")) == @[newToken(NwtString, "{nope")] 227 | assert toSeq(nwtTokenize("nope}")) == @[newToken(NwtString, "nope}")] 228 | 229 | assert toSeq(nwtTokenize("{")) == @[newToken(NwtString, "{")] 230 | assert toSeq(nwtTokenize("}")) == @[newToken(NwtString, "}")] 231 | 232 | assert toSeq(nwtTokenize("""{%block 'first'%}{%blockend%}""")) == @[newToken(NwtEval, "block 'first'"), newToken(NwtEval, "blockend")] 233 | assert toSeq(nwtTokenize("foo {baa}")) == @[newToken(NwtString, "foo {baa}")] 234 | 235 | assert toSeq(nwtTokenize("foo {{baa}} {baa}")) == @[newToken(NwtString, "foo "), 236 | newToken(NwtVariable, "baa"), 237 | newToken(NwtString, " {baa}")] 238 | # extractTemplateName tests 239 | assert extractTemplateName("""extends "foobaa.html" """) == "foobaa.html" 240 | assert extractTemplateName("""extends "foobaa.html"""") == "foobaa.html" 241 | assert extractTemplateName("""extends 'foobaa.html'""") == "foobaa.html" 242 | assert extractTemplateName("""extends foobaa.html""") == "foobaa.html" 243 | assert extractTemplateName("""extends foobaa.html""") == "foobaa.html" 244 | assert extractTemplateName(toSeq(nwtTokenize("""{% extends "foobaa.html" %}"""))[0].value) == "foobaa.html" 245 | block: 246 | var tokens = toSeq(nwtTokenize("""{% extends "foobaa.html" %}{% extends "goo.html" %} """)) 247 | assert extractTemplateName(tokens[0].value) == "foobaa.html" 248 | assert extractTemplateName(tokens[1].value) == "goo.html" 249 | block: 250 | var tokens = toSeq(nwtTokenize("""{% extends foobaa.html %}{% extends goo.html %}""")) 251 | assert extractTemplateName(tokens[0].value) == "foobaa.html" 252 | assert extractTemplateName(tokens[1].value) == "goo.html" 253 | block: 254 | var tokens = toSeq(nwtTokenize("""{%extends "foobaa.html" %}{% extends 'goo.html' %}""")) 255 | assert extractTemplateName(tokens[0].value) == "foobaa.html" 256 | assert extractTemplateName(tokens[1].value) == "goo.html" 257 | block: 258 | var tokens = toSeq(nwtTokenize("""{%extends foobaa.html%}""")) 259 | assert extractTemplateName(tokens[0].value) == "foobaa.html" -------------------------------------------------------------------------------- /src/stack.nim: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | ## For building a stack easily..... 3 | ## Check if they exists in stdlib 4 | ## 5 | proc popr*[T](s: var seq[T]): T = 6 | ## removes and returns the rightest/last item 7 | result = s[^1] 8 | s.delete(s.len) 9 | 10 | proc popl*[T](s: var seq[T]): T = 11 | ## removes and return the leftest/first item 12 | result = s[0] 13 | s.delete(0) 14 | 15 | proc pushr*[T](s: var seq[T], itm: T) = 16 | ## adds a value to the back/right 17 | s.add(itm) 18 | 19 | proc pushl*[T](s: var seq[T], itm: T)= 20 | ## adds a value to the front/left 21 | s.insert(itm,0) 22 | ######################################################## -------------------------------------------------------------------------------- /t/t1.nim: -------------------------------------------------------------------------------- 1 | import ../nwt 2 | import ../nwtTokenizer 3 | import sequtils 4 | import tables 5 | 6 | var baseStr = """{%block "title" %}BASE{%endblock%}""" 7 | var baseTokenList = toSeq(nwtTokenize baseStr) 8 | echo baseTokenList 9 | 10 | # var base2Str = """{%block "title" %}BASE2{%endblock%}""" 11 | # var base2TokenList = toSeq(nwtTokenize base2Str) 12 | # echo base2TokenList 13 | 14 | var fillerStr = """{%block "title" %}filler and some more{%endblock%}""" 15 | var fillerTokenList = toSeq(nwtTokenize fillerStr) 16 | echo fillerTokenList 17 | 18 | 19 | 20 | var baseBlockTable = baseTokenList.getBlocks() 21 | echo baseBlockTable 22 | 23 | echo baseTokenList.fillBlocks(fillerTokenList) -------------------------------------------------------------------------------- /t/t2.nim: -------------------------------------------------------------------------------- 1 | import ../nwt 2 | import ../nwtTokenizer 3 | import sequtils 4 | import tables 5 | import commandParser 6 | 7 | # var baseStr = """{%block "title" %}BASE{%endblock%}""" 8 | # var baseTokenList = toSeq(nwtTokenize baseStr) 9 | # echo baseTokenList 10 | 11 | # # var base2Str = """{%block "title" %}BASE2{%endblock%}""" 12 | # # var base2TokenList = toSeq(nwtTokenize base2Str) 13 | # # echo base2TokenList 14 | 15 | # var fillerStr = """{%block "title" %}filler and some more{%endblock%}""" 16 | # var fillerTokenList = toSeq(nwtTokenize fillerStr) 17 | # echo fillerTokenList 18 | 19 | 20 | 21 | # var baseBlockTable = baseTokenList.getBlocks() 22 | # echo baseBlockTable 23 | 24 | # echo baseTokenList.fillBlocks(fillerTokenList) 25 | 26 | 27 | 28 | # var baseStr = """{%block "title" %}BASE {%block "klaus" %}ich bin der klaus{%endblock%} ich bin _NICH_ der klaus{%endblock%} hallo """ 29 | # var baseStr = """{%block "title" %}BASE {%block "klaus" %}ich bin der klaus ich bin _NICH_ der klaus{%endblock%} hallo {%endblock%}a asdfasf""" 30 | # var baseTokenList = toSeq(nwtTokenize baseStr) 31 | # echo baseTokenList 32 | # for k,v in getBlocks(baseTokenList).pairs: 33 | # echo k, "->", v 34 | 35 | 36 | 37 | var baseStr = """{%for foo in baa%}BASE {%for uggu in foo%}ich bin der klaus {{uggu}} ich bin _NICH_ der klaus{%endfor%} hallo {%endfor%}a asdfasf""" 38 | var baseTokenList = toSeq(nwtTokenize baseStr) 39 | echo baseTokenList 40 | for k,v in getBlocks(baseTokenList, "for", "endfor").pairs: 41 | echo k, "->", v -------------------------------------------------------------------------------- /t/testsuit.nim: -------------------------------------------------------------------------------- 1 | ## testsuit for nwt 2 | ## 3 | ## Everything here should run. 4 | ## If not its a bug! 5 | ## !!cd into the test directory!! 6 | import ../nwt 7 | import json 8 | 9 | var tmpls = newNwt() 10 | 11 | block: 12 | tmpls.templates.add("a",""); assert tmpls.renderTemplate("a") == "" 13 | tmpls.templates.add("a","test"); assert tmpls.renderTemplate("a") == "test" 14 | 15 | block: 16 | tmpls.templates.add("base.html","{%block 'bar'%}{%endblock%}") 17 | tmpls.templates.add("extends.html","{%extends base.html%}{%block 'bar'%}Nim likes you!!{%endblock%}") 18 | assert tmpls.renderTemplate("extends.html") == "Nim likes you!!" 19 | 20 | block: ## Import 21 | tmpls.templates.add("imp1.html","{%set 'var' 'one two three'%}{%block block1%}b1{%endblock%}") 22 | tmpls.templates.add("imp2.html","{%set 'var2' 'tralalala'%}{%block block2%}b2{%endblock%}") 23 | tmpls.templates.add("base.html","{%import imp1.html%}{{var}}") 24 | assert tmpls.renderTemplate("base.html") == "one two three" 25 | 26 | tmpls.templates.add("base.html","{%import imp1.html%}{{var}}{{var}}") 27 | assert tmpls.renderTemplate("base.html") == "one two threeone two three" 28 | 29 | tmpls.templates.add("base.html","{%import imp1.html%}{%import imp2.html%}{{var}}{{var2}}") 30 | assert tmpls.renderTemplate("base.html") == "one two threetralalala" 31 | 32 | tmpls.templates.add("base.html","{%import imp1.html%}{%import imp2.html%}{{var}}{{var2}} {%block block1%}{%endblock%}{%block block2%}{%endblock%}") 33 | assert tmpls.renderTemplate("base.html") == "one two threetralalala b1b2" 34 | 35 | tmpls.templates.add("base.html","{%import imp1.html%}{%import imp2.html%}{{var}}{{var2}} {%block block1%}{%endblock%}{%block block2%}{%endblock%}") 36 | assert tmpls.renderTemplate("base.html") == "one two threetralalala b1b2" 37 | 38 | # TODO BUG blocks from an import gets not filled 39 | # tmpls.templates.add("imps.html","{%import imp1.html%}") ## this is how it should be right? 40 | tmpls.templates.add("imps.html","{%import imp1.html%}{%block block1%}{%endblock%}") ## WORKAROUND why is this neccesarry? 41 | tmpls.templates.add("base.html","{%import imps.html%}{{var}} {%block block1%}{%endblock%}") 42 | # tmpls.templates.add("base.html","{%import imps.html%}{{var}} {{self.block1}}") 43 | # echo "\n\n", tmpls.renderTemplate("base.html") ,"\n\n" 44 | assert tmpls.renderTemplate("base.html") == "one two three b1" 45 | 46 | ## TODO BUG blocks from an import gets not filled 47 | # tmpls.templates.add("imps.html","{%import imp1.html%}{%import imp2.html%}") ## this is how it should be right? 48 | tmpls.templates.add("imps.html","{%import imp1.html%}{%import imp2.html%}{%block block1%}{%endblock%}{%block block2%}{%endblock%}") ## WORKAROUND why is this neccesarry? 49 | tmpls.templates.add("base.html","{%import imps.html%}{{var}}{{var2}} {%block block1%}{%endblock%}{%block block2%}{%endblock%}") 50 | # echo "\n\n", tmpls.renderTemplate("base.html") ,"\n\n" 51 | assert tmpls.renderTemplate("base.html") == "one two threetralalala b1b2" 52 | 53 | tmpls.templates.add("layout.html","{%block layout%}{%block inside1%}{%endblock%}{%block inside2%}{%endblock%}{%endblock%}") 54 | tmpls.templates.add("base.html","{%extends layout.html%}{%block inside1%}a{%endblock%}{%block inside2%}b{%endblock%}") # here is the ab 55 | tmpls.templates.add("info.html","{%import base.html%}{%block layout%}{%endblock%}") 56 | assert tmpls.renderTemplate("info.html") == "ab" 57 | 58 | 59 | block: ## self.templatename tests 60 | var tmpls = newNwt() 61 | tmpls.templates.add("imp1.html", "{%block blk1%}b1{%endblock%}") 62 | tmpls.templates.add("imp2.html", "{%block blk2%}b2{%endblock%}") 63 | tmpls.templates.add("imp3.html", "{%block blk3%}b3{%endblock%}{%block blk4%}b4{%endblock%}") 64 | tmpls.templates.add("run.html", "{%import imp1.html%}{{self.blk1}}") 65 | assert tmpls.renderTemplate("run.html") == "b1" 66 | 67 | tmpls.templates.add("run.html", "{%import imp1.html%}{%import imp2.html%}{{self.blk1}}{{self.blk2}}") 68 | assert tmpls.renderTemplate("run.html") == "b1b2" 69 | 70 | tmpls.templates.add("run.html", "{%import imp1.html%}{%import imp2.html%}{%import imp3.html%}{{self.blk1}}{{self.blk2}}{{self.blk3}}{{self.blk4}}") 71 | assert tmpls.renderTemplate("run.html") == "b1b2b3b4" 72 | 73 | block: ## empty variable tests 74 | var tmpls = newNwt() 75 | tmpls.templates.add("run.html", "{{nothere}}") 76 | tmpls.templates.add("run2.html", "{{self.nothere}}") 77 | 78 | tmpls.echoEmptyVars = true 79 | assert tmpls.renderTemplate("run.html") == "{{nothere}}" 80 | assert tmpls.renderTemplate("run2.html") == "{{self.nothere}}" 81 | 82 | ## release 83 | tmpls.echoEmptyVars = false 84 | assert tmpls.renderTemplate("run.html") == "" 85 | assert tmpls.renderTemplate("run2.html") == "" 86 | 87 | block: ## param tests 88 | var tmpls = newNwt() 89 | tmpls.templates.add("run.html", "{{var1}}{{var2}}{{var3}}") 90 | 91 | tmpls.echoEmptyVars = false # this should be default 92 | assert tmpls.renderTemplate("run.html", %* {"var1": "1", "var2": 2}) == "12" 93 | # assert tmpls.renderTemplate("run.html", %* {"var1": "1", "var2": 2}) == "12" 94 | 95 | tmpls.echoEmptyVars = true # but in development we can enable the placeholder printing 96 | assert tmpls.renderTemplate("run.html", %* {"var1": "1", "var2": 2}) == "12{{var3}}" 97 | assert tmpls.renderTemplate("run.html", %* {"var1": "1", "var2": 2.0}) == "12.0{{var3}}" 98 | assert tmpls.renderTemplate("run.html", %* {"var4": "1", "var2": 2}) == "{{var1}}2{{var3}}" 99 | 100 | tmpls.templates.add("base.html", "{%block content%}{%endblock%}") 101 | tmpls.templates.add("run2.html", "{%extends base.html%}{%block content%}{{var1}}{{var2}}{{var3}}{%endblock%}") 102 | 103 | tmpls.echoEmptyVars = true 104 | assert tmpls.renderTemplate("run2.html", %* {"var1": "1", "var2": 2}) == "12{{var3}}" 105 | 106 | tmpls.echoEmptyVars = false 107 | assert tmpls.renderTemplate("run2.html", %* {"var1": "1", "var2": 2}) == "12" 108 | 109 | block: # autoescape test # TODO api (with option.`some`) is ugly 110 | var t = newNwt(nil) 111 | t.addTemplate("foo.html","{{foo}}") 112 | t.autoEscape = true 113 | assert t.renderTemplate("foo.html", %* {"foo": ">"}) == ">" 114 | assert t.renderTemplate("foo.html", %* {"foo": ">"}, autoEscape = some false) == ">" 115 | 116 | t.autoEscape = false 117 | assert t.renderTemplate("foo.html", %* {"foo": ">"}) == ">" 118 | assert t.renderTemplate("foo.html", %* {"foo": ">"}, autoEscape = some true) == ">" 119 | 120 | 121 | 122 | # tmpls.templates.add("run.html", "{{var1}}{{var2}}{{var3}}") 123 | 124 | # block: 125 | # let tst = """ 126 | # 127 | # engine 128 | # 129 | # 130 | # 134 | #

    Welcome from baseasdfasdf

    135 | #
    136 | #
    137 | # 138 | # """ 139 | # block: 140 | # var tst = """{%extends "base.html"%} 141 | # {%block "klausi"%} 142 | # ass : ) 143 | # {%endblock%} 144 | # {%block "content2"%} 145 | # ass : ) 146 | # {%endblock%} 147 | # {%block "peter"%} 148 | # ass petr 149 | # {%endblock%}""" 150 | 151 | # block: ## if tests 152 | # var tmpls = newNwt() 153 | # tmpls.templates.add("run.html", "{%if false%}1{%endif%}") 154 | # assert tmpls.renderTemplate("run.html") == "" 155 | 156 | # tmpls.templates.add("run.html", "{%if true%}1{%endif%}") 157 | # assert tmpls.renderTemplate("run.html") == "1" 158 | 159 | # tmpls.templates.add("run.html", "{%if 1%}1{%endif%}") 160 | # assert tmpls.renderTemplate("run.html") == "1" 161 | 162 | # block: #double extends are not supported 163 | # tmpls.templates.add("ext1.html", "{%block ext1%}e1{%endblock%}") 164 | # tmpls.templates.add("ext2.html", "{%block ext1%}e2{%endblock%}") 165 | 166 | 167 | -------------------------------------------------------------------------------- /templates/2blocks.html: -------------------------------------------------------------------------------- 1 | {%block "block"%}A BLOCK WITH SOME CONTENT{%endblock%} 2 | {%block "block2"%}ANOTHER BLOCK WITH SOME CONTENT{%endblock%} -------------------------------------------------------------------------------- /templates/ass.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | 3 | 4 | {%block "title"%}ass{%endblock%} 5 | 6 | {%block "klausi"%} 7 | ass : ) 8 | {%endblock%} 9 | 10 | {%block "content2"%} 11 | ass : ) 12 | {%endblock%} 13 | 14 | {%block "peter"%} 15 | ass petr 16 | {%endblock%} 17 | 18 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {%block "title" %}BASE{%endblock%} 4 | 5 | 6 | 7 | 13 | 14 | 15 |

    Welcome from base

    16 |
    17 | {#

    {{content}}

    #} 18 |
    19 | {%block "content2" %}{%endblock%} 20 |
    21 | 22 |
    23 | 24 |
    25 | Hier beschreibe ich nwt's syntax: 26 | 27 | 28 |
    29 | 30 | -------------------------------------------------------------------------------- /templates/block.html: -------------------------------------------------------------------------------- 1 | {%block "block"%}A BLOCK WITH SOME CONTENT{%endblock%} -------------------------------------------------------------------------------- /templates/block2.html: -------------------------------------------------------------------------------- 1 | {%block "block2"%}Another BLOCK WITH SOME CONTENT{%endblock%} -------------------------------------------------------------------------------- /templates/extendsImportsTwoBlocksFromOneFile.html: -------------------------------------------------------------------------------- 1 | {%extends "importsTwoBlocksFromOneFile.html" %} 2 | -------------------------------------------------------------------------------- /templates/for.html: -------------------------------------------------------------------------------- 1 | {% for item in items %} 2 | {{ item }}
    3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/for2.html: -------------------------------------------------------------------------------- 1 | {% for name in names %} 2 | {{ name }}
    3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /templates/import.html: -------------------------------------------------------------------------------- 1 | {%import "vars.html"%} 2 | {%import "vars2.html"%} 3 | {%import "vars3.html"%} 4 | {{klaus}} 5 | {{ch4t}} 6 | 7 | 8 | hahahah: 9 | {%block "one"%}{%endblock%} 10 | ugga 11 | {%block "two"%}{%endblock%} 12 | 13 | vars3: {{mega}} -------------------------------------------------------------------------------- /templates/importsTwoBlocksFromOneFile.html: -------------------------------------------------------------------------------- 1 | {% import "2blocks.html" %} 2 | 3 | {%block "block" %}i get replaced 1{%endblock %} 4 | {%block "block2"%}i get replaced 2{% endblock%} -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | 3 | {%block "content2"%} 4 | I AM THE FUCKING INDEX!!!!!! 5 | {%endblock%} 6 | 7 | {%block "peter"%} 8 | ass petr 9 | {%endblock%} 10 | 11 | -------------------------------------------------------------------------------- /templates/ji.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %}Memberlist{% endblock %} 3 | {% block content %} 4 | 9 | {% endblock %} -------------------------------------------------------------------------------- /templates/multi.html: -------------------------------------------------------------------------------- 1 | {%extends "one.html"%} 2 | {%extends "two.html"%} -------------------------------------------------------------------------------- /templates/one.html: -------------------------------------------------------------------------------- 1 | {%block "one"%}i am the one from the one!{%endblock%} -------------------------------------------------------------------------------- /templates/self.html: -------------------------------------------------------------------------------- 1 | {%block "peter"%}HALLO{%endblock%} 2 | -------------------------------------------------------------------------------- /templates/selfT.html: -------------------------------------------------------------------------------- 1 | {# {%extends "self.html"%} #} 2 | {%import "self.html"%} 3 | 4 | {{self.peter}} 5 | 6 | {{self.peter}} 7 | 8 | {{self.peter}} 9 | 10 | 11 | -------------------------------------------------------------------------------- /templates/setter.html: -------------------------------------------------------------------------------- 1 | 2 | {%extends "vars.html" %} 3 | {# a #} 4 | {# {%extends "vars2.html" %} #} 5 | {# {%block "two"%}{%endblock%} #} 6 | {%block "one"%} 7 | {{email}} 8 | {{klaus}} 9 | {{icq}} 10 | {{ch4t}} 11 | {{mega}} 12 | 13 | {{content}} 14 | {%endblock%} 15 | 16 | -------------------------------------------------------------------------------- /templates/styleBase.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: {{bgcolor}}; 3 | color: {{color}}; 4 | peter: {{peter}}; 5 | klaus: {{klaus}}; 6 | } -------------------------------------------------------------------------------- /templates/styleDark.css: -------------------------------------------------------------------------------- 1 | {%extends "styleBase.css"%} 2 | {%set bgcolor "red" %} 3 | {%set color "white" %} -------------------------------------------------------------------------------- /templates/styleLight.css: -------------------------------------------------------------------------------- 1 | {%extends "styleBase.css"%} 2 | {%set bgcolor "grey" %} 3 | {%set color "darkslategray" %} -------------------------------------------------------------------------------- /templates/t01.html: -------------------------------------------------------------------------------- 1 | {%import "block.html"%} 2 | {%block block%}{%endblock%} -------------------------------------------------------------------------------- /templates/two.html: -------------------------------------------------------------------------------- 1 | {%block "two"%}i am the two from the two!{%endblock%} -------------------------------------------------------------------------------- /templates/ugga.html: -------------------------------------------------------------------------------- 1 | {%extends "base.html"%} 2 | 3 | {%block "content2"%} 4 | I AM THE UGGA 5 | {%endblock%} 6 | 7 | 8 | {%block "nichtda"%} 9 | ich bin ein block der nicht da ist 10 | {%endblock%} -------------------------------------------------------------------------------- /templates/vars.html: -------------------------------------------------------------------------------- 1 | {# {%extends "vars2.html"%} #} 2 | {%set klaus "peter ist der beste" %} 3 | {%set email "david@code0.xyz" %} 4 | 5 | 6 | {%block "one" %}from one{%endblock%} 7 | -------------------------------------------------------------------------------- /templates/vars2.html: -------------------------------------------------------------------------------- 1 | 2 | {% set icq "icq ist doof" %} 3 | {% set ch4t "ch4t is kuhl" %} 4 | {%block "two" %}from two{%endblock%} -------------------------------------------------------------------------------- /templates/vars3.html: -------------------------------------------------------------------------------- 1 | {% set mega "sehr mega" %} 2 | --------------------------------------------------------------------------------