]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/shared/shared-custom-markup/custom-markup.service.ts
Create peertube-container html tag
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-custom-markup / custom-markup.service.ts
CommitLineData
2539932e
C
1import { ComponentRef, Injectable } from '@angular/core'
2import { MarkdownService } from '@app/core'
3import {
63042139 4 ButtonMarkupData,
2539932e 5 ChannelMiniatureMarkupData,
f7894f09 6 ContainerMarkupData,
2539932e
C
7 EmbedMarkupData,
8 PlaylistMiniatureMarkupData,
9 VideoMiniatureMarkupData,
10 VideosListMarkupData
11} from '@shared/models'
2539932e 12import { DynamicElementService } from './dynamic-element.service'
8ee25e17
C
13import {
14 ButtonMarkupComponent,
15 ChannelMiniatureMarkupComponent,
16 EmbedMarkupComponent,
17 PlaylistMiniatureMarkupComponent,
18 VideoMiniatureMarkupComponent,
19 VideosListMarkupComponent
20} from './peertube-custom-tags'
2539932e 21
f7894f09
C
22type AngularBuilderFunction = (el: HTMLElement) => ComponentRef<any>
23type HTMLBuilderFunction = (el: HTMLElement) => HTMLElement
2539932e
C
24
25@Injectable()
26export class CustomMarkupService {
f7894f09 27 private angularBuilders: { [ selector: string ]: AngularBuilderFunction } = {
63042139 28 'peertube-button': el => this.buttonBuilder(el),
2539932e
C
29 'peertube-video-embed': el => this.embedBuilder(el, 'video'),
30 'peertube-playlist-embed': el => this.embedBuilder(el, 'playlist'),
31 'peertube-video-miniature': el => this.videoMiniatureBuilder(el),
32 'peertube-playlist-miniature': el => this.playlistMiniatureBuilder(el),
33 'peertube-channel-miniature': el => this.channelMiniatureBuilder(el),
34 'peertube-videos-list': el => this.videosListBuilder(el)
35 }
36
f7894f09
C
37 private htmlBuilders: { [ selector: string ]: HTMLBuilderFunction } = {
38 'peertube-container': el => this.containerBuilder(el)
39 }
40
8ee25e17
C
41 private customMarkdownRenderer: (text: string) => Promise<HTMLElement>
42
2539932e
C
43 constructor (
44 private dynamicElementService: DynamicElementService,
45 private markdown: MarkdownService
8ee25e17
C
46 ) {
47 this.customMarkdownRenderer = async (text: string) => this.buildElement(text)
48 }
49
50 getCustomMarkdownRenderer () {
51 return this.customMarkdownRenderer
52 }
2539932e
C
53
54 async buildElement (text: string) {
55 const html = await this.markdown.customPageMarkdownToHTML(text, this.getSupportedTags())
56
57 const rootElement = document.createElement('div')
58 rootElement.innerHTML = html
59
f7894f09
C
60 for (const selector of Object.keys(this.htmlBuilders)) {
61 rootElement.querySelectorAll(selector)
62 .forEach((e: HTMLElement) => {
63 try {
64 const element = this.execHTMLBuilder(selector, e)
65 // Insert as first child
66 e.insertBefore(element, e.firstChild)
67 } catch (err) {
68 console.error('Cannot inject component %s.', selector, err)
69 }
70 })
71 }
72
73 for (const selector of Object.keys(this.angularBuilders)) {
2539932e
C
74 rootElement.querySelectorAll(selector)
75 .forEach((e: HTMLElement) => {
76 try {
f7894f09 77 const component = this.execAngularBuilder(selector, e)
2539932e
C
78
79 this.dynamicElementService.injectElement(e, component)
80 } catch (err) {
81 console.error('Cannot inject component %s.', selector, err)
82 }
83 })
84 }
85
86 return rootElement
87 }
88
89 private getSupportedTags () {
f7894f09
C
90 return Object.keys(this.angularBuilders)
91 .concat(Object.keys(this.htmlBuilders))
2539932e
C
92 }
93
f7894f09
C
94 private execHTMLBuilder (selector: string, el: HTMLElement) {
95 return this.htmlBuilders[selector](el)
96 }
97
98 private execAngularBuilder (selector: string, el: HTMLElement) {
99 return this.angularBuilders[selector](el)
2539932e
C
100 }
101
102 private embedBuilder (el: HTMLElement, type: 'video' | 'playlist') {
103 const data = el.dataset as EmbedMarkupData
104 const component = this.dynamicElementService.createElement(EmbedMarkupComponent)
105
106 this.dynamicElementService.setModel(component, { uuid: data.uuid, type })
107
108 return component
109 }
110
111 private videoMiniatureBuilder (el: HTMLElement) {
112 const data = el.dataset as VideoMiniatureMarkupData
113 const component = this.dynamicElementService.createElement(VideoMiniatureMarkupComponent)
114
115 this.dynamicElementService.setModel(component, { uuid: data.uuid })
116
117 return component
118 }
119
120 private playlistMiniatureBuilder (el: HTMLElement) {
121 const data = el.dataset as PlaylistMiniatureMarkupData
122 const component = this.dynamicElementService.createElement(PlaylistMiniatureMarkupComponent)
123
124 this.dynamicElementService.setModel(component, { uuid: data.uuid })
125
126 return component
127 }
128
129 private channelMiniatureBuilder (el: HTMLElement) {
130 const data = el.dataset as ChannelMiniatureMarkupData
131 const component = this.dynamicElementService.createElement(ChannelMiniatureMarkupComponent)
132
133 this.dynamicElementService.setModel(component, { name: data.name })
134
135 return component
136 }
137
63042139
C
138 private buttonBuilder (el: HTMLElement) {
139 const data = el.dataset as ButtonMarkupData
140 const component = this.dynamicElementService.createElement(ButtonMarkupComponent)
141
142 const model = {
143 theme: data.theme,
144 href: data.href,
145 label: data.label,
146 blankTarget: this.buildBoolean(data.blankTarget)
147 }
148 this.dynamicElementService.setModel(component, model)
149
150 return component
151 }
152
2539932e
C
153 private videosListBuilder (el: HTMLElement) {
154 const data = el.dataset as VideosListMarkupData
155 const component = this.dynamicElementService.createElement(VideosListMarkupComponent)
156
157 const model = {
2539932e
C
158 sort: data.sort,
159 categoryOneOf: this.buildArrayNumber(data.categoryOneOf),
160 languageOneOf: this.buildArrayString(data.languageOneOf),
161 count: this.buildNumber(data.count) || 10
162 }
163
164 this.dynamicElementService.setModel(component, model)
165
166 return component
167 }
168
f7894f09
C
169 private containerBuilder (el: HTMLElement) {
170 const data = el.dataset as ContainerMarkupData
171
172 const root = document.createElement('div')
173 root.classList.add('peertube-container')
174
175 if (data.width) {
176 root.setAttribute('width', data.width)
177 }
178
179 if (data.title) {
180 const titleElement = document.createElement('h4')
181 titleElement.innerText = data.title
182 root.appendChild(titleElement)
183 }
184
185 if (data.description) {
186 const descriptionElement = document.createElement('div')
187 descriptionElement.innerText = data.description
188 root.appendChild(descriptionElement)
189 }
190
191 return root
192 }
193
2539932e
C
194 private buildNumber (value: string) {
195 if (!value) return undefined
196
197 return parseInt(value, 10)
198 }
199
63042139
C
200 private buildBoolean (value: string) {
201 if (value === 'true') return true
202 if (value === 'false') return false
203
204 return undefined
205 }
206
2539932e
C
207 private buildArrayNumber (value: string) {
208 if (!value) return undefined
209
210 return value.split(',').map(v => parseInt(v, 10))
211 }
212
213 private buildArrayString (value: string) {
214 if (!value) return undefined
215
216 return value.split(',')
217 }
218}