ambient-display 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/build/client/_app/immutable/assets/0.Dl9__I4E.css +1 -0
  2. package/build/client/_app/immutable/assets/0.Dl9__I4E.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.Dl9__I4E.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/2.BtzFOBtk.css +1 -0
  5. package/build/client/_app/immutable/assets/2.BtzFOBtk.css.br +0 -0
  6. package/build/client/_app/immutable/assets/2.BtzFOBtk.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/3.BvcZlbFP.css +1 -0
  8. package/build/client/_app/immutable/assets/3.BvcZlbFP.css.br +0 -0
  9. package/build/client/_app/immutable/assets/3.BvcZlbFP.css.gz +0 -0
  10. package/build/client/_app/immutable/assets/LoadingIndicator.D0m6rSKQ.css +1 -0
  11. package/build/client/_app/immutable/assets/LoadingIndicator.D0m6rSKQ.css.br +0 -0
  12. package/build/client/_app/immutable/assets/LoadingIndicator.D0m6rSKQ.css.gz +0 -0
  13. package/build/client/_app/immutable/assets/PlayingTracker.9uM5nyWg.css +1 -0
  14. package/build/client/_app/immutable/assets/PlayingTracker.9uM5nyWg.css.br +0 -0
  15. package/build/client/_app/immutable/assets/PlayingTracker.9uM5nyWg.css.gz +0 -0
  16. package/build/client/_app/immutable/assets/_layout.Dl9__I4E.css +1 -0
  17. package/build/client/_app/immutable/assets/_layout.Dl9__I4E.css.br +0 -0
  18. package/build/client/_app/immutable/assets/_layout.Dl9__I4E.css.gz +0 -0
  19. package/build/client/_app/immutable/assets/_page.BtzFOBtk.css +1 -0
  20. package/build/client/_app/immutable/assets/_page.BtzFOBtk.css.br +0 -0
  21. package/build/client/_app/immutable/assets/_page.BtzFOBtk.css.gz +0 -0
  22. package/build/client/_app/immutable/assets/_page.BvcZlbFP.css +1 -0
  23. package/build/client/_app/immutable/assets/_page.BvcZlbFP.css.br +0 -0
  24. package/build/client/_app/immutable/assets/_page.BvcZlbFP.css.gz +0 -0
  25. package/build/client/_app/immutable/chunks/LoadingIndicator.COVTk436.js +1 -0
  26. package/build/client/_app/immutable/chunks/LoadingIndicator.COVTk436.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/LoadingIndicator.COVTk436.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/PlayingTracker.Cymhk9j5.js +5 -0
  29. package/build/client/_app/immutable/chunks/PlayingTracker.Cymhk9j5.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/PlayingTracker.Cymhk9j5.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/disclose-version.N57b1q78.js +1 -0
  32. package/build/client/_app/immutable/chunks/disclose-version.N57b1q78.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/disclose-version.N57b1q78.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/entry.CTcSu7Oh.js +3 -0
  35. package/build/client/_app/immutable/chunks/entry.CTcSu7Oh.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/entry.CTcSu7Oh.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/index-client.DV07uIiZ.js +1 -0
  38. package/build/client/_app/immutable/chunks/index-client.DV07uIiZ.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/index-client.DV07uIiZ.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/index.DFULH2AN.js +1 -0
  41. package/build/client/_app/immutable/chunks/index.DFULH2AN.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/index.DFULH2AN.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/index.DLR8Bvs6.js +4 -0
  44. package/build/client/_app/immutable/chunks/index.DLR8Bvs6.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/index.DLR8Bvs6.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/legacy.DZgMwYuQ.js +1 -0
  47. package/build/client/_app/immutable/chunks/legacy.DZgMwYuQ.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/legacy.DZgMwYuQ.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/props.DYfFZGja.js +1 -0
  50. package/build/client/_app/immutable/chunks/props.DYfFZGja.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/props.DYfFZGja.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/snippet.BVSCguOu.js +1 -0
  53. package/build/client/_app/immutable/chunks/snippet.BVSCguOu.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/snippet.BVSCguOu.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/this.EZLWgc5v.js +1 -0
  56. package/build/client/_app/immutable/chunks/this.EZLWgc5v.js.br +0 -0
  57. package/build/client/_app/immutable/chunks/this.EZLWgc5v.js.gz +0 -0
  58. package/build/client/_app/immutable/chunks/utils.BXcQV2KO.js +1 -0
  59. package/build/client/_app/immutable/chunks/utils.BXcQV2KO.js.br +0 -0
  60. package/build/client/_app/immutable/chunks/utils.BXcQV2KO.js.gz +0 -0
  61. package/build/client/_app/immutable/entry/app.DHMJZPqS.js +2 -0
  62. package/build/client/_app/immutable/entry/app.DHMJZPqS.js.br +0 -0
  63. package/build/client/_app/immutable/entry/app.DHMJZPqS.js.gz +0 -0
  64. package/build/client/_app/immutable/entry/start.BGJKS9mR.js +1 -0
  65. package/build/client/_app/immutable/entry/start.BGJKS9mR.js.br +2 -0
  66. package/build/client/_app/immutable/entry/start.BGJKS9mR.js.gz +0 -0
  67. package/build/client/_app/immutable/nodes/0.DaJoi3FK.js +2 -0
  68. package/build/client/_app/immutable/nodes/0.DaJoi3FK.js.br +0 -0
  69. package/build/client/_app/immutable/nodes/0.DaJoi3FK.js.gz +0 -0
  70. package/build/client/_app/immutable/nodes/1.0EwG4xPT.js +1 -0
  71. package/build/client/_app/immutable/nodes/1.0EwG4xPT.js.br +0 -0
  72. package/build/client/_app/immutable/nodes/1.0EwG4xPT.js.gz +0 -0
  73. package/build/client/_app/immutable/nodes/2.CIcrK0Mf.js +1 -0
  74. package/build/client/_app/immutable/nodes/2.CIcrK0Mf.js.br +0 -0
  75. package/build/client/_app/immutable/nodes/2.CIcrK0Mf.js.gz +0 -0
  76. package/build/client/_app/immutable/nodes/3.vzk8SJSG.js +1 -0
  77. package/build/client/_app/immutable/nodes/3.vzk8SJSG.js.br +0 -0
  78. package/build/client/_app/immutable/nodes/3.vzk8SJSG.js.gz +0 -0
  79. package/build/client/_app/version.json +1 -0
  80. package/build/client/_app/version.json.br +0 -0
  81. package/build/client/_app/version.json.gz +0 -0
  82. package/build/client/manifest.json.br +0 -0
  83. package/build/client/manifest.json.gz +0 -0
  84. package/build/env.js +45 -0
  85. package/build/handler.js +1375 -0
  86. package/build/index.js +334 -0
  87. package/build/server/chunks/0-D_yciAvo.js +91 -0
  88. package/build/server/chunks/0-D_yciAvo.js.map +1 -0
  89. package/build/server/chunks/1-BA-qTHOy.js +9 -0
  90. package/build/server/chunks/1-BA-qTHOy.js.map +1 -0
  91. package/build/server/chunks/2-DPsIhdeI.js +9 -0
  92. package/build/server/chunks/2-DPsIhdeI.js.map +1 -0
  93. package/build/server/chunks/3-CZREXwZu.js +9 -0
  94. package/build/server/chunks/3-CZREXwZu.js.map +1 -0
  95. package/build/server/chunks/LoadingIndicator-CTUFEPkL.js +10 -0
  96. package/build/server/chunks/LoadingIndicator-CTUFEPkL.js.map +1 -0
  97. package/build/server/chunks/LoadingIndicator.svelte_svelte_type_style_lang-CVdBHA1v.js +172 -0
  98. package/build/server/chunks/LoadingIndicator.svelte_svelte_type_style_lang-CVdBHA1v.js.map +1 -0
  99. package/build/server/chunks/PlayingTracker-BIq1bdv_.js +18 -0
  100. package/build/server/chunks/PlayingTracker-BIq1bdv_.js.map +1 -0
  101. package/build/server/chunks/_layout.svelte-BoFI04Ng.js +24 -0
  102. package/build/server/chunks/_layout.svelte-BoFI04Ng.js.map +1 -0
  103. package/build/server/chunks/_page.svelte-C3Mw7ZhL.js +115 -0
  104. package/build/server/chunks/_page.svelte-C3Mw7ZhL.js.map +1 -0
  105. package/build/server/chunks/_page.svelte-CzGyTA7b.js +368 -0
  106. package/build/server/chunks/_page.svelte-CzGyTA7b.js.map +1 -0
  107. package/build/server/chunks/error.svelte-BZE1ioPX.js +116 -0
  108. package/build/server/chunks/error.svelte-BZE1ioPX.js.map +1 -0
  109. package/build/server/chunks/exports-DAjI6ZSp.js +125 -0
  110. package/build/server/chunks/exports-DAjI6ZSp.js.map +1 -0
  111. package/build/server/chunks/index2-BA59f76P.js +1214 -0
  112. package/build/server/chunks/index2-BA59f76P.js.map +1 -0
  113. package/build/server/index.js +4868 -0
  114. package/build/server/index.js.map +1 -0
  115. package/build/server/manifest.js +50 -0
  116. package/build/server/manifest.js.map +1 -0
  117. package/build/shims.js +32 -0
  118. package/package.json +9 -2
  119. package/server/index.js +4 -0
  120. package/server/spotify_auth.json +1 -0
  121. package/.prettierignore +0 -4
  122. package/.prettierrc +0 -17
  123. package/CHANGELOG.md +0 -34
  124. package/env.template +0 -2
  125. package/eslint.config.js +0 -24
  126. package/jsconfig.json +0 -19
  127. package/screenshot.png +0 -0
  128. package/src/app.d.ts +0 -13
  129. package/src/app.html +0 -12
  130. package/src/lib/actions/qr.svelte.js +0 -23
  131. package/src/lib/comms.js +0 -25
  132. package/src/lib/components/AuthenticateTrigger.svelte +0 -74
  133. package/src/lib/components/Controls.svelte +0 -91
  134. package/src/lib/components/ImageLoad.svelte +0 -79
  135. package/src/lib/components/LoadingIndicator.svelte +0 -75
  136. package/src/lib/components/MediaItem.svelte +0 -75
  137. package/src/lib/components/PlayingTracker.svelte +0 -94
  138. package/src/lib/components/ResultsList.svelte +0 -80
  139. package/src/lib/components/Toast/Item.svelte +0 -71
  140. package/src/lib/components/Toast/Manager.svelte +0 -34
  141. package/src/lib/icons/disc.svg +0 -1
  142. package/src/lib/index.js +0 -1
  143. package/src/lib/store.js +0 -146
  144. package/src/lib/styles.scss +0 -166
  145. package/src/lib/toast.js +0 -57
  146. package/src/lib/utils.js +0 -723
  147. package/src/routes/+layout.server.js +0 -25
  148. package/src/routes/+layout.svelte +0 -72
  149. package/src/routes/+page.svelte +0 -381
  150. package/src/routes/player/+page.svelte +0 -294
  151. package/svelte.config.js +0 -19
  152. package/tools/BuildManifest.js +0 -87
  153. package/vite.config.js +0 -46
  154. /package/{static → build/client}/favicon.ico +0 -0
  155. /package/{static → build/client}/favicon.png +0 -0
  156. /package/{static → build/client}/icons/144.favicon.png +0 -0
  157. /package/{static → build/client}/icons/168.favicon.png +0 -0
  158. /package/{static → build/client}/icons/192.favicon.png +0 -0
  159. /package/{static → build/client}/icons/48.favicon.png +0 -0
  160. /package/{static → build/client}/icons/72.favicon.png +0 -0
  161. /package/{static → build/client}/icons/96.favicon.png +0 -0
  162. /package/{static → build/client}/manifest.json +0 -0
