diff --git a/lib/haml.js b/lib/haml.js index 0bfd22c..058430c 100755 --- a/lib/haml.js +++ b/lib/haml.js @@ -1,5 +1,5 @@ var Haml; - + (function () { var matchers, self_close_tags, embedder, forceXML, escaperName, escapeHtmlByDefault; @@ -24,10 +24,15 @@ var Haml; break; default: try { - value = JSON.parse("[" + attribs[key] +"]")[0]; + var value_str = attribs[key]; + var single_quote_match = /^'([^']*)'$/.exec(value_str); + if (single_quote_match) { + value_str = '"' + single_quote_match[1].replace(/\\/, '\\\\').replace(/"/, '\"') + '"'; + } + value = JSON.parse("[" + value_str +"]")[0]; if (value === true) { value = key; - } else if (typeof value === 'string' && embedder.test(value)) { + } else if (!single_quote_match && typeof value === 'string' && embedder.test(value)) { value = '" +\n' + parse_interpol(html_escape(value)) + ' +\n"'; } else { value = html_escape(value); @@ -159,7 +164,7 @@ var Haml; //unsafe!!! items.push(match[2] || match[3]); } - + pos += next; } return items.filter(function (part) { return part && part.length > 0}).join(" +\n"); @@ -194,7 +199,7 @@ var Haml; var leader0 = attribs._content.charAt(0), leader1 = attribs._content.charAt(1), leaderLength = 0; - + if(leader0 == "<"){ leaderLength++; whitespace.inside = true; @@ -251,7 +256,7 @@ var Haml; if (content === '""') { content = ''; } - + if(whitespace.inside){ if(content.length==0){ content='" "' @@ -260,7 +265,7 @@ var Haml; content = '" '+JSON.parse(content)+' "'; }catch(e){ content = '" "+\n'+content+'+\n" "'; - } + } } } @@ -271,7 +276,7 @@ var Haml; } else { output = '"<' + tag + attribs + ' />"'; } - + if(whitespace.around){ //output now contains '"hello"' //we need to crack it open to insert whitespace. @@ -318,7 +323,7 @@ var Haml; '} else { return ""; } }).call(this)'; } }, - + // else if statements { name: "else if", @@ -340,7 +345,7 @@ var Haml; '} else { return ""; } }).call(this)'; } }, - + // else statements { name: "else", @@ -359,7 +364,7 @@ var Haml; '} else { return ""; } }).call(this)'; } }, - + // silent-comments { name: "silent-comments", @@ -368,18 +373,18 @@ var Haml; return '""'; } }, - + //html-comments { name: "silent-comments", regexp: /^(\s*)\/\s*(.*)\s*$/i, process: function () { this.contents.unshift(this.matches[2]); - + return '""'; } }, - + // raw js { name: "rawjs", @@ -399,7 +404,7 @@ var Haml; return '"
"+\n' + JSON.stringify(this.contents.join("\n"))+'+\n""';
}
},
-
+
// declarations
{
name: "doctype",
@@ -535,7 +540,7 @@ var Haml;
}
}
});
-
+
// Match plain text
if (!found) {
output.push(function () {
@@ -552,7 +557,7 @@ var Haml;
return escaperName+'(' + line + ')';
}
}
-
+
function unescapedLine(){
try {
return parse_interpol(JSON.parse(line));
@@ -560,19 +565,19 @@ var Haml;
return line;
}
}
-
+
// always escaped
if((line.substr(0, 2) === "&=")) {
line = line.substr(2, line.length).trim();
return escapedLine();
}
-
+
//never escaped
if((line.substr(0, 2) === "!=")) {
line = line.substr(2, line.length).trim();
return unescapedLine();
}
-
+
// sometimes escaped
if ( (line[0] === '=')) {
line = line.substr(1, line.length).trim();
@@ -592,7 +597,7 @@ var Haml;
if (block) {
output.push(block.process());
}
-
+
var txt = output.filter(function (part) { return part && part.length > 0}).join(" +\n");
if(txt.length == 0){
txt = '""';
@@ -659,7 +664,7 @@ var Haml;
forceXML = config;
config = {};
}
-
+
var escaper;
if(config.customEscape){
escaper = "";
@@ -668,11 +673,11 @@ var Haml;
escaper = html_escape.toString() + "\n";
escaperName = "html_escape";
}
-
+
escapeHtmlByDefault = (config.escapeHtmlByDefault || config.escapeHTML || config.escape_html);
-
+
var js = optimize(compile(haml));
-
+
var str = "with(locals || {}) {\n" +
" try {\n" +
" var _$output=" + js + ";\n return _$output;" +
diff --git a/test/single_quoted_attribs.haml b/test/single_quoted_attribs.haml
new file mode 100644
index 0000000..e7fcd9d
--- /dev/null
+++ b/test/single_quoted_attribs.haml
@@ -0,0 +1,6 @@
+%input#single-quote{ type: 'hidden' }
+%input#single-quote-with-interpolation{ type: '#{hidden}' }
+%input#single-quote-alt( type='hidden' )
+%input#single-quote-with-interpolation-alt( type='#{hidden}' )
+%input#single-quote-with-double-quote( type='hi"dden' )
+%input#single-quote-with-backslash( type='hi\dden' )
diff --git a/test/single_quoted_attribs.html b/test/single_quoted_attribs.html
new file mode 100644
index 0000000..97e8abe
--- /dev/null
+++ b/test/single_quoted_attribs.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/test.js b/test/test.js
index adcc36f..c571296 100644
--- a/test/test.js
+++ b/test/test.js
@@ -14,15 +14,15 @@ function compare(haml_file, haml, expected, scope, options){
var js_opt = Haml.optimize(js);
var jsFn = Haml(haml, options);
var actual = jsFn.call(scope.context, scope.locals);
-
+
assert.equal(actual, expected);
sys.puts(haml_file + " Passed")
-
+
actual = Haml.render(haml, {context:scope.context, locals:scope.locals})
-
+
assert.equal(actual, expected);
sys.puts(haml_file + " Haml.render Passed")
-
+
} catch (e) {
var message = e.name;
if (e.message) { message += ": " + e.message; }
@@ -36,7 +36,7 @@ function compare(haml_file, haml, expected, scope, options){
sys.error("\nActual["+actual.length+"]:\n\n" + actual);
sys.error("\nExpected["+expected.length+"]:\n\n" + expected);
}catch(e2){}
-
+
process.exit();
}
}
@@ -79,7 +79,6 @@ fs.readdir('.', function (err, files) {
}catch(e){
sys.error(e.stack);
sys.error(customEscape);
- process.exit();
}
})();
@@ -88,24 +87,23 @@ fs.readdir('.', function (err, files) {
var hamlSrc = fs.readFileSync("./other/custom_escape.haml", "utf8");
var expected = fs.readFileSync("./other/custom_escape.html", "utf8");
var scope = eval("(" + fs.readFileSync("escaping.js") + ")");
-
+
sys.puts("custom_escape" + " Begun")
var jsFn = Haml(hamlSrc, {customEscape:"$esc"});
-
+
this.$esc = function(){
return "moo"
};
-
- var actual = jsFn.call(scope.context, scope.locals);
- try{
+
+ var actual = jsFn.call(scope.context, scope.locals);
+ try{
assert.equal(actual, expected);
+ sys.puts("custom_escape" + " Passed")
}catch(e){
sys.error("\nActual["+actual.length+"]:\n\n" + actual);
sys.error("\nExpected["+expected.length+"]:\n\n" + expected);
- process.exit();
}
- sys.puts("custom_escape" + " Passed")
-
+
})();
@@ -113,25 +111,24 @@ fs.readdir('.', function (err, files) {
var hamlSrc = fs.readFileSync("./other/escape_by_default.haml", "utf8");
var expected = fs.readFileSync("./other/escape_by_default.html", "utf8");
var scope = {};
-
+
sys.puts("escape_by_default" + " Begun")
- var js = Haml.compile(hamlSrc);
-
+ var js = Haml.compile(hamlSrc);
+
var jsFn = Haml(hamlSrc, {escapeHtmlByDefault:true});
-
+
this.$esc = function(){
return "moo"
};
-
- var actual = jsFn.call(scope.context, scope.locals);
- try{
+
+ var actual = jsFn.call(scope.context, scope.locals);
+ try{
assert.equal(actual, expected);
+ sys.puts("escape_by_default" + " Passed")
}catch(e){
sys.error("\nActual["+actual.length+"]:\n\n" + actual);
sys.error("\nExpected["+expected.length+"]:\n\n" + expected);
- process.exit();
}
- sys.puts("escape_by_default" + " Passed")
-
+
})();