chocolate
Version:
A full stack Node.js web framework built using Coffeescript
465 lines (457 loc) • 16.2 kB
JavaScript
// Generated by CoffeeScript 1.12.6
(function() {
var _module, debug, doctypes, htmlkup, render,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
hasProp = {}.hasOwnProperty;
htmlkup = function(html, output_debug) {
var SINGLETONS, c, condition, current, detect, final_whitespace, initial_indent, initial_whitespace, last_attr, last_tag, new_tag, next, nexts, noun, parent_tags, possibles, post_whitespace, pre_whitespace, preserve_ws, ref, ref1, regex, state, value, was_ws;
preserve_ws = ['style', 'script'];
SINGLETONS = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source'];
possibles = {
start: ['tag-open', 'doctype', 'predoctype-whitespace', 'predoctype-comment-open', 'text', 'end'],
doctype: ['reset'],
reset: ['tag-open', 'ie-open', 'ie-close', 'comment-open', 'tag-close', 'text', 'end'],
end: [],
'tag-open': ['attr-reset'],
'tag-close': ['reset'],
'tag-ws': ['attr', 'singleton', 'tag-gt'],
'tag-gt': ['cdata', 'reset'],
'singleton': ['reset'],
'attr-reset': ['tag-ws', 'singleton', 'tag-gt'],
'attr': ['tag-ws', 'attr-eq', 'tag-gt', 'singleton'],
'attr-eq': ['attr-value', 'attr-dqt', 'attr-sqt'],
'attr-dqt': ['attr-dvalue'],
'attr-sqt': ['attr-svalue'],
'attr-value': ['tag-ws', 'tag-gt', 'singleton'],
'attr-dvalue': ['attr-cdqt'],
'attr-svalue': ['attr-csqt'],
'attr-cdqt': ['tag-ws', 'tag-gt', 'singleton'],
'attr-csqt': ['tag-ws', 'tag-gt', 'singleton'],
text: ['reset'],
cdata: ['tag-close'],
'ie-open': ['reset'],
'ie-close': ['reset'],
'predoctype-whitespace': ['start'],
'predoctype-comment-open': ['predoctype-comment'],
'predoctype-comment': ['predoctype-comment-close'],
'predoctype-comment-close': ['start'],
'comment-open': ['comment'],
'comment': ['comment-close'],
'comment-close': ['reset']
};
detect = {
'start': /^/,
'reset': /^/,
'doctype': /^/i,
'end': /^$/,
'tag-open': /^<[a-zA-Z]([-_]?[a-zA-Z0-9])*/,
'tag-close': /^<\/[a-zA-Z]([-_]?[a-zA-Z0-9])*>/,
'tag-ws': /^[ \t\n]+/,
'tag-gt': /^>/,
'singleton': /^\/>/,
'attr-reset': /^/,
'attr': /^[a-zA-Z]([-_]?[a-zA-Z0-9])*/,
'attr-eq': /^=/,
'attr-dqt': /^"/,
'attr-sqt': /^'/,
'attr-value': /^[a-zA-Z0-9]([-_]?[a-zA-Z0-9])*/,
'attr-dvalue': /^[^"]*/,
'attr-svalue': /^[^']*/,
'attr-cdqt': /^"/,
'attr-csqt': /^'/,
'cdata': /^(\/\/)?<!\[CDATA\[([^>]|>)*?\/\/]]>/,
'text': /^(.|\n)+?($|(?=<[!\/a-zA-Z]))/,
'ie-open': /^<!(?:--)?\[if.*?\]>/,
'ie-close': /^<!\[endif\](?:--)?>/,
'predoctype-whitespace': /^[ \t\n]+/,
'predoctype-comment-open': /^<!--/,
'predoctype-comment': /^(.|\n)*?(?=-->)/,
'predoctype-comment-close': /^-->/,
'comment-open': /^<!--/,
'comment': /^(.|\n)*?(?=-->)/,
'comment-close': /^-->/
};
state = 'start';
last_tag = {
name: '',
tags: []
};
last_attr = null;
parent_tags = [];
html = html.replace(/\t/g, ' ');
html = html.replace(/\r/g, "\n");
initial_whitespace = (html.match(/^[ \n]*/))[0];
initial_indent = initial_whitespace.match(/[ ]*$/)[0];
initial_whitespace = initial_whitespace.substring(0, initial_whitespace.length - initial_indent.length);
final_whitespace = (html.match(/[ \n]*$/))[0];
html = html.trim();
if (output_debug) {
console.error({
initial_whitespace: initial_whitespace,
initial_indent: initial_indent,
final_whitespace: final_whitespace,
html: html
});
}
was_ws = initial_whitespace.length ? true : false;
c = 0;
while (state !== 'end') {
current = html.substring(c);
if (!possibles[state]) {
throw new Error("Missing possibles for " + state);
}
nexts = (function() {
var i, len, ref, results;
ref = possibles[state];
results = [];
for (i = 0, len = ref.length; i < len; i++) {
next = ref[i];
if (current.match(detect[next])) {
results.push(next);
}
}
return results;
})();
if (!nexts.length) {
throw new Error("At: " + state + " (" + (JSON.stringify(current.length > 10 ? current.substring(0, 10) + '...' : current)) + " @" + c + "), expected: " + (((function() {
var i, len, ref, results;
ref = possibles[state];
results = [];
for (i = 0, len = ref.length; i < len; i++) {
noun = ref[i];
results.push(noun);
}
return results;
})()).join(' ')) + ", found " + (((function() {
var results;
results = [];
for (noun in detect) {
regex = detect[noun];
if (current.match(regex)) {
results.push(noun);
}
}
return results;
})()).join(' ')));
}
next = nexts[0];
value = (current.match(detect[next]))[0];
c += value.length;
if (output_debug) {
console.error({
state: state,
next: next,
value: value
});
}
switch (next) {
case 'doctype':
last_tag.tags.push({
doctype: value.match(/^$/i)[1].toLowerCase()
});
break;
case 'tag-open':
new_tag = {
name: value.substring(1),
attrs: {},
tags: []
};
new_tag.is_singleton = (ref = new_tag.name, indexOf.call(SINGLETONS, ref) >= 0);
last_tag.tags.push(new_tag);
parent_tags.push(last_tag);
if (output_debug) {
console.error('push: ', parent_tags.length, last_tag);
}
last_tag = new_tag;
last_attr = null;
break;
case 'attr':
if (output_debug && last_attr) {
console.error({
last_attr: last_attr
});
}
last_attr = value;
break;
case 'tag-ws':
if (last_attr) {
last_tag.attrs[last_attr] = true;
}
last_attr = null;
break;
case 'attr-value':
case 'attr-dvalue':
case 'attr-svalue':
last_tag.attrs[last_attr] = value;
last_attr = null;
break;
case 'tag-gt':
if (last_attr) {
last_tag.attrs[last_attr] = true;
}
if (last_tag.is_singleton && parent_tags.length) {
last_tag = parent_tags.pop();
}
if (output_debug) {
console.error('closing: ', last_tag);
}
break;
case 'singleton':
case 'tag-close':
case 'ie-close':
if (parent_tags.length) {
last_tag = parent_tags.pop();
}
if (output_debug) {
console.error('pop: ', parent_tags.length, last_tag);
}
break;
case 'text':
if (ref1 = last_tag.name, indexOf.call(preserve_ws, ref1) >= 0) {
last_tag.tags.push(value);
} else {
pre_whitespace = (value.match(/^[ \n]*/))[0];
post_whitespace = (value.match(/[ \n]*$/))[0];
last_tag.tags.push((pre_whitespace ? ' ' : '') + value.trim() + (post_whitespace ? ' ' : ''));
}
break;
case 'cdata':
last_tag.tags.push(value);
break;
case 'comment':
case 'predoctype-comment':
last_tag.tags.push({
comment: value
});
break;
case 'ie-open':
condition = value.match(/^<!(?:--)?\[if(.*?)\]>/)[1].trim();
new_tag = {
name: 'ie',
attrs: {},
tags: [],
is_singleton: false,
ie_condition: condition
};
last_tag.tags.push(new_tag);
parent_tags.push(last_tag);
if (output_debug) {
console.error('push: ', parent_tags.length, last_tag);
}
last_tag = new_tag;
last_attr = null;
}
state = next;
}
if (output_debug) {
console.error(parent_tags.length, last_tag);
}
while (parent_tags.length) {
last_tag = parent_tags.pop();
}
if (output_debug) {
debug(last_tag.tags);
}
return initial_whitespace + (render(last_tag.tags, [initial_indent])).replace(/\n$/, '') + final_whitespace;
};
doctypes = {
'html': 'default',
'html': '5',
'xml version="1.0" encoding="utf-8" ': 'xml',
'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"': 'transitional',
'html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"': 'strict',
'html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"': 'frameset',
' 0) {
if (added_something) {
ret += ', ';
} else {
ret += ' ';
}
if (str.match(/\n/)) {
indent.push(' ');
str = str.replace(/"""/g, '"\\""').replace(/^\n|\n$/g, '').replace(/\n/g, "\n" + (indent.join('')));
ret += "\"\"\"\n" + (indent.join('')) + str + "\n" + (indent.join('')) + "\"\"\"";
indent.pop();
} else {
ret += JSON.stringify(str);
}
ret += "\n";
}
} else {
if (added_something) {
ret += ', ';
} else {
ret += ' ';
}
ret += "->\n";
indent.push(' ');
if (tag.tags.length) {
ret += render(tag.tags, indent);
}
indent.pop();
}
}
}
return ret;
};
debug = function(tags, indent) {
var ak, av, i, len, mapped, ref, results, tag;
if (indent == null) {
indent = [];
}
results = [];
for (i = 0, len = tags.length; i < len; i++) {
tag = tags[i];
if (typeof tag === 'string') {
results.push(console.error((indent.join('')) + "text: " + tag));
} else if (tag.doctype) {
mapped = doctypes[tag.doctype.replace(/\s+/g, ' ')];
results.push(console.error((indent.join('')) + "doctype: " + (JSON.stringify(tag.doctype)) + " => " + (JSON.stringify(mapped))));
} else if (tag.comment) {
results.push(console.error((indent.join('')) + "comment: " + (JSON.stringify(tag.comment))));
} else {
console.error((indent.join('')) + "{ name: " + tag.name);
indent.push(' ');
if (Object.keys(tag.attrs).length) {
console.error((indent.join('')) + "attrs: {");
indent.push(' ');
ref = tag.attrs;
for (ak in ref) {
if (!hasProp.call(ref, ak)) continue;
av = ref[ak];
console.error("" + (indent.join('')) + ak + ": " + (JSON.stringify(av)));
}
console.error((indent.join('')) + "}");
indent.pop();
}
if (tag.ie_condition) {
console.error((indent.join('')) + "ie_condition: " + (JSON.stringify(tag.ie_condition)));
}
console.error((indent.join('')) + "tags:");
indent.push(' ');
if (tag.tags.length) {
debug(tag.tags, indent);
}
console.error((indent.join('')) + "}");
indent.pop();
console.error((indent.join('')) + "}");
results.push(indent.pop());
}
}
return results;
};
_module = typeof window !== "undefined" && window !== null ? window : module;
_module[_module.exports != null ? "exports" : "htmlkup"] = htmlkup;
}).call(this);