aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/helpers')
-rw-r--r--client/src/app/helpers/constants.ts1
-rw-r--r--client/src/app/helpers/i18n-utils.ts14
-rw-r--r--client/src/app/helpers/index.ts6
-rw-r--r--client/src/app/helpers/locales/index.ts1
-rw-r--r--client/src/app/helpers/locales/oc.ts104
-rw-r--r--client/src/app/helpers/peertube-web-storage.ts81
-rw-r--r--client/src/app/helpers/utils.ts210
-rw-r--r--client/src/app/helpers/zone.ts40
8 files changed, 457 insertions, 0 deletions
diff --git a/client/src/app/helpers/constants.ts b/client/src/app/helpers/constants.ts
new file mode 100644
index 000000000..bb4a0884e
--- /dev/null
+++ b/client/src/app/helpers/constants.ts
@@ -0,0 +1 @@
export const POP_STATE_MODAL_DISMISS = 'pop state dismiss'
diff --git a/client/src/app/helpers/i18n-utils.ts b/client/src/app/helpers/i18n-utils.ts
new file mode 100644
index 000000000..bbfb12959
--- /dev/null
+++ b/client/src/app/helpers/i18n-utils.ts
@@ -0,0 +1,14 @@
1import { environment } from '../../environments/environment'
2
3function isOnDevLocale () {
4 return environment.production === false && window.location.search === '?lang=fr'
5}
6
7function getDevLocale () {
8 return 'fr-FR'
9}
10
11export {
12 getDevLocale,
13 isOnDevLocale
14}
diff --git a/client/src/app/helpers/index.ts b/client/src/app/helpers/index.ts
new file mode 100644
index 000000000..06806402e
--- /dev/null
+++ b/client/src/app/helpers/index.ts
@@ -0,0 +1,6 @@
1export * from './locales'
2export * from './constants'
3export * from './i18n-utils'
4export * from './peertube-web-storage'
5export * from './utils'
6export * from './zone'
diff --git a/client/src/app/helpers/locales/index.ts b/client/src/app/helpers/locales/index.ts
new file mode 100644
index 000000000..b0e4d6148
--- /dev/null
+++ b/client/src/app/helpers/locales/index.ts
@@ -0,0 +1 @@
export * from './oc'
diff --git a/client/src/app/helpers/locales/oc.ts b/client/src/app/helpers/locales/oc.ts
new file mode 100644
index 000000000..d3b2e8407
--- /dev/null
+++ b/client/src/app/helpers/locales/oc.ts
@@ -0,0 +1,104 @@
1
2// This code is not generated
3// See angular/tools/gulp-tasks/cldr/extract.js
4
5const u: any = undefined
6
7function plural (n: number): number {
8 const i = Math.floor(Math.abs(n))
9 if (i === 0 || i === 1) return 1
10 return 5
11}
12
13export default [
14 'oc',
15 [['a. m.', 'p. m.'], u, u],
16 u,
17 [
18 ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.'],
19 ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'],
20 ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.']
21 ],
22 u,
23 [
24 ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'],
25 [
26 'de gen.', 'de febr.', 'de març', 'd’abr.', 'de mai', 'de junh', 'de jul.', 'd’ag.',
27 'de set.', 'd’oct.', 'de nov.', 'de dec.'
28 ],
29 [
30 'de genièr', 'de febrièr', 'de març', 'd’abril', 'de mai', 'de junh', 'de julhet',
31 'd’agòst', 'de setembre', 'd’octòbre', 'de novembre', 'de decembre'
32 ]
33 ],
34 [
35 ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'],
36 [
37 'gen.', 'febr.', 'març', 'abr.', 'mai', 'junh', 'jul.', 'ag.', 'set.', 'oct.', 'nov.',
38 'dec.'
39 ],
40 [
41 'genièr', 'febrièr', 'març', 'abril', 'mai', 'junh', 'julhet', 'agòst', 'setembre', 'octòbre',
42 'novembre', 'decembre'
43 ]
44 ],
45 [['aC', 'dC'], u, ['abans Jèsus-Crist', 'aprèp Jèsus-Crist']],
46 1,
47 [6, 0],
48 ['d/M/yy', 'd MMM y', 'd MMMM \'de\' y', 'EEEE, d MMMM \'de\' y'],
49 ['H:mm', 'H:mm:ss', 'H:mm:ss z', 'H:mm:ss zzzz'],
50 ['{1} {0}', '{1}, {0}', '{1} \'a\' \'les\' {0}', u],
51 [',', '.', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'],
52 ['#,##0.###', '#,##0%', '#,##0.00 ¤', '#E0'],
53 'EUR',
54 '€',
55 'euro',
56 {
57 'ARS': ['$AR', '$'],
58 'AUD': ['$AU', '$'],
59 'BEF': ['FB'],
60 'BMD': ['$BM', '$'],
61 'BND': ['$BN', '$'],
62 'BZD': ['$BZ', '$'],
63 'CAD': ['$CA', '$'],
64 'CLP': ['$CL', '$'],
65 'CNY': [u, '¥'],
66 'COP': ['$CO', '$'],
67 'CYP': ['£CY'],
68 'EGP': [u, '£E'],
69 'FJD': ['$FJ', '$'],
70 'FKP': ['£FK', '£'],
71 'FRF': ['F'],
72 'GBP': ['£GB', '£'],
73 'GIP': ['£GI', '£'],
74 'HKD': [u, '$'],
75 'IEP': ['£IE'],
76 'ILP': ['£IL'],
77 'ITL': ['₤IT'],
78 'JPY': [u, '¥'],
79 'KMF': [u, 'FC'],
80 'LBP': ['£LB', '£L'],
81 'MTP': ['£MT'],
82 'MXN': ['$MX', '$'],
83 'NAD': ['$NA', '$'],
84 'NIO': [u, '$C'],
85 'NZD': ['$NZ', '$'],
86 'RHD': ['$RH'],
87 'RON': [u, 'L'],
88 'RWF': [u, 'FR'],
89 'SBD': ['$SB', '$'],
90 'SGD': ['$SG', '$'],
91 'SRD': ['$SR', '$'],
92 'TOP': [u, '$T'],
93 'TTD': ['$TT', '$'],
94 'TWD': [u, 'NT$'],
95 'USD': ['$US', '$'],
96 'UYU': ['$UY', '$'],
97 'WST': ['$WS'],
98 'XCD': [u, '$'],
99 'XPF': ['FCFP'],
100 'ZMW': [u, 'Kw']
101 },
102 'ltr',
103 plural
104]
diff --git a/client/src/app/helpers/peertube-web-storage.ts b/client/src/app/helpers/peertube-web-storage.ts
new file mode 100644
index 000000000..0db1301bd
--- /dev/null
+++ b/client/src/app/helpers/peertube-web-storage.ts
@@ -0,0 +1,81 @@
1// Thanks: https://github.com/capaj/localstorage-polyfill
2
3const valuesMap = new Map()
4
5function proxify (instance: MemoryStorage) {
6 return new Proxy(instance, {
7 set: function (obj, prop: string | number, value) {
8 if (MemoryStorage.prototype.hasOwnProperty(prop)) {
9 instance[prop] = value
10 } else {
11 instance.setItem(prop, value)
12 }
13 return true
14 },
15 get: function (target, name: string | number) {
16 if (MemoryStorage.prototype.hasOwnProperty(name)) {
17 return instance[name]
18 }
19 if (valuesMap.has(name)) {
20 return instance.getItem(name)
21 }
22 }
23 })
24}
25
26class MemoryStorage {
27 [key: string]: any
28 [index: number]: string
29
30 getItem (key: any) {
31 const stringKey = String(key)
32 if (valuesMap.has(key)) {
33 return String(valuesMap.get(stringKey))
34 }
35
36 return null
37 }
38
39 setItem (key: any, val: any) {
40 valuesMap.set(String(key), String(val))
41 }
42
43 removeItem (key: any) {
44 valuesMap.delete(key)
45 }
46
47 clear () {
48 valuesMap.clear()
49 }
50
51 key (i: any) {
52 if (arguments.length === 0) {
53 throw new TypeError('Failed to execute "key" on "Storage": 1 argument required, but only 0 present.')
54 }
55
56 const arr = Array.from(valuesMap.keys())
57 return arr[i]
58 }
59
60 get length () {
61 return valuesMap.size
62 }
63}
64
65let peertubeLocalStorage: Storage
66let peertubeSessionStorage: Storage
67try {
68 peertubeLocalStorage = localStorage
69 peertubeSessionStorage = sessionStorage
70} catch (err) {
71 const instanceLocalStorage = new MemoryStorage()
72 const instanceSessionStorage = new MemoryStorage()
73
74 peertubeLocalStorage = proxify(instanceLocalStorage)
75 peertubeSessionStorage = proxify(instanceSessionStorage)
76}
77
78export {
79 peertubeLocalStorage,
80 peertubeSessionStorage
81}
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts
new file mode 100644
index 000000000..879f697f4
--- /dev/null
+++ b/client/src/app/helpers/utils.ts
@@ -0,0 +1,210 @@
1import { DatePipe } from '@angular/common'
2import { environment } from '../../environments/environment'
3import { AuthService } from '../core/auth'
4
5// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
6function getParameterByName (name: string, url: string) {
7 if (!url) url = window.location.href
8 name = name.replace(/[\[\]]/g, '\\$&')
9
10 const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
11 const results = regex.exec(url)
12
13 if (!results) return null
14 if (!results[2]) return ''
15
16 return decodeURIComponent(results[2].replace(/\+/g, ' '))
17}
18
19function populateAsyncUserVideoChannels (authService: AuthService, channel: { id: number, label: string, support?: string }[]) {
20 return new Promise(res => {
21 authService.userInformationLoaded
22 .subscribe(
23 () => {
24 const user = authService.getUser()
25 if (!user) return
26
27 const videoChannels = user.videoChannels
28 if (Array.isArray(videoChannels) === false) return
29
30 videoChannels.forEach(c => channel.push({ id: c.id, label: c.displayName, support: c.support }))
31
32 return res()
33 }
34 )
35 })
36}
37
38function getAbsoluteAPIUrl () {
39 let absoluteAPIUrl = environment.apiUrl
40 if (!absoluteAPIUrl) {
41 // The API is on the same domain
42 absoluteAPIUrl = window.location.origin
43 }
44
45 return absoluteAPIUrl
46}
47
48const datePipe = new DatePipe('en')
49function dateToHuman (date: string) {
50 return datePipe.transform(date, 'medium')
51}
52
53function durationToString (duration: number) {
54 const hours = Math.floor(duration / 3600)
55 const minutes = Math.floor((duration % 3600) / 60)
56 const seconds = duration % 60
57
58 const minutesPadding = minutes >= 10 ? '' : '0'
59 const secondsPadding = seconds >= 10 ? '' : '0'
60 const displayedHours = hours > 0 ? hours.toString() + ':' : ''
61
62 return (
63 displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString()
64 ).replace(/^0/, '')
65}
66
67function immutableAssign <A, B> (target: A, source: B) {
68 return Object.assign({}, target, source)
69}
70
71function objectToUrlEncoded (obj: any) {
72 const str: string[] = []
73 for (const key of Object.keys(obj)) {
74 str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
75 }
76
77 return str.join('&')
78}
79
80// Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34
81function objectToFormData (obj: any, form?: FormData, namespace?: string) {
82 const fd = form || new FormData()
83 let formKey
84
85 for (const key of Object.keys(obj)) {
86 if (namespace) formKey = `${namespace}[${key}]`
87 else formKey = key
88
89 if (obj[key] === undefined) continue
90
91 if (Array.isArray(obj[key]) && obj[key].length === 0) {
92 fd.append(key, null)
93 continue
94 }
95
96 if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) {
97 objectToFormData(obj[ key ], fd, formKey)
98 } else {
99 fd.append(formKey, obj[ key ])
100 }
101 }
102
103 return fd
104}
105
106function objectLineFeedToHtml (obj: any, keyToNormalize: string) {
107 return immutableAssign(obj, {
108 [keyToNormalize]: lineFeedToHtml(obj[keyToNormalize])
109 })
110}
111
112function lineFeedToHtml (text: string) {
113 if (!text) return text
114
115 return text.replace(/\r?\n|\r/g, '<br />')
116}
117
118function removeElementFromArray <T> (arr: T[], elem: T) {
119 const index = arr.indexOf(elem)
120 if (index !== -1) arr.splice(index, 1)
121}
122
123function sortBy (obj: any[], key1: string, key2?: string) {
124 return obj.sort((a, b) => {
125 const elem1 = key2 ? a[key1][key2] : a[key1]
126 const elem2 = key2 ? b[key1][key2] : b[key1]
127
128 if (elem1 < elem2) return -1
129 if (elem1 === elem2) return 0
130 return 1
131 })
132}
133
134function scrollToTop () {
135 window.scroll(0, 0)
136}
137
138// Thanks: https://github.com/uupaa/dynamic-import-polyfill
139function importModule (path: string) {
140 return new Promise((resolve, reject) => {
141 const vector = '$importModule$' + Math.random().toString(32).slice(2)
142 const script = document.createElement('script')
143
144 const destructor = () => {
145 delete window[ vector ]
146 script.onerror = null
147 script.onload = null
148 script.remove()
149 URL.revokeObjectURL(script.src)
150 script.src = ''
151 }
152
153 script.defer = true
154 script.type = 'module'
155
156 script.onerror = () => {
157 reject(new Error(`Failed to import: ${path}`))
158 destructor()
159 }
160 script.onload = () => {
161 resolve(window[ vector ])
162 destructor()
163 }
164 const absURL = (environment.apiUrl || window.location.origin) + path
165 const loader = `import * as m from "${absURL}"; window.${vector} = m;` // export Module
166 const blob = new Blob([ loader ], { type: 'text/javascript' })
167 script.src = URL.createObjectURL(blob)
168
169 document.head.appendChild(script)
170 })
171}
172
173function isInViewport (el: HTMLElement) {
174 const bounding = el.getBoundingClientRect()
175 return (
176 bounding.top >= 0 &&
177 bounding.left >= 0 &&
178 bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
179 bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
180 )
181}
182
183function isXPercentInViewport (el: HTMLElement, percentVisible: number) {
184 const rect = el.getBoundingClientRect()
185 const windowHeight = (window.innerHeight || document.documentElement.clientHeight)
186
187 return !(
188 Math.floor(100 - (((rect.top >= 0 ? 0 : rect.top) / +-(rect.height / 1)) * 100)) < percentVisible ||
189 Math.floor(100 - ((rect.bottom - windowHeight) / rect.height) * 100) < percentVisible
190 )
191}
192
193export {
194 sortBy,
195 durationToString,
196 lineFeedToHtml,
197 objectToUrlEncoded,
198 getParameterByName,
199 populateAsyncUserVideoChannels,
200 getAbsoluteAPIUrl,
201 dateToHuman,
202 immutableAssign,
203 objectToFormData,
204 objectLineFeedToHtml,
205 removeElementFromArray,
206 importModule,
207 scrollToTop,
208 isInViewport,
209 isXPercentInViewport
210}
diff --git a/client/src/app/helpers/zone.ts b/client/src/app/helpers/zone.ts
new file mode 100644
index 000000000..74eed7032
--- /dev/null
+++ b/client/src/app/helpers/zone.ts
@@ -0,0 +1,40 @@
1import { SchedulerLike, Subscription } from 'rxjs'
2import { NgZone } from '@angular/core'
3
4class LeaveZoneScheduler implements SchedulerLike {
5 constructor (private zone: NgZone, private scheduler: SchedulerLike) {
6 }
7
8 schedule (...args: any[]): Subscription {
9 return this.zone.runOutsideAngular(() =>
10 this.scheduler.schedule.apply(this.scheduler, args)
11 )
12 }
13
14 now (): number {
15 return this.scheduler.now()
16 }
17}
18
19class EnterZoneScheduler implements SchedulerLike {
20 constructor (private zone: NgZone, private scheduler: SchedulerLike) {
21 }
22
23 schedule (...args: any[]): Subscription {
24 return this.zone.run(() =>
25 this.scheduler.schedule.apply(this.scheduler, args)
26 )
27 }
28
29 now (): number {
30 return this.scheduler.now()
31 }
32}
33
34export function leaveZone (zone: NgZone, scheduler: SchedulerLike): SchedulerLike {
35 return new LeaveZoneScheduler(zone, scheduler)
36}
37
38export function enterZone (zone: NgZone, scheduler: SchedulerLike): SchedulerLike {
39 return new EnterZoneScheduler(zone, scheduler)
40}