1 | # Slm
|
2 |
|
3 | Slm is a template language for js. Port of [Slim](http://slim-lang.com/) but slimmer :)
|
4 |
|
5 | [![Build Status](https://img.shields.io/travis/slm-lang/slm/master.svg)](https://travis-ci.org/slm-lang/slm)
|
6 | [![Dependency Status](https://img.shields.io/gemnasium/slm-lang/slm.svg)](https://gemnasium.com/slm-lang/slm)
|
7 | [![NPM version](https://badge.fury.io/js/slm.svg)](http://badge.fury.io/js/slm)
|
8 | [![Code Climate](https://codeclimate.com/github/slm-lang/slm/badges/gpa.svg)](https://codeclimate.com/github/slm-lang/slm)
|
9 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/slm-lang/slm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
10 |
|
11 |
|
12 | ## A little bit of history
|
13 |
|
14 | HAML -> Jade -> Slim -> Slm
|
15 |
|
16 | ### Short list of the features
|
17 |
|
18 | * Elegant syntax
|
19 | * Short syntax without closing tags (Using indentation instead)
|
20 | * HTML style mode with closing tags
|
21 | * Shortcut tags (based on css selectors)
|
22 | * Safety
|
23 | * Automatic HTML escaping by default
|
24 | * Support `htmlSafe` attribute on String objects
|
25 | * Highly configurable and extendable via plugins
|
26 | * High performance
|
27 | * Comparable speed to ECT and Hogan [see benchmakrs](https://github.com/slm-lang/template-benchmark#results)
|
28 | * Easy integration with hapijs
|
29 | * Embedded engines like [Markdown](https://github.com/slm-lang/slm-markdown) and Textile (TBD)
|
30 |
|
31 | ## Links
|
32 |
|
33 | * Source: <http://github.com/slm-lang/slm>
|
34 | * Bugs: <http://github.com/slm-lang/slm/issues>
|
35 | * Highlighters:
|
36 | * Atom: <https://github.com/slm-lang/language-slm>
|
37 | * Sublime: <https://github.com/slm-lang/sublime-slm>
|
38 | * Vim: <https://github.com/slm-lang/vim-slm>
|
39 | * Benchmark: <https://github.com/slm-lang/template-benchmark#results>
|
40 | * Gulp: <https://github.com/simnalamburt/gulp-slm>
|
41 | * Grunt: <https://github.com/MichaelDanilov/grunt-slm>
|
42 |
|
43 | ### How to start?
|
44 |
|
45 | Install Slm with npm:
|
46 |
|
47 | npm install slm --save
|
48 |
|
49 | ### Configure to work with hapijs
|
50 |
|
51 | ```js
|
52 | var Hapi = require('hapi');
|
53 |
|
54 | var server = new Hapi.Server(3000);
|
55 | server.views({
|
56 | engines: {
|
57 | 'slm': require('slm')
|
58 | },
|
59 | basePath: __dirname + '/views',
|
60 | compileOptions: {
|
61 | basePath: __dirname + '/views',
|
62 | useCache: false // disable internal cache - useful for development
|
63 | },
|
64 | isCached: false // disable hapi view cache
|
65 | });
|
66 |
|
67 | server.route({
|
68 | method: 'GET', path: '/',
|
69 | handler: function (request, reply) {
|
70 | reply.view('index', {hello: "word"});
|
71 | }
|
72 | });
|
73 |
|
74 | server.start(function () {
|
75 | console.log('Server running at:', server.info.uri);
|
76 | });
|
77 | ```
|
78 |
|
79 | ### Syntax example
|
80 |
|
81 | Here's a quick example to demonstrate what a Slm template looks like:
|
82 |
|
83 | doctype html
|
84 | html
|
85 | head
|
86 | title Slm Examples
|
87 | meta name="keywords" content="template language"
|
88 | meta name="author" content=this.author
|
89 | javascript:
|
90 | alert('Slm supports embedded javascript!')
|
91 |
|
92 | body
|
93 | h1 Markup examples
|
94 |
|
95 | #content
|
96 | p This example shows you how a basic Slm file looks.
|
97 |
|
98 | == content()
|
99 |
|
100 | - if this.items.length
|
101 | table#items
|
102 | - for item in this.items
|
103 | tr
|
104 | td.name = item.name
|
105 | td.price = item.price
|
106 | - else
|
107 | p No items found Please add some inventory.
|
108 | Thank you!
|
109 |
|
110 | div id="footer"
|
111 | == partial('footer')
|
112 | | Copyright © ${this.year} ${this.author}
|
113 |
|
114 | Indentation matters, but the indentation depth can be chosen as you like. If you want to first indent 2 spaces, then 5 spaces, it's your choice. To nest markup you only need to indent by one space, the rest is gravy.
|
115 |
|
116 | ## Line indicators
|
117 |
|
118 | ### Text `|`
|
119 |
|
120 | The pipe tells Slm to just copy the line. It essentially escapes any processing.
|
121 | Each following line that is indented greater than the pipe is copied over.
|
122 |
|
123 | body
|
124 | p
|
125 | |
|
126 | This is a test of the text block.
|
127 |
|
128 | The parsed result of the above:
|
129 |
|
130 | <body><p>This is a test of the text block.</p></body>
|
131 |
|
132 | The left margin is set at the indent of the pipe + one space.
|
133 | Any additional spaces will be copied over.
|
134 |
|
135 | body
|
136 | p
|
137 | | This line is on the left margin.
|
138 | This line will have one space in front of it.
|
139 | This line will have two spaces in front of it.
|
140 | And so on...
|
141 |
|
142 | You can also embed html in the text line
|
143 |
|
144 | - for (var a in this.articles)
|
145 | | <tr><td>${a.name}</td><td>${a.description}</td></tr>
|
146 |
|
147 | ### Text with trailing white space `.`
|
148 |
|
149 | The single dot tells Slm to copy the line (similar to `|`), but makes sure that a single trailing white space is appended.
|
150 |
|
151 | ### Inline html `<` (HTML style)
|
152 |
|
153 | You can write html tags directly in Slm which allows you to write your templates in a more html like style with closing tags or mix html and Slm style.
|
154 |
|
155 | <html>
|
156 | head
|
157 | title Example
|
158 | <body>
|
159 | - if this.articles.length
|
160 | table
|
161 | - for (var a in this.articles)
|
162 | <tr><td>${a.name}</td><td>${a.description}</td></tr>
|
163 | </body>
|
164 | </html>
|
165 |
|
166 | ### Control code `-`
|
167 |
|
168 | The dash denotes control code. Examples of control code are loops and conditionals. Blocks are defined only by indentation.
|
169 | If your js code needs to use multiple lines, append a backslash `\` at the end of the lines. If your line ends with comma `,` (e.g because of a method call) you don't need the additional backslash before the linebreak.
|
170 | Slm inserts `(` and `)` for `if`, `for`, `else if` automatically. So you JS code is more readable.
|
171 |
|
172 | body
|
173 | - if !this.articles.length
|
174 | | No inventory
|
175 |
|
176 | ### Output `=`
|
177 |
|
178 | The equal sign tells Slm it's a JS call that produces output to add to the buffer. If your JS code needs to use multiple lines, append a backslash `\` at the end of the lines, for example:
|
179 |
|
180 | = javascript_include_tag(\
|
181 | "jquery",
|
182 | "application")
|
183 |
|
184 | If your line ends with comma `,` (e.g because of a method call) you don't need the additional backslash before the linebreak. For trailing or leading whitespace the modifiers `>` and `<` are supported.
|
185 |
|
186 | * Output with trailing white space `=>`. Same as the single equal sign (`=`), except that it adds a trailing white space. The legacy syntax `='` is also supported.
|
187 | * Output with leading white space `=<`. Same as the single equal sign (`=`), except that it adds a leading white space.
|
188 |
|
189 |
|
190 | ### Output without HTML escaping `==`
|
191 |
|
192 | Same as the single equal sign (`=`), but does not go through the `escapeHtml` method. For trailing or leading whitespace the modifiers `>` and `<` are supported.
|
193 |
|
194 | * Output without HTML escaping and trailing white space `==>`. Same as the double equal sign (`==`), except that it adds a trailing white space. The legacy syntax `=='` is also supported.
|
195 | * Output without HTML escaping and leading white space `==<`. Same as the double equal sign (`==`), except that it adds a leading white space.
|
196 |
|
197 | ### Code comment `/`
|
198 |
|
199 | Use the forward slash for code comments - anything after it won't get displayed in the final render. Use `/` for code comments and `/!` for html comments
|
200 |
|
201 | body
|
202 | p
|
203 | / This line won't get displayed.
|
204 | Neither does this line.
|
205 | /! This will get displayed as html comments.
|
206 |
|
207 | The parsed result of the above:
|
208 |
|
209 | <body><p><!--This will get displayed as html comments.--></p></body>
|
210 |
|
211 | ### HTML comment `/!`
|
212 |
|
213 | Use the forward slash immediately followed by an exclamation mark for html comments (`<!-- ... -->`).
|
214 |
|
215 | ### IE conditional comment `/[...]`
|
216 |
|
217 | /[if IE]
|
218 | p Get a better browser.
|
219 |
|
220 | renders as
|
221 |
|
222 | <!--[if IE]><p>Get a better browser.</p><![endif]-->
|
223 |
|
224 |
|
225 | ## HTML tags
|
226 |
|
227 | ### Doctype tag
|
228 |
|
229 | The doctype tag is a special tag which can be used to generate the complex doctypes in a very simple way.
|
230 |
|
231 | XML VERSION
|
232 |
|
233 | doctype xml
|
234 | <?xml version="1.0" encoding="utf-8" ?>
|
235 |
|
236 | doctype xml ISO-8859-1
|
237 | <?xml version="1.0" encoding="iso-8859-1" ?>
|
238 |
|
239 | XHTML DOCTYPES
|
240 |
|
241 | doctype html
|
242 | <!DOCTYPE html>
|
243 |
|
244 | doctype 5
|
245 | <!DOCTYPE html>
|
246 |
|
247 | doctype 1.1
|
248 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
249 | "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
250 |
|
251 | doctype strict
|
252 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
253 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
254 |
|
255 | doctype frameset
|
256 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
257 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
258 |
|
259 | doctype basic
|
260 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
|
261 | "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
|
262 |
|
263 | doctype transitional
|
264 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
265 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
266 |
|
267 | HTML 4 DOCTYPES
|
268 |
|
269 | doctype strict
|
270 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
271 | "http://www.w3.org/TR/html4/strict.dtd">
|
272 |
|
273 | doctype frameset
|
274 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
|
275 | "http://www.w3.org/TR/html4/frameset.dtd">
|
276 |
|
277 | doctype transitional
|
278 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
279 | "http://www.w3.org/TR/html4/loose.dtd">
|
280 |
|
281 | ### Closed tags (trailing `/`)
|
282 |
|
283 | You can close tags explicitly by appending a trailing `/`.
|
284 |
|
285 | img src="image.png"/
|
286 |
|
287 | Note, that this is usually not necessary since the standard html
|
288 | tags (img, br, ...) are closed automatically.
|
289 |
|
290 | ### Trailing and leading whitespace (`<`, `>`)
|
291 |
|
292 | You can force Slm to add a trailing whitespace after a tag by adding a >.
|
293 |
|
294 | a> href='url1' Link1
|
295 | a> href='url2' Link2
|
296 |
|
297 | You can add a leading whitespace by adding <.
|
298 |
|
299 | a< href='url1' Link1
|
300 | a< href='url2' Link2
|
301 |
|
302 | You can also combine both.
|
303 |
|
304 | a<> href='url1' Link1
|
305 |
|
306 | ### Inline tags
|
307 |
|
308 | Sometimes you may want to be a little more compact and inline the tags.
|
309 |
|
310 | ul
|
311 | li.first: a href="/a" A link
|
312 | li: a href="/b" B link
|
313 |
|
314 | For readability, don't forget you can wrap the attributes.
|
315 |
|
316 | ul
|
317 | li.first: a[href="/a"] A link
|
318 | li: a[href="/b"] B link
|
319 |
|
320 | ### Text content
|
321 |
|
322 | Either start on the same line as the tag
|
323 |
|
324 | body
|
325 | h1 id="headline" Welcome to my site.
|
326 |
|
327 | Or nest it. You must use a pipe or an apostrophe to escape processing
|
328 |
|
329 | body
|
330 | h1 id="headline"
|
331 | | Welcome to my site.
|
332 |
|
333 | ### Dynamic content (`=` and `==`)
|
334 |
|
335 | Can make the call on the same line
|
336 |
|
337 | body
|
338 | h1 id="headline" = this.pageHeadline
|
339 |
|
340 | Or nest it.
|
341 |
|
342 | body
|
343 | h1 id="headline"
|
344 | = this.pageHeadline
|
345 |
|
346 | ### Attributes
|
347 |
|
348 | You write attributes directly after the tag. For normal text attributes you must use double `"` or single quotes `'` (Quoted attributes).
|
349 |
|
350 | a href="http://slm-lang.com" title='Slm Homepage' Goto the Slm homepage
|
351 |
|
352 | You can use text interpolation in the quoted attributes.
|
353 |
|
354 | #### Attributes wrapper
|
355 |
|
356 | If a delimiter makes the syntax more readable for you,
|
357 | you can use the characters `(...)`, `[...]` to wrap the attributes.
|
358 | You can configure these symbols.
|
359 |
|
360 | body
|
361 | h1(id="logo") = this.pageLogo
|
362 | h2[id="tagline" class="small tagline"] = this.pageTagline
|
363 |
|
364 | If you wrap the attributes, you can spread them across multiple lines:
|
365 |
|
366 | h2[id="tagline"
|
367 | class="small tagline"] = this.pageTagline
|
368 |
|
369 | You may use spaces around the wrappers and assignments:
|
370 |
|
371 | h1 id = "logo" = page_logo
|
372 | h2 [ id = "tagline" ] = this.pageTagline
|
373 |
|
374 | #### Quoted attributes
|
375 |
|
376 | Example:
|
377 |
|
378 | a href="http://slm-lang.com" title='Slm Homepage' Goto the slm homepage
|
379 |
|
380 | You can use text interpolation in the quoted attributes:
|
381 |
|
382 | a href="http://${url}" Goto the ${url}
|
383 |
|
384 | The attribute value will be escaped by default. Use == if you want to disable escaping in the attribute.
|
385 |
|
386 | a href=="&"
|
387 |
|
388 | You can break quoted attributes with backslash `\`
|
389 |
|
390 | a data-title="help" data-content="extremely long help text that goes on\
|
391 | and one and one and then starts over...."
|
392 |
|
393 | #### Javascript attributes
|
394 |
|
395 | Write the javascript code directly after the `=`. If the code contains spaces you have to wrap
|
396 | the code into parentheses `(...)`. You can also directly write hashes `{...}` and arrays `[...]`.
|
397 |
|
398 | body
|
399 | table
|
400 | - for var user in this.users
|
401 | td id="user-#{user.id}" class=user.role
|
402 | a href=userAction(user, 'edit') Edit ${user.name}
|
403 | a href=pathToUser(user) = user.name
|
404 |
|
405 | The attribute value will be escaped by default. Use == if you want to disable escaping in the attribute.
|
406 |
|
407 | a href==actionPath('start')
|
408 |
|
409 | You can also break javascript attributes with backslash `\` or trailing `,` as describe for control sections.
|
410 |
|
411 | #### Boolean attributes
|
412 |
|
413 | The attribute values `true`, `false`, `null` and `undefinded` are interpreted
|
414 | as booleans. If you use the attribute wrapper you can omit the attribute assigment.
|
415 |
|
416 | input type="text" disabled="disabled"
|
417 | input type="text" disabled=true
|
418 | input(type="text" disabled)
|
419 |
|
420 | input type="text"
|
421 | input type="text" disabled=false
|
422 | input type="text" disabled=null
|
423 |
|
424 |
|
425 | #### Attribute merging
|
426 |
|
427 | a.menu class="highlight" href="http://slm-lang.com/" Slm-lang.com
|
428 |
|
429 | This renders as
|
430 |
|
431 | <a class="menu highlight" href="http://slm-lang.com/">Slm-lang.com</a>
|
432 |
|
433 | You can also use an `Array` as attribute value and the array elements will be merged using the delimiter.
|
434 |
|
435 | a class=['menu','highlight']
|
436 | a class='menu','highlight'
|
437 |
|
438 | ## Text interpolation
|
439 |
|
440 | Use ES6 interpolation. The text will be html escaped by default.
|
441 |
|
442 | body
|
443 | h1 Welcome ${current_user.name} to the show.
|
444 | | Unescaped ${=content} is also possible.
|
445 |
|
446 | To escape the interpolation (i.e. render as is)
|
447 |
|
448 | body
|
449 | h1 Welcome \${current_user.name} to the show.
|
450 |
|
451 |
|
452 |
|
453 |
|
454 | # Special Thanks
|
455 |
|
456 | * [Andrew Stone](https://github.com/stonean), for [Slim](https://github.com/slim-template/slim)
|
457 | * [Magnus Holm](https://github.com/judofyr), for [Temple](https://github.com/judofyr/temple)
|
458 | * [Daniel Mendler](https://github.com/minad), for maintenance of both
|
459 | * [John Firebaugh](https://github.com/jfirebaugh), for [Skim](https://github.com/jfirebaugh/skim)
|
460 | * [AnjLab](http://anjlab.com), for such great team
|