var Haml; (function () { var matchers, self_close_tags, embedder, forceXML, escaperName, escapeHtmlByDefault; function html_escape(text) { return (text + ""). replace(/&/g, "&"). replace(//g, ">"). replace(/\"/g, """); } function render_attribs(attribs) { var key, value, result = []; for (key in attribs) { if (key !== '_content' && attribs.hasOwnProperty(key)) { switch (attribs[key]) { case 'undefined': case 'false': case 'null': case '""': break; default: try { value = JSON.parse("[" + attribs[key] +"]")[0]; if (value === true) { value = key; } else if (typeof value === 'string' && embedder.test(value)) { value = '" +\n' + parse_interpol(html_escape(value)) + ' +\n"'; } else { value = html_escape(value); } result.push(" " + key + '=\\"' + value + '\\"'); } catch (e) { result.push(" " + key + '=\\"" + '+escaperName+'(' + attribs[key] + ') + "\\"'); } } } } return result.join(""); } // Parse the attribute block using a state machine function parse_attribs(line) { var attributes = {}, l = line.length, i, c, count = 1, quote = false, skip = false, open, close, joiner, seperator, pair = { start: 1, middle: null, end: null }; if (!(l > 0 && (line.charAt(0) === '{' || line.charAt(0) === '('))) { return { _content: line[0] === ' ' ? line.substr(1, l) : line }; } open = line.charAt(0); close = (open === '{') ? '}' : ')'; joiner = (open === '{') ? ':' : '='; seperator = (open === '{') ? ',' : ' '; function process_pair() { if (typeof pair.start === 'number' && typeof pair.middle === 'number' && typeof pair.end === 'number') { var key = line.substr(pair.start, pair.middle - pair.start).trim(), value = line.substr(pair.middle + 1, pair.end - pair.middle - 1).trim(); attributes[key] = value; } pair = { start: null, middle: null, end: null }; } for (i = 1; count > 0; i += 1) { // If we reach the end of the line, then there is a problem if (i > l) { throw "Malformed attribute block"; } c = line.charAt(i); if (skip) { skip = false; } else { if (quote) { if (c === '\\') { skip = true; } if (c === quote) { quote = false; } } else { if (c === '"' || c === "'") { quote = c; } if (count === 1) { if (c === joiner) { pair.middle = i; } if (c === seperator || c === close) { pair.end = i; process_pair(); if (c === seperator) { pair.start = i + 1; } } } if (c === open || c === "(") { count += 1; } if (c === close || (count > 1 && c === ")")) { count -= 1; } } } } attributes._content = line.substr(i, line.length); return attributes; } // Split interpolated strings into an array of literals and code fragments. function parse_interpol(value) { var items = [], pos = 0, next = 0, match; while (true) { // Match up to embedded string next = value.substr(pos).search(embedder); if (next < 0) { if (pos < value.length) { items.push(JSON.stringify(value.substr(pos))); } break; } items.push(JSON.stringify(value.substr(pos, next))); pos += next; // Match embedded string match = value.substr(pos).match(embedder); next = match[0].length; if (next < 0) { break; } if(match[1] === "#"){ items.push(escaperName+"("+(match[2] || match[3])+")"); }else{ //unsafe!!! items.push(match[2] || match[3]); } pos += next; } return items.filter(function (part) { return part && part.length > 0}).join(" +\n"); } // Used to find embedded code in interpolated strings. embedder = /([#!])\{([^}]*)\}/; self_close_tags = ["meta", "img", "link", "br", "hr", "input", "area", "base"]; // All matchers' regexps should capture leading whitespace in first capture // and trailing content in last capture matchers = [ // html tags { name: "html tags", regexp: /^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i, process: function () { var line_beginning, tag, classes, ids, attribs, content, whitespaceSpecifier, whitespace={}, output; 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
"+\n' + JSON.stringify(this.contents.join("\n"))+'+\n""'; } }, // declarations { name: "doctype", regexp: /^()!!!(?:\s*(.*))\s*$/, process: function () { var line = ''; switch ((this.matches[2] || '').toLowerCase()) { case '': // XHTML 1.0 Transitional line = ''; break; case 'strict': case '1.0': // XHTML 1.0 Strict line = ''; break; case 'frameset': // XHTML 1.0 Frameset line = ''; break; case '5': // XHTML 5 line = ''; break; case '1.1': // XHTML 1.1 line = ''; break; case 'basic': // XHTML Basic 1.1 line = ''; break; case 'mobile': // XHTML Mobile 1.2 line = ''; break; case 'xml': // XML line = ""; break; case 'xml iso-8859-1': // XML iso-8859-1 line = ""; break; } return JSON.stringify(line + "\n"); } }, // 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"))); } }, // script blocks { name: "script", regexp: /^(\s*):(?:java)?script\s*$/, process: function () { return parse_interpol('\n\n"); } }, // css blocks { name: "css", regexp: /^(\s*):css\s*$/, process: function () { return JSON.stringify('"); } } ]; function compile(lines) { var block = false, output = [], ifConditions = []; // If lines is a string, turn it into an array if (typeof lines === 'string') { lines = lines.trim().replace(/\n\r|\r/g, '\n').split('\n'); } lines.forEach(function(line) { var match, found = false; // Collect all text as raw until outdent if (block) { match = block.check_indent.exec(line); if (match) { block.contents.push(match[1] || ""); return; } else { output.push(block.process()); block = false; } } matchers.forEach(function (matcher) { if (!found) { match = matcher.regexp.exec(line); if (match) { block = { contents: [], indent_level: (match[1]), matches: match, check_indent: new RegExp("^(?:\\s*|" + match[1] + " (.*))$"), process: matcher.process, getIfConditions: function() { return ifConditions; }, pushIfCondition: function(condition) { ifConditions.push(condition); }, popIfCondition: function() { return ifConditions.pop(); }, render_contents: function () { return compile(this.contents); } }; found = true; } } }); // Match plain text if (!found) { output.push(function () { // Escaped plain text if (line[0] === '\\') { return parse_interpol(line.substr(1, line.length)); } function escapedLine(){ try { return escaperName+'('+JSON.stringify(JSON.parse(line)) +')'; } catch (e2) { return escaperName+'(' + line + ')'; } } function unescapedLine(){ try { return parse_interpol(JSON.parse(line)); } catch (e) { 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(); if(escapeHtmlByDefault){ return escapedLine(); }else{ return unescapedLine(); } } // Plain text return parse_interpol(line); }()); } }); if (block) { output.push(block.process()); } var txt = output.filter(function (part) { return part && part.length > 0}).join(" +\n"); if(txt.length == 0){ txt = '""'; } return txt; }; function optimize(js) { var new_js = [], buffer = [], part, end; function flush() { if (buffer.length > 0) { new_js.push(JSON.stringify(buffer.join("")) + end); buffer = []; } } js.replace(/\n\r|\r/g, '\n').split('\n').forEach(function (line) { part = line.match(/^(\".*\")(\s*\+\s*)?$/); if (!part) { flush(); new_js.push(line); return; } end = part[2] || ""; part = part[1]; try { buffer.push(JSON.parse(part)); } catch (e) { flush(); new_js.push(line); } }); flush(); return new_js.join("\n"); }; function render(text, options) { options = options || {}; text = text || ""; var js = compile(text, options); if (options.optimize) { js = Haml.optimize(js); } return execute(js, options.context || Haml, options.locals); }; function execute(js, self, locals) { return (function () { with(locals || {}) { try { var _$output; eval("_$output =" + js ); return _$output; //set in eval } catch (e) { return "\n
" + html_escape(e.stack) + "\n"; } } }).call(self); }; Haml = function Haml(haml, config) { if(typeof(config) != "object"){ forceXML = config; config = {}; } var escaper; if(config.customEscape){ escaper = ""; escaperName = config.customEscape; }else{ 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;" + " } catch (e) {\n" + " return \"\\n
\" + "+escaperName+"(e.stack) + \"\\n\";\n" + " }\n" + "}" try{ var f = new Function("locals", escaper + str ); return f; }catch(e){ if ( typeof(console) !== 'undefined' ) { console.error(str); } throw e; } } Haml.compile = compile; Haml.optimize = optimize; Haml.render = render; Haml.execute = execute; Haml.html_escape = html_escape; }()); // Hook into module system if (typeof module !== 'undefined') { module.exports = Haml; }