diff --git a/hsp/compiler/parser.js b/hsp/compiler/parser.js index 657ba23..5d4e629 100644 --- a/hsp/compiler/parser.js +++ b/hsp/compiler/parser.js @@ -6,6 +6,30 @@ var blockParser = PEG.buildParser(grammar, { trackLineAndColumn : true }); +//http://www.w3.org/TR/html-markup/syntax.html#syntax-elements +var VOID_HTML_ELEMENTS = { + "area": true, + "base": true, + "br": true, + "col": true, + "command": true, + "embed": true, + "hr": true, + "img": true, + "input": true, + "keygen": true, + "link": true, + "meta": true, + "param": true, + "source": true, + "track": true, + "wbr": true +}; + +function isVoidElement(elName) { + return VOID_HTML_ELEMENTS.hasOwnProperty(elName.toLowerCase()); +} + /** * Return the list of instruction blocks that compose a template file at this stage the template AST is not complete - * cf. parse() function to get the complete syntax tree Note: this function is exposed for unit test purposes and should @@ -403,6 +427,10 @@ var SyntaxTree = klass({ * Element block management */ _element : function (idx, blocks, out) { + var b = blocks[idx]; + if (isVoidElement(b.name)) { + b.closed=true; + } return this._elementOrComponent("element", idx, blocks, out); }, @@ -600,7 +628,11 @@ var SyntaxTree = klass({ _endelement : function (idx, blocks, out) { // only called in case of error var b = blocks[idx], nm = b.name; - this._logError("End element does not match any <" + nm + "> element", b); + if (isVoidElement(nm)) { + this._logError("The end element was rejected as <" + nm + "> is a void HTML element and can't have a closing element", b); + } else { + this._logError("End element does not match any <" + nm + "> element", b); + } return idx; }, diff --git a/public/test/compiler/errsamples/voidelement.txt b/public/test/compiler/errsamples/voidelement.txt new file mode 100644 index 0000000..c0d95b0 --- /dev/null +++ b/public/test/compiler/errsamples/voidelement.txt @@ -0,0 +1,13 @@ +##### Template: +# template test + Hello
World
+# /template + +##### Errors: +[ + { + "description": "The end element
was rejected as
is a void HTML element and can't have a closing element", + "line": 2, + "column": 19 + } +] diff --git a/public/test/compiler/errtests.js b/public/test/compiler/errtests.js index 3ca9f40..552dc0b 100644 --- a/public/test/compiler/errtests.js +++ b/public/test/compiler/errtests.js @@ -23,8 +23,8 @@ describe('Template compilation errors: ', function () { var samples = ["text1", "text2", "text3", "if1", "if2", "if3", "if4", "if5", "if6", "if7", "foreach1", "foreach2", "foreach3", "element1", "element2", "element3", "element4", "element5", "element6", "element7", "element8", "insert", "template1", "template2", "template3", "template4", "template5", "template6", "template7", - "jsexpression1", "jsexpression2", "component1", "component2"]; - //samples=["component2"]; + "jsexpression1", "jsexpression2", "component1", "component2", "voidelement"]; + //samples=["voidelement"]; for (var i = 0, sz = samples.length; sz > i; i++) { // create one test for each sample it('tests error sample ('+samples[i]+')', testFn.bind({ diff --git a/public/test/compiler/samples/voidelement.txt b/public/test/compiler/samples/voidelement.txt new file mode 100644 index 0000000..ab7ff71 --- /dev/null +++ b/public/test/compiler/samples/voidelement.txt @@ -0,0 +1,75 @@ +##### Template: +# template test + Hello
World + + + + + +
+ + + + + + + + +# /template + +##### Parsed Tree +[ + { + "type": "template", "name": "test", "args": [], "content": [ + {"type": "text", "value": "Hello"}, + {"type": "element", "name": "br", "closed": false}, + {"type": "text", "value": "World "}, + {"type": "element", "name": "area", "closed": false}, + {"type": "element", "name": "base", "closed": false}, + {"type": "element", "name": "col", "closed": false}, + {"type": "element", "name": "command", "closed": false}, + {"type": "element", "name": "embed", "closed": false}, + {"type": "element", "name": "hr", "closed": false}, + {"type": "element", "name": "img", "closed": false}, + {"type": "element", "name": "input", "closed": false}, + {"type": "element", "name": "keygen", "closed": false}, + {"type": "element", "name": "meta", "closed": false}, + {"type": "element", "name": "param", "closed": false}, + {"type": "element", "name": "source", "closed": false}, + {"type": "element", "name": "track", "closed": false}, + {"type": "element", "name": "wbr", "closed": false} + ], + "closed": true, + } +] + +##### Syntax Tree +[ + { + "type": "template", + "name": "test", + "args": [], + "export": false, + "content": [ + {"type": "text", "value": "Hello"}, + {"type": "element", "name": "br", "closed": true}, + {"type": "text", "value": "World "}, + {"type": "element", "name": "area", "closed": true}, + {"type": "element", "name": "base", "closed": true}, + {"type": "element", "name": "col", "closed": true}, + {"type": "element", "name": "command", "closed": true}, + {"type": "element", "name": "embed", "closed": true}, + {"type": "element", "name": "hr", "closed": true}, + {"type": "element", "name": "img", "closed": true}, + {"type": "element", "name": "input", "closed": true}, + {"type": "element", "name": "keygen", "closed": true}, + {"type": "element", "name": "meta", "closed": true}, + {"type": "element", "name": "param", "closed": true}, + {"type": "element", "name": "source", "closed": true}, + {"type": "element", "name": "track", "closed": true}, + {"type": "element", "name": "wbr", "closed": true} + ] + } +] + +##### Template Code diff --git a/public/test/compiler/tests.js b/public/test/compiler/tests.js index 022ef7a..c0296e6 100644 --- a/public/test/compiler/tests.js +++ b/public/test/compiler/tests.js @@ -91,8 +91,8 @@ describe('Block Parser: ', function () { "comment", "foreach1", "foreach2", "foreach3", "element1", "element2", "element3", "element4", "element5", "evthandler1", "evthandler2", "evthandler3", "component1", "component2", "component3", "component4", "component5", "component6", "component7", "jsexpression1", "jsexpression2", "jsexpression3", "jsexpression4", "jsexpression5", - "class1", "class2", "class3", "class4", "class5", "insert1", "insert2", "log1", "log2"]; - //samples=["class5"]; + "class1", "class2", "class3", "class4", "insert1", "insert2", "log1", "log2", "voidelement"]; + //samples=["voidelement"]; for (var i = 0, sz = samples.length; sz > i; i++) { // create one test for each sample diff --git a/public/test/rt/foreach.spec.hsp b/public/test/rt/foreach.spec.hsp index db1b12d..b115cdd 100644 --- a/public/test/rt/foreach.spec.hsp +++ b/public/test/rt/foreach.spec.hsp @@ -73,7 +73,7 @@ var hsp=require("hsp/rt"), # template test6(itemsList) {foreach item in itemsList} {if item.edit} - + {else} {item.value} {/if}