module.exports = function (txt, filename, sourceFileName) { var Title = txt.match(/^[^"]*"([^"]+)"/), rdoc = /\/\*\\[\s\S]*?\\\*\//g, rdoc2 = /^\/\*\\\n\s*\*\s+(.*)\n([\s\S]*)\\\*\/$/, rows = /^\s*(\S)(?:(?!\n)\s(.*))?$/, // rcode = /`([^`]+)`/g, rkeywords = /\b(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|volatile|while|with|undefined)\b/g, rstrings = /("[^"]*?(?:\\"[^"]*?)*"|'[^']*?(?:\\'[^']*?)*')/g, roperators = /( \= | \- | \+ | % | \* | \&\& | \&\& | \& | \& | \|\| | \| | \/ | == | === )/g, rdigits = /(\b(0[xX][\da-fA-F]+)|((\.\d+|\b\d+(\.\d+)?)(?:e[-+]?\d+)?))\b/g, rcomments = /(\/\/.*?(?:\n|$)|\/\*(?:.|\s)*?\*\/)$/g, // rhref = /(https?:\/\/[^\s"]+[\d\w_\-\/])/g, rlink = /(^|\s)@([\w\.\_\$]*[\w\_\$])/g, ramp = /&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, rantiwrap = /^<([^>]+)>(.*)<\/\1>$/, main = txt.match(rdoc), root = {}, mode, html, jsonLevel = 0, src = "", list = [[]], curlist = list[0], srcfilename = sourceFileName || (path.basename(filename, path.extname(filename)) + "-src.html"), clas = "", TOC = [], utoc = {}, chunks = {}, out = {}; if (!main) { return {}; } function esc(text) { return markdown.toHTML(String(text)) .replace(rantiwrap, "$2") .replace(ramp, '<em class="amp">&</em>') .replace(rlink, '$1<a href="#$2" class="dr-link">$2</a>'); } function syntax(text) { return text.replace(/</g, "<") .replace(ramp, "&") .replace(rkeywords, "<b>$1</b>") .replace(rstrings, "<i>$1</i>") .replace(roperators, '<span class="s">$1</span>') .replace(rdigits, '<span class="d">$1</span>') .replace(rcomments, '<span class="c">$1</span>') + "\n"; } function syntaxSrc(text) { var isend = text.match(/\*\//); if (text.match(/\/\*/)) { syntaxSrc.inc = true; } var out = text.replace(/</g, "<") .replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, "&") .replace(rkeywords, "<b>$1</b>") .replace(rstrings, "<i>$1</i>") .replace(roperators, '<span class="s">$1</span>') .replace(rdigits, '<span class="d">$1</span>') .replace(/(\/\*(?:.(?!\*\/))+(?:\*\/)?)/g, '<span class="c">$1</span>') .replace(rcomments, '<span class="c">$1</span>') + "\n"; if (syntaxSrc.inc) { out = out.replace(/(^.*\*\/)/, '<span class="c">$1</span>'); if (!isend) { out = '<span class="c">' + out + '</span>'; } } if (isend) { syntaxSrc.inc = false; } return out; } eve.on("doc.*.list", function (mod, text) { this != "-" && (html += "</ol></div>\n"); }); eve.on("doc.*.json", function (mod, text) { this != "o" && (html += "</ol>\n"); }); eve.on("doc.*.text", function (mod, text) { this != "*" && (html += "</p>\n"); }); eve.on("doc.*.head", function (mod, text) { // this != "*" && (html += "</h3>\n"); }); eve.on("doc.*.code", function (mod, text) { this != "|" && (html += "</code></pre></section>\n"); }); eve.on("doc.s*.*", function (mod, text) { mode != "text" && (html += "<p>"); if (text) { html += esc(text) + "\n"; } else { html += "</p>\n<p>"; } mode = "text"; }); eve.on("doc.s|.*", function (mod, text) { mode != "code" && (html += '<section class="code"><pre class="javascript code"><code data-language="javascript" class="language-javascript">'); html += text.replace(/</g, "<").replace(ramp, "&") + "\n"; mode = "code"; }); eve.on("doc.s#.*", function (mod, text) { html += text + "\n"; mode = "html"; }); eve.on("doc.s>.*", function (mod, text) { mode != "head" && (html += '<h3>'); html += esc(text) + "</h3>"; mode = "head"; }); eve.on("doc.s[.*", function (mod, text) { var type; text = esc(text).replace(/\(([^\)]+)\)/, function (all, t) { type = t; return ""; }); itemData.type = esc(text).replace(/\s*\]\s*$/, "").split(/\s*,\s*/); clas = itemData.clas = "dr-" + itemData.type.join(" dr-"); html += '<div class="' + clas + '">'; type && (html += '<em class="dr-type dr-type-' + type + '">' + type + '</em>'); mode = ""; }); eve.on("doc.end.*", function (mod, text) { clas && (html += "</div>"); }); eve.on("doc.s=.*", function (mod, text) { var split = text.split(/(\s*[\(\)]\s*)/); split.shift(); split.shift(); var types = split.shift().split(/\s*\|\s*/); split.shift(); html += '<p class="dr-returns"><strong class="dr-title">Returns:</strong> '; for (var i = 0, ii = types.length; i < ii; i++) { types[i] = '<em class="dr-type-' + types[i] + '">' + types[i] + '</em>'; } html += types.join(" ") + ' <span class="dr-description">' + esc(split.join("")) + "</span></p>\n"; mode = ""; }); eve.on("doc.s-.*", function (mod, text) { itemData.params = itemData.params || []; if (mode != "list") { html += '<div class="topcoat-list__container"><h3 class="topcoat-list__header">Parameters</h3><ol class="topcoat-list">'; itemData.params.push([]); } var optional, data = itemData.params[itemData.params.length - 1]; text = text.replace(/#optional\s*/g, function () { optional = true; return ""; }); var split = text.split(/(\s*[\(\)]\s*)/); data.push((optional ? "[" : "") + split[0] + (optional ? "]" : "")); html += '<li class="topcoat-list__item"><span class="dr-param' + (optional ? " optional" : "") + '">' + split.shift() + '</span>\n'; split.shift(); if (optional) { html += '<span class="dr-optional">optional</span>\n'; } var types = split.shift().split(/\s*\|\s*/); split.shift(); html += '<span class="dr-type">'; for (var i = 0, ii = types.length; i < ii; i++) { types[i] = '<em class="dr-type-' + types[i] + '">' + types[i] + '</em>'; } html += types.join(" ") + '</span>\n<span class="dr-description">' + (esc(split.join("")) || " ") + '</span></li>\n'; mode = "list"; }); eve.on("doc.so.*", function (mod, text) { if (mode != "json") { html += '<ol class="dr-json">'; } var desc = text.match(/^\s*([^\(\s]+)\s*\(([^\)]+)\)\s*(.*?)\s*$/), start = text.match(/\s*\{\s*$/), end = text.match(/\s*\}\s*,?\s*$/); !end && (html += "<li>"); if (desc) { html += '<span class="dr-json-key">' + desc[1] + '</span>'; var types = desc[2].split(/\s*\|\s*/); html += '<span class="dr-type">'; for (var i = 0, ii = types.length; i < ii; i++) { types[i] = '<em class="dr-type-' + types[i] + '">' + types[i] + '</em>'; } html += types.join(" ") + '</span><span class="dr-json-description">' + (esc(desc[3]) || " ") + '</span>\n'; } else { !end && (html += text); } if (start) { html += '<ol class="dr-json">'; } if (end) { html += '</ol></li><li>' + text + '</li>'; } mode = "json"; }); out.sections = main.length; main = txt.split("\n"); out.loc = main.length; var beginrg = /^\s*\/\*\\\s*$/, endrg = /^\s*\\\*\/\s*$/, line = 0, pointer, firstline = false, inside = false; for (var i = 0, ii = main.length; i < ii; i++) { var doc = main[i]; line++; src += '<code id="L' + line + '"><span class="ln">' + line + '</span>' + syntaxSrc(doc) + '</code>'; if (doc.match(beginrg)) { inside = firstline = true; pointer = root; itemData = {}; html = ""; mode = ""; continue; } if (doc.match(endrg)) { inside = false; eve("doc.end." + mode, null, mode, ""); itemData.line = line + 1; (function (value, Clas, data, pointer) { eve.on("doc.item", function () { if (this == pointer) { html += value; itemData = data; } }); })(html, clas, itemData, pointer); clas = ""; continue; } if (inside) { var title, data = doc.match(rows); if (data) { var symbol = data[1], text = data[2]; if (symbol == "*" && firstline) { firstline = false; title = text.split("."); for (var j = 0, jj = title.length; j < jj; j++) { pointer = pointer[title[j]] = pointer[title[j]] || {}; } } else { eve("doc.s" + symbol + "." + mode, symbol, mode, text); } } } } html = ""; var lvl = [], toc = "", itemData, res = ""; var runner = function (pointer, hx) { var level = [], name, chunk; for (var node in pointer) { level.push(node); } level.sort(); for (var j = 0, jj = level.length; j < jj; j++) { lvl.push(level[j]); name = lvl.join("."); html = ""; chunk = ""; itemData = {}; eve("doc.item", pointer[level[j]]); chunk += '<article id="' + name + '" class="' + name.replace(/\./g, "-") + '-section"><header><h' + hx + ' id="' + name + '" class="' + itemData.clas + '">' + name; if (itemData.type && itemData.type.indexOf("method") + 1) { if (itemData.params) { if (itemData.params.length == 1) { chunk += "(" + itemData.params[0].join(", ") + ")"; } else if (!itemData.params.length) { chunk += "()"; } else { chunk += "(\u2026)"; } } else { chunk += "()"; } } chunk += '<a href="#' + name + '" title="Link to this section" class="dr-hash">⚓</a>'; itemData.line && (chunk += '<a class="dr-sourceline" title="Go to line ' + itemData.line + ' in the source" href="' + srcfilename + '#L' + itemData.line + '">➭</a>'); chunk += '</h' + hx + '></header>\n<section>'; chunk += '<div class="extra" id="' + name + '-extra"></div>'; chunk += html; chunk += '</section></article>'; chunks[name] = chunks[name] || ""; chunks[name] += chunk; res += chunk; var indent = 0; name.replace(/\./g, function () { indent++; }); toc += '<li class="dr-lvl' + indent + '"><a href="#' + name + '" class="' + itemData.clas + '"><span>' + name; if (itemData.type && itemData.type.indexOf("method") + 1) { toc += "()"; } toc += '</span></a></li>'; if (!utoc[name]) { TOC.push({ indent: indent, name: name, clas: itemData.clas, brackets: itemData.type && itemData.type.indexOf("method") + 1 ? "()" : "" }); utoc[name] = 1; } runner(pointer[level[j]], hx + 1); lvl.pop(); } }; runner(root, 2); out.chunks = chunks; out.toc = TOC; out.title = Title ? Title[1] : ""; out.source = '<!DOCTYPE html>\n<!-- Generated with Dr.js -->\n<html lang="en"><head><meta charset="utf-8"><title>' + path.basename(filename) + '</title><link rel="stylesheet" href="dr.css"></head><body id="src-dr-js">' + src + '</body></html>'; eve.unbind("doc.*"); return out; };
module.exports = function (txt, filename, sourceFileName) { var Title = txt.match(/^[^"]*"([^"]+)"/), rdoc = /\/\*\\[\s\S]*?\\\*\//g, rdoc2 = /^\/\*\\\n\s*\*\s+(.*)\n([\s\S]*)\\\*\/$/, rows = /^\s*(\S)(?:(?!\n)\s(.*))?$/, rlink = /(^|\s)@([\w\.\_\$]*[\w\_\$])/g, ramp = /&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, rantiwrap = /^<([^>]+)>(.*)<\/\1>$/, main = txt.match(rdoc), root = {}, mode, html, jsonLevel = 0, src = "", list = [[]], curlist = list[0], srcfilename = sourceFileName || (path.basename(filename, path.extname(filename)) + "-src.html"), clas = "", TOC = [], sections = [], current = sections, prev = sections, utoc = {}, chunks = {}, out = {}; if (!main) { return {}; } function esc(text) { return markdown.toHTML(String(text)) .replace(rantiwrap, "$2") .replace(ramp, '<em class="amp">&</em>') .replace(rlink, '$1<a href="#$2" class="dr-link">$2</a>'); } eve.on("doc.*.list", function (mod, text) { if (this != "-") { current = prev; } }); eve.on("doc.*.json", function (mod, text) { if (this != "o") { current = prev; } }); eve.on("doc.*.text", function (mod, text) { if (this != "*") { current = prev; } }); eve.on("doc.*.code", function (mod, text) { if (this != "|") { current = prev; } }); eve.on("doc.s*.*", function (mod, text) { if (mode != "text") { prev = current; var arr = [""]; current.push({text: arr}); current = arr; } if (text) { current[current.length - 1] += esc(text) + "\n"; } else { current.push(""); } mode = "text"; }); eve.on("doc.s|.*", function (mod, text) { if (mode != "code") { prev = current; var arr = []; current.push({code: arr}); current = arr; } current.push(text); mode = "code"; }); eve.on("doc.s#.*", function (mod, text) { current.push({html: text + "\n"}); mode = "html"; }); eve.on("doc.s>.*", function (mod, text) { mode != "head" && (html += '<h3>'); current.push({head: esc(text)}); mode = "head"; }); eve.on("doc.s[.*", function (mod, text) { var type; text = esc(text).replace(/\(([^\)]+)\)/, function (all, t) { type = t; return ""; }); itemData.type = esc(text).replace(/\s*\]\s*$/, "").split(/\s*,\s*/); clas = itemData.clas = "dr-" + itemData.type.join(" dr-"); mode = ""; }); eve.on("doc.end.*", function (mod, text) { }); eve.on("doc.s=.*", function (mod, text) { var split = text.split(/(\s*[\(\)]\s*)/); split.shift(); split.shift(); var types = split.shift().split(/\s*\|\s*/); split.shift(); current.push({rtrn: { type: types, desc: esc(split.join("")) }}); mode = ""; }); eve.on("doc.s-.*", function (mod, text) { itemData.params = itemData.params || []; if (mode != "list") { itemData.params.push([]); prev = current; var arr = []; current.push({attr: arr}); current = arr; } var optional, data = itemData.params[itemData.params.length - 1]; text = text.replace(/#optional\s*/g, function () { optional = true; return ""; }); var split = text.split(/(\s*[\(\)]\s*)/), item = {}; data.push((optional ? "[" : "") + split[0] + (optional ? "]" : "")); item.name = split.shift(); split.shift(); if (optional) { item.optional = true; } var types = split.shift().split(/\s*\|\s*/); split.shift(); item.type = types; var stypes = []; item.desc = esc(split.join("")); current.push(item); mode = "list"; }); eve.on("doc.so.*", function (mod, text) { if (mode != "json") { prev = current; var arr = []; current.push({json: arr}); current = arr; } var desc = text.match(/^\s*([^\(\s]+)\s*\(([^\)]+)\)\s*(.*?)\s*$/), start = text.match(/\s*\{\s*$/), end = text.match(/\s*\}\s*,?\s*$/), item = {}; if (desc) { item.key = desc[1]; item.type = desc[2].split(/\s*\|\s*/); item.desc = esc(desc[3]); var types = desc[2].split(/\s*\|\s*/); } else if (!end) { item = text; } if (start) { item = { start: text }; } if (end) { item = { end: text }; } current.push(item); mode = "json"; }); out.sections = main.length; main = txt.split("\n"); out.loc = main.length; var beginrg = /^\s*\/\*\\\s*$/, endrg = /^\s*\\\*\/\s*$/, line = 0, pointer, firstline = false, inside = false; for (var i = 0, ii = main.length; i < ii; i++) { var doc = main[i]; line++; if (doc.match(beginrg)) { inside = firstline = true; pointer = root; itemData = {}; html = ""; mode = ""; continue; } if (doc.match(endrg) && inside) { inside = false; eve("doc.end." + mode, null, mode, ""); itemData.line = line + 1; (function (value, Clas, data, pointer) { eve.on("doc.item", function () { if (this == pointer) { html += value; itemData = data; } }); })(html, clas, itemData, pointer); clas = ""; continue; } if (inside) { var title, data = doc.match(rows); if (data) { var symbol = data[1], text = data[2]; if (symbol == "*" && firstline) { firstline = false; title = text.split("."); for (var j = 0, jj = title.length; j < jj; j++) { pointer = pointer[title[j]] = pointer[title[j]] || {}; } sections.push(current = prev = [itemData]); } else { eve("doc.s" + symbol + "." + mode, symbol, mode, text); } } } } html = ""; var lvl = [], toc = "", itemData, res = ""; var runner = function (pointer, hx) { var level = [], name, chunk, brackets; for (var node in pointer) { level.push(node); } level.sort(); for (var j = 0, jj = level.length; j < jj; j++) { lvl.push(level[j]); name = lvl.join("."); itemData = {}; eve("doc.item", pointer[level[j]]); itemData.title = name; if (itemData.type && itemData.type.indexOf("method") + 1) { if (itemData.params) { if (itemData.params.length == 1) { brackets = "(" + itemData.params[0].join(", ") + ")"; } else if (!itemData.params.length) { brackets = "()"; } else { brackets = "(\u2026)"; } } else { brackets = "()"; } } itemData.name = name + (brackets || ""); var indent = 0; name.replace(/\./g, function () { indent++; }); if (!utoc[name]) { TOC.push({ indent: indent, name: name, clas: itemData.clas, brackets: itemData.type && itemData.type.indexOf("method") + 1 ? "()" : "" }); utoc[name] = 1; } runner(pointer[level[j]], hx + 1); lvl.pop(); } }; runner(root, 2); out.toc = TOC; out.out = sections; // console.log(JSON.stringify(sections)); out.title = Title ? Title[1] : ""; eve.unbind("doc.*"); return out; };