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") - + })();