1 import { ComponentRef, Injectable } from '@angular/core'
2 import { MarkdownService } from '@app/core'
5 ChannelMiniatureMarkupData,
7 PlaylistMiniatureMarkupData,
8 VideoMiniatureMarkupData,
10 } from '@shared/models'
11 import { DynamicElementService } from './dynamic-element.service'
13 ButtonMarkupComponent,
14 ChannelMiniatureMarkupComponent,
16 PlaylistMiniatureMarkupComponent,
17 VideoMiniatureMarkupComponent,
18 VideosListMarkupComponent
19 } from './peertube-custom-tags'
21 type BuilderFunction = (el: HTMLElement) => ComponentRef<any>
24 export class CustomMarkupService {
25 private builders: { [ selector: string ]: BuilderFunction } = {
26 'peertube-button': el => this.buttonBuilder(el),
27 'peertube-video-embed': el => this.embedBuilder(el, 'video'),
28 'peertube-playlist-embed': el => this.embedBuilder(el, 'playlist'),
29 'peertube-video-miniature': el => this.videoMiniatureBuilder(el),
30 'peertube-playlist-miniature': el => this.playlistMiniatureBuilder(el),
31 'peertube-channel-miniature': el => this.channelMiniatureBuilder(el),
32 'peertube-videos-list': el => this.videosListBuilder(el)
35 private customMarkdownRenderer: (text: string) => Promise<HTMLElement>
38 private dynamicElementService: DynamicElementService,
39 private markdown: MarkdownService
41 this.customMarkdownRenderer = async (text: string) => this.buildElement(text)
44 getCustomMarkdownRenderer () {
45 return this.customMarkdownRenderer
48 async buildElement (text: string) {
49 const html = await this.markdown.customPageMarkdownToHTML(text, this.getSupportedTags())
51 const rootElement = document.createElement('div')
52 rootElement.innerHTML = html
54 for (const selector of this.getSupportedTags()) {
55 rootElement.querySelectorAll(selector)
56 .forEach((e: HTMLElement) => {
58 const component = this.execBuilder(selector, e)
60 this.dynamicElementService.injectElement(e, component)
62 console.error('Cannot inject component %s.', selector, err)
70 private getSupportedTags () {
71 return Object.keys(this.builders)
74 private execBuilder (selector: string, el: HTMLElement) {
75 return this.builders[selector](el)
78 private embedBuilder (el: HTMLElement, type: 'video' | 'playlist') {
79 const data = el.dataset as EmbedMarkupData
80 const component = this.dynamicElementService.createElement(EmbedMarkupComponent)
82 this.dynamicElementService.setModel(component, { uuid: data.uuid, type })
87 private videoMiniatureBuilder (el: HTMLElement) {
88 const data = el.dataset as VideoMiniatureMarkupData
89 const component = this.dynamicElementService.createElement(VideoMiniatureMarkupComponent)
91 this.dynamicElementService.setModel(component, { uuid: data.uuid })
96 private playlistMiniatureBuilder (el: HTMLElement) {
97 const data = el.dataset as PlaylistMiniatureMarkupData
98 const component = this.dynamicElementService.createElement(PlaylistMiniatureMarkupComponent)
100 this.dynamicElementService.setModel(component, { uuid: data.uuid })
105 private channelMiniatureBuilder (el: HTMLElement) {
106 const data = el.dataset as ChannelMiniatureMarkupData
107 const component = this.dynamicElementService.createElement(ChannelMiniatureMarkupComponent)
109 this.dynamicElementService.setModel(component, { name: data.name })
114 private buttonBuilder (el: HTMLElement) {
115 const data = el.dataset as ButtonMarkupData
116 const component = this.dynamicElementService.createElement(ButtonMarkupComponent)
122 blankTarget: this.buildBoolean(data.blankTarget)
124 this.dynamicElementService.setModel(component, model)
129 private videosListBuilder (el: HTMLElement) {
130 const data = el.dataset as VideosListMarkupData
131 const component = this.dynamicElementService.createElement(VideosListMarkupComponent)
135 description: data.description,
137 categoryOneOf: this.buildArrayNumber(data.categoryOneOf),
138 languageOneOf: this.buildArrayString(data.languageOneOf),
139 count: this.buildNumber(data.count) || 10
142 this.dynamicElementService.setModel(component, model)
147 private buildNumber (value: string) {
148 if (!value) return undefined
150 return parseInt(value, 10)
153 private buildBoolean (value: string) {
154 if (value === 'true') return true
155 if (value === 'false') return false
160 private buildArrayNumber (value: string) {
161 if (!value) return undefined
163 return value.split(',').map(v => parseInt(v, 10))
166 private buildArrayString (value: string) {
167 if (!value) return undefined
169 return value.split(',')