Permalink
Browse files

Adding executable-js blocks!

  • Loading branch information...
1 parent 811b823 commit 23e2260a293cd87f640f4c8a6c634d42eacfd205 @aaronblohowiak aaronblohowiak committed May 7, 2011
Showing with 212 additions and 31 deletions.
  1. +56 −7 README.markdown
  2. +47 −19 lib/haml.js
  3. 0 test/blank.haml
  4. 0 test/blank.html
  5. +16 −0 test/css.haml
  6. +17 −0 test/css.html
  7. +1 −1 test/meta.haml
  8. +7 −0 test/raw.haml
  9. +1 −0 test/raw.html
  10. +62 −0 test/raw_complex.haml
  11. +1 −0 test/raw_complex.html
  12. +2 −4 test/script_css.html
  13. +2 −0 test/test.js
View
@@ -112,9 +112,62 @@ The three recognized `options` are:
See [test.js][] for an example usage of Haml.render
+## Executable JavaScript (not output)
+
+New in version 0.2.6 is the ability to embed javascript in your template function. This lets you do variable assignments, if/else, switch statements, and even define functions. In Haml.js, execution blocks begin with a `-` and define a raw js block. This behaves slightly differently from Ruby's Haml. The advantage is that you can easily have multi-line executable blocks and comments, but the downside is that that you have to "outdent" the haml if you want to output from within a javascript block.
+
+Simple example:
+
+ - var area = 0.5 * length * height
+ .area= area
+
+Multi-line example:
+
+ - var obj = {
+ area: 0.5 * b * h,
+ r: opposite / adjacent
+ }
+ .triangle-details Area is: #{area} and the ratio is: #{r}
+
+"Outdent" the haml in a javascript block (the "goodbye" div is not rendered!)
+
+ .conditional
+ - var a = "strings are truthy"
+ - if(a){
+ .hello
+ - } else{
+ .goodbye
+ - }
+
+You can even define functions:
+
+ - function b(item){
+ .item
+ %b= item
+ %span.length= item.length
+ - }
+ - b("Hi")
+ - b("World")
+
+This outputs:
+
+ <div class="item"><b>Hi</b><span class="length">2</span></div><div class="item"><b>World</b><span class="length">5</span></div>
+
+Please see test/raw_complex.haml for more details and examples.
+
+## Comments
+
+Just use the new Executable blocks with JavaScript comments.
+
+ - /*
+ here we can have a comment that will not be output. Since executable-JS is block-level,
+ we can have as much comment as we want, and it will not be output to html */
+
+These comments **will** appear in the source of the function returned by Haml.compile, but will not appear as html comments. If you precompile your templates and share them with the browser, then your minifier should strip out comments for you.
+
## Code interpolation
-New in version 0.2.0 there is string interpolation throughout. This means that the body of regular text areas can have embedded code. This is true for attributes and the contents of plugins like javascript and markdown also. If you notice an area that doesn't support interpolation and it should then send me a note and I'll add it.
+As of version 0.2.0 there is string interpolation throughout. This means that the body of regular text areas can have embedded code. This is true for attributes and the contents of plugins like javascript and markdown also. If you notice an area that doesn't support interpolation and it should then send me a note and I'll add it.
## Plugins
@@ -146,9 +199,9 @@ You can loop over the keys and values of objects too (Note the inner `:each` loo
%dt&= name
%dd&= value
-### `:css` and `:javascript` helpers.
+### `:css` and `:script` helpers.
-It's easy to embed javascript and css blocks in an haml document.
+It's easy to embed script and css tags in an haml document. Note that both `:script` and `:javascript` will work.
%head
:javascript
@@ -199,7 +252,3 @@ Haml-js is [licensed][] under the [MIT license][].
[jquery-haml]: http://github.com/creationix/jquery-haml
[haml]: http://haml-lang.com/
[test.js]: http://github.com/creationix/haml-js/blob/master/test/test.js
-
-
-
-
View
@@ -169,13 +169,14 @@ var Haml;
matchers = [
// html tags
{
+ name: "html tags",
regexp: /^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i,
process: function () {
- var tag, classes, ids, attribs, content;
- tag = this.matches[2];
- classes = tag.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);
- ids = tag.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);
- tag = tag.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);
+ var line_beginning, tag, classes, ids, attribs, content;
+ line_beginning = this.matches[2];
+ classes = line_beginning.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);
+ ids = line_beginning.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);
+ tag = line_beginning.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);
// Default to <div> tag
tag = tag ? tag[0].substr(1, tag[0].length) : 'div';
@@ -184,6 +185,8 @@ var Haml;
if (attribs) {
attribs = parse_attribs(attribs);
if (attribs._content) {
+ //once we've identified the tag and its attributes, the rest is content.
+ // this is currently trimmed for neatness.
this.contents.unshift(attribs._content.trim());
delete(attribs._content);
}
@@ -235,6 +238,7 @@ var Haml;
// each loops
{
+ name: "each loop",
regexp: /^(\s*)(?::for|:each)\s+(?:([a-z_][a-z_\-]*),\s*)?([a-z_][a-z_\-]*)\s+in\s+(.*)(\s*)$/i,
process: function () {
var ivar = this.matches[2] || '__key__', // index
@@ -257,6 +261,7 @@ var Haml;
// if statements
{
+ name: "if",
regexp: /^(\s*):if\s+(.*)\s*$/i,
process: function () {
var condition = this.matches[2];
@@ -266,9 +271,20 @@ var Haml;
'} else { return ""; } }).call(this)';
}
},
+
+ // raw js
+ {
+ name: "raw",
+ regexp: /^(\s*)-\s*(.*)\s*$/i,
+ process: function () {
+ this.contents.unshift(this.matches[2]);
+ return '"";' + this.contents.join("\n")+"; _$output = _$output ";
+ }
+ },
// declarations
{
+ name: "doctype",
regexp: /^()!!!(?:\s*(.*))\s*$/,
process: function () {
var line = '';
@@ -317,6 +333,7 @@ var Haml;
// Embedded markdown. Needs to be added to exports externally.
{
+ name: "markdown",
regexp: /^(\s*):markdown\s*$/i,
process: function () {
return parse_interpol(exports.Markdown.encode(this.contents.join("\n")));
@@ -325,6 +342,7 @@ var Haml;
// script blocks
{
+ name: "script",
regexp: /^(\s*):(?:java)?script\s*$/,
process: function () {
return parse_interpol('\n<script type="text/javascript">\n' +
@@ -336,11 +354,12 @@ var Haml;
// css blocks
{
+ name: "css",
regexp: /^(\s*):css\s*$/,
process: function () {
- return JSON.stringify('\n<style type="text/css">\n' +
+ return JSON.stringify('<style type="text/css">\n' +
this.contents.join("\n") +
- "\n</style>\n");
+ "\n</style>");
}
},
@@ -376,11 +395,12 @@ var Haml;
if (match) {
block = {
contents: [],
+ indent_level: (match[1]),
matches: match,
check_indent: new RegExp("^(?:\\s*|" + match[1] + " (.*))$"),
process: matcher.process,
render_contents: function () {
- return compile(this. contents);
+ return compile(this.contents);
}
};
found = true;
@@ -425,7 +445,12 @@ var Haml;
if (block) {
output.push(block.process());
}
- return output.filter(function (part) { return part && part.length > 0}).join(" +\n");
+
+ var txt = output.filter(function (part) { return part && part.length > 0}).join(" +\n");
+ if(txt.length == 0){
+ txt = '""';
+ }
+ return txt;
};
function optimize(js) {
@@ -471,7 +496,9 @@ var Haml;
return (function () {
with(locals || {}) {
try {
- return eval("(" + js + ")");
+ var _$output;
+ eval("(" + js + ")");
+ return _$output; //set in eval
} catch (e) {
return "\n<pre class='error'>" + html_escape(e.stack) + "</pre>\n";
}
@@ -483,15 +510,16 @@ var Haml;
Haml = function Haml(haml, xml) {
forceXML = xml;
var js = optimize(compile(haml));
- return new Function("locals",
- html_escape + "\n" +
- "with(locals || {}) {\n" +
- " try {\n" +
- " return (" + js + ");\n" +
- " } catch (e) {\n" +
- " return \"\\n<pre class='error'>\" + html_escape(e.stack) + \"</pre>\\n\";\n" +
- " }\n" +
- "}");
+
+ var str = "with(locals || {}) {\n" +
+ " try {\n" +
+ " var _$output=" + js + ";\n return _$output;" +
+ " } catch (e) {\n" +
+ " return \"\\n<pre class='error'>\" + html_escape(e.stack) + \"</pre>\\n\";\n" +
+ " }\n" +
+ "}"
+
+ return new Function("locals", html_escape + "\n" + str );
}
Haml.compile = compile;
Haml.optimize = optimize;
View
No changes.
View
No changes.
View
@@ -0,0 +1,16 @@
+:css
+ #pants{
+ font-weight:"bold";
+ }
+
+ a:link{
+ color: "red";
+ }
+
+ a:visited{
+ color: "#ff00ff";
+ }
+
+ .visited{
+ font-weight: bold;
+ }
View
@@ -0,0 +1,17 @@
+<style type="text/css">
+#pants{
+ font-weight:"bold";
+}
+
+a:link{
+ color: "red";
+}
+
+a:visited{
+ color: "#ff00ff";
+}
+
+.visited{
+ font-weight: bold;
+}
+</style>
View
@@ -1 +1 @@
-%meta(http-equiv="content-type" content="text/html; charset=UTF-8")
+%meta(http-equiv="content-type" content="text/html; charset=UTF-8")
View
@@ -0,0 +1,7 @@
+.conditional
+ - var a = "strings are truthy"
+ - if(a){
+ .hello
+ - } else{
+ .goodbye
+ - }
View
@@ -0,0 +1 @@
+<div class="conditional"><div class="hello"></div></div>
View
@@ -0,0 +1,62 @@
+- /*
+ here we can have a comment that will not be output.
+ Since executable-JS is a block-level thing, we can
+ have as much comment as we want */
+
+
+- /* now you can have arbitrary control logic
+ This will output <div>1</div><div>2</div><div>3</div>
+ notice: the %div isn't indented! Explained below!
+ */
+
+- for(var i=1; i < 4; i++){
+%div= i
+-}
+
+
+%h1 Woah!
+
+- /* we can include new variable declarations as well! */
+ var someObj = {
+ a: 1,
+ b: 2
+ }
+= someObj.b
+
+
+%br
+
+
+- /* this is going to be funky. i DO NOT expect you to do this.
+ Here we will begin with a comment, then define our variable.
+ Next we begin a function definition.
+
+ We will use this function to output a div that contains a number.
+ Successive calls should output the next number in the div.
+
+ We want our function to output the value of the counter,
+ so we "outdent" to escape the JavaScript block in haml.
+ Note that we have not closed our function definition's
+ parenthesis yet. So, even though we outdent, the
+ concatenation statement will be in the function's body.
+ */
+
+ var counter = 0;
+ function increment(){
+ counter++;
+.count
+ = counter
+-}
+- increment() /* the tags wil be appended to the buffer, so use - instead of = */
+- increment()
+
+
+
+
+- function b(item){
+.item
+ %b= item
+ %span.length= item.length
+- }
+- b("Hi")
+- b("World")
View
@@ -0,0 +1 @@
+<div>1</div><div>2</div><div>3</div><h1>Woah!</h1>2<br /><div class="count">1</div><div class="count">2</div><div class="item"><b>Hi</b><span class="length">2</span></div><div class="item"><b>World</b><span class="length">5</span></div>
View
@@ -6,10 +6,8 @@
}
//]]>
</script>
-<title>Script and Css test</title>
-<style type="text/css">
+<title>Script and Css test</title><style type="text/css">
body {
color: pink;
}
-</style>
-</head><body onload="greet(&quot;I'm Pink&quot;)">COLOR ME PINK</body>
+</style></head><body onload="greet(&quot;I'm Pink&quot;)">COLOR ME PINK</body>
View
@@ -17,6 +17,7 @@ fs.readdir('.', function (err, files) {
fs.readFile(haml_file, "utf8", function (err, haml) {
fs.readFile(base + ".html", "utf8", function (err, expected) {
try {
+ sys.puts(haml_file + " Begun")
var js = Haml.compile(haml);
var js_opt = Haml.optimize(js);
var actual = Haml(haml).call(scope.context, scope.locals);
@@ -28,6 +29,7 @@ fs.readdir('.', function (err, files) {
if (e.message) { message += ": " + e.message; }
sys.error(haml_file + " FAILED")
sys.error(message);
+ sys.error(e.stack);
sys.error("\nJS:\n\n" + js);
sys.error("\nOptimized JS:\n\n" + js_opt);
sys.error("\nActual:\n\n" + actual);

0 comments on commit 23e2260

Please sign in to comment.