]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/shared/misc/utils.ts
Show default avatar on network error for comments
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / misc / utils.ts
1 // Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
2
3 import { DatePipe } from '@angular/common'
4 import { environment } from '../../../environments/environment'
5 import { AuthService } from '../../core/auth'
6
7 type ElementEvent = Omit<Event, 'target'> & {
8 target: HTMLElement
9 }
10
11 function getParameterByName (name: string, url: string) {
12 if (!url) url = window.location.href
13 name = name.replace(/[\[\]]/g, '\\$&')
14
15 const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
16 const results = regex.exec(url)
17
18 if (!results) return null
19 if (!results[2]) return ''
20
21 return decodeURIComponent(results[2].replace(/\+/g, ' '))
22 }
23
24 function populateAsyncUserVideoChannels (authService: AuthService, channel: { id: number, label: string, support?: string }[]) {
25 return new Promise(res => {
26 authService.userInformationLoaded
27 .subscribe(
28 () => {
29 const user = authService.getUser()
30 if (!user) return
31
32 const videoChannels = user.videoChannels
33 if (Array.isArray(videoChannels) === false) return
34
35 videoChannels.forEach(c => channel.push({ id: c.id, label: c.displayName, support: c.support }))
36
37 return res()
38 }
39 )
40 })
41 }
42
43 function getAbsoluteAPIUrl () {
44 let absoluteAPIUrl = environment.apiUrl
45 if (!absoluteAPIUrl) {
46 // The API is on the same domain
47 absoluteAPIUrl = window.location.origin
48 }
49
50 return absoluteAPIUrl
51 }
52
53 const datePipe = new DatePipe('en')
54 function dateToHuman (date: string) {
55 return datePipe.transform(date, 'medium')
56 }
57
58 function durationToString (duration: number) {
59 const hours = Math.floor(duration / 3600)
60 const minutes = Math.floor((duration % 3600) / 60)
61 const seconds = duration % 60
62
63 const minutesPadding = minutes >= 10 ? '' : '0'
64 const secondsPadding = seconds >= 10 ? '' : '0'
65 const displayedHours = hours > 0 ? hours.toString() + ':' : ''
66
67 return displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString()
68 }
69
70 function immutableAssign <A, B> (target: A, source: B) {
71 return Object.assign({}, target, source)
72 }
73
74 function objectToUrlEncoded (obj: any) {
75 const str: string[] = []
76 for (const key of Object.keys(obj)) {
77 str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
78 }
79
80 return str.join('&')
81 }
82
83 // Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34
84 function objectToFormData (obj: any, form?: FormData, namespace?: string) {
85 const fd = form || new FormData()
86 let formKey
87
88 for (const key of Object.keys(obj)) {
89 if (namespace) formKey = `${namespace}[${key}]`
90 else formKey = key
91
92 if (obj[key] === undefined) continue
93
94 if (Array.isArray(obj[key]) && obj[key].length === 0) {
95 fd.append(key, null)
96 continue
97 }
98
99 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) {
100 objectToFormData(obj[ key ], fd, formKey)
101 } else {
102 fd.append(formKey, obj[ key ])
103 }
104 }
105
106 return fd
107 }
108
109 function objectLineFeedToHtml (obj: any, keyToNormalize: string) {
110 return immutableAssign(obj, {
111 [keyToNormalize]: lineFeedToHtml(obj[keyToNormalize])
112 })
113 }
114
115 function lineFeedToHtml (text: string) {
116 if (!text) return text
117
118 return text.replace(/\r?\n|\r/g, '<br />')
119 }
120
121 function removeElementFromArray <T> (arr: T[], elem: T) {
122 const index = arr.indexOf(elem)
123 if (index !== -1) arr.splice(index, 1)
124 }
125
126 function sortBy (obj: any[], key1: string, key2?: string) {
127 return obj.sort((a, b) => {
128 const elem1 = key2 ? a[key1][key2] : a[key1]
129 const elem2 = key2 ? b[key1][key2] : b[key1]
130
131 if (elem1 < elem2) return -1
132 if (elem1 === elem2) return 0
133 return 1
134 })
135 }
136
137 function scrollToTop () {
138 window.scroll(0, 0)
139 }
140
141 // Thanks: https://github.com/uupaa/dynamic-import-polyfill
142 function importModule (path: string) {
143 return new Promise((resolve, reject) => {
144 const vector = '$importModule$' + Math.random().toString(32).slice(2)
145 const script = document.createElement('script')
146
147 const destructor = () => {
148 delete window[ vector ]
149 script.onerror = null
150 script.onload = null
151 script.remove()
152 URL.revokeObjectURL(script.src)
153 script.src = ''
154 }
155
156 script.defer = true
157 script.type = 'module'
158
159 script.onerror = () => {
160 reject(new Error(`Failed to import: ${path}`))
161 destructor()
162 }
163 script.onload = () => {
164 resolve(window[ vector ])
165 destructor()
166 }
167 const absURL = (environment.apiUrl || window.location.origin) + path
168 const loader = `import * as m from "${absURL}"; window.${vector} = m;` // export Module
169 const blob = new Blob([ loader ], { type: 'text/javascript' })
170 script.src = URL.createObjectURL(blob)
171
172 document.head.appendChild(script)
173 })
174 }
175
176 function isInViewport (el: HTMLElement) {
177 const bounding = el.getBoundingClientRect()
178 return (
179 bounding.top >= 0 &&
180 bounding.left >= 0 &&
181 bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
182 bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
183 )
184 }
185
186 function isXPercentInViewport (el: HTMLElement, percentVisible: number) {
187 const rect = el.getBoundingClientRect()
188 const windowHeight = (window.innerHeight || document.documentElement.clientHeight)
189
190 return !(
191 Math.floor(100 - (((rect.top >= 0 ? 0 : rect.top) / +-(rect.height / 1)) * 100)) < percentVisible ||
192 Math.floor(100 - ((rect.bottom - windowHeight) / rect.height) * 100) < percentVisible
193 )
194 }
195
196 export {
197 ElementEvent,
198 sortBy,
199 durationToString,
200 lineFeedToHtml,
201 objectToUrlEncoded,
202 getParameterByName,
203 populateAsyncUserVideoChannels,
204 getAbsoluteAPIUrl,
205 dateToHuman,
206 immutableAssign,
207 objectToFormData,
208 objectLineFeedToHtml,
209 removeElementFromArray,
210 importModule,
211 scrollToTop,
212 isInViewport,
213 isXPercentInViewport
214 }