UNPKG

haml-coffee

Version:

Haml templates where you can write inline CoffeeScript.

308 lines (290 loc) 12.2 kB
(function() { var Haml, Node, escapeQuotes, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Node = require('./node'); escapeQuotes = require('../util/text').escapeQuotes; module.exports = Haml = (function(_super) { __extends(Haml, _super); function Haml() { Haml.__super__.constructor.apply(this, arguments); } Haml.prototype.evaluate = function() { var assignment, code, identifier, match, prefix, tokens; tokens = this.parseExpression(this.expression); if (tokens.doctype) { return this.opener = this.markText("" + (escapeQuotes(this.buildDocType(tokens.doctype)))); } else { if (this.isNotSelfClosing(tokens.tag)) { prefix = escapeQuotes(this.buildHtmlTagPrefix(tokens)); if (tokens.assignment) { match = tokens.assignment.match(/^(=|!=|&=|~)\s*(.*)$/); identifier = match[1]; assignment = match[2]; if (identifier === '~') { code = "\#{$fp " + assignment + " }"; } else if (identifier === '&=' || (identifier === '=' && this.escapeHtml)) { if (this.preserve) { if (this.cleanValue) { code = "\#{ $p($e($c(" + assignment + "))) }"; } else { code = "\#{ $p($e(" + assignment + ")) }"; } } else { if (this.cleanValue) { code = "\#{ $e($c(" + assignment + ")) }"; } else { code = "\#{ $e(" + assignment + ") }"; } } } else if (identifier === '!=' || (identifier === '=' && !this.escapeHtml)) { if (this.preserve) { if (this.cleanValue) { code = "\#{ $p($c(" + assignment + ")) }"; } else { code = "\#{ $p(" + assignment + ") }"; } } else { if (this.cleanValue) { code = "\#{ $c(" + assignment + ") }"; } else { code = "\#{ " + assignment + " }"; } } } this.opener = this.markText("" + prefix + ">" + code); return this.closer = this.markText("</" + tokens.tag + ">"); } else if (tokens.text) { this.opener = this.markText("" + prefix + ">" + tokens.text); return this.closer = this.markText("</" + tokens.tag + ">"); } else { this.opener = this.markText(prefix + '>'); return this.closer = this.markText("</" + tokens.tag + ">"); } } else { tokens.tag = tokens.tag.replace(/\/$/, ''); prefix = escapeQuotes(this.buildHtmlTagPrefix(tokens)); return this.opener = this.markText("" + prefix + (this.format === 'xhtml' ? ' /' : '') + ">"); } } }; Haml.prototype.parseExpression = function(exp) { var attribute, attributes, classes, id, tag, _i, _len, _ref, _ref2; tag = this.parseTag(exp); if (this.preserveTags.indexOf(tag.tag) !== -1) this.preserve = true; id = (_ref = tag.ids) != null ? _ref.pop() : void 0; classes = tag.classes; attributes = []; if (tag.attributes) { _ref2 = tag.attributes; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { attribute = _ref2[_i]; if (attribute.key === 'id') { if (id) { id += '_' + attribute.value; } else { id = attribute.value; } } else if (attribute.key === 'class') { classes || (classes = []); classes.push(attribute.value); } else { attributes.push(attribute); } } } return { doctype: tag.doctype, tag: tag.tag, id: id, classes: classes, text: tag.text, attributes: attributes, assignment: tag.assignment }; }; Haml.prototype.parseTag = function(exp) { var assignment, attributes, classes, doctype, haml, id, ids, klass, tag, text, tokens, whitespace, _ref; try { doctype = (_ref = exp.match(/^(\!{3}.*)/)) != null ? _ref[1] : void 0; if (doctype) { return { doctype: doctype }; } tokens = exp.match(/^((?:[#%\.][a-z0-9_:\-]*[\/]?)+)(?:([\(\{].*[\)\}])?([\<\>]{0,2})(?=[=&!~])(.*)?|([\(\{].*[\)\}])?([\<\>]{0,2}))(.*)?/i); haml = tokens[1]; attributes = tokens[2] || tokens[5]; whitespace = tokens[3] || tokens[6]; assignment = tokens[4] || tokens[7]; if (assignment && !assignment.match(/^(=|!=|&=|~)/)) { text = assignment.replace(/^ /, ''); assignment = void 0; } if (whitespace) { if (whitespace.indexOf('>') !== -1) this.wsRemoval.around = true; if (whitespace.indexOf('<') !== -1) { this.wsRemoval.inside = true; this.preserve = true; } } tag = haml.match(/\%([a-z_\-][a-z0-9_:\-]*[\/]?)/i); ids = haml.match(/\#([a-z_\-][a-z0-9_\-]*)/gi); classes = haml.match(/\.([a-z0-9_\-]*)/gi); return { tag: tag ? tag[1] : 'div', ids: ids ? (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = ids.length; _i < _len; _i++) { id = ids[_i]; _results.push(id.substr(1)); } return _results; })() : void 0, classes: classes ? (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = classes.length; _i < _len; _i++) { klass = classes[_i]; _results.push(klass.substr(1)); } return _results; })() : void 0, attributes: this.parseAttributes(attributes), assignment: assignment, text: text }; } catch (error) { throw "Unable to parse tag from " + exp + ": " + error; } }; Haml.prototype.parseAttributes = function(exp) { var attributes, bool, datas, findAttributes, key, match, quoted, value, _ref; attributes = []; if (exp === void 0) return attributes; _ref = this.getDataAttributes(exp), exp = _ref[0], datas = _ref[1]; findAttributes = /(?:([-\w]+[\w:-]*\w?|'\w+[\w:-]*\w?'|"\w+[\w:-]*\w?")\s*=\s*("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[\w@.]+)|(:\w+[\w:-]*\w?|'[-\w]+[\w:-]*\w?'|"[-\w]+[\w:-]*\w?")\s*=>\s*("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^},]+)|(\w+[\w:-]*\w?|'[-\w]+[\w:-]*\w?'|"[-\w]+[\w:-]*\w?"):\s*("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^},]+))/g; while (match = findAttributes.exec(exp)) { key = (match[1] || match[3] || match[5]).replace(/^:/, ''); value = match[2] || match[4] || match[6]; bool = false; if (['false', ''].indexOf(value) === -1) { if (['true'].indexOf(value) !== -1) { value = "'" + key + "'"; bool = true; } else if (!value.match(/^("|').*\1$/)) { if (this.escapeAttributes) { if (this.cleanValue) { value = '\'#{ $e($c(' + value + ')) }\''; } else { value = '\'#{ $e(' + value + ') }\''; } } else { if (this.cleanValue) { value = '\'#{ $c(' + value + ') }\''; } else { value = '\'#{ (' + value + ') }\''; } } } if (quoted = value.match(/^("|')(.*)\1$/)) value = quoted[2]; if (quoted = key.match(/^("|')(.*)\1$/)) key = quoted[2]; attributes.push({ key: key, value: value, bool: bool }); } } return attributes.concat(datas); }; Haml.prototype.getDataAttributes = function(exp) { var attribute, attributes, data, _i, _len; data = /:?data:?\s*(?:=>\s*)?\{([^}]*)\},?/gi.exec(exp); if (!(data != null ? data[1] : void 0)) return [exp, []]; exp = exp.replace(data[0], ''); attributes = this.parseAttributes(data[1]); for (_i = 0, _len = attributes.length; _i < _len; _i++) { attribute = attributes[_i]; attribute.key = "data-" + attribute.key; } return [exp, attributes]; }; Haml.prototype.buildHtmlTagPrefix = function(tokens) { var attribute, classes, interpolation, klass, tagParts, _i, _j, _len, _len2, _ref, _ref2; tagParts = ["<" + tokens.tag]; if (tokens.classes) { classes = tokens.classes.sort().join(' '); if (tokens.classes.length > 1 && classes.match(/#\{/)) { classes = '#{ ['; _ref = tokens.classes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { klass = _ref[_i]; if (interpolation = klass.match(/#{(.*)}/)) { classes += "(" + interpolation[1] + "),"; } else { classes += "'" + klass + "',"; } } classes += '].sort().join(\' \') }'; } tagParts.push("class='" + classes + "'"); } if (tokens.id) tagParts.push("id='" + tokens.id + "'"); if (tokens.attributes) { _ref2 = tokens.attributes; for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { attribute = _ref2[_j]; if (attribute.bool && this.format === 'html5') { tagParts.push("" + attribute.key); } else { tagParts.push("" + attribute.key + "=" + (this.quoteAttributeValue(attribute.value))); } } } return tagParts.join(' '); }; Haml.prototype.quoteAttributeValue = function(value) { var quoted; if (value.indexOf("'") === -1) { quoted = "'" + value + "'"; } else { quoted = "\"" + value + "\""; } return quoted; }; Haml.prototype.buildDocType = function(doctype) { switch ("" + this.format + " " + doctype) { case 'xhtml !!! XML': return '<?xml version=\'1.0\' encoding=\'utf-8\' ?>'; case 'xhtml !!!': return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'; case 'xhtml !!! 1.1': return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'; case 'xhtml !!! mobile': return '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'; case 'xhtml !!! basic': return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'; case 'xhtml !!! frameset': return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'; case 'xhtml !!! 5': case 'html5 !!!': return '<!DOCTYPE html>'; case 'html5 !!! XML': case 'html4 !!! XML': return ''; case 'html4 !!!': return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'; case 'html4 !!! frameset': return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'; case 'html4 !!! strict': return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'; } }; Haml.prototype.isNotSelfClosing = function(tag) { return this.selfCloseTags.indexOf(tag) === -1 && !tag.match(/\/$/); }; return Haml; })(Node); }).call(this);