diff --git a/exec/load/markdown.js b/exec/load/markdown.js index 0780de17ce6b13cda94878c4338fe55270abdc46..389c22dc6bf342cf5de90148971c0d13cb338306 100644 --- a/exec/load/markdown.js +++ b/exec/load/markdown.js @@ -21,21 +21,16 @@ */ /* To do: - * configurable list indentation - * configurable heading underline - * configurable heading attribute - * configurable link attribute - * configurable image attribute - * configurable class names to add to: - * <ul> <ol> <li> <table> <th> <tr> <td> <img> <a> * configurable tags instead of <b> and <i> + * code blocks / syntax highlighting */ load('sbbsdefs.js'); if (typeof Frame == 'undefined') Frame = false; -function Markdown(target) { +function Markdown(target, settings) { + const state = { list_level : 0, links : [], @@ -43,11 +38,52 @@ function Markdown(target) { table : [], blockquote : false, list_stack : [] + }; + + const config = { + console : { + bold_style : '\1h', + list_indent : '\t', + heading_underline : true, + heading_style : '\1h', + link_style : '\1h\1c', + image_style : '\1h\1m' + }, + html : { + a : '', + ul : 'list-group', + ol : 'list-group', + li : 'list-group-item', + table : 'table table-striped', + thead : '', + tbody : '', + th : '', + tr : '', + td : '', + img : '', + hr : '', + blockquote : 'blockquote' + } + }; + if (typeof settings == 'object') { + if (typeof settings.console == 'object') { + Object.keys(settings.console).forEach(function (e) { + config.console[e] = settings.console[e]; + }); + } + if (typeof settings.html == 'object') { + Object.keys(settings.html).forEach(function (e) { + config.html[e] = settings.html[e]; + }); + } } + if (Frame && target instanceof Frame) target.word_wrap = true; + Object.defineProperty(this, 'state', { get : function () { return state; }}); + Object.defineProperty(this, 'target', { get : function () { return target; @@ -66,6 +102,7 @@ function Markdown(target) { } } }); + Object.defineProperty(this, 'columns', { get : function () { if (target == 'html') { return 0; @@ -75,6 +112,22 @@ function Markdown(target) { return target.screen_columns; } }}); + + Object.defineProperty(this, 'config', { value : config }); + +} + +Markdown.prototype.html_tag_format = function (tag, attributes) { + var ret = '<' + tag; + if (this.config.html[tag] != '') { + ret += ' class="' + this.config.html[tag] + '"'; + } + if (attributes) { + Object.keys(attributes).forEach(function (e) { + ret += ' ' + e + '="' + attributes[e] + '"' + }); + } + return ret + '>'; } Markdown.prototype.colorize_console = function (str) { @@ -88,15 +141,15 @@ Markdown.prototype.colorize_console = function (str) { Markdown.prototype.render_text_console = function (text) { const self = this; var ret = text.replace(/\*([^\*]+)\*/g, function (m, c) { - return '\1+\1h' + c + '\1-'; + return '\1+' + self.config.console.bold_style + c + '\1-'; }); ret = ret.replace(/!\[([^\]]+)\]\(([^\)]+)\)/g, function (m, c1, c2) { self.state.images.push({ text : c1, link : c2 }); - return '\1+\1m' + c1 + ' [' + self.state.images.length + ']\1-'; + return '\1+' + self.config.console.image_style + c1 + ' [' + self.state.images.length + ']\1-'; }); ret = ret.replace(/\[([^\]]+)\]\(([^\)]+)\)/g, function (m, c1, c2) { self.state.links.push({ text : c1, link : c2 }); - return '\1+\1c' + c1 + ' [' + self.state.links.length + ']\1-'; + return '\1+' + self.config.console.link_style + c1 + ' [' + self.state.links.length + ']\1-'; }); return this.colorize_console(ret); } @@ -115,10 +168,10 @@ Markdown.prototype.render_text_html = function (text) { return '<b>' + c + '</b>'; }); ret = ret.replace(/!\[([^\]]+)\]\(([^\)]+)\)/g, function (m, c1, c2) { - return '<img alt="' + c1 + '" src="' + c2 + '">'; + return self.html_tag_format('img', { alt : c1, src : c2 }); }); ret = ret.replace(/\[([^\]]+)\]\(([^\)]+)\)/g, function (m, c1, c2) { - return '<a href="' + c2 + '">' + c1 + '</a>'; + return self.html_tag_format('a', { href : c2 }) + c1 + '</a>'; }); return this.italicize(ret); } @@ -139,14 +192,20 @@ Markdown.prototype.render_table = function () { }); }); if (this.target == 'html') { - var ret = '<table>'; + var ret = this.html_tag_format('table'); this.state.table.forEach(function (e, i) { - ret += '<tr>'; - const tag = (i == 0 ? ['<th>', '</th>'] : ['<td>', '</td>']); + if (i == 0) { + ret += self.html_tag_format('thead'); + var tag = [self.html_tag_format('th'), '</th>']; + } else { + var tag = [self.html_tag_format('td'), '</td>']; + } + ret += self.html_tag_format('tr'); for (var n = 0; n < columns.length; n++) { ret += tag[0] + (typeof e[n] == 'undefined' ? '' : e[n]) + tag[1]; } ret += '</tr>'; + if (i == 0) ret += '</thead>'; }); ret += '</table><br>'; this.state.table = []; @@ -207,7 +266,7 @@ Markdown.prototype.render_line_console = function (line) { this.state.list_level--; } for (var n = 0; n < this.state.list_level; n++) { - ret += ' '; + ret += this.config.console.list_indent; } if (lt == 'ul') { ret += match[2]; @@ -233,9 +292,19 @@ Markdown.prototype.render_line_console = function (line) { } // Heading - match = line.match(/^#+\s+(.*)$/); + match = line.match(/^(#+)\s+(.*)$/); if (match !== null) { - return ret + '\1+\1h' + this.render_text_console(match[1]) + '\1-\r\n\r\n'; + ret += '\1+'; + ret += this.config.console.heading_style; + ret += this.render_text_console(match[2]); + if (this.config.console.heading_underline) { + ret += '\r\n'; + for (var n = 0; n < match[2].length; n++) { + ret += '-'; + } + } + ret += '\1-\r\n\r\n'; + return ret; } // Blockquote @@ -271,7 +340,7 @@ Markdown.prototype.render_line_html = function (line) { if (match !== null) { if (this.state.table.length) ret += this.render_table(); if (!this.state.blockquote) { - ret += '<blockquote>'; + ret += this.html_tag_format('blockquote'); this.state.blockquote = true; } return ret + match[1]; @@ -287,17 +356,20 @@ Markdown.prototype.render_line_html = function (line) { var lt = (match[2] == '*' ? 'ul' : 'ol'); if (!match[1].length) { while (this.state.list_stack.length > 1) { - ret += '</' + this.state.list_stack.pop() + '>'; + ret += '</' + this.state.list_stack.pop() + '></li>'; } if (this.state.list_stack.length < 1) { this.state.list_stack.push(lt); - ret += '<' + lt + '>'; + ret += this.html_tag_format(lt); } - } else if (match[1].length > this.state.list_stack.length) { + } else if (match[1].length >= this.state.list_stack.length) { this.state.list_stack.push(lt); - ret += '<' + lt + '>'; + ret += this.html_tag_format('li'); + ret += this.html_tag_format(lt); } - ret += '<li>' + this.render_text_html(match[3]) + '</li>'; + ret += this.html_tag_format('li'); + ret += this.render_text_html(match[3]); + ret += '</li>'; return ret; } while (this.state.list_stack.length) { @@ -327,7 +399,7 @@ Markdown.prototype.render_line_html = function (line) { // Horizontal Rule match = line.match(/^---+$/); if (match !== null) { - return ret + '<hr>'; + return ret + this.html_tag_format('hr'); } return ret + this.render_text_html(line) + '<br>'; @@ -343,16 +415,16 @@ Markdown.prototype.render_console = function (text) { } }); if (this.state.links.length) { - this.target.putmsg('\1+\1cLinks:\1-\r\n'); + this.target.putmsg('\1+' + self.config.console.link_style + 'Links:\1-\r\n'); this.state.links.forEach(function (e, i) { - self.target.putmsg('\1+\1c[' + (i + 1) + '] ' + e.link + '\1-\r\n'); + self.target.putmsg('\1+' + self.config.console.link_style + '[' + (i + 1) + '] ' + e.link + '\1-\r\n'); }); this.target.putmsg('\r\n'); } if (this.state.images.length) { - this.target.putmsg('\1+\1mImages:\1-\r\n'); + this.target.putmsg('\1+' + self.config.console.image_style + 'Images:\1-\r\n'); this.state.images.forEach(function (e, i) { - self.target.putmsg('\1+\1m[' + (i + 1) + '] ' + e.link + '\1-\r\n'); + self.target.putmsg('\1+' + self.config.console.image_style + '[' + (i + 1) + '] ' + e.link + '\1-\r\n'); }); this.target.putmsg('\r\n'); }