Better logic for wrapping in p tags or not

pull/21833/head
David Baker 2016-09-22 18:57:46 +01:00
parent de0c92dadf
commit 6ba20ec012
1 changed files with 42 additions and 39 deletions

View File

@ -16,33 +16,9 @@ limitations under the License.
import marked from 'marked'; import marked from 'marked';
// replace the default link renderer function
// to prevent marked from turning plain URLs
// into links, because tits algorithm is fairly
// poor, so let's send plain URLs rather than
// badly linkified ones (the linkifier Vector
// uses on message display is way better, eg.
// handles URLs with closing parens at the end).
const renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
if (text == href) {
return href;
}
return marked.Renderer.prototype.apply(this, arguments);
}
const PARAGRAPH_SUFFIX = '<br/><br/>';
// suffix paragraphs with double line breaks instead of
// wrapping them in 'p' tags: this makes it much easier
// for us to just strip one set of these off at the end,
// leaving valid markup if there were multiple paragraphs.
renderer.paragraph = function(text) {
return text + PARAGRAPH_SUFFIX;
}
// marked only applies the default options on the high // marked only applies the default options on the high
// level marked() interface, so we do it here. // level marked() interface, so we do it here.
const marked_options = Object.assign({}, marked.defaults, { const marked_options = Object.assign({}, marked.defaults, {
renderer: renderer,
gfm: true, gfm: true,
tables: true, tables: true,
breaks: true, breaks: true,
@ -53,8 +29,6 @@ const marked_options = Object.assign({}, marked.defaults, {
xhtml: true, // return self closing tags (ie. <br /> not <br>) xhtml: true, // return self closing tags (ie. <br /> not <br>)
}); });
const real_parser = new marked.Parser(marked_options);
/** /**
* Class that wraps marked, adding the ability to see whether * Class that wraps marked, adding the ability to see whether
* a given message actually uses any markdown syntax or whether * a given message actually uses any markdown syntax or whether
@ -90,36 +64,65 @@ export default class Markdown {
is_plain = false; is_plain = false;
} }
const dummyRenderer = {}; const dummy_renderer = {};
for (const k of Object.keys(marked.Renderer.prototype)) { for (const k of Object.keys(marked.Renderer.prototype)) {
dummyRenderer[k] = setNotPlain; dummy_renderer[k] = setNotPlain;
} }
// text and paragraph are just text // text and paragraph are just text
dummyRenderer.text = function(t){return t;} dummy_renderer.text = function(t){return t;}
dummyRenderer.paragraph = function(t){return t;} dummy_renderer.paragraph = function(t){return t;}
// ignore links where text is just the url: // ignore links where text is just the url:
// this ignores plain URLs that markdown has // this ignores plain URLs that markdown has
// detected whilst preserving markdown syntax links // detected whilst preserving markdown syntax links
dummyRenderer.link = function(href, title, text) { dummy_renderer.link = function(href, title, text) {
if (text != href) { if (text != href) {
is_plain = false; is_plain = false;
} }
} }
const dummyOptions = {}; const dummy_options = Object.assign({}, marked_options, {
Object.assign(dummyOptions, marked_options, { renderer: dummy_renderer,
renderer: dummyRenderer,
}); });
const dummyParser = new marked.Parser(dummyOptions); const dummy_parser = new marked.Parser(dummy_options);
dummyParser.parse(this._copyTokens()); dummy_parser.parse(this._copyTokens());
return is_plain; return is_plain;
} }
toHTML() { toHTML() {
return real_parser.parse(this._copyTokens()).slice( const real_renderer = new marked.Renderer();
0, 0 - PARAGRAPH_SUFFIX.length real_renderer.link = function(href, title, text) {
); // prevent marked from turning plain URLs
// into links, because tits algorithm is fairly
// poor. Let's send plain URLs rather than
// badly linkified ones (the linkifier Vector
// uses on message display is way better, eg.
// handles URLs with closing parens at the end).
if (text == href) {
return href;
}
return marked.Renderer.prototype.apply(this, arguments);
}
real_renderer.paragraph = (text) => {
// The tokens at the top level are the 'blocks', so if we
// have more than one, there are multiple 'paragraphs'.
// If there is only one top level token, just return the
// bare text: it's a single line of text and so should be
// 'inline', rather than necessarily wrapped in its own
// p tag. If, however, we have multiple tokens, each gets
// its own p tag to keep them as separate paragraphs.
if (this.tokens.length == 1) {
return text;
}
return '<p>' + text + '</p>';
}
const real_options = Object.assign({}, marked_options, {
renderer: real_renderer,
});
const real_parser = new marked.Parser(real_options);
return real_parser.parse(this._copyTokens());
} }
} }