package/src/lib/utils.js DELETED
@@ -1,723 +0,0 @@
1
- /**
2
- * A performant version of a function call, used to keep functions
3
- * running at 60fps
4
- *
5
- * @param {function} cb
6
- * @returns {function}
7
- */
8
- export const tickUpdate = (cb) => {
9
- let ticking = false;
10
-
11
- const update = (e) => {
12
- cb(e);
13
- ticking = false;
14
- };
15
-
16
- const requestTick = (e) => {
17
- if (!ticking) {
18
- requestAnimationFrame(() => update(e));
19
- ticking = true;
20
- }
21
- };
22
-
23
- return requestTick;
24
- };
25
-
26
- /**
27
- * Clamps a number
28
- *
29
- * @param {number} num
30
- * @param {number} min
31
- * @param {number} max
32
- * @returns {number}
33
- */
34
- export const clamp = (num, min, max) => {
35
- return Math.min(Math.max(num, min), max);
36
- };
37
-
38
- /**
39
- * Maps a number from 1 range to another. I cannot believe
40
- * how much I've used this function in my lifetime
41
- *
42
- * @param {number} value
43
- * @param {number} x1
44
- * @param {number} y1
45
- * @param {number} x2
46
- * @param {number} y2
47
- * @returns {number}
48
- */
49
- export const mapRange = (value, x1, y1, x2, y2) => ((value - x1) * (y2 - x2)) / (y1 - x1) + x2;
50
-
51
- /**
52
- * A simple linear interpolation function
53
- *
54
- * @param {number} v0 Lower value
55
- * @param {number} v1 Target Value
56
- * @param {*} t
57
- * @returns
58
- */
59
- export const lerp = (v0, v1, t) => {
60
- return v0 * (1 - t) + v1 * t;
61
- };
62
-
63
- /**
64
- * A promise version of setTimeout
65
- *
66
- * @param {number} time
67
- * @param {boolean} error
68
- * @returns {Promise<void>}
69
- */
70
- export const timer = (time = 2000, error = false) => {
71
- return new Promise((resolve, reject) => {
72
- setTimeout(() => {
73
- if (error) {
74
- reject();
75
- } else {
76
- resolve();
77
- }
78
- }, time);
79
- });
80
- };
81
-
82
- /**
83
- * Serialised object of form elements
84
- *
85
- * @param {FormData} fd
86
- * @returns {object}
87
- */
88
- export const formToObject = (fd) => {
89
- return [...fd.entries()].reduce(
90
- (prev, curr) => ({
91
- ...prev,
92
- [curr[0]]: curr[1]
93
- }),
94
- {}
95
- );
96
- };
97
-
98
- /**
99
- * Convenience function that registers the 2 window events needed
100
- * to listen for window resizes
101
- *
102
- * @param {function} cb
103
- * @returns {function} Unlisten
104
- */
105
- export const onWindowResize = (cb) => {
106
- window.addEventListener('resize', cb, {
107
- passive: true
108
- });
109
-
110
- window.addEventListener('orientationchange', cb, {
111
- passive: true
112
- });
113
-
114
- return () => {
115
- window.removeEventListener('resize', cb);
116
- window.removeEventListener('orientationchange', cb);
117
- };
118
- };
119
-
120
- /**
121
- * Registers a 'bootleg' vh unit, to be used in css like
122
- * var(--vh), based off of actual dimensions not necessarily what
123
- * the real css value gives
124
- *
125
- * Should be superceded by lvh / dvh / svh
126
- *
127
- * @returns {function} Unlisten
128
- */
129
- export const registerBootlegVH = () => {
130
- // Cached value of window outer height
131
- let wh = 0;
132
-
133
- const setVh = () => {
134
- const nh = window.outerHeight;
135
-
136
- // If window outer height doesnt match the browser window really has changed
137
- // not just the nav bar disappearing
138
- if (wh !== nh) {
139
- wh = nh;
140
-
141
- // Set a 'global' property of 1vh to use in your css like
142
- // calc(var(--vh) * WHATEVER_NUM)
143
- document.documentElement.style.setProperty('--vh', `${window.innerHeight / 100}px`);
144
- }
145
- };
146
-
147
- const cb = tickUpdate(() => {
148
- setVh();
149
- });
150
-
151
- setVh();
152
-
153
- return onWindowResize(cb);
154
- };
155
-
156
- /**
157
- * Returns a debounced version of the function, so that it runs
158
- * only after X seconds of not being called
159
- *
160
- * @param {function} cb
161
- * @param {number} time
162
- * @returns {function}
163
- */
164
- export const debounce = (cb, time = 1000) => {
165
- let timer = 0;
166
-
167
- return function () {
168
- clearTimeout(timer);
169
- timer = setTimeout(() => {
170
- cb(...arguments);
171
- }, time);
172
- };
173
- };
174
-
175
- /**
176
- * Returns a throttled version of the function, so that it runs only
177
- * a maximum of X seconds
178
- *
179
- * @param {function} cb
180
- * @param {number} time
181
- * @returns {function}
182
- */
183
- export const throttle = (cb, time = 1000) => {
184
- let timer;
185
-
186
- return function () {
187
- if (timer) {
188
- return;
189
- }
190
-
191
- timer = setTimeout(() => {
192
- cb(...arguments);
193
- timer = undefined;
194
- }, time);
195
- };
196
- };
197
-
198
- /**
199
- * A promise to load an image element
200
- *
201
- * @param {string} src
202
- * @returns {Promise<HTMLImageElement>}
203
- */
204
- export const loadImageElement = (src) => {
205
- return new Promise((resolve, reject) => {
206
- const img = new Image();
207
- img.onload = () => resolve(img);
208
- img.onerror = () => reject();
209
- img.src = src;
210
- });
211
- };
212
-
213
- /**
214
- * A promise to load an image as a blob url
215
- *
216
- * @param {string} src
217
- * @param {function} onSignal A callback to get an abort signal
218
- * @returns {Promise<string>}
219
- */
220
- export const loadImage = (src, onSignal = () => false) => {
221
- const controller = new AbortController();
222
- const { signal } = controller;
223
-
224
- onSignal(controller);
225
-
226
- return fetch(src, { signal })
227
- .then((resp) => resp.blob())
228
- .then((blob) => URL.createObjectURL(blob));
229
- };
230
-
231
- /**
232
- * A function to register a change listener on a media query
233
- *
234
- * @param {string} query
235
- * @param {function} cb
236
- * @returns {function} Unlisten
237
- */
238
- export const breakpointListen = (query, cb) => {
239
- const mq = window.matchMedia(query);
240
- const onCall = (ev) => {
241
- cb(ev.matches);
242
- };
243
- mq.addListener(onCall);
244
-
245
- cb(mq.matches);
246
-
247
- return () => mq.removeListener(onCall);
248
- };
249
-
250
- /**
251
- * Adds an event listener and returns a cb to unlisten
252
- *
253
- * @param {HTMLElement | Window | Document} el
254
- * @param {string} evt
255
- * @param {function} cb
256
- * @param {object} opts
257
- * @returns {() => void)} Unlisten
258
- */
259
- export const listenCb = (el, evt, cb, opts = false) => {
260
- el.addEventListener(evt, cb, opts);
261
- return () => el.removeEventListener(evt, cb);
262
- };
263
-
264
- /**
265
- * Get random number between 2 numbers
266
- *
267
- * @param {any[]} arr
268
- * @returns {any}
269
- */
270
- export const random = (arr) => {
271
- return arr[Math.floor(Math.random() * arr.length)];
272
- };
273
-
274
- /**
275
- * Get random number between 2 numbers
276
- *
277
- * @param {number} min
278
- * @param {number} max
279
- * @returns {number}
280
- */
281
- export const randomBetween = (min, max) => {
282
- return Math.random() * (max - min) + min;
283
- };
284
-
285
- /**
286
- * Randomises array
287
- *
288
- * @param {array} array
289
- * @returns {array}
290
- */
291
- export const shuffle = (array) => {
292
- let currentIndex = array.length,
293
- randomIndex;
294
-
295
- // While there remain elements to shuffle...
296
- while (currentIndex != 0) {
297
- // Pick a remaining element...
298
- randomIndex = Math.floor(Math.random() * currentIndex);
299
- currentIndex--;
300
-
301
- // And swap it with the current element.
302
- [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
303
- }
304
-
305
- return array;
306
- };
307
-
308
- const TWEETS = [() => `Hey this is a tweet, https://mschf.com/`];
309
-
310
- /**
311
- * A function that will randomly return a constructed twitter intent link, to circumvent twitter
312
- * flagging the same text as spam
313
- *
314
- * @param {function[]} options An array of functions that return a string, for the text of the tweet
315
- * @param {Object} values Values that are passed into the tweet string for variables
316
- * @returns {string} Twitter intent URL
317
- */
318
- export const tweeter = (options = TWEETS, values = {}) => {
319
- const generateTweet = () => {
320
- return TWEETS[Math.floor(Math.random() * TWEETS.length)]();
321
- };
322
-
323
- return `https://twitter.com/intent/tweet?text=${encodeURIComponent(generateTweet())}`;
324
- };
325
-
326
- /**
327
- * Utility to help detect low power mode
328
- *
329
- * @returns {Promise<Boolean>}
330
- */
331
- export const isLowPower = () => {
332
- /**
333
- * Sets up a promise for making sure the video loads before testing.
334
- * Uses the suspend event which apparently fires regardless for when
335
- * the video has finished downloading. Then sets a can play event listener
336
- * to be double sure but also sets a timer, if the timer resolves (which
337
- * on environments in which low power mode is off, should never happen) it
338
- * can assume the video can't be played
339
- *
340
- * @param {HTMLVideoElement} el
341
- * @returns
342
- */
343
- const loadVideo = (el) => {
344
- return new Promise((resolve, reject) => {
345
- let timer = 0;
346
-
347
- const onCanPlay = () => {
348
- clearTimeout(timer);
349
-
350
- //loadedmetadata not canplay for ios boohoo
351
- el.removeEventListener('loadedmetadata', onCanPlay);
352
- resolve(el);
353
- };
354
-
355
- const onSuspend = () => {
356
- el.removeEventListener('suspend', onSuspend);
357
-
358
- timer = setTimeout(() => {
359
- reject('too long');
360
- }, 500);
361
- };
362
-
363
- el.addEventListener('suspend', onSuspend);
364
- el.addEventListener('loadedmetadata', onCanPlay);
365
- el.load();
366
- });
367
- };
368
-
369
- return new Promise((resolve, reject) => {
370
- let videoEl = document.querySelector(`#lowpower`);
371
-
372
- // Create a video element if the test one can't be found already mounted
373
- if (!videoEl) {
374
- // console.log("no video test creating");
375
- videoEl = document.createElement('video');
376
- videoEl.loop = true;
377
- videoEl.playsInline = true;
378
- videoEl.muted = true;
379
- videoEl.id = `lowpower`;
380
- // Very specifically dont add autoplay
381
- // videoEl.autoplay = true;
382
-
383
- const source = document.createElement('source');
384
- source.type = `video/mp4`;
385
- // 5kb
386
- source.src = `data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACU1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2MyByMzA2MCA1ZGI2YWE2IC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyMSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAACDmWIhAB//vSj+BTSaXUBsxCW6PzALg74FA/dgccVg8EGt2Axk/JyOG7stC2aelhG3YCVoMYV2/RArmL7h0CSik6m2NszuBRPfgJVVEdSBW9gY0RFBzNtpfAni35C88ncWv2vVr3OgW5fxUvf1kHhSL8kOfc4pR8/DB7NsbDd1WSarzAKL2PFpHv0I8wFitjhss2xi0KGby5UFyRLoTo4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`;
387
-
388
- videoEl.appendChild(source);
389
-
390
- document.body.appendChild(videoEl);
391
- }
392
-
393
- // Needs to be displayed, some browsers will not play
394
- // if display is none
395
- videoEl.style.position = 'fixed';
396
- videoEl.style.width = `100px`;
397
- videoEl.style.zIndex = 100;
398
- // videoEl.style.visibility = "hidden";
399
- // videoEl.style.opacity = 0;
400
-
401
- loadVideo(videoEl)
402
- .then(() => resolve(videoEl))
403
- .catch((err) => reject(err));
404
- })
405
- .then((el) => {
406
- // Play the video which should be allowed as its
407
- // muted and can play inline
408
- return el
409
- .play()
410
- .then(() => false)
411
- .catch((e) => {
412
- console.error(e);
413
- return true;
414
- });
415
- })
416
- .catch(() => {
417
- return true;
418
- });
419
- };
420
-
421
- const LOCAL_STORAGE_PREFIX = 'mschf';
422
-
423
- /**
424
- * Convenience feature for grabbing a value from the local storage
425
- * with a default fallback if not present
426
- *
427
- * @param {string} key
428
- * @param {any} defaultValue
429
- * @param {function} transform
430
- * @returns {string|any}
431
- */
432
- export const getPersistedValue = (key, defaultValue, transform = (val) => val) => {
433
- const item = localStorage.getItem([LOCAL_STORAGE_PREFIX, key].join('-'));
434
-
435
- return item ? transform(item) : defaultValue;
436
- };
437
-
438
- /**
439
- * Saves a value to local storage
440
- *
441
- * @param {string} key
442
- * @param {string} value
443
- */
444
- export const persistValue = (key, value) => {
445
- localStorage.setItem([LOCAL_STORAGE_PREFIX, key].join('-'), value);
446
- };
447
-
448
- /**
449
- * Convenience function to remove item with global prefix
450
- *
451
- * @param {string} key
452
- */
453
- export const removePersistedValue = (key) => {
454
- localStorage.removeItem([LOCAL_STORAGE_PREFIX, key].join('-'));
455
- };
456
-
457
- /**
458
- * Turns a ISO-like date string into a readable date format
459
- *
460
- * @param {string} dateString
461
- * @returns {string}
462
- */
463
- export const readableDate = (dateString) => {
464
- const d = new Date(dateString);
465
-
466
- return [d.getMonth() + 1, d.getDate()].map((t) => t.toString().padStart(2, '0')).join('.');
467
- };
468
-
469
- /**
470
- * Turns a ISO-like date string into a readable time format
471
- *
472
- * @param {string} dateString
473
- * @returns {string}
474
- */
475
- export const readableTime = (dateString) => {
476
- const d = new Date(dateString);
477
- let timeStr = new Intl.DateTimeFormat('en-US', {
478
- timeZone: 'America/New_York',
479
- hour: 'numeric',
480
- hour12: true
481
- }).format(d);
482
- return `${timeStr.replace(' ', '')} ET`;
483
- };
484
-
485
- /**
486
- * A promisified version of a requestAnimationFrame as a utility
487
- *
488
- * @returns {Promise<void>}
489
- */
490
- export const singleRaf = () => {
491
- return new Promise((resolve) => {
492
- requestAnimationFrame(() => {
493
- resolve();
494
- });
495
- });
496
- };
497
-
498
- /**
499
- * A double version of the requestAnimationFrame promise
500
- *
501
- * @returns {Promise<void>}
502
- */
503
- export const doubleRaf = async () => {
504
- await singleRaf();
505
- return singleRaf();
506
- };
507
-
508
- const KEYS = {
509
- ESCAPE: 27
510
- };
511
-
512
- /**
513
- * A function that registers a key listener for escaping
514
- *
515
- * @param {function} onEscape
516
- * @returns {function} Unlisten
517
- */
518
- export const registerExits = (onEscape) => {
519
- const cb = (e) => {
520
- if ([KEYS.ESCAPE].includes(e.keyCode)) {
521
- onEscape();
522
- }
523
- };
524
-
525
- return listenCb(document, 'keyup', cb);
526
- };
527
-
528
- /**
529
- * A function for adding a listener to determine if an interaction was made outside
530
- * an element of its descendents
531
- *
532
- * @param {HTMLElement} el
533
- * @param {function} onClickOutside
534
- * @param {function} [validator]
535
- * @returns {function} Unlisten
536
- */
537
- export const clickOutside = (
538
- el,
539
- onClickOutside,
540
- validator // (el: HTMLElement, e: MouseEvent) => boolean
541
- ) => {
542
- const cb = (e) => {
543
- if (validator) {
544
- if (validator(el, e)) {
545
- onClickOutside();
546
- }
547
- } else if (e.target && e.target !== el && !el.contains(e.target)) {
548
- onClickOutside();
549
- }
550
- };
551
-
552
- const unlisten = listenCb(document, 'click', cb);
553
- const unregisterExits = registerExits(onClickOutside);
554
-
555
- return () => {
556
- unregisterExits();
557
- unlisten();
558
- };
559
- };
560
-
561
- export const formatCurrency = (val, { division = 1, currency = 'USD', trim = true } = {}) => {
562
- const formatted = new Intl.NumberFormat('en-US', {
563
- style: 'currency',
564
- currency
565
- }).format(val / division);
566
-
567
- return formatted.endsWith('.00') && trim ? formatted.split('.').shift() : formatted;
568
- };
569
-
570
- export const getBearerToken = (str) => {
571
- const [header, token] = str.split(' ');
572
-
573
- return token;
574
- };
575
-
576
- export const offscreenCanvasToUrl = async (canvas) => {
577
- const blob = await canvas.convertToBlob();
578
-
579
- return new Promise((resolve) => {
580
- const fr = new FileReader();
581
- fr.addEventListener('load', () => resolve(fr.result));
582
- fr.readAsDataURL(blob);
583
- });
584
- };
585
-
586
- /**
587
- *
588
- * @param {any} val
589
- * @param {any[]} options
590
- * @param {any} defaultVal
591
- * @returns
592
- */
593
- export function validate(val, options, defaultVal = null) {
594
- return options.includes(val) ? val : (defaultVal ?? options[0]);
595
- }
596
-
597
- /**
598
- * An array unlisten function
599
- *
600
- * @param {function[]} arr
601
- */
602
- export function u(arr) {
603
- return () => arr.forEach((cb) => cb());
604
- }
605
-
606
- const EasingFunctions = Object.freeze({
607
- // no easing, no acceleration
608
- linear: (t) => t,
609
- // accelerating from zero velocity
610
- easeInQuad: (t) => t * t,
611
- // decelerating to zero velocity
612
- easeOutQuad: (t) => t * (2 - t),
613
- // acceleration until halfway, then deceleration
614
- easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
615
- // accelerating from zero velocity
616
- easeInCubic: (t) => t * t * t,
617
- // decelerating to zero velocity
618
- easeOutCubic: (t) => --t * t * t + 1,
619
- // acceleration until halfway, then deceleration
620
- easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
621
- // accelerating from zero velocity
622
- easeInQuart: (t) => t * t * t * t,
623
- // decelerating to zero velocity
624
- easeOutQuart: (t) => 1 - --t * t * t * t,
625
- // acceleration until halfway, then deceleration
626
- easeInOutQuart: (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),
627
- // accelerating from zero velocity
628
- easeInQuint: (t) => t * t * t * t * t,
629
- // decelerating to zero velocity
630
- easeOutQuint: (t) => 1 + --t * t * t * t * t,
631
- // acceleration until halfway, then deceleration
632
- easeInOutQuint: (t) => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t)
633
- });
634
-
635
- /**
636
- *
637
- * @param {number} top
638
- * @param {{distancePerSecond?: number, easing?: keyof typeof EasingFunctions}} [options]
639
- * @returns {Promise<void>}
640
- */
641
- export function scrollAtRate(top, { distancePerSecond = 1000, easing = 'linear' } = {}) {
642
- const windowY = window.scrollY;
643
- const totalDistance = Math.abs(windowY - top);
644
- const totalTime = (totalDistance / distancePerSecond) * 1000;
645
-
646
- return scrollPromise(top, { duration: totalTime, easing });
647
- }
648
-
649
- /**
650
- *
651
- * @param {number} top
652
- * @param {{duration?: number, easing?: keyof typeof EasingFunctions}} [options]
653
- * @returns {Promise<void>}
654
- */
655
- export function scrollPromise(top, { duration = 1000, easing = 'linear' } = {}) {
656
- return new Promise((resolve) => {
657
- const windowY = window.scrollY;
658
- const totalTime = duration;
659
-
660
- const startTime = Date.now();
661
-
662
- let running = true;
663
-
664
- window.addEventListener('wheel', () => {
665
- resolve();
666
- running = false;
667
- return;
668
- });
669
-
670
- const next = (dt = 0) => {
671
- if (window.scrollY === top) {
672
- resolve();
673
- return;
674
- }
675
-
676
- const perc = (Date.now() - startTime) / totalTime;
677
- const place = lerp(windowY, top, EasingFunctions[easing](perc));
678
-
679
- window.scrollTo(0, place);
680
-
681
- if (running) {
682
- requestAnimationFrame(next);
683
- }
684
- };
685
- next();
686
- });
687
- }
688
-
689
- export const scroll_lock = (lock = true) => {
690
- const { documentElement, body } = document;
691
-
692
- // RTL <body> scrollbar
693
- const documentLeft = documentElement.getBoundingClientRect().left;
694
- const scrollbarX = Math.round(documentLeft) + documentElement.scrollLeft;
695
- const paddingProp = scrollbarX ? 'paddingLeft' : 'paddingRight';
696
-
697
- if (lock) {
698
- body.style[paddingProp] = `${window.innerWidth - documentElement.clientWidth}px`;
699
- body.style.top = `-${window.scrollY}px`;
700
- body.style.left = `-${window.scrollX}px`;
701
- body.style.right = 0;
702
- body.style.position = 'fixed';
703
-
704
- body.classList.add('scroll-locked');
705
- } else {
706
- if (!body.classList.contains('scroll-locked')) {
707
- return;
708
- }
709
-
710
- const currentScrollY = parseInt(body.style.top || '0') * -1;
711
- const currentScrollX = parseInt(body.style.left || '0') * -1;
712
-
713
- body.style[paddingProp] = '';
714
- body.style.position = '';
715
- body.style.top = '';
716
- body.style.left = '';
717
- body.style.right = '';
718
-
719
- body.classList.remove('scroll-locked');
720
-
721
- window.scrollTo(currentScrollX, currentScrollY);
722
- }
723
- };