@Input() previewColumn = false
@Input() truncate: number
@Input() markdownType: 'text' | 'enhanced' = 'text'
+ @Input() markdownVideo = false
textareaMarginRight = '0'
flexDirection = 'column'
this.previewHTML = await this.markdownRender(this.content)
}
- private markdownRender (text: string) {
- if (this.markdownType === 'text') return this.markdownService.textMarkdownToHTML(text)
+ private async markdownRender (text: string) {
+ const html = this.markdownType === 'text' ?
+ await this.markdownService.textMarkdownToHTML(text) :
+ await this.markdownService.enhancedMarkdownToHTML(text)
- return this.markdownService.enhancedMarkdownToHTML(text)
+ return this.markdownVideo ? this.markdownService.processVideoTimestamps(html) : html
}
}
import { Injectable } from '@angular/core'
import { MarkdownIt } from 'markdown-it'
+import { buildVideoLink } from '../../../assets/player/utils'
import { HtmlRendererService } from '@app/shared/renderer/html-renderer.service'
type MarkdownParsers = {
return html
}
+ async processVideoTimestamps (html: string) {
+ return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) {
+ const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0))
+ const url = buildVideoLink({ startTime: t })
+ return `<a href="${url}">${str}</a>`
+ })
+ }
+
private async createMarkdownIt (config: MarkdownConfig) {
// FIXME: import('...') returns a struct module, containing a "default" field corresponding to our sanitizeHtml function
const MarkdownItClass: typeof import ('markdown-it') = (await import('markdown-it') as any).default
private avoidTruncatedTags (html: string) {
return html.replace(/\*\*?([^*]+)$/, '$1')
.replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
- .replace(/\[[^\]]+\]\(([^\)]+)$/m, '$1')
+ .replace(/\[[^\]]+\]?\(?([^\)]+)$/, '$1')
.replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>')
}
}
</ng-template>
</my-help>
- <my-markdown-textarea truncate="250" formControlName="description"></my-markdown-textarea>
+ <my-markdown-textarea truncate="250" formControlName="description" markdownVideo="true"></my-markdown-textarea>
<div *ngIf="formErrors.description" class="form-error">
{{ formErrors.description }}
import { AuthService } from '../../../core/auth'
import { Video } from '../../../shared/video/video.model'
import { VideoComment } from './video-comment.model'
-import { MarkdownService } from '@app/shared/renderer'
+import { HtmlRendererService, MarkdownService } from '@app/shared/renderer'
@Component({
selector: 'my-video-comment',
newParentComments: VideoComment[] = []
constructor (
+ private htmlRenderer: HtmlRendererService,
private markdownService: MarkdownService,
private authService: AuthService
) {}
}
isRemovableByUser () {
- return this.comment.account && this.isUserLoggedIn() &&
+ return this.isUserLoggedIn() &&
(
this.user.account.id === this.comment.account.id ||
this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT)
}
private async init () {
- this.sanitizedCommentHTML = await this.markdownService.textMarkdownToHTML(this.comment.text, true)
-
+ const safeHTML = await this.htmlRenderer.toSafeHtml(this.comment.text)
+ this.sanitizedCommentHTML = await this.markdownService.processVideoTimestamps(safeHTML)
this.newParentComments = this.parentComments.concat([ this.comment ])
}
}
}
private async setVideoDescriptionHTML () {
- this.videoHTMLDescription = await this.markdownService.textMarkdownToHTML(this.video.description)
+ const html = await this.markdownService.textMarkdownToHTML(this.video.description)
+ this.videoHTMLDescription = await this.markdownService.processVideoTimestamps(html)
}
private setVideoLikesBarTooltipText () {