]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/app/videos/shared/markdown.service.ts
Handle markdown in account/video channel pages
[github/Chocobozzz/PeerTube.git] / client / src / app / videos / shared / markdown.service.ts
index 82745f0c68283ab2e7f7385091b4f22e1713f3c8..14eeba77750c85acc5aa0091240dd1ee75ad7420 100644 (file)
@@ -4,40 +4,73 @@ import * as MarkdownIt from 'markdown-it'
 
 @Injectable()
 export class MarkdownService {
-  private markdownIt: MarkdownIt.MarkdownIt
+  static TEXT_RULES = [
+    'linkify',
+    'autolink',
+    'emphasis',
+    'link',
+    'newline',
+    'list'
+  ]
+  static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ])
+
+  private textMarkdownIt: MarkdownIt.MarkdownIt
+  private enhancedMarkdownIt: MarkdownIt.MarkdownIt
 
   constructor () {
-    this.markdownIt = new MarkdownIt('zero', { linkify: true })
-      .enable('linkify')
-      .enable('autolink')
-      .enable('emphasis')
-      .enable('link')
-      .enable('newline')
+    this.textMarkdownIt = this.createMarkdownIt(MarkdownService.TEXT_RULES)
+    this.enhancedMarkdownIt = this.createMarkdownIt(MarkdownService.ENHANCED_RULES)
+  }
+
+  textMarkdownToHTML (markdown: string) {
+    if (!markdown) return ''
+
+    const html = this.textMarkdownIt.render(markdown)
+    return this.avoidTruncatedLinks(html)
+  }
+
+  enhancedMarkdownToHTML (markdown: string) {
+    if (!markdown) return ''
+
+    const html = this.enhancedMarkdownIt.render(markdown)
+    return this.avoidTruncatedLinks(html)
+  }
 
+  private createMarkdownIt (rules: string[]) {
+    const markdownIt = new MarkdownIt('zero', { linkify: true, breaks: true })
+
+    for (let rule of rules) {
+      markdownIt.enable(rule)
+    }
+
+    this.setTargetToLinks(markdownIt)
+
+    return markdownIt
+  }
+
+  private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
     // Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
-    const defaultRender = this.markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
+    const defaultRender = markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
       return self.renderToken(tokens, idx, options)
     }
 
-    this.markdownIt.renderer.rules.link_open = function (tokens, idx, options, env, self) {
-      // If you are sure other plugins can't add `target` - drop check below
-      const aIndex = tokens[idx].attrIndex('target')
+    markdownIt.renderer.rules.link_open = function (tokens, index, options, env, self) {
+      const token = tokens[index]
 
-      if (aIndex < 0) {
-        tokens[idx].attrPush(['target', '_blank']) // add new attribute
-      } else {
-        tokens[idx].attrs[aIndex][1] = '_blank'    // replace value of existing attr
-      }
+      const targetIndex = token.attrIndex('target')
+      if (targetIndex < 0) token.attrPush([ 'target', '_blank' ])
+      else token.attrs[targetIndex][1] = '_blank'
+
+      const relIndex = token.attrIndex('rel')
+      if (relIndex < 0) token.attrPush([ 'rel', 'noopener noreferrer' ])
+      else token.attrs[relIndex][1] = 'noopener noreferrer'
 
       // pass token to default renderer.
-      return defaultRender(tokens, idx, options, env, self)
+      return defaultRender(tokens, index, options, env, self)
     }
   }
 
-  markdownToHTML (markdown: string) {
-    const html = this.markdownIt.render(markdown)
-
-    // Avoid linkify truncated links
-    return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...(<\/p>)?$/mi, '$1...')
+  private avoidTruncatedLinks (html: string) {
+    return html.replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
   }
 }