@profitlich/template-toolkit 2.9.4 → 2.11.0
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.
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import '@mux/mux-player';
|
|
2
|
+
import config from '../../config.json' with { type: 'json' };
|
|
2
3
|
import './mux-player.scss';
|
|
3
4
|
|
|
4
5
|
export class MuxPlayer {
|
|
5
6
|
#lazyLoadObserver;
|
|
6
7
|
#playPauseObserver;
|
|
8
|
+
#loop;
|
|
9
|
+
#onPlayerCreated;
|
|
7
10
|
|
|
8
|
-
constructor() {
|
|
11
|
+
constructor({ loop = false, onPlayerCreated = null } = {}) {
|
|
12
|
+
this.#loop = loop;
|
|
13
|
+
this.#onPlayerCreated = onPlayerCreated;
|
|
9
14
|
this.#lazyLoadObserver = new IntersectionObserver(this.#handleLazyLoad.bind(this));
|
|
10
15
|
this.#playPauseObserver = new IntersectionObserver(this.#handlePlayPause.bind(this));
|
|
11
16
|
}
|
|
@@ -16,6 +21,40 @@ export class MuxPlayer {
|
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
23
|
|
|
24
|
+
#setupAutoplay(player) {
|
|
25
|
+
// Guard Clause: Führe das Setup nur aus, wenn es noch nicht passiert ist.
|
|
26
|
+
if (player.dataset.isSetup === 'true') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
player.muted = true;
|
|
31
|
+
player.style.setProperty('--controls', 'none');
|
|
32
|
+
if (this.#loop) player.loop = true;
|
|
33
|
+
|
|
34
|
+
// Flag setzen, um zukünftige Ausführungen zu verhindern.
|
|
35
|
+
player.dataset.isSetup = 'true';
|
|
36
|
+
|
|
37
|
+
// Den playPauseObserver starten, sobald die Daten geladen sind.
|
|
38
|
+
if (player.readyState >= 2) { // HAVE_CURRENT_DATA oder höher
|
|
39
|
+
this.#playPauseObserver.observe(player);
|
|
40
|
+
} else {
|
|
41
|
+
player.addEventListener('loadeddata', () => {
|
|
42
|
+
this.#playPauseObserver.observe(player);
|
|
43
|
+
}, { once: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
handleVisibilityChange(player) {
|
|
48
|
+
const isVisible = window.getComputedStyle(player).display !== 'none';
|
|
49
|
+
|
|
50
|
+
if (isVisible && player.hasAttribute('data-autoplay') && player.dataset.isSetup !== 'true') {
|
|
51
|
+
this.#setupAutoplay(player);
|
|
52
|
+
} else if (!isVisible) {
|
|
53
|
+
this.#playPauseObserver.unobserve(player);
|
|
54
|
+
player.pause();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
19
58
|
#handleLazyLoad(entries) {
|
|
20
59
|
for (const entry of entries) {
|
|
21
60
|
if (entry.isIntersecting) {
|
|
@@ -30,69 +69,28 @@ export class MuxPlayer {
|
|
|
30
69
|
player.streamType = 'on-demand';
|
|
31
70
|
player.playsInline = true;
|
|
32
71
|
player.style.aspectRatio = aspectRatio;
|
|
72
|
+
player.thumbnailTime = 0;
|
|
73
|
+
player.minResolution = config.muxMinResolution || '1080p';
|
|
74
|
+
|
|
75
|
+
if (container.dataset.accentColor) {
|
|
76
|
+
player.accentColor = container.dataset.accentColor;
|
|
77
|
+
}
|
|
33
78
|
|
|
34
|
-
// Autoplay-Attribut am Player-Element setzen, damit es später gefunden werden kann
|
|
35
79
|
if (autoplay) {
|
|
36
80
|
player.setAttribute('data-autoplay', 'true');
|
|
37
81
|
}
|
|
38
82
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// Wenn ein Wrapper gefunden wird, richte den MutationObserver ein
|
|
42
|
-
if (wrapper) {
|
|
43
|
-
const visibilityObserver = new MutationObserver(() => {
|
|
44
|
-
this.#handleVisibilityChange(player);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Beobachte den gefundenen Wrapper
|
|
48
|
-
visibilityObserver.observe(wrapper, { attributes: true, childList: true, subtree: true });
|
|
49
|
-
|
|
50
|
-
// Container durch Player ersetzen BEVOR die Sichtbarkeitsprüfung
|
|
51
|
-
container.replaceWith(player);
|
|
83
|
+
container.replaceWith(player);
|
|
52
84
|
|
|
53
|
-
|
|
54
|
-
this.#
|
|
85
|
+
if (this.#onPlayerCreated) {
|
|
86
|
+
this.#onPlayerCreated(player, container);
|
|
55
87
|
} else if (autoplay) {
|
|
56
|
-
|
|
57
|
-
player.muted = true;
|
|
58
|
-
player.style.setProperty('--controls', 'none');
|
|
59
|
-
container.replaceWith(player);
|
|
60
|
-
|
|
61
|
-
player.addEventListener('loadeddata', () => {
|
|
62
|
-
this.#playPauseObserver.observe(player);
|
|
63
|
-
});
|
|
64
|
-
} else {
|
|
65
|
-
// Für Videos ohne Autoplay
|
|
66
|
-
container.replaceWith(player);
|
|
88
|
+
this.handleVisibilityChange(player);
|
|
67
89
|
}
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
92
|
}
|
|
71
93
|
|
|
72
|
-
#handleVisibilityChange(player) {
|
|
73
|
-
// Prüfe direkt am Player, ob er durch CSS sichtbar ist
|
|
74
|
-
const isVisible = window.getComputedStyle(player).display !== 'none';
|
|
75
|
-
|
|
76
|
-
if (isVisible && player.hasAttribute('data-autoplay')) {
|
|
77
|
-
// Wenn der Player sichtbar ist und Autoplay haben soll
|
|
78
|
-
player.muted = true;
|
|
79
|
-
player.style.setProperty('--controls', 'none');
|
|
80
|
-
|
|
81
|
-
// Warte auf loadeddata bevor der IntersectionObserver aktiviert wird
|
|
82
|
-
if (player.readyState >= 2) { // HAVE_CURRENT_DATA oder höher
|
|
83
|
-
this.#playPauseObserver.observe(player);
|
|
84
|
-
} else {
|
|
85
|
-
player.addEventListener('loadeddata', () => {
|
|
86
|
-
this.#playPauseObserver.observe(player);
|
|
87
|
-
}, { once: true });
|
|
88
|
-
}
|
|
89
|
-
} else if (!isVisible) {
|
|
90
|
-
// Wenn der Player unsichtbar wird, pausiere ihn und stoppe die Beobachtung
|
|
91
|
-
this.#playPauseObserver.unobserve(player);
|
|
92
|
-
player.pause();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
94
|
#handlePlayPause(entries) {
|
|
97
95
|
for (const entry of entries) {
|
|
98
96
|
const player = entry.target;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profitlich/template-toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Shared SCSS layout system, JS utilities, components and build scripts for profitlich template repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"./utils/MailAdresses": "./utils/MailAdresses.js",
|
|
17
17
|
"./utils/MediaQueries": "./utils/MediaQueries.js",
|
|
18
18
|
"./utils/Vh100": "./utils/Vh100.js",
|
|
19
|
+
"./utils/images-loaded/ImagesLoaded": "./utils/images-loaded/ImagesLoaded.js",
|
|
19
20
|
"./scripts/copy-files": "./scripts/copy-files.js",
|
|
20
21
|
"./scripts/deploy": "./scripts/deploy.js",
|
|
21
22
|
"./vite/jsonToScss": "./vite/jsonToScss.js"
|
package/scripts/deploy.js
CHANGED
|
@@ -135,12 +135,25 @@ export async function runDeploy(mode, uploadTasks, options = {}) {
|
|
|
135
135
|
);
|
|
136
136
|
|
|
137
137
|
const queue = [...filesToUpload];
|
|
138
|
-
await Promise.all(workers.map(async (
|
|
138
|
+
await Promise.all(workers.map(async (initialWorkerClient) => {
|
|
139
|
+
let workerClient = initialWorkerClient;
|
|
139
140
|
try {
|
|
140
141
|
while (true) {
|
|
141
142
|
const item = queue.shift();
|
|
142
143
|
if (!item) break;
|
|
143
|
-
|
|
144
|
+
let retries = 0;
|
|
145
|
+
while (true) {
|
|
146
|
+
try {
|
|
147
|
+
await workerClient.uploadFrom(item.file, item.remotePath);
|
|
148
|
+
break;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
retries++;
|
|
151
|
+
workerClient.close();
|
|
152
|
+
if (retries >= 3) throw err;
|
|
153
|
+
await delay(1000 * retries);
|
|
154
|
+
workerClient = await createClient(accessOptions);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
144
157
|
progressBar.increment();
|
|
145
158
|
}
|
|
146
159
|
} finally {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import './images-loaded.scss';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Steuert das sanfte Einblenden von Bildern, die nativ (loading="lazy") geladen wurden.
|
|
5
|
+
* Fügt dem Bild das Attribut 'loaded="true"' hinzu, sobald der Ladevorgang erfolgreich abgeschlossen ist.
|
|
6
|
+
*/
|
|
7
|
+
export class ImagesLoaded { // <-- Export hinzufügen
|
|
8
|
+
#selector;
|
|
9
|
+
#attributeName;
|
|
10
|
+
#attributeValue;
|
|
11
|
+
|
|
12
|
+
constructor(selector, loadedAttribute = 'data-loaded', loadedValue = 'true') {
|
|
13
|
+
this.#selector = selector;
|
|
14
|
+
this.#attributeName = loadedAttribute;
|
|
15
|
+
this.#attributeValue = loadedValue;
|
|
16
|
+
|
|
17
|
+
// Führt die Initialisierung erst nach dem vollständigen Laden des DOM aus.
|
|
18
|
+
document.addEventListener('DOMContentLoaded', this.#init.bind(this));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#init() {
|
|
22
|
+
const images = document.querySelectorAll(this.#selector);
|
|
23
|
+
images.forEach(img => this.#setupImage(img));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#setupImage(img) {
|
|
27
|
+
// Prüfen auf geladenen Zustand und gültige Dimensionen
|
|
28
|
+
if (img.complete && img.naturalHeight > 0) {
|
|
29
|
+
this.#signalLoaded(img);
|
|
30
|
+
} else {
|
|
31
|
+
// Ansonsten auf das erfolgreiche Lade-Event warten
|
|
32
|
+
img.addEventListener('load', () => this.#signalLoaded(img));
|
|
33
|
+
|
|
34
|
+
// Fehlerbehandlung (optional)
|
|
35
|
+
img.addEventListener('error', () => {
|
|
36
|
+
console.warn(`[ImagesLoaded] Ladevorgang fehlgeschlagen für: ${img.src}`);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#signalLoaded(img) {
|
|
42
|
+
img.setAttribute(this.#attributeName, this.#attributeValue);
|
|
43
|
+
}
|
|
44
|
+
}
|