+ This method handles the list wrapper, which in terms of HTML would be <ol> or <ul>. + This method is not responsible for handling list elements, all such processing should + already have occured on text pased to the method . All that it is intended + to do is to wrap the text parameter in anything needed. +
+ + @example + out.s += "<ul>" + text.s + "</ul>" + + + @method + @param {Buffer} out The output string buffer to append to. + @param {Buffer} text The input that goes inside the list. + @param {Number} flags A bitfield holding a portion of the render state. The only bit that this should be concerned with is MKD_LIST_ORDERED + @param {?Object} context A renderer specific context object. + */ + list: null, + /** + Renders a list. ++ Wraps the text in a list element. +
+ + @example + out.s += "<li>" + text.s + "</li>" + + + @method + @param {Buffer} out The output string buffer to append to. + @param {Buffer} text The contents of the list element. + @param {Number} flags A bitfield holding a portion of the render state. The only bit that this should be concerned with is MKD_LI_BLOCK. + @param {?Object} context A renderer specific context object. + */ + listitem: null, + /** + Renders a paragraph. + + @example + + out.s += "<p>" + text.s + "</p>"; + + @method + @param {Buffer} out The output string buffer to append to. + @param {Buffer} text The input text. + @param {?Object} context A renderer specific context object. + */ + paragraph: null, + /** + Renders a table. + + @example + + out.s += "';
+ } else
+ out.s += '';
+
+ if (text) escape_html(out, text.s, false);
+
+ out.s += '
\n';
+ }
+
+ // void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_blockquote(out, text, options) {
+ if (out.s.length) out.s += '\n';
+ out.s += '\n';
+ if (text) out.s += text.s;
+ out.s += '
\n';
+ }
+
+ // void (*blockhtml)(struct buf *ob,const struct buf *text, void *opaque);
+ function cb_blockhtml(out, text, options) {
+ var org, sz;
+ if (!text) return;
+ sz = text.s.length;
+ while (sz > 0 && text.s[sz - 1] == '\n') sz--;
+ org = 0;
+ while (org < sz && text.s[org] == '\n') org++;
+ if (org >= sz) return;
+ if (out.s.length) out.s += '\n';
+ out.s += text.s.slice(org, sz);
+ out.s += '\n';
+ }
+
+ // header(Buffer out, Buffer text, int level, void *opaque);
+ function cb_header(out, text, level, options) {
+ if (out.s.length) out.s += '\n';
+
+ if (options.flags & HTML_TOC) {
+ out.s += '';
+ } else {
+ out.s += '';
+ }
+
+ if (text) out.s += text.s;
+ out.s += ' \n';
+ }
+
+ // void (*hrule)(struct buf *ob, void *opaque);
+ function cb_hrule(out, options) {
+ if (out.s.length) out.s += '\n';
+ out.s += (options.flags & HTML_USE_XHTML) ? '
\n' : '
\n';
+ }
+
+ // void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+ function cb_list(out, text, flags, options) {
+ if (out.s.length) out.s += '\n';
+ out.s += (flags&MKD_LIST_ORDERED?'\n':'\n');
+ if (text) out.s += text.s;
+ out.s += (flags&MKD_LIST_ORDERED?'
\n':'\n');
+ }
+
+ // void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+ function cb_listitem(out, text, flags, options) {
+ out.s += '';
+ if (text) {
+ var size = text.s.length;
+ while (size && text.s[size - 1] == '\n') size--;
+ out.s += text.s.slice(0, size);
+ }
+ out.s += ' \n';
+ }
+
+ // void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_paragraph(out, text, options) {
+ var i = 0;
+
+ if (out.s.length) out.s += '\n';
+
+ if (!text || !text.s.length) return;
+
+ while (i < text.s.length && isspace(text.s[i])) i++;
+
+ if (i == text.s.length) return;
+
+ out.s += '';
+ if (options.flags & HTML_HARD_WRAP) {
+ var org;
+ while (i < text.s.length) {
+ org = i;
+ while (i < text.s.length && text.data[i] != '\n')
+ i++;
+
+ if (i > org) out.s += text.s.slice(org, i);
+
+ /*
+ * do not insert a line break if this newline
+ * is the last character on the paragraph
+ */
+ if (i >= text.s.length - 1) break;
+
+ cb_linebreak(out, options);
+ i++;
+ }
+ } else {
+ out.s += text.s.slice(i);
+ }
+ out.s += '
\n';
+ }
+
+ // void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque);
+ function cb_table(out, header, body, options) {
+ if (out.s.length) out.s += '\n';
+ out.s += '\n';
+ if (header) out.s += header.s;
+ out.s += '\n';
+ if (body) out.s += body.s;
+ out.s += '
\n';
+ }
+
+ // void (*table_row)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_table_row(out, text, options) {
+ out.s += '\n';
+ if (text) out.s += text.s;
+ out.s += ' \n';
+ }
+
+ // void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+ function cb_table_cell(out, text, flags, options, col_span) {
+ if (flags & MKD_TABLE_HEADER) {
+ out.s += ' 1) {
+ out.s += " colspan=\"" + col_span + "\" ";
+ }
+ switch (flags & MKD_TABLE_ALIGNMASK) {
+ case MKD_TABLE_ALIGN_CENTER:
+ out.s += ' align="center">';
+ break;
+
+ case MKD_TABLE_ALIGN_L:
+ out.s += ' align="left">';
+ break;
+
+ case MKD_TABLE_ALIGN_R:
+ out.s += ' align="right">';
+ break;
+ default:
+ out.s += '>';
+ }
+
+ if (text) out.s += text.s;
+
+ if (flags & MKD_TABLE_HEADER) {
+ out.s += ' \n';
+ } else {
+ out.s += '\n';
+ }
+ }
+
+ /* span level callbacks - NULL or return 0 prints the span verbatim */
+ // int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque);
+ function cb_autolink(out, link, type, options) {
+ var offset = 0;
+
+ if (!link || !link.s.length) return 0;
+
+ if ((options.flags & HTML_SAFELINK) != 0 &&
+ !sd_autolink_issafe(link.s) && type != MKDA_EMAIL)
+ return 0;
+
+ out.s += '';
+ } else {
+ out.s += '">';
+ }
+
+ /*
+ * Pretty printing: if we get an email address as
+ * an actual URI, e.g. `mailto:foo@bar.com`, we don't
+ * want to print the `mailto:` prefix
+ */
+ if (link.s.indexOf('mailto:')==0) {
+ escape_html(out, link.s.slice(7), false);
+ } else {
+ escape_html(out, link.s, false);
+ }
+
+ out.s += '';
+
+ return 1;
+ }
+
+ // int (*codespan)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_codespan(out, text, options) {
+ out.s += '';
+ if (text) escape_html(out, text.s, false);
+ out.s += '';
+ return 1;
+ }
+
+ // int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_double_emphasis(out, text, options) {
+ if (!text || !text.s.length) return 0;
+ out.s += '' + text.s + '';
+ return 1;
+ }
+
+ // int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_emphasis(out, text, options) {
+ if (!text || !text.s.length) return 0;
+ out.s += '' + text.s + '';
+ return 1;
+ }
+
+ // int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque);
+ function cb_image(out, link, title, alt, options) {
+ if (!link || !link.s.length) return 0;
+
+ out.s += '
':'">');
+ return 1;
+ }
+
+
+ // int (*linebreak)(struct buf *ob, void *opaque);
+ function cb_linebreak(out, options) {
+ out.s += (options.flags&HTML_USE_XHTML?'
\n':'
\n');
+ return 1;
+ }
+
+ // int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque);
+ function cb_link(out, link, title, content, options) {
+ if (link != null && (options.flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link.s)) return 0;
+
+ out.s += '';
+ } else {
+ out.s += '">';
+ }
+
+ if (content && content.s.length) out.s += content.s;
+ out.s += '';
+ return 1;
+ }
+
+ // rndr_html_tag(struct buf *ob, const struct buf *text, void *opaque, char* tagname, char** whitelist, int tagtype)
+ //NOT A CALLBACK!
+ function rndr_html_tag(out, text, options, tagname, whitelist, tagtype) {
+ var i, x, z, in_str = 0, seen_equals = 0, done = 0, done_attr = 0, reset = 0;
+ var attr, value;
+ var c;
+
+ out.s += '<';
+
+ if(tagtype == HTML_TAG_CLOSE) {
+ out.s += "/" + tagname + ">";
+ return;
+ }
+
+ out.s += tagname;
+ var i = 1 + tagname.length;
+
+ attr = new Buffer;
+ value = new Buffer;
+
+ for(;i < text.s.length && !done; i++) {
+ c = text.s[i];
+ done = 0;
+ reset = 0;
+ done_attr = 0;
+
+ switch(c) {
+ case '>':
+ done = 1;
+ break;
+ case '\'':
+ case '"':
+ if (!seen_equals) {
+ reset = 1;
+ } else if(!in_str) {
+ in_str = c;
+ } else if(in_str == c) {
+ in_str = 0;
+ done_attr = 1;
+ } else {
+ value.s += c;
+ }
+ break;
+ case ' ':
+ if (in_str) {
+ value.s += ' ';
+ } else {
+ reset = 1;
+ }
+ break;
+ case '=':
+ if (seen_equals) {
+ reset = 1;
+ break;
+ }
+ seen_equals = 1;
+ break;
+ default:
+ if (seen_equals && in_str || !seen_equals) {
+ if (seen_equals) {
+ value.s += c;
+ } else {
+ attr.s += c;
+ }
+ }
+ break;
+ }
+
+ if (done_attr) {
+ var valid = 0;
+ for (z = 0; whitelist[z]; z++) {
+ if (whitelist[z].length != attr.s.length) {
+ continue;
+ }
+ for (x = 0; x < attr.s.length; x++) {
+ if (whitelist[z][x].toLowerCase() != attr.s[x].toLowerCase()) {
+ break;
+ }
+ }
+ if (x == attr.s.length) {
+ valid = 1;
+ break;
+ }
+ }
+ if (valid && value.s.length && attr.s.length) {
+ // console.log("VALD", value.s.length, attr.s.length);
+ out.s += ' ';
+ escape_html(out, attr.s, false);
+ out.s += '="';
+ escape_html(out, value.s, false)
+ out.s += '"';
+ }
+ reset = 1;
+ }
+ if (reset) {
+ seen_equals = 0;
+ in_str = 0;
+ attr = new Buffer;
+ value = new Buffer;
+ }
+ }
+ out.s += '>';
+ }
+
+ // int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque);
+ function cb_raw_html_tag(out, text, options) {
+ var whitelist = options.html_element_whitelist;
+
+ /* Items on the whitelist ignore all other flags and just output */
+ if (((options.flags & HTML_ALLOW_ELEMENT_WHITELIST) != 0) && whitelist) {
+ for(var i = 0; whitelist[i]; i++) {
+ var tagtype = sdhtml_is_tag(text.s, whitelist[i]);
+ if(tagtype != HTML_TAG_NONE) {
+ rndr_html_tag(out, text, options, whitelist[i], options.html_attr_whitelist, tagtype);
+ return 1;
+ }
+ }
+ }
+
+ /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
+ * It doens't see if there are any valid tags, just escape all of them. */
+ if((options.flags & HTML_ESCAPE) != 0) {
+ escape_html(out, text.s, false);
+ return 1;
+ }
+
+ if ((options.flags & HTML_SKIP_HTML) != 0) return 1;
+
+ if ((options.flags & HTML_SKIP_STYLE) != 0 &&
+ sdhtml_is_tag(text.s, "style"))
+ return 1;
+
+ if ((options.flags & HTML_SKIP_LINKS) != 0 &&
+ sdhtml_is_tag(text.s, "a"))
+ return 1;
+
+ if ((options.flags & HTML_SKIP_IMAGES) != 0 &&
+ sdhtml_is_tag(text.s, "img"))
+ return 1;
+
+ out.s += text.s;
+ return 1;
+ }
+
+ // int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_triple_emphasis(out, text, options) {
+ if (!text || !text.s.length) return 0;
+ out.s += '' + text.s + '';
+ return 1;
+ }
+
+ // int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_strikethrough(out, text, options) {
+ if (!text || !text.s.length) return 0;
+ out.s += '' + text.s + '';
+ return 1;
+ }
+
+ // int (*superscript)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_superscript(out, text, options) {
+ if (!text || !text.s.length) return 0;
+ out.s += '' + text.s + '';
+ return 1;
+ }
+
+ /* low level callbacks - NULL copies input directly into the output */
+ //do not use
+ // void (*entity)(struct buf *ob, const struct buf *entity, void *opaque);
+ // function cb_entity(out, entity, options) {}
+
+ // void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque);
+ function cb_normal_text(out, text, options) {
+ if (text) escape_html(out, text.s, false);
+ }
+
+ // toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
+ function cb_toc_header(out, text, level, options) {
+ /* set the level offset if this is the first header
+ * we're parsing for the document */
+ if (options.tocData.currentLevel== 0) {
+ out.s += '\n';
+ options.tocData.levelOffset = level - 1;
+ }
+ level -= options.tocData.levelOffset;
+
+ if (level > options.tocData.currentLevel) {
+ while (level > options.tocData.currentLevel) {
+ out.s += '\n- \n';
+ options.tocData.currentLevel++;
+ }
+ } else if (level < options.tocData.currentLevel) {
+ out.s += '
\n';
+ while (level < options.tocData.currentLevel) {
+ out.s += '
\n\n';
+ options.tocData.currentLevel--;
+ }
+ out.s += '\n';
+ } else {
+ out.s += ' \n\n';
+ }
+
+ out.s += '';
+ if (text) escape_html(out, text.s, false);
+ out.s += '\n';
+ }
+
+ //toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
+ function cb_toc_link(out, link, title, content, options) {
+ if (content && content.s)
+ out.s += content.s;
+ return 1;
+ }
+
+
+ // reset_toc(struct buf *ob, void *opaque)
+ function cb_reset_toc(out, options) {
+ options.tocData = {
+ headerCount: 0,
+ currentLevel: 0,
+ levelOffset: 0
+ };
+ }
+
+ //toc_finalize(struct buf *ob, void *opaque)
+ function cb_toc_finalize(out, options) {
+ var hasToc = false;
+ while (options.tocData.currentLevel > 0) {
+ out.s += ' \n\n';
+ options.tocData.currentLevel--;
+ hasToc = true;
+ }
+ if (hasToc) {
+ out.s += '\n';
+ }
+ cb_reset_toc(out, options);
+ }
+
+ /* header and footer */
+ // doc_header(Buffer out}, context);
+ // doc_header: null,
+ // doc_footer(Buffer out, context);
+ // doc_footer: null
+
+
+ /* char_emphasis • single and double emphasis parsing */
+ //Buffer, md, str, int
+ function char_emphasis(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var size = data.length;
+ var c = data[0];
+ var ret;
+
+ if (size > 2 && data[1] != c) {
+ /* whitespace cannot follow an opening emphasis;
+ * strikethrough only takes two characters '~~' */
+ if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(out, md, data, c)) == 0)
+ return 0;
+
+ return ret + 1;
+ }
+
+ if (data.length > 3 && data[1] == c && data[2] != c) {
+ if (_isspace(data[2]) || (ret = parse_emph2(out, md, data, c)) == 0)
+ return 0;
+
+ return ret + 2;
+ }
+
+ if (data.length > 4 && data[1] == c && data[2] == c && data[3] != c) {
+ if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(out, md, data, c)) == 0)
+ return 0;
+
+ return ret + 3;
+ }
+
+ return 0;
+ }
+
+ /* char_codespan - '`' parsing a code span (assuming codespan != 0) */
+ function char_codespan(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var end, nb = 0, i, f_begin, f_end;
+
+ /* counting the number of backticks in the delimiter */
+ while (nb < data.length && data[nb] == '`')
+ nb++;
+
+ /* finding the next delimiter */
+ i = 0;
+ for (end = nb; end < data.length && i < nb; end++) {
+ if (data[end] == '`') i++;
+ else i = 0;
+ }
+
+ if (i < nb && end >= data.length)
+ return 0; /* no matching delimiter */
+
+ /* trimming outside whitespaces */
+ f_begin = nb;
+ while (f_begin < end && data[f_begin] == ' ') f_begin++;
+
+ f_end = end - nb;
+ while (f_end > nb && data[f_end-1] == ' ') f_end--;
+
+ /* real code span */
+ if (f_begin < f_end) {
+ var work = new Buffer(data.slice(f_begin, f_end));
+ if (!md.callbacks.codespan(out, work, md.context))
+ end = 0;
+ } else {
+ if (!md.callbacks.codespan(out, null, md.context))
+ end = 0;
+ }
+
+ return end;
+ }
+
+ /* char_linebreak - '\n' preceded by two spaces (assuming linebreak != 0) */
+ function char_linebreak(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ if (offset < 2 || data_[offset-1] != ' ' || data_[offset-2] != ' ')
+ return 0;
+
+ /* removing the last space from ob and rendering */
+ var len = out.s.length;
+ while (len && out.s[len - 1] == ' ') len--;
+ out.s = out.s.slice(0, len);
+
+ return md.callbacks.linebreak(out, md.context) ? 1 : 0;
+ }
+
+ /* char_link - '[': parsing a link or an image */
+ function char_link(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var is_img = (offset && data_[offset - 1] == '!'), level;
+ var i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
+ //4 bufs
+ var content = null;
+ var link = null;
+ var title = null;
+ var u_link = null;
+ var org_work_size = md.spanStack.length;
+ var text_has_nl = 0, ret = 0;
+ var in_title = 0, qtype = 0;
+
+ function cleanup() {
+ md.spanStack.length = org_work_size;
+ return ret ? i : 0;
+ }
+
+ /* checking whether the correct renderer exists */
+ if ((is_img && !md.callbacks.image) || (!is_img && !md.callbacks.link))
+ return cleanup();
+ /* looking for the matching closing bracket */
+ for (level = 1; i < data.length; i++) {
+ if (data[i] == '\n') text_has_nl = 1;
+ else if (data[i - 1] == '\\') continue;
+ else if (data[i] == '[') level++;
+ else if (data[i] == ']') {
+ level--;
+ if (level <= 0) break;
+ }
+ }
+
+ if (i >= data.length) return cleanup();
+
+ txt_e = i;
+ i++;
+
+ /* skip any amount of whitespace or newline */
+ /* (this is much more laxist than original markdown syntax) */
+ while (i < data.length && _isspace(data[i])) i++;
+
+ /* inline style link */
+ if (i < data.length && data[i] == '(') {
+ /* skipping initial whitespace */
+ i++;
+
+ while (i < data.length && _isspace(data[i])) i++;
+
+ link_b = i;
+
+ /* looking for link end: ' " ) */
+ while (i < data.length) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == ')') break;
+ else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
+ else i++;
+ }
+
+ if (i >= data.length) return cleanup();
+ link_e = i;
+
+ /* looking for title end if present */
+ if (data[i] == '\'' || data[i] == '"') {
+ qtype = data[i];
+ in_title = 1;
+ i++;
+ title_b = i;
+
+ while (i < data.length) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == qtype) {in_title = 0; i++;}
+ else if ((data[i] == ')') && !in_title) break;
+ else i++;
+ }
+
+ if (i >= data.length) return cleanup();
+
+ /* skipping whitespaces after title */
+ title_e = i - 1;
+ while (title_e > title_b && _isspace(data[title_e])) title_e--;
+
+ /* checking for closing quote presence */
+ if (data[title_e] != '\'' && data[title_e] != '"') {
+ title_b = title_e = 0;
+ link_e = i;
+ }
+ }
+
+ /* remove whitespace at the end of the link */
+ while (link_e > link_b && _isspace(data[link_e - 1])) link_e--;
+
+ /* remove optional angle brackets around the link */
+ if (data[link_b] == '<') link_b++;
+ if (data[link_e - 1] == '>') link_e--;
+
+ /* building escaped link and title */
+ if (link_e > link_b) {
+ link = new Buffer();
+ md.spanStack.push(link);
+ link.s += data.slice(link_b, link_e);
+ }
+
+ if (title_e > title_b) {
+ title = new Buffer();
+ md.spanStack.push(title);
+ title.s += data.slice(title_b, title_e);
+ }
+
+ i++;
+ }
+
+ /* reference style link */
+ else if (i < data.length && data[i] == '[') {
+ var id = new Buffer();
+ var lr = null;
+
+ /* looking for the id */
+ i++;
+ link_b = i;
+ while (i < data.length && data[i] != ']') i++;
+ if (i >= data.length) return cleanup();
+ link_e = i;
+
+ /* finding the link_ref */
+ if (link_b == link_e) {
+ if (text_has_nl) {
+ var b = new Buffer();
+ md.spanStack.push(b);
+ var j;
+
+ for (j = 1; j < txt_e; j++) {
+ if (data[j] != '\n')
+ b.s += data[j];
+ else if (data[j - 1] != ' ')
+ b.s += ' ';
+ }
+
+ id.s = b.s;
+ } else {
+ id.s = data.slice(1);
+ }
+ } else {
+ id.s = data.slice(link_b, link_e);
+ }
+
+ //TODO
+ lr = md.refs[id.s];
+ if (!lr) return cleanup();
+
+ /* keeping link and title from link_ref */
+ link = lr.link;
+ title = lr.title;
+ i++;
+ }
+
+ /* shortcut reference style link */
+ else {
+ var id = new Buffer();
+ var lr = null;
+
+ /* crafting the id */
+ if (text_has_nl) {
+ var b = new Buffer();
+ md.spanStack.push(b);
+
+ var j;
+ for (j = 1; j < txt_e; j++) {
+ if (data[j] != '\n') b.s += data[j];
+ else if (data[j - 1] != ' ') b.s += ' ';
+ }
+
+ id.s = b.s;
+ } else {
+ id.s = data.slice(1, txt_e);
+ }
+
+ /* finding the link_ref */
+ lr = md.refs[id.s];
+ if (!lr) return cleanup();
+
+ /* keeping link and title from link_ref */
+ link = lr.link;
+ title = lr.title;
+
+ /* rewinding the whitespace */
+ i = txt_e + 1;
+ }
+
+ /* building content: img alt is escaped, link content is parsed */
+ if (txt_e > 1) {
+ content = new Buffer();
+ md.spanStack.push(content);
+ if (is_img) {
+ content.s += data.slice(1, txt_e);
+ } else {
+ /* disable autolinking when parsing inline the
+ * content of a link */
+ md.inLinkBody = 1;
+ parse_inline(content, md, data.slice(1, txt_e));
+ md.inLinkBody = 0;
+ }
+ }
+
+ if (link) {
+ u_link = new Buffer();
+ md.spanStack.push(u_link);
+ unscape_text(u_link, link);
+ } else {
+ return cleanup();
+ }
+
+ /* calling the relevant rendering function */
+ if (is_img) {
+ if (out.s.length && out.s[out.s.length - 1] == '!')
+ out.s = out.s.slice(0, -1);
+
+ ret = md.callbacks.image(out, u_link, title, content, md.context);
+ } else {
+ ret = md.callbacks.link(out, u_link, title, content, md.context);
+ }
+
+ /* cleanup */
+ // cleanup:
+ // rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size;
+ // return ret ? i : 0;
+ return cleanup();
+ }
+
+
+ /* char_langle_tag - '<' when tags or autolinks are allowed */
+ function char_langle_tag(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var altype = {p:MKDA_NOT_AUTOLINK};
+ var end = tag_length(data, altype);
+ var work = new Buffer(data.slice(0, end));
+ var ret = 0;
+
+ if (end > 2) {
+ if (md.callbacks.autolink && altype.p != MKDA_NOT_AUTOLINK) {
+ var u_link = new Buffer();
+ md.spanStack.push(u_link);
+ work.s = data.substr(1 , end - 2);
+ unscape_text(u_link, work);
+ ret = md.callbacks.autolink(out, u_link, altype.p, md.context);
+ md.spanStack.pop();
+ }
+ else if (md.callbacks.raw_html_tag)
+ ret = md.callbacks.raw_html_tag(out, work, md.context);
+ }
+
+ if (!ret) return 0;
+ else return end;
+ }
+
+
+ /* char_escape - '\\' backslash escape */
+ function char_escape(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var escape_chars = "\\`*_{}[]()#+-.!:|&<>/^~";
+ var work = new Buffer();
+
+ if (data.length > 1) {
+ if (escape_chars.indexOf(data[1]) == -1) return 0;
+
+ if (md.callbacks.normal_text) {
+ work.s = data[1];
+ md.callbacks.normal_text(out, work, md.context);
+ }
+ else out.s += data[1];
+ } else if (data.length == 1) {
+ out.s += data[0];
+ }
+
+ return 2;
+ }
+
+
+
+ /* char_entity - '&' escaped when it doesn't belong to an entity */
+ //-/* valid entities are assumed to be anything matching ?[A-Za-z0-9]+; */
+ function char_entity(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var end = 1, content_start, content_end;
+ var numeric = false, hex = false, entity_base, entity_val;
+ var work = new Buffer();
+
+ if (end < data.length && data[end] === '#') {
+ numeric = true;
+ end++;
+ }
+
+ if (end < data.length && numeric && data[end].toLowerCase() === 'x') {
+ hex = true;
+ end++;
+ }
+
+ content_start = end;
+ while (end < data.length) {
+ var c = data[end];
+ if (hex) {
+ if (!isxdigit(c)) break;
+ } else if (numeric) {
+ if (!isdigit(c)) break;
+ } else if (!isalnum(c)) {
+ break;
+ }
+ end++;
+ }
+
+
+ if (end > content_start && end < data.length && data[end] === ';') {
+ /* well formed entity */
+ end++;
+ } else {
+ return 0; /* not an entity */
+ }
+
+ /* way too long to be a valid numeric entity */
+ if (numeric && content_end - content_start > MAX_NUM_ENTITY_LEN) {
+ return 0;
+ }
+
+ /* Validate the entity's contents */
+ if (numeric) {
+ if (hex) entity_base = 16;
+ else entity_base = 10;
+ entity_val = parseInt(data.slice(content_start), entity_base);
+ if (!is_valid_numeric_entity(entity_val)) return 0;
+ } else {
+ if (VALID_ENTITIES.indexOf(data.slice(0, end)) === -1) return 0;
+ }
+
+ if (md.callbacks.entity) {
+ work.s = data.slice(0, end);
+ md.callbacks.entity(out, work, md.context);
+ }
+ else out.s += data.slice(0, end);
+
+ return end;
+ }
+
+ function char_autolink_url(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var link = null;
+ var link_len, rewind = {p: null};
+
+ if (!md.callbacks.autolink || md.inLinkBody) return 0;
+
+ link = new Buffer();
+ md.spanStack.push(link);
+
+ if ((link_len = sd_autolink__url(rewind, link, data_, offset, data.length, 0)) > 0) {
+ if (rewind.p > 0) out.truncate(out.s.length - rewind.p);
+ md.callbacks.autolink(out, link, MKDA_NORMAL, md.context);
+ }
+
+ md.spanStack.pop();
+ return link_len;
+ }
+
+
+ function char_autolink_email(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var link = null;
+ var link_len, rewind = {p: null};
+
+ if (!md.callbacks.autolink || md.inLinkBody) return 0;
+
+ link = new Buffer();
+ md.spanStack.push(link);
+
+ if ((link_len = sd_autolink__email(rewind, link, data_, offset, data.length, 0)) > 0) {
+ if (rewind.p > 0) out.truncate(out.s.length - rewind.p);
+ md.callbacks.autolink(out, link, MKDA_EMAIL, md.context);
+ }
+
+ md.spanStack.pop();
+ return link_len;
+ }
+
+
+ function char_autolink_www(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var link = null, link_url = null, link_text = null;
+ var link_len, rewind = {p: null};
+
+ if (!md.callbacks.link || md.inLinkBody) return 0;
+
+ link = new Buffer();
+ md.spanStack.push(link);
+
+ if ((link_len = sd_autolink__www(rewind, link, data_, offset, data.length, 0)) > 0) {
+ link_url = new Buffer();
+ md.spanStack.push(link_url);
+ link_url.s += 'http://';
+ link_url.s += link.s;
+
+ if (rewind.p > 0) out.truncate(out.s.length - rewind.p);
+
+ if (md.callbacks.normal_text) {
+ link_text = new Buffer();
+ md.spanStack.push(link_text);
+ md.callbacks.normal_text(link_text, link, md.context);
+ md.callbacks.link(out, link_url, null, link_text, md.context);
+ md.spanStack.pop();
+ } else {
+ md.callbacks.link(out, link_url, null, link, md.context);
+ }
+ md.spanStack.pop();
+ }
+
+ md.spanStack.pop();
+ return link_len;
+ }
+
+ function char_autolink_subreddit_or_username(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var link = null;
+ var link_len, rewind = {p: null};
+
+ if (!md.callbacks.autolink || md.inLinkBody) return 0;
+
+ link = new Buffer();
+ md.spanStack.push(link);
+ if ((link_len = sd_autolink__subreddit(rewind, link, data_, offset, data.length)) > 0) {
+ //don't slice because the rewind pointer will always be 0
+ if (rewind.p > 0) out.truncate(out.s.length - rewind.p);
+ md.callbacks.autolink(out, link, MKDA_NORMAL, md.context);
+ } else if ((link_len = sd_autolink__username(rewind, link, data_, offset, data.length)) > 0) {
+ //don't slice because the rewind pointer will always be 0
+ if (rewind.p > 0) out.truncate(out.s.length - rewind.p);
+ md.callbacks.autolink(out, link, MKDA_NORMAL, md.context);
+ }
+ md.spanStack.pop();
+
+ return link_len;
+ }
+
+ function char_superscript(out, md, data_, offset) {
+ var data = data_.slice(offset);
+ var size = data.length;
+ var sup_start, sup_len;
+ var sup = null;
+
+ if (!md.callbacks.superscript) return 0;
+
+ if (size < 2) return 0;
+
+ if (data[1] == '(') {
+ sup_start = sup_len = 2;
+
+ while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\') sup_len++;
+
+ if (sup_len == size) return 0;
+ } else {
+ sup_start = sup_len = 1;
+
+ while (sup_len < size && !_isspace(data[sup_len])) sup_len++;
+ }
+
+ if (sup_len - sup_start == 0) return (sup_start == 2) ? 3 : 0;
+
+ sup = new Buffer();
+ md.spanStack.push(sup);
+ parse_inline(sup, md, data.slice(sup_start, sup_len));
+ md.callbacks.superscript(out, sup, md.context);
+ md.spanStack.pop();
+
+ return (sup_start == 2) ? sup_len + 1 : sup_len;
+ }
+
+
+ var markdown_char_ptrs = [
+ null,
+ char_emphasis,
+ char_codespan,
+ char_linebreak,
+ char_link,
+ char_langle_tag,
+ char_escape,
+ char_entity,
+ char_autolink_url,
+ char_autolink_email,
+ char_autolink_www,
+ char_autolink_subreddit_or_username,
+ char_superscript
+ ];
+
+ var MKD_LIST_ORDERED = 1;
+ var MKD_LI_BLOCK = 2; /* containing block data */
+ var MKD_LI_END = 8; /* internal list flag */
+
+ var enumCounter = 0;
+ var MD_CHAR_NONE = enumCounter++;
+ var MD_CHAR_EMPHASIS = enumCounter++;
+ var MD_CHAR_CODESPAN = enumCounter++;
+ var MD_CHAR_LINEBREAK = enumCounter++;
+ var MD_CHAR_LINK = enumCounter++;
+ var MD_CHAR_LANGLE = enumCounter++;
+ var MD_CHAR_ESCAPE = enumCounter++;
+ var MD_CHAR_ENTITITY = enumCounter++;
+ var MD_CHAR_AUTOLINK_URL = enumCounter++;
+ var MD_CHAR_AUTOLINK_EMAIL = enumCounter++;
+ var MD_CHAR_AUTOLINK_WWW = enumCounter++;
+ var MD_CHAR_AUTOLINK_SUBREDDIT_OR_USERNAME = enumCounter++;
+ var MD_CHAR_SUPERSCRIPT = enumCounter++;
+
+ var SD_AUTOLINK_SHORT_DOMAINS = (1 << 0);
+
+ enumCounter = 0;
+ var MKDA_NOT_AUTOLINK = enumCounter++; /* used internally when it is not an autolink*/
+ var MKDA_NORMAL = enumCounter++; /* normal http/http/ftp/mailto/etc link */
+ var MKDA_EMAIL = enumCounter++; /* e-mail link without explit mailto: */
+
+ var MKDEXT_NO_INTRA_EMPHASIS = (1 << 0);
+ var MKDEXT_TABLES = (1 << 1);
+ var MKDEXT_FENCED_CODE = (1 << 2);
+ var MKDEXT_AUTOLINK = (1 << 3);
+ var MKDEXT_STRIKETHROUGH = (1 << 4);
+ // var MKDEXT_LAX_HTML_BLOCKS = (1 << 5);
+ var MKDEXT_SPACE_HEADERS = (1 << 6);
+ var MKDEXT_SUPERSCRIPT = (1 << 7);
+ var MKDEXT_LAX_SPACING = (1 << 8)
+ var MKDEXT_NO_EMAIL_AUTOLINK = (1 << 9)
+
+ var HTML_SKIP_HTML = (1 << 0);
+ var HTML_SKIP_STYLE = (1 << 1);
+ var HTML_SKIP_IMAGES = (1 << 2);
+ var HTML_SKIP_LINKS = (1 << 3);
+ var HTML_EXPAND_TABS = (1 << 4);
+ var HTML_SAFELINK = (1 << 5);
+ var HTML_TOC = (1 << 6);
+ var HTML_HARD_WRAP = (1 << 7);
+ var HTML_USE_XHTML = (1 << 8);
+ var HTML_ESCAPE = (1 << 9);
+ var HTML_ALLOW_ELEMENT_WHITELIST = (1 << 10);
+
+ var MKD_TABLE_ALIGN_L = 1;
+ var MKD_TABLE_ALIGN_R = 2;
+ var MKD_TABLE_ALIGN_CENTER = 3;
+ var MKD_TABLE_ALIGNMASK = 3;
+ var MKD_TABLE_HEADER = 4
+
+ var HTML_TAG_NONE = 0;
+ var HTML_TAG_OPEN = 1;
+ var HTML_TAG_CLOSE = 2;
+
+
+ /**
+ * A string buffer wrapper because JavaScript doesn't have mutable strings.
+ * @constructor
+ * @param {string=} str Optional string to initialize the Buffer with.
+ */
+ function Buffer(str) {
+ this.s = str || "";
+ };
+
+ /**
+ */
+ Buffer.prototype.truncate = function(size) {
+ if (this.s.length < size) throw new RangeError("Buffer smaller than desired size");
+ if (size < 0) throw new RangeError("Size argument is negative");
+ this.s = this.s.slice(0, size);
+ }
+
+
+ /**
+ * A Markdown parser object.
+ * @constructor
+ */
+ function Markdown() {
+
+ //Becase javascript strings are immutable they must be wrapped with Buffer()
+ this.spanStack = [];
+ this.blockStack = [];
+ this.extensions = MKDEXT_NO_INTRA_EMPHASIS | MKDEXT_SUPERSCRIPT | MKDEXT_AUTOLINK | MKDEXT_STRIKETHROUGH | MKDEXT_TABLES | MKDEXT_TABLES;
+ var renderer = getRedditRenderer();
+ this.context = renderer.context;
+ this.callbacks = renderer.callbacks;
+ this.inLinkBody = 0;
+ this.activeChars = {};
+ this.refs = {};
+ this.nestingLimit = 16;
+ this.maxTableCols = 64;
+ };
+
+
+ /* is_empty - returns the line length when it is empty, 0 otherwise */
+ function is_empty(data) {
+ var i;
+ for (i = 0; i < data.length && data[i] != '\n'; i++)
+ if (data[i] != ' ') return 0;
+
+ return i + 1;
+ }
+
+
+ /* is_hrule - returns whether a line is a horizontal rule */
+ function is_hrule(data) {
+ var i = 0, n = 0;
+ var c;
+
+ /* skipping initial spaces */
+ if (data.length < 3) return 0;
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ if (i + 2 >= data.length
+ || (data[i] != '*' && data[i] != '-' && data[i] != '_'))
+ return 0;
+ c = data[i];
+
+ /* the whole line must be the char or whitespace */
+ while (i < data.length && data[i] != '\n') {
+ if (data[i] == c) n++;
+ else if (data[i] != ' ')
+ return 0;
+
+ i++;
+ }
+
+ return n >= 3;
+ }
+
+
+ /* check if a line begins with a code fence; return the
+ * width if it is */
+ function prefix_codefence(data) {
+ var i = 0, n = 0;
+ var c;
+
+ /* skipping initial spaces */
+ if (data.length < 3) return 0;
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
+
+ /* looking at the hrule uint8_t */
+ if (i + 2 >= data.length || !(data[i] == '~' || data[i] == '`'))
+ return 0;
+
+ c = data[i];
+
+ /* the whole line must be the uint8_t or whitespace */
+ while (i < data.length && data[i] == c) {
+ n++; i++;
+ }
+
+ if (n < 3) return 0;
+
+ return i;
+ }
+
+ /* check if a line is a code fence; return its size if it */
+ function is_codefence(data, syntax) {
+ var i = 0, syn_len = 0;
+ i = prefix_codefence(data);
+ if (i == 0) return 0;
+
+
+ while (i < data.length && data[i] == ' ') i++;
+
+ var syn_start;
+ //syn_start = data + i;
+ syn_start = i;
+
+ if (i < data.length && data[i] == '{') {
+ i++; syn_start++;
+
+ while (i < data.length && data[i] != '}' && data[i] != '\n') {
+ syn_len++; i++;
+ }
+
+ if (i == data.length || data[i] != '}')
+ return 0;
+
+ /* strip all whitespace at the beginning and the end
+ * of the {} block */
+ /*remember not to remove the +0, it helps me keep syncronised with snudown*/
+ while (syn_len > 0 && _isspace(data[syn_start+0])) {
+ syn_start++; syn_len--;
+ }
+
+ // while (syn_len > 0 && _isspace(syn_start[syn_len - 1]))
+ while (syn_len > 0 && _isspace(data[syn_start+syn_len - 1]))
+ syn_len--;
+
+ i++;
+ } else {
+ while (i < data.length && !_isspace(data[i])) {
+ syn_len++; i++;
+ }
+ }
+
+ if (syntax) syntax.s = data.substr(syn_start, syn_len);
+ // syntax->size = syn;
+
+ while (i < data.length && data[i] != '\n') {
+ if (!_isspace(data[i])) return 0;
+
+ i++;
+ }
+
+ return i + 1;
+ }
+
+ /* find_emph_char - looks for the next emph uint8_t, skipping other constructs */
+ function find_emph_char(data, c) {
+ var i = 1;
+ while (i < data.length) {
+ while (i < data.length && data[i] != c && data[i] != '`' && data[i] != '[')
+ i++;
+
+ if (i == data.length) return 0;
+
+ if (data[i] == c)
+ return i;
+
+ /* not counting escaped chars */
+ if (i && data[i - 1] == '\\') {
+ i++; continue;
+ }
+
+ if (data[i] == '`') {
+ var span_nb = 0, bt;
+ var tmp_i = 0;
+
+ /* counting the number of opening backticks */
+ while (i < data.length && data[i] == '`') {
+ i++; span_nb++;
+ }
+
+ if (i >= data.length) return 0;
+
+ /* finding the matching closing sequence */
+ bt = 0;
+ while (i < data.length && bt < span_nb) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ if (data[i] == '`') bt++;
+ else bt = 0;
+ i++;
+ }
+
+ if (i >= data.length) return tmp_i;
+ }
+ /* skipping a link */
+ else if (data[i] == '[') {
+ var tmp_i = 0;
+ var cc;
+
+ i++;
+ while (i < data.length && data[i] != ']') {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ i++;
+ while (i < data.length && (data[i] == ' ' || data[i] == '\n'))
+ i++;
+
+ if (i >= data.length) return tmp_i;
+
+ switch (data[i]) {
+ case '[':
+ cc = ']'; break;
+
+ case '(':
+ cc = ')'; break;
+
+ default:
+ if (tmp_i)
+ return tmp_i;
+ else
+ continue;
+ }
+
+ i++;
+ while (i < data.length && data[i] != cc) {
+ if (!tmp_i && data[i] == c) tmp_i = i;
+ i++;
+ }
+
+ if (i >= data.length) return tmp_i;
+ i++;
+ }
+ }
+
+ return 0;
+ }
+
+ /* parse_emph1 - parsing single emphase */
+ /* closed by a symbol not preceded by whitespace and not followed by symbol */
+ function parse_emph1(out, md, data_, c) {
+ var data = data_.slice(1);
+ var i = 0, len;
+ var r;
+
+ if (!md.callbacks.emphasis) return 0;
+
+ /* skipping one symbol if coming from emph3 */
+ if (data.length > 1 && data[0] == c && data[1] == c) i = 1;
+
+ while (i < data.length) {
+ len = find_emph_char(data.slice(i), c);
+ if (!len) return 0;
+ i += len;
+ if (i >= data.length) return 0;
+
+ if (data[i] == c && !_isspace(data[i - 1])) {
+ if ((md.extensions & MKDEXT_NO_INTRA_EMPHASIS) && (c == '_')) {
+ if (!(i + 1 == data.length || _isspace(data[i + 1]) || ispunct(data[i + 1])))
+ continue;
+ }
+
+ var work = new Buffer();
+ md.spanStack.push(work);
+ parse_inline(work, md, data.slice(0, i));
+ r = md.callbacks.emphasis(out, work, md.context);
+ md.spanStack.pop();
+ return r ? i + 1 : 0;
+ }
+ }
+
+ return 0;
+ }
+
+ /* parse_emph2 - parsing single emphase */
+ function parse_emph2(out, md, data_, c) {
+ var data = data_.slice(2);
+ var i = 0, len;
+ var r;
+
+ var render_method = (c == '~') ? md.callbacks.strikethrough : md.callbacks.double_emphasis;
+
+ if (!render_method) return 0;
+
+ while (i < data.length) {
+ len = find_emph_char(data.slice(i), c);
+ if (!len) return 0;
+ i += len;
+
+ if (i + 1 < data.length && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
+ var work = new Buffer();
+ md.spanStack.push(work);
+ parse_inline(work, md, data.slice(0, i));
+ r = render_method(out, work, md.context);
+ md.spanStack.pop();
+ return r ? i + 2 : 0;
+ }
+ i++;
+ }
+ return 0;
+ }
+
+ /* parse_emph3 • parsing single emphase */
+ /* finds the first closing tag, and delegates to the other emph */
+ function parse_emph3(out, md, data_, c) {
+ var data = data_.slice(3);
+ var i = 0, len;
+ var r;
+
+ while (i < data.length) {
+ len = find_emph_char(data.slice(i), c);
+ if (!len) return 0;
+ i += len;
+
+ /* skip whitespace preceded symbols */
+ if (data[i] != c || _isspace(data[i - 1])) continue;
+
+ if (i + 2 < data.length && data[i + 1] == c && data[i + 2] == c && md.callbacks.triple_emphasis) {
+ /* triple symbol found */
+ var work = new Buffer();
+ md.spanStack.push(work);
+ parse_inline(work, md, data.slice(0, i));
+ r = md.callbacks.triple_emphasis(out, work, md.context);
+ md.spanStack.pop();
+ return r ? i + 3 : 0;
+
+ } else if (i + 1 < data.length && data[i + 1] == c) {
+ /* double symbol found, handing over to emph1 */
+ len = parse_emph1(out, md, data_, c);
+ if (!len) return 0;
+ else return len - 2;
+
+ } else {
+ /* single symbol found, handing over to emph2 */
+ len = parse_emph2(out, md, data_, c);
+ if (!len) return 0;
+ else return len - 1;
+ }
+ }
+ return 0;
+ }
+
+ function is_atxheader(md, data) {
+ if (data[0] != '#') return false;
+
+ if (md.extensions & MKDEXT_SPACE_HEADERS) {
+ var level = 0;
+
+ while (level < data.length && level < 6 && data[level] == '#')
+ level++;
+
+ if (level < data.length && data[level] != ' ')
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /* is_headerline . returns whether the line is a setext-style hdr underline */
+ function is_headerline(data) {
+ var i = 0;
+ var size = data.length;
+
+ /* test of level 1 header */
+ if (data[i] == '=') {
+ for (i = 1; i < size && data[i] == '='; i++) {}
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 1 : 0; }
+
+ /* test of level 2 header */
+ if (data[i] == '-') {
+ for (i = 1; i < size && data[i] == '-'; i++) {}
+ while (i < size && data[i] == ' ') i++;
+ return (i >= size || data[i] == '\n') ? 2 : 0; }
+
+ return 0;
+ }
+
+ function is_next_headerline(data) {
+ var size = data.length;
+ var i = 0;
+
+ while (i < size && data[i] != '\n') i++;
+
+ if (++i >= size) return 0;
+
+ return is_headerline(data.slice(i));
+ }
+
+ /* prefix_quote - returns blockquote prefix length */
+ function prefix_quote(data) {
+ var i = 0;
+ var size = data.length;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i < size && data[i] == '>') {
+ if (i + 1 < size && data[i + 1] == ' ')
+ return i + 2;
+
+ return i + 1;
+ }
+
+ return 0;
+ }
+
+ /* prefix_code • returns prefix length for block code*/
+ function prefix_code(data) {
+ if (data.length > 3 && data[0] == ' ' && data[1] == ' '
+ && data[2] == ' ' && data[3] == ' ') return 4;
+
+ return 0;
+ }
+
+ /* prefix_oli - returns ordered list item prefix */
+ function prefix_oli(data) {
+ var size = data.length;
+ var i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i >= size || data[i] < '0' || data[i] > '9') return 0;
+
+ while (i < size && data[i] >= '0' && data[i] <= '9') i++;
+
+ if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') return 0;
+
+ if (is_next_headerline(data.slice(i))) return 0;
+
+ return i + 2;
+ }
+
+ /* prefix_uli - returns ordered list item prefix */
+ function prefix_uli(data) {
+ var size = data.length;
+ var i = 0;
+
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+
+ if (i + 1 >= size ||
+ (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
+ data[i + 1] != ' ')
+ return 0;
+
+ if (is_next_headerline(data.slice(i))) return 0;
+
+ return i + 2;
+ }
+
+ /* is_mail_autolink - looks for the address part of a mail autolink and '>' */
+ /* this is less strict than the original markdown e-mail address matching */
+ function is_mail_autolink(data) {
+ var i = 0, nb = 0;
+
+ /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */
+ for (i = 0; i < data.length; ++i) {
+ if (isalnum(data[i]))
+ continue;
+
+ switch (data[i]) {
+ case '@':
+ nb++;
+
+ case '-':
+ case '.':
+ case '_':
+ break;
+
+ case '>':
+ return (nb == 1) ? i + 1 : 0;
+
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+ }
+
+ /* tag_length - returns the length of the given tag, or 0 is it's not valid */
+ function tag_length(data, autolink) {
+ var i, j;
+
+ /* a valid tag can't be shorter than 3 chars */
+ if (data.length < 3) return 0;
+
+ /* begins with a '<' optionally followed by '/', followed by letter or number */
+ if (data[0] != '<') return 0;
+ i = (data[1] == '/') ? 2 : 1;
+
+ if (!isalnum(data[i])) return 0;
+
+ /* scheme test */
+ autolink.p = MKDA_NOT_AUTOLINK;
+
+ /* try to find the beginning of an URI */
+ while (i < data.length && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) i++;
+
+ if (i > 1 && data[i] == '@') {
+ if ((j = is_mail_autolink(data.slice(i))) != 0) {
+ autolink.p = MKDA_EMAIL;
+ return i + j;
+ }
+ }
+
+ if (i > 2 && data[i] == ':') {
+ autolink.p = MKDA_NORMAL;
+ i++;
+ }
+
+ /* completing autolink test: no whitespace or ' or " */
+ if (i >= data.length) autolink.p = MKDA_NOT_AUTOLINK;
+ else if (autolink.p) {
+ j = i;
+
+ while (i < data.length) {
+ if (data[i] == '\\') i += 2;
+ else if (data[i] == '>' || data[i] == '\'' ||
+ data[i] == '"' || data[i] == ' ' || data[i] == '\n')
+ break;
+ else i++;
+ }
+
+ if (i >= data.length) return 0;
+ if (i > j && data[i] == '>') return i + 1;
+ /* one of the forbidden chars has been found */
+ autolink.p = MKDA_NOT_AUTOLINK;
+ }
+
+ /* looking for sometinhg looking like a tag end */
+ while (i < data.length && data[i] != '>') i++;
+ if (i >= data.length) return 0;
+ return i + 1;
+ }
+
+ // parse_inline - parses inline markdown elements
+ //Buffer, md, String
+ function parse_inline(out, md, data) {
+ var i = 0, end = 0;
+ var action = 0;
+ var work = new Buffer();
+
+ if (md.spanStack.length + md.blockStack.length > md.nestingLimit)
+ return;
+
+ while (i < data.length) {
+ /* copying inactive chars into the output */
+ while (end < data.length && !(action = md.activeChars[data[end]])) {
+ end++;
+ }
+
+ if (md.callbacks.normal_text) {
+ work.s = data.slice(i, end);
+ md.callbacks.normal_text(out, work, md.context);
+ }
+ else
+ out.s += data.slice(i, end);
+
+ if (end >= data.length) break;
+ i = end;
+
+ end = markdown_char_ptrs[action](out, md, data, i);
+ if (!end) /* no action from the callback */
+ end = i + 1;
+ else {
+ i += end;
+ end = i;
+ }
+ }
+ }
+
+ /* parse_atxheader - parsing of atx-style headers */
+ function parse_atxheader(out, md, data) {
+ var level = 0;
+ var i, end, skip;
+
+ while (level < data.length && level < 6 && data[level] == '#') level++;
+
+ for (i = level; i < data.length && data[i] == ' '; i++) {}
+
+ for (end = i; end < data.length && data[end] != '\n'; end++) {}
+ skip = end;
+
+ while (end && data[end - 1] == '#') end--;
+
+ while (end && data[end - 1] == ' ') end--;
+
+ if (end > i) {
+ var work = new Buffer();
+ md.spanStack.push(work);
+
+ parse_inline(work, md, data.slice(i, end));
+
+ if (md.callbacks.header)
+ md.callbacks.header(out, work, level, md.context);
+
+ md.spanStack.pop();
+ }
+
+ return skip;
+ }
+
+ /* htmlblock_end - checking end of HTML block : [ \t]*\n[ \t*]\n */
+ /* returns the length on match, 0 otherwise */
+ // htmlblock_end(const char *tag, size_t tag_len, struct sd_markdown *rndr, uint8_t *data, size_t size)
+ function htmlblock_end(tag, md, data) {
+ var i, w;
+
+ /* checking if tag is a match */
+ //tag should already be lowercase
+ if (tag.length + 3 >= data.length ||
+ data.slice(2).toLowerCase() != tag ||
+ data[tag.length + 2] != '>')
+ return 0;
+
+ /* checking white lines */
+ i = tag.length + 3;
+ w = 0;
+ if (i < data.length && (w = is_empty(data.slice(i))) == 0)
+ return 0; /* non-blank after tag */
+ i += w;
+ w = 0;
+
+ if (i < data.length) w = is_empty(data.slice(i));
+
+ return i + w;
+ }
+
+ /* parse_htmlblock - parsing of inline HTML block */
+ //TODO
+ function parse_htmlblock(out, md, data, do_render) {
+ var i, j = 0;
+ var curtag = null;
+ var found;
+ var work = new Buffer(data);
+
+ /* identification of the opening tag */
+ if (data.length < 2 || data[0] != '<') return 0;
+
+ i = 1;
+ while (i < data.length && data[i] != '>' && data[i] != ' ') i++;
+
+ if (i < data.length) curtag = find_block_tag(data.slice(1));
+
+ /* handling of special cases */
+ if (!curtag) {
+
+ /* HTML comment, laxist form */
+ if (data.length > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {
+ i = 5;
+
+ while (i < data.length && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) i++;
+
+ i++;
+
+ if (i < size)
+ j = is_empty(data.slice(i));
+
+ if (j) {
+ //TODO: HANDLE WORK!!!
+ // work.size = i + j;
+ work.s = data.slice(0, i + j);
+ if (do_render && md.callbacks.blockhtml)
+ md.callbacks.blockhtml(out, work, md.context);
+ return work.s.length;
+ }
+ }
+
+ /* HR, which is the only self-closing block tag considered */
+ if (data.length > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
+ i = 3;
+ while (i < data.length && data[i] != '>') i++;
+
+ if (i + 1 < data.length) {
+ i++;
+ j = is_empty(data.slice(i));
+ if (j) {
+ work.s = data.slice(0, i + j);
+ if (do_render && md.callbacks.blockhtml)
+ md.callbacks.blockhtml(out, work, md.context);
+ return work.s.length;
+ }
+ }
+ }
+
+ /* no special case recognised */
+ return 0;
+ }
+
+ /* looking for an unindented matching closing tag */
+ /* followed by a blank line */
+ i = 1;
+ found = 0;
+
+ /* if not found, trying a second pass looking for indented match */
+ /* but not if tag is "ins" or "del" (following original Markdown.pl) */
+ if (curtag != 'ins' && curtag != 'del') {
+ var tag_size = curtag.length;
+ i = 1;
+ while (i < data.length) {
+ i++;
+ while (i < data.length && !(data[i - 1] == '<' && data[i] == '/'))
+ i++;
+
+ if (i + 2 + tag_size >= data.length)
+ break;
+
+ // j = htmlblock_end(tag, md, data + i - 1, size - i + 1);
+ //TODO
+ j = htmlblock_end(tag, md, data.slice(i - 1));
+
+ if (j) {
+ i += j - 1;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ if (!found) return 0;
+
+ /* the end of the block has been found */
+ //TODO:
+ work.s = work.s.slice(0, i);
+ if (do_render && md.callbacks.blockhtml)
+ md.callbacks.blockhtml(out, work, md.context);
+
+ return i;
+ }
+
+ /* parse_blockquote - handles parsing of a blockquote fragment */
+ function parse_blockquote(out, md, data) {
+ var size = data.length;
+ var beg, end = 0, pre, work_size = 0;
+ // uint8_t *work_data = 0;
+ var work_data = "";
+ var work_data_cursor = 0;
+
+ var out_ = new Buffer();
+ md.blockStack.push(out_);
+
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}
+
+ pre = prefix_quote(data.slice(beg, end));
+
+ if (pre) beg += pre; /* skipping prefix */
+
+ /* empty line followed by non-quote line */
+ else if (is_empty(data.slice(beg, end)) &&
+ (end >= size || (prefix_quote(data.slice(end)) == 0 &&
+ !is_empty(data.slice(end)))))
+ break;
+
+ if (beg < end) { /* copy into the in-place working buffer */
+ /* bufput(work, data + beg, end - beg); */
+ //TODO:!!! FIX THIS!!!
+ // if (!work_data) work_data = data.slice(beg, end);
+ // work_data = data + beg;
+ work_data += data.slice(beg, end);
+ /*
+ if (!work_data) work_data_cursor = beg;
+ else if (beg != work_data_cursor + work_size)
+ work_data += data.slice(beg, end);
+ */
+ // memmove(work_data + work_size, data + beg, end - beg);
+ work_size += end - beg;
+ }
+ beg = end;
+ }
+
+ parse_block(out_, md, work_data);
+ if (md.callbacks.blockquote)
+ md.callbacks.blockquote(out, out_, md.context);
+ md.blockStack.pop();
+ return end;
+ }
+
+ /* parse_paragraph - handles parsing of a regular paragraph */
+ function parse_paragraph(out, md, data) {
+ var i = 0, end = 0;
+ var level = 0;
+ var size = data.length;
+ var work = new Buffer(data);
+
+ while (i < size) {
+ for (end = i + 1; end < size && data[end - 1] != '\n'; end++) {/* empty */}
+
+ if (prefix_quote(data.slice(i, end)) != 0) {
+ end = i;
+ break;
+ }
+
+ var tempdata = data.slice(i);
+ if (is_empty(tempdata) || (level = is_headerline(tempdata)) != 0) break;
+ if (is_empty(tempdata)) break;
+ if ((level = is_headerline(tempdata)) != 0) break;
+
+ if (is_atxheader(md, tempdata)
+ || is_hrule(tempdata)
+ || prefix_quote(tempdata)) {
+ end = i;
+ break;
+ }
+
+ /*
+ * Early termination of a paragraph with the same logic
+ * as markdown 1.0.0. If this logic is applied, the
+ * Markdown 1.0.3 test suite wont pass cleanly.
+ *
+ * :: If the first character in a new line is not a letter
+ * lets check to see if there's some kind of block starting here
+ */
+ if ((md.extensions & MKDEXT_LAX_SPACING) && !isalnum(data[i])) {
+ if (prefix_oli(tempdata)
+ || prefix_uli(tempdata)) {
+ end = i;
+ break;
+ }
+ /* see if an html block starts here */
+ if (data[i] == '<' && md.callbacks.blockhtml
+ && parse_htmlblock(out, md, tempdata, 0)) {
+ end = i;
+ break
+ }
+
+ /* see if a code fence starts here */
+ if ((md.extensions && MKDEXT_FENCED_CODE) != 0
+ && is_codefence(tempdata, null) != 0) {
+ end = i;
+ break;
+ }
+ }
+
+ i = end;
+ }
+
+ var work_size = i;
+ while (work_size && data[work_size - 1] == '\n') work_size--;
+ work.s = work.s.slice(0, work_size);
+
+ if (!level) {
+ var tmp = new Buffer();
+ md.blockStack.push(tmp);
+ parse_inline(tmp, md, work.s);
+ if (md.callbacks.paragraph)
+ md.callbacks.paragraph(out, tmp, md.context);
+ md.blockStack.pop();
+ } else {
+ var header_work = null;
+
+ if (work.size) {
+ var beg;
+ i = work.s.length;
+ // var work_size = work.s.length - 1;
+ // work.size -= 1;
+
+ while (work_size && data[work_size] != '\n')
+ work_size -= 1;
+
+ beg = work_size + 1;
+ while (work_size && data[work_size - 1] == '\n')
+ work_size -= 1;
+
+ work.s = work.s.slice(0, work_size);
+ if (work_size > 0) {
+ var tmp = new Buffer();
+ md.blockStack.push(tmp);
+ parse_inline(tmp, md, work.s);
+
+ if (md.callbacks.paragraph)
+ md.callbacks.paragraph(out, tmp, md.context);
+
+ md.blockStack.pop();
+ work.s = work.s.slice(beg, i);
+ }
+ else work.s = work.s.slice(0, i);
+ }
+
+ header_work = new Buffer();
+ md.spanStack.push(header_work);
+ parse_inline(header_work, md, work.s);
+
+ if (md.callbacks.header)
+ md.callbacks.header(out, header_work, level, md.context);
+
+ md.spanStack.pop();
+ }
+
+ return end;
+ }
+
+ /* parse_fencedcode - handles parsing of a block-level code fragment */
+ function parse_fencedcode(out, md, data) {
+ var beg, end;
+ var work = null;
+ var lang = new Buffer();
+
+ beg = is_codefence(data, lang);
+ if (beg == 0) return 0;
+
+ work = new Buffer();
+ md.blockStack.push(work);
+
+ while (beg < data.length) {
+ var fence_end;
+ var fence_trail = new Buffer();
+
+ fence_end = is_codefence(data.slice(beg), fence_trail);
+ if (fence_end != 0 && fence_trail.s.length == 0) {
+ beg += fence_end;
+ break;
+ }
+
+ for (end = beg + 1; end < data.length && data[end - 1] != '\n'; end++) {}
+
+ if (beg < end) {
+ /* verbatim copy to the working buffer,
+ escaping entities */
+ var tempData = data.slice(beg, end);
+ if (is_empty(tempData)) work.s += '\n';
+ else work.s += tempData;
+ }
+ beg = end;
+ }
+
+ if (work.s.length && work.s[work.s.length - 1] != '\n')
+ work.s += '\n';
+
+ if (md.callbacks.blockcode)
+ md.callbacks.blockcode(out, work, lang.s.length ? lang : null, md.context);
+
+ md.blockStack.pop();
+ return beg;
+ }
+
+ function parse_blockcode(out, md, data) {
+ var size = data.length;
+ var beg, end, pre;
+
+ var work = null;
+ md.blockStack.push(work = new Buffer());
+
+ beg = 0;
+ while (beg < size) {
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};
+ pre = prefix_code(data.slice(beg, end));
+
+ if (pre) beg += pre; /* skipping prefix */
+ else if (!is_empty(data.slice(beg, end)))
+ /* non-empty non-prefixed line breaks the pre */
+ break;
+
+ if (beg < end) {
+ /* verbatim copy to the working buffer,
+ escaping entities */
+ if (is_empty(data.slice(beg, end))) work.s += '\n';
+ else work.s += data.slice(beg, end);
+ }
+ beg = end;
+ }
+
+ var work_size = work.s.length;
+ while (work_size && work.s[work_size - 1] == '\n') work_size -= 1;
+ work.s = work.s.slice(0, work_size);
+
+ work.s += '\n';
+
+ if (md.callbacks.blockcode)
+ md.callbacks.blockcode(out, work, null, md.context);
+
+ md.blockStack.pop();
+ return beg;
+ }
+
+ /* parse_listitem - parsing of a single list item */
+ /* assuming initial prefix is already removed */
+ //FLAGS is pointer
+ function parse_listitem(out, md, data, flags) {
+ var size = data.length;
+ var work = null, inter = null;
+ var beg = 0, end, pre, sublist = 0, orgpre = 0, i;
+ var in_empty = 0, has_inside_empty = 0, in_fence = 0;
+
+ /* keeping track of the first indentation prefix */
+ while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
+ orgpre++;
+
+ //TODO
+ beg = prefix_uli(data);
+ if (!beg) beg = prefix_oli(data);
+
+ if (!beg) return 0;
+
+ /* skipping to the beginning of the following line */
+ end = beg;
+ while (end < size && data[end - 1] != '\n') end++;
+
+ /* getting working buffers */
+ md.spanStack.push(work = new Buffer());
+ md.spanStack.push(inter = new Buffer());
+
+ /* putting the first line into the working buffer */
+ work.s += data.slice(beg, end);
+ beg = end;
+
+ /* process the following lines */
+ while (beg < size) {
+ var has_next_uli, has_next_oli;
+ end++;
+
+ while (end < size && data[end - 1] != '\n') end++;
+
+ /* process an empty line */
+ if (is_empty(data.slice(beg, end))) {
+ in_empty = 1;
+ beg = end;
+ continue;
+ }
+
+ /* calculating the indentation */
+ i = 0;
+ while (i < 4 && beg + i < end && data[beg + i] == ' ') i++;
+
+ pre = i;
+
+ //TODO: Cache this slice?
+ if (md.flags & MKDEXT_FENCED_CODE) {
+ if (is_codefence(data.slice(beg+i, end), null) != 0) {
+ in_fence = !in_fence;
+ }
+ }
+
+ /* only check for new list items if we are **not** in a fenced code block */
+ if (!in_fence) {
+ has_next_uli = prefix_uli(data.slice(beg+i, end));
+ has_next_oli = prefix_oli(data.slice(beg+i, end));
+ }
+
+ /* checking for ul/ol switch */
+ if (in_empty && (
+ ((flags.p & MKD_LIST_ORDERED) && has_next_uli) ||
+ (!(flags.p & MKD_LIST_ORDERED) && has_next_oli))){
+ flags.p |= MKD_LI_END;
+ break; /* the following item must have same list type */
+ }
+
+ /* checking for a new item */
+ if ((has_next_uli && !is_hrule(data.slice(beg+i, end))) || has_next_oli) {
+ if (in_empty) has_inside_empty = 1;
+
+ if (pre == orgpre) /* the following item must have */
+ break; /* the same indentation */
+
+ if (!sublist) sublist = work.s.length;
+ }
+ /* joining only indented stuff after empty lines;
+ * note that now we only require 1 space of indentation
+ * to continue list */
+ else if (in_empty && pre == 0) {
+ flags.p |= MKD_LI_END;
+ break;
+ }
+ else if (in_empty) {
+ work.s += '\n';
+ has_inside_empty = 1;
+ }
+
+ in_empty = 0;
+
+ /* adding the line without prefix into the working buffer */
+ work.s += data.slice(beg + i, end);
+ beg = end;
+ }
+
+ /* render of li contents */
+ if (has_inside_empty) flags.p |= MKD_LI_BLOCK;
+
+ if (flags.p & MKD_LI_BLOCK) {
+ /* intermediate render of block li */
+ if (sublist && sublist < work.s.length) {
+ parse_block(inter, md, work.s.slice(0, sublist));
+ parse_block(inter, md, work.s.slice(sublist));
+ }
+ else
+ parse_block(inter, md, work.s);
+ } else {
+ //TODO:
+ /* intermediate render of inline li */
+ if (sublist && sublist < work.s.length) {
+ parse_inline(inter, md, work.s.slice(0, sublist));
+ parse_block(inter, md, work.s.slice(sublist));
+ }
+ else
+ parse_inline(inter, md, work.s);
+ }
+
+ /* render of li itself */
+ if (md.callbacks.listitem)
+ md.callbacks.listitem(out, inter, flags.p, md.context);
+
+ md.spanStack.pop();
+ md.spanStack.pop();
+ return beg;
+ }
+
+
+ /* parse_list - parsing ordered or unordered list block */
+ function parse_list(out, md, data, flags) {
+ var size = data.length;
+ var i = 0, j;
+
+ var work = null;
+ md.blockStack.push(work = new Buffer());
+
+ while (i < size) {
+ var flag_p = {p: flags};
+ j = parse_listitem(work, md, data.slice(i), flag_p);
+ flags = flag_p.p;
+ i += j;
+
+ if (!j || (flags & MKD_LI_END)) break;
+ }
+
+ if (md.callbacks.list)
+ md.callbacks.list(out, work, flags, md.context);
+ md.blockStack.pop();
+ return i;
+ }
+
+ function parse_table_row(out, md, data, columns, header_flag) {
+ var i = 0, col, cols_left;
+ var row_work = null;
+
+ if (!md.callbacks.table_cell || !md.callbacks.table_row) return;
+
+ md.spanStack.push(row_work = new Buffer());
+
+ if (i < data.length && data[i] == '|') i++;
+
+ for (col = 0; col < columns.length && i < data.length; ++col) {
+ var cell_start, cell_end;
+ var cell_work;
+
+ md.spanStack.push(cell_work = new Buffer());
+
+ while (i < data.length && _isspace(data[i])) i++;
+
+ cell_start = i;
+
+ while (i < data.length && data[i] != '|') i++;
+
+ cell_end = i - 1;
+
+ while (cell_end > cell_start && _isspace(data[cell_end])) cell_end--;
+
+ // parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
+ parse_inline(cell_work, md, data.slice(cell_start, 1 + cell_end));
+ md.callbacks.table_cell(row_work, cell_work, columns[col] | header_flag, md.context, 0);
+
+ md.spanStack.pop();
+ i++;
+ }
+
+ cols_left = columns.length - col;
+ // for (; col < columns.length; ++col) {
+ if (cols_left > 0) {
+ var empty_cell = null;
+ md.callbacks.table_cell(row_work, empty_cell, columns[col] | header_flag, md.context, cols_left);
+ }
+
+ md.callbacks.table_row(out, row_work, md.context);
+
+ md.spanStack.pop();
+ }
+
+ function parse_table_header(out, md, data, columns) {
+ var i = 0, col, header_end, under_end;
+
+ var pipes = 0;
+ while (i < data.length && data[i] != '\n')
+ if (data[i++] == '|') pipes++;
+
+ if (i == data.length || pipes == 0)
+ return 0;
+
+ header_end = i;
+
+ while (header_end > 0 && _isspace(data[header_end - 1])) header_end--;
+
+ if (data[0] == '|') pipes--;
+
+ if (header_end && data[header_end - 1] == '|') pipes--;
+
+ if (pipes + 1 > md.maxTableCols) return 0;
+ // columns.p = pipes + 1;
+ // column_data.p = new Array(columns.p);
+ columns.p = new Array(pipes + 1);
+ for (var k = 0; k < columns.p.length; k++) columns.p[k] = 0;
+
+ /* Parse the header underline */
+ i++;
+ if (i < data.length && data[i] == '|') i++;
+
+ under_end = i;
+ while (under_end < data.length && data[under_end] != '\n') under_end++;
+
+ for (col = 0; col < columns.p.length && i < under_end; ++col) {
+ var dashes = 0;
+
+ while (i < under_end && data[i] == ' ') i++;
+
+ if (data[i] == ':') {
+ i++;
+ columns.p[col] |= MKD_TABLE_ALIGN_L;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == '-') {
+ i++; dashes++;
+ }
+
+ if (i < under_end && data[i] == ':') {
+ i++; columns.p[col] |= MKD_TABLE_ALIGN_R;
+ dashes++;
+ }
+
+ while (i < under_end && data[i] == ' ') i++;
+
+ if (i < under_end && data[i] != '|') break;
+
+ if (dashes < 1) break;
+
+ i++;
+ }
+
+ if (col < columns.p.length) return 0;
+
+ parse_table_row(out, md, data.slice(0, header_end), columns.p, MKD_TABLE_HEADER);
+
+ return under_end + 1;
+ }
+
+ function parse_table(out, md, data) {
+ var i;
+ var header_work, body_work;
+
+ var columns = {p: null};
+
+ md.spanStack.push(header_work = new Buffer());
+ md.blockStack.push(body_work = new Buffer());
+
+ i = parse_table_header(header_work, md, data, columns);
+ if (i > 0) {
+
+ while (i < data.length) {
+ var row_start;
+ var pipes = 0;
+
+ row_start = i;
+
+ while (i < data.length && data[i] != '\n')
+ if (data[i++] == '|')
+ pipes++;
+
+ if (pipes == 0 || i == data.length) {
+ i = row_start;
+ break;
+ }
+
+ parse_table_row(body_work, md, data.slice(row_start, i), columns.p, 0);
+
+ i++;
+ }
+
+ if (md.callbacks.table)
+ md.callbacks.table(out, header_work, body_work, md.context);
+ }
+
+ md.spanStack.pop();
+ md.blockStack.pop();
+ return i;
+ }
+
+ function parse_block(out, md, data) {
+ var beg = 0, end, i;
+ var textData;
+
+ if (md.spanStack.length +
+ md.blockStack.length > md.nestingLimit)
+ return;
+
+ while (beg < data.length) {
+ textData = data.slice(beg);
+ end = data.length - beg;
+
+ if (is_atxheader(md, textData))
+ beg += parse_atxheader(out, md, textData);
+
+ else if (data[beg] == '<' && md.callbacks.blockhtml &&
+ (i = parse_htmlblock(out, md, textData, 1)) != 0)
+ beg += i;
+ else if ((i = is_empty(textData)) != 0)
+ beg += i;
+ else if (is_hrule(textData)) {
+ if (md.callbacks.hrule)
+ md.callbacks.hrule(out, md.context);
+
+ while (beg < data.length && data[beg] != '\n') beg++;
+
+ beg++;
+ }
+
+ else if ((md.extensions & MKDEXT_FENCED_CODE) != 0 &&
+ (i = parse_fencedcode(out, md, textData)) != 0)
+ beg += i;
+
+ else if ((md.extensions & MKDEXT_TABLES) != 0 &&
+ (i = parse_table(out, md, textData)) != 0)
+ beg += i;
+
+ else if (prefix_quote(textData))
+ beg += parse_blockquote(out, md, textData);
+
+ else if (prefix_code(textData))
+ beg += parse_blockcode(out, md, textData);
+
+ else if (prefix_uli(textData))
+ beg += parse_list(out, md, textData, 0);
+
+ else if (prefix_oli(textData))
+ beg += parse_list(out, md, textData, MKD_LIST_ORDERED);
+
+ else {
+ beg += parse_paragraph(out, md, textData);
+ }
+ }
+ }
+
+ function is_ref(data, beg, end, md) {
+ /* int n; */
+ var i = 0;
+ var idOffset, idEnd;
+ var linkOffset, linkEnd;
+ var titleOffset, titleEnd;
+ var lineEnd;
+
+ /* up to 3 optional leading spaces */
+ if (beg + 3 >= end) return 0;
+ if (data[beg] == ' ') { i = 1;
+ if (data[beg + 1] == ' ') { i = 2;
+ if (data[beg + 2] == ' ') { i = 3;
+ if (data[beg + 3] == ' ') return 0; } } }
+ i += beg;
+
+ /* id part: anything but a newline between brackets */
+ if (data[i] != '[') return 0;
+ i++;
+ idOffset = i;
+ while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') i++;
+ if (i >= end || data[i] != ']') return 0;
+ idEnd = i;
+
+ /* spacer: colon (space | tab)* newline? (space | tab)* */
+ i++;
+ if (i >= end || data[i] != ':') return 0;
+ i++;
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+ i++;
+ if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
+ while (i < end && data[i] == ' ') i++;
+ if (i >= end) return 0;
+
+ /* link: whitespace-free sequence, optionally between angle brackets */
+ if (data[i] == '<') i++;
+
+ linkOffset = i;
+ while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') i++;
+
+ if (data[i - 1] == '>') linkEnd = i - 1; else linkEnd = i;
+
+ /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
+ while (i < end && data[i] == ' ') i++;
+ if (i < end && data[i] != '\n' && data[i] != '\r'
+ && data[i] != '\'' && data[i] != '"' && data[i] != '(')
+ return 0;
+ lineEnd = 0;
+ /* computing end-of-line */
+ if (i >= end || data[i] == '\r' || data[i] == '\n') lineEnd = i;
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+ lineEnd = i + 1;
+
+ /* optional (space|tab)* spacer after a newline */
+ if (lineEnd) {
+ i = lineEnd + 1;
+ while (i < end && data[i] == ' ') i++;
+ }
+
+ /* optional title: any non-newline sequence enclosed in '"()
+ alone on its line */
+ titleOffset = titleEnd = 0;
+ if (i + 1 < end && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
+ i++;
+ titleOffset = i;
+ /* looking for EOL */
+ while (i < end && data[i] != '\n' && data[i] != '\r') i++;
+ if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+ titleEnd = i + 1;
+ else titleEnd = i;
+ /* stepping back */
+ i -= 1;
+ while (i > titleOffset && data[i] == ' ')
+ i -= 1;
+ if (i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
+ lineEnd = titleEnd;
+ titleEnd = i;
+ }
+ }
+
+ if (!lineEnd || linkEnd == linkOffset)
+ return 0; /* garbage after the link empty link */
+
+ var id = data.slice(idOffset, idEnd);
+ var link = data.slice(linkOffset, linkEnd);
+ var title = null;
+ if (titleEnd > titleOffset) title = data.slice(titleOffset, titleEnd);
+ md.refs[id] = {
+ id: id,
+ link: new Buffer(link),
+ title: new Buffer(title)
+ };
+ return lineEnd;
+ }
+
+ function expand_tabs(out, line) {
+ var i = 0, tab = 0;
+
+ while (i < line.length) {
+ var org = i;
+
+ while (i < line.length && line[i] != '\t') {
+ i++; tab++;
+ }
+
+ if (i > org) out.s += line.slice(org, i);
+
+ if (i >= line.length) break;
+
+ do {
+ out.s += ' ';
+ tab++;
+ } while (tab % 4);
+
+ i++;
+ }
+ }
+
+ /**
+ Render markdown code to HTML.
+
+ @param {string} source Markdown code.
+ @returns {string} HTML code.
+ */
+ function render(source) {
+ var text = new Buffer();
+ var beg = 0, end;
+ this.refs = {};
+
+ while (beg < source.length) { /* iterating over lines */
+ if (end = is_ref(source, beg, source.length, this))
+ beg = end;
+ else { /* skipping to the next line */
+ end = beg;
+ while (end < source.length && source[end] != '\n' && source[end] != '\r') end++;
+
+ /* adding the line body if present */
+ if (end > beg) expand_tabs(text, source.slice(beg, end));
+
+ while (end < source.length && (source[end] == '\n' || source[end] == '\r')) {
+ /* add one \n per newline */
+ if (source[end] == '\n' || (end + 1 < source.length && source[end + 1] != '\n'))
+ text.s += '\n';
+ end++;
+ }
+
+ beg = end;
+ }
+ }
+
+ var out = new Buffer();
+
+ /* second pass: actual rendering */
+ if (this.callbacks.doc_header)
+ this.callbacks.doc_header(out, this.context);
+
+ if (text.s.length) {
+ /* adding a final newline if not already present */
+ if (text.s[text.s.length - 1] != '\n' && text.s[text.s.length - 1] != '\r')
+ text.s += '\n';
+ parse_block(out, this, text.s);
+ }
+
+ if (this.callbacks.doc_footer)
+ this.callbacks.doc_footer(out, this.context);
+
+ return out.s;
+ }
+ Markdown.prototype['render'] = render;
+
+ /**
+ Create a parser object using the given configuration parameters.
+
+ To get a Reddit equivelent configuration, pass no arguments.
+
+ @param {?Renderer=} renderer A renderer object.
+ @param {?Number=} extensions A series of OR'd extension flags. (Extension flags start with MKDEXT_)
+ @param {?Number=} nestingLimit The maximum depth to which inline elements can be nested.
+ @return {Markdown} A configured markdown object.
+ */
+ exports.getParser = function getParser(renderer, extensions, nestingLimit, columnLimit) {
+ var md = new Markdown();
+ if (renderer) md.callbacks = renderer.callbacks;
+ if (nestingLimit) md.nestingLimit = nestingLimit;
+ if (nestingLimit) md.maxTableCols = columnLimit;
+ if (renderer) md.context = renderer.context;
+ if (extensions != undefined && extensions != null) md.extensions = extensions;
+
+ // if (!(nestingLimit > 0 && columnLimit > 0 && callbacks)) throw new Error;
+ var cb = md.callbacks;
+ if (cb.emphasis || cb.double_emphasis || cb.triple_emphasis) {
+ md.activeChars['*'] = MD_CHAR_EMPHASIS;
+ md.activeChars['_'] = MD_CHAR_EMPHASIS;
+ if (md.extensions & MKDEXT_STRIKETHROUGH) md.activeChars['~'] = MD_CHAR_EMPHASIS;
+ }
+
+ if (cb.codespan) md.activeChars['`'] = MD_CHAR_CODESPAN;
+ if (cb.linebreak) md.activeChars['\n'] = MD_CHAR_LINEBREAK;
+ if (cb.image || cb.link) md.activeChars['['] = MD_CHAR_LINK;
+
+ md.activeChars['<'] = MD_CHAR_LANGLE;
+ md.activeChars['\\'] = MD_CHAR_ESCAPE;
+ md.activeChars['&'] = MD_CHAR_ENTITITY;
+
+ if (md.extensions & MKDEXT_AUTOLINK) {
+ if (!(md.extensions & MKDEXT_NO_EMAIL_AUTOLINK)) {
+ md.activeChars['@'] = MD_CHAR_AUTOLINK_EMAIL;
+ }
+ md.activeChars[':'] = MD_CHAR_AUTOLINK_URL;
+ md.activeChars['w'] = MD_CHAR_AUTOLINK_WWW;
+ md.activeChars['/'] = MD_CHAR_AUTOLINK_SUBREDDIT_OR_USERNAME;
+ }
+ if (md.extensions & MKDEXT_SUPERSCRIPT) md.activeChars['^'] = MD_CHAR_SUPERSCRIPT;
+
+ return md;
+ }
+
+ var DEFAULT_BODY_FLAGS = HTML_SKIP_HTML | HTML_SKIP_IMAGES | HTML_SAFELINK | HTML_ESCAPE | HTML_USE_XHTML;
+ var DEFAULT_WIKI_FLAGS = HTML_SKIP_HTML | HTML_SAFELINK | HTML_ALLOW_ELEMENT_WHITELIST | HTML_ESCAPE | HTML_USE_XHTML;
+ var DEFAULT_HTML_ATTR_WHITELIST = ['colspan', 'rowspan', 'cellspacing', 'cellpadding', 'scope'];
+ var DEFAULT_HTML_ELEMENT_WHITELIST = ['tr', 'th', 'td', 'table', 'tbody', 'thead', 'tfoot', 'caption'];
+ exports.DEFAULT_HTML_ELEMENT_WHITELIST = DEFAULT_HTML_ELEMENT_WHITELIST;
+ exports.DEFAULT_HTML_ATTR_WHITELIST = DEFAULT_HTML_ATTR_WHITELIST;
+ exports.DEFAULT_BODY_FLAGS = DEFAULT_BODY_FLAGS;
+ exports.DEFAULT_WIKI_FLAGS = DEFAULT_WIKI_FLAGS;
+
+ exports.HTML_SKIP_HTML = HTML_SKIP_HTML;
+ exports.HTML_SKIP_STYLE = HTML_SKIP_STYLE;
+ exports.HTML_SKIP_IMAGES = HTML_SKIP_IMAGES;
+ exports.HTML_SKIP_LINKS = HTML_SKIP_LINKS;
+ exports.HTML_EXPAND_TABS = HTML_EXPAND_TABS;
+ exports.HTML_SAFELINK = HTML_SAFELINK;
+ exports.HTML_TOC = HTML_TOC;
+ exports.HTML_HARD_WRAP = HTML_HARD_WRAP;
+ exports.HTML_USE_XHTML = HTML_USE_XHTML;
+ exports.HTML_ESCAPE = HTML_ESCAPE;
+ exports.HTML_ALLOW_ELEMENT_WHITELIST = HTML_ALLOW_ELEMENT_WHITELIST;
+
+ exports.MKDEXT_NO_INTRA_EMPHASIS = MKDEXT_NO_INTRA_EMPHASIS;
+ exports.MKDEXT_TABLES = MKDEXT_TABLES;
+ exports.MKDEXT_FENCED_CODE = MKDEXT_FENCED_CODE;
+ exports.MKDEXT_AUTOLINK = MKDEXT_AUTOLINK;
+ exports.MKDEXT_STRIKETHROUGH = MKDEXT_STRIKETHROUGH;
+ exports.MKDEXT_SPACE_HEADERS = MKDEXT_SPACE_HEADERS;
+ exports.MKDEXT_SUPERSCRIPT = MKDEXT_SUPERSCRIPT;
+ exports.MKDEXT_LAX_SPACING = MKDEXT_LAX_SPACING;
+ exports.MKDEXT_NO_EMAIL_AUTOLINK = MKDEXT_NO_EMAIL_AUTOLINK;
+
+ exports['SD_AUTOLINK_SHORT_DOMAINS'] = SD_AUTOLINK_SHORT_DOMAINS;
+
+ exports.MKDA_NOT_AUTOLINK = MKDA_NOT_AUTOLINK;
+ exports.MKDA_NORMAL = MKDA_NORMAL;
+ exports.MKDA_EMAIL = MKDA_EMAIL;
+
+ if (typeof define === 'function') {
+ define('snuownd', [], exports);
+ }
+})(typeof(exports)!=='undefined'?exports:typeof(window)!=='undefined'?window.SnuOwnd={}:{});
+
diff --git a/static/subreddit.js b/static/subreddit.js
new file mode 100644
index 0000000..3918c74
--- /dev/null
+++ b/static/subreddit.js
@@ -0,0 +1 @@
+"use strict";
diff --git a/static/thread.js b/static/thread.js
index 2a0d105..5e1d7e3 100644
--- a/static/thread.js
+++ b/static/thread.js
@@ -15,11 +15,17 @@ return {
return Promise.all([
fetch(URLs.thread, Reddit.init)
.then(Fetch2.json)
- .then(ThreadHTML.createThread),
+ .then(ThreadHTML.createThread)
+ .catch(function(error) {
+ return Promise.reject("Could not get thread from Reddit");
+ }),
fetch(URLs.pushshiftIDs)
.then(Fetch2.json)
- .then(_.property("data"))
+ .then(_.property("data"))
+ .catch(function(error) {
+ return Promise.reject("Could not get removed comment IDs");
+ }),
])
})
.then(function(results) {
@@ -29,15 +35,23 @@ return {
Status.loading("Loading comments from reddit...");
HandleIDs.normal(thread);
console.log("After normal", Comments.ids.length);
- return HandleIDs.morechildren();
+ return HandleIDs.morechildren()
+ .catch(function(error){
+ return Promise.reject("Could not get comments from Reddit (moreChildren)");
+ });
})
.then(function(){
console.log("After morechildren", Comments.ids.length);
return Promise.all(_.map(_.uniq(Comments.countinuethread), function(id) {
return fetch(URLs.thread+"/_/"+id.split("_")[1], Reddit.init)
.then(Fetch2.json)
- }));
- }).then(function(smallerThreads){
+ .catch(function(error){ return Promise.reject("Could not get comments from Reddit (continueThisThread)") })
+ }))
+ .catch(function(error){
+ return Promise.reject("Could not get comments from Reddit (continueThisThread)");
+ });
+ })
+ .then(function(smallerThreads){
_.forEach(smallerThreads, function(thread){
HandleIDs.normal(thread);
})
@@ -48,7 +62,10 @@ return {
HandleIDs.removed();
console.log("Removed", Comments.removed.length);
ThreadHTML.createCommentInfo(Comments.removed.length);
- return Fetch2.multiple(URLs.format(URLs.pushshiftComments, Comments.removed), null, "data");
+ return Fetch2.multiple(URLs.format(URLs.pushshiftComments, Comments.removed), null, "data")
+ .catch(function(error){
+ return Promise.reject("Could not get removed comments");
+ });
})
.then(function(removedComments){
Status.loading("Generating comments...");
@@ -56,6 +73,13 @@ return {
})
.then(function(){
Status.success();
+ })
+ .catch(function(error) {
+ if(_.includes(_.lowerCase(error), "error")) {
+ Status.error(error);
+ } else {
+ Status.error("Error: "+error);
+ }
});
}
}})()
@@ -384,4 +408,6 @@ var ThreadHTML = (function(){
}
})()
-app.loadPage()
\ No newline at end of file
+if(isSupported) {
+ app.loadPage()
+}
\ No newline at end of file
diff --git a/subreddit.html b/subreddit.html
new file mode 100644
index 0000000..7f9cd38
--- /dev/null
+++ b/subreddit.html
@@ -0,0 +1,30 @@
+
+
+
+ Removeddit
+
+
+
+
+
+
+
+
+
+
+ Removeddit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thread.html b/thread.html
index 0d7a65a..ac4d7ec 100644
--- a/thread.html
+++ b/thread.html
@@ -18,15 +18,13 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+