Skip to content

Commit

Permalink
Implemented support for the raw tag nodes 'script' and 'style' to be …
Browse files Browse the repository at this point in the history
…able to specify inline JavaScript and CSS.

Also added the shortcuts ':css' and ':javascript' for style(type="text/css") et al.
Fixes issue #9.
  • Loading branch information
s-ludwig committed Apr 28, 2012
1 parent b660e9e commit 2e0bbed
Showing 1 changed file with 114 additions and 41 deletions.
155 changes: 114 additions & 41 deletions source/vibe/templ/diet.d
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import std.variant;
htmlEscape is necessary in a few places to avoid corrupted html (e.g. in buildInterpolatedString)
to!string and htmlEscape should not be used in conjunction with ~ at run time. instead,
use filterHtmlEncode().
implement general :filters instead of the two special cases :javascript and :css
*/


Expand Down Expand Up @@ -282,8 +283,6 @@ private struct DietParser {
assertp(node_stack.length >= base_level);

for( ; curline < lines.length; curline++ ){
auto current_line = lines[curline];

if( !in_string ) ret ~= lineMarker(lines[curline]);
auto level = indentLevel(lines[curline].text, indentStyle) + base_level;
assertp(level <= node_stack.length+1);
Expand All @@ -298,7 +297,7 @@ private struct DietParser {
else if( ln[0] == '|' ) ret ~= buildTextNodeWriter(node_stack, ln[1 .. ln.length], level, in_string);
else {
size_t j = 0;
auto tag = isAlpha(ln[0]) || ln[0] == '/' ? skipIdent(ln, j, "/:-_") : "div";
auto tag = isAlpha(ln[0]) || ln[0] == '/' || ln[0] == ':' ? skipIdent(ln, j, "/:-_") : "div";
switch(tag){
default:
ret ~= buildHtmlNodeWriter(node_stack, tag, ln[j .. $], level, in_string, next_indent_level > level);
Expand All @@ -324,6 +323,21 @@ private struct DietParser {
ret ~= buildSpecialTag!(node_stack)("!--[if "~ln[j .. $]~"]", level, in_string);
node_stack ~= "<![endif]-->";
break;
case ":css":
case ":javascript":
case "script":
case "style":
// pass all child lines to buildRawTag and continue with the next sibling
size_t next_tag = curline+1;
while( next_tag < lines.length &&
indentLevel(lines[next_tag].text, indentStyle) > level-base_level )
{
next_tag++;
}
ret ~= buildRawNodeWriter(node_stack, tag, ln[j .. $], level, base_level,
in_string, lines[curline+1 .. next_tag]);
curline = next_tag-1;
break;
case "//":
case "//-":
case "each":
Expand Down Expand Up @@ -390,17 +404,104 @@ private struct DietParser {

string buildHtmlNodeWriter(ref string[] node_stack, string tag, string line, int level, ref bool in_string, bool has_child_nodes)
{
size_t i = 0;
// parse the HTML tag, leaving any trailing text as line[i .. $]
size_t i;
Tuple!(string, string)[] attribs;
parseHtmlTag(line, i, attribs);

// determine if we need a closing tag
bool has_children = true;
switch(tag){
case "br", "hr", "img", "link":
has_children = false;
break;
default:
}
assertp(has_children || !has_child_nodes, "Singular HTML tag '"~tag~"' may not have children.");

// parse any text contents (either using "= code" or as plain text)
string textstring;
bool textstring_isdynamic = true;
if( i < line.length && line[i] == '=' ){
textstring = "htmlEscape(_toString("~ctstrip(line[i+1 .. line.length])~"))";
} else if( i+1 < line.length && line[i .. i+2] == "!=" ){
textstring = "_toString("~ctstrip(line[i+2 .. line.length])~")";
} else {
if( hasInterpolations(line[i .. line.length]) ){
textstring = "htmlEscape("~buildInterpolatedString(line[i .. line.length], false, false)~")";
} else {
textstring = dstringEscape(htmlEscape(line[i .. line.length]));
textstring_isdynamic = false;
}
}

string tail;
if( has_child_nodes || !has_children ) tail = "";
else tail = "</" ~ tag ~ ">";

if( has_child_nodes ) node_stack ~= tag;

string ret = buildHtmlTag(node_stack, tag, level, in_string, attribs);
if( textstring_isdynamic ){
ret ~= endString(in_string);
ret ~= StreamVariableName~".write(" ~ textstring ~ ", false);\n";
} else ret ~= startString(in_string) ~ textstring;
if( tail.length ) ret ~= startString(in_string) ~ tail;

return ret;
}

string buildRawNodeWriter(ref string[] node_stack, string tag, string tagline, int level,
int base_level, ref bool in_string, in Line[] lines)
{
// parse the HTML tag leaving any trailing text as tagline[i .. $]
size_t i;
Tuple!(string, string)[] attribs;
parseHtmlTag(tagline, i, attribs);

// special case some jade "filters" - they are not yet implemented as filters
switch(tag){
default: assert(false);
case "script": break;
case "style": break;
case ":javascript":
tag = "script";
attribs ~= tuple("type", "text/javascript");
break;
case ":css":
tag = "style";
attribs ~= tuple("type", "text/css");
break;
}

assertp(has_children || !has_child_nodes, "Singular HTML tag '"~tag~"' may now have children.");
// write the tag
string ret = buildHtmlTag(node_stack, tag, level, in_string, attribs);

string indent_string = "\\t";
foreach( j; 0 .. level ) if( node_stack[j][0] != '-' ) indent_string ~= "\\t";

// write the block contents wrapped in a CDATA for old browsers
ret ~= startString(in_string);
if( tag == "script" ) ret ~= "\\n"~indent_string~"//<![CDATA[\\n";
else ret ~= "\\n"~indent_string~"<!--\\n";

// write out all lines
if( i < tagline.length )
ret ~= indent_string ~ dstringEscape(tagline[i .. $]) ~ "\\n";
foreach( ln; lines ){
// remove indentation
string lnstr = ln.text[(level-base_level+1)*indentStyle.length .. $];
ret ~= indent_string ~ dstringEscape(lnstr) ~ "\\n";
}
if( tag == "script" ) ret ~= indent_string~"//]]>\\n";
else ret ~= indent_string~"-->\\n";
ret ~= indent_string[0 .. $-2] ~ "</" ~ tag ~ ">";
return ret;
}

void parseHtmlTag(string line, out size_t i, out Tuple!(string, string)[] attribs)
{
i = 0;

string id;
string classes;
Expand All @@ -420,7 +521,6 @@ private struct DietParser {
}

// put #id and .classes into the attribs list
Tuple!(string, string)[] attribs;
if( id.length ) attribs ~= tuple("id", id);
if( classes.length ) attribs ~= tuple("class", classes);

Expand All @@ -432,46 +532,19 @@ private struct DietParser {
i++;
}

// write the tag
string tagstring = "\\n";
// skip until the optional tag text contents begin
skipWhitespace(line, i);
}

string buildHtmlTag(ref string[] node_stack, string tag, int level, ref bool in_string, ref Tuple!(string, string)[] attribs)
{
string tagstring = startString(in_string) ~ "\\n";
assertp(node_stack.length >= level);
foreach( j; 0 .. level ) if( node_stack[j][0] != '-' ) tagstring ~= "\\t";
tagstring ~= "<" ~ tag;
foreach( att; attribs ) tagstring ~= " "~att[0]~"=\\\"\"~htmlAttribEscape("~buildInterpolatedString(att[1])~")~\"\\\"";
tagstring ~= ">";

skipWhitespace(line, i);

// parse any text contents (either using "= code" or as plain text)
string textstring;
bool textstring_isdynamic = true;
if( i < line.length && line[i] == '=' ){
textstring = "htmlEscape(_toString("~ctstrip(line[i+1 .. line.length])~"))";
} else if( i+1 < line.length && line[i .. i+2] == "!=" ){
textstring = "_toString("~ctstrip(line[i+2 .. line.length])~")";
} else {
if( hasInterpolations(line[i .. line.length]) ){
textstring = "htmlEscape("~buildInterpolatedString(line[i .. line.length], false, false)~")";
} else {
textstring = dstringEscape(htmlEscape(line[i .. line.length]));
textstring_isdynamic = false;
}
}

string tail;
if( has_child_nodes || !has_children ) tail = "";
else tail = "</" ~ tag ~ ">";

if( has_child_nodes ) node_stack ~= tag;

string ret = startString(in_string) ~ tagstring;
if( textstring_isdynamic ){
ret ~= endString(in_string);
ret ~= StreamVariableName~".write(" ~ textstring ~ ", false);\n";
} else ret ~= textstring;
if( tail.length ) ret ~= startString(in_string) ~ tail;

return ret;
return tagstring;
}

void parseAttributes(string str, ref Tuple!(string, string)[] attribs)
Expand Down

0 comments on commit 2e0bbed

Please sign in to comment.