@unlk/keymaster 1.6.9 → 1.7.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.
- package/CHANGELOG.md +8 -0
- package/dist/css/keymaster.css +2 -0
- package/dist/css/keymaster.css.map +1 -1
- package/dist/css/keymaster.min.css +1 -1
- package/dist/js/keymaster.js +126 -54
- package/dist/js/keymaster.js.map +1 -1
- package/dist/js/keymaster.min.js +7 -7
- package/dist/js/keymaster.min.js.map +1 -1
- package/js/video-modal.js +174 -58
- package/package.json +1 -1
- package/scss/theme/_version.scss +1 -1
- package/scss/theme/forms/_form-check.scss +2 -1
package/js/video-modal.js
CHANGED
|
@@ -10,55 +10,128 @@ const SELECTOR_MODAL = '.modal';
|
|
|
10
10
|
const SELECTOR_DIALOG = '.modal-dialog-media';
|
|
11
11
|
const SELECTOR_RATIO = '.ratio';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
let
|
|
15
|
-
|
|
13
|
+
let youTubeAPIInitPromise = null;
|
|
14
|
+
let vimeoAPIInitPromise = null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load the YouTube iframe API only when a YouTube video is opened.
|
|
18
|
+
*/
|
|
19
|
+
function loadYouTubeAPI() {
|
|
16
20
|
if (globalThis.YT?.Player) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
return Promise.resolve();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (youTubeAPIInitPromise) {
|
|
25
|
+
return youTubeAPIInitPromise;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
youTubeAPIInitPromise = new Promise((resolve, reject) => {
|
|
29
|
+
const existingScript = document.querySelector(
|
|
30
|
+
'script[src="https://www.youtube.com/iframe_api"]'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const previousReady = globalThis.onYouTubeIframeAPIReady;
|
|
34
|
+
|
|
21
35
|
globalThis.onYouTubeIframeAPIReady = () => {
|
|
22
36
|
try {
|
|
23
|
-
if (typeof
|
|
37
|
+
if (typeof previousReady === 'function') {
|
|
38
|
+
previousReady();
|
|
39
|
+
}
|
|
24
40
|
} catch {}
|
|
25
41
|
|
|
26
|
-
youTubeAPIReady = true;
|
|
27
42
|
resolve();
|
|
28
43
|
};
|
|
29
44
|
|
|
45
|
+
if (existingScript) {
|
|
46
|
+
if (globalThis.YT?.Player) {
|
|
47
|
+
resolve();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
const script = document.createElement('script');
|
|
54
|
+
|
|
31
55
|
script.src = 'https://www.youtube.com/iframe_api';
|
|
56
|
+
script.async = true;
|
|
57
|
+
|
|
58
|
+
script.addEventListener('error', () => {
|
|
59
|
+
youTubeAPIInitPromise = null;
|
|
60
|
+
reject(new Error('Failed to load YouTube iframe API'));
|
|
61
|
+
});
|
|
62
|
+
|
|
32
63
|
document.head.append(script);
|
|
33
|
-
}
|
|
34
|
-
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return youTubeAPIInitPromise;
|
|
67
|
+
}
|
|
35
68
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Load the Vimeo Player API only when a Vimeo video is opened.
|
|
71
|
+
*/
|
|
72
|
+
function loadVimeoAPI() {
|
|
39
73
|
if (globalThis.Vimeo?.Player) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (vimeoAPIInitPromise) {
|
|
78
|
+
return vimeoAPIInitPromise;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
vimeoAPIInitPromise = new Promise((resolve, reject) => {
|
|
82
|
+
const existingScript = document.querySelector(
|
|
83
|
+
'script[src="https://player.vimeo.com/api/player.js"]'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (existingScript) {
|
|
87
|
+
existingScript.addEventListener('load', () => resolve(), { once: true });
|
|
88
|
+
|
|
89
|
+
existingScript.addEventListener(
|
|
90
|
+
'error',
|
|
91
|
+
() => {
|
|
92
|
+
vimeoAPIInitPromise = null;
|
|
93
|
+
reject(new Error('Failed to load Vimeo Player API'));
|
|
94
|
+
},
|
|
95
|
+
{ once: true }
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
43
101
|
const script = document.createElement('script');
|
|
102
|
+
|
|
44
103
|
script.src = 'https://player.vimeo.com/api/player.js';
|
|
45
|
-
script.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
104
|
+
script.async = true;
|
|
105
|
+
|
|
106
|
+
script.addEventListener('load', () => resolve(), { once: true });
|
|
107
|
+
|
|
108
|
+
script.addEventListener(
|
|
109
|
+
'error',
|
|
110
|
+
() => {
|
|
111
|
+
vimeoAPIInitPromise = null;
|
|
112
|
+
reject(new Error('Failed to load Vimeo Player API'));
|
|
113
|
+
},
|
|
114
|
+
{ once: true }
|
|
115
|
+
);
|
|
49
116
|
|
|
50
117
|
document.head.append(script);
|
|
51
|
-
}
|
|
52
|
-
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return vimeoAPIInitPromise;
|
|
121
|
+
}
|
|
53
122
|
|
|
54
123
|
class VideoModal extends BaseComponent {
|
|
55
124
|
constructor(element) {
|
|
56
125
|
super(element);
|
|
57
126
|
|
|
58
127
|
this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);
|
|
128
|
+
|
|
59
129
|
this._container = SelectorEngine.findOne(SELECTOR_RATIO, this._element);
|
|
130
|
+
|
|
60
131
|
this._youtubeSrc = this._container?.getAttribute('data-src');
|
|
132
|
+
|
|
61
133
|
this._vimeoUrl = this._container?.getAttribute('data-vimeo-url');
|
|
134
|
+
|
|
62
135
|
this._youtubePlayer = null;
|
|
63
136
|
this._vimeoPlayer = null;
|
|
64
137
|
}
|
|
@@ -72,53 +145,87 @@ class VideoModal extends BaseComponent {
|
|
|
72
145
|
const { id } = this._element;
|
|
73
146
|
const playerID = `video-${id}-youtube-player`;
|
|
74
147
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
148
|
+
try {
|
|
149
|
+
if (this._vimeoUrl) {
|
|
150
|
+
await loadVimeoAPI();
|
|
78
151
|
|
|
79
|
-
|
|
80
|
-
id: vimeoId,
|
|
81
|
-
autoplay: true
|
|
82
|
-
});
|
|
152
|
+
const vimeoId = this._getVimeoId(this._vimeoUrl);
|
|
83
153
|
|
|
84
|
-
|
|
85
|
-
|
|
154
|
+
if (!vimeoId) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
86
157
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
})
|
|
92
|
-
);
|
|
158
|
+
this._vimeoPlayer = new globalThis.Vimeo.Player(this._container, {
|
|
159
|
+
id: vimeoId,
|
|
160
|
+
autoplay: true
|
|
161
|
+
});
|
|
93
162
|
|
|
94
|
-
|
|
95
|
-
}
|
|
163
|
+
this._element.dataset.vimeoId = String(vimeoId);
|
|
96
164
|
|
|
97
|
-
|
|
98
|
-
await youTubeAPIInitPromise;
|
|
99
|
-
const youtubeId = this._getYouTubeId(this._youtubeSrc);
|
|
100
|
-
const playerDiv = this._container.querySelector(`#${playerID}`);
|
|
165
|
+
this._element.__unlkVimeoPlayer = this._vimeoPlayer;
|
|
101
166
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
167
|
+
this._element.dispatchEvent(
|
|
168
|
+
new CustomEvent('videomodal.ready', {
|
|
169
|
+
bubbles: true,
|
|
170
|
+
detail: {
|
|
171
|
+
provider: 'vimeo',
|
|
172
|
+
player: this._vimeoPlayer,
|
|
173
|
+
videoId: vimeoId
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
);
|
|
106
177
|
|
|
107
|
-
|
|
108
|
-
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
109
180
|
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
181
|
+
if (this._youtubeSrc) {
|
|
182
|
+
await loadYouTubeAPI();
|
|
183
|
+
|
|
184
|
+
const youtubeId = this._getYouTubeId(this._youtubeSrc);
|
|
185
|
+
|
|
186
|
+
if (!youtubeId) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const playerDiv = this._container.querySelector(`#${playerID}`);
|
|
191
|
+
|
|
192
|
+
if (!playerDiv) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this._youtubePlayer = new globalThis.YT.Player(playerDiv, {
|
|
197
|
+
videoId: youtubeId,
|
|
198
|
+
playerVars: {
|
|
199
|
+
autoplay: 1,
|
|
200
|
+
rel: 0
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
this._element.dataset.ytId = youtubeId;
|
|
205
|
+
|
|
206
|
+
this._element.__unlkYtPlayer = this._youtubePlayer;
|
|
207
|
+
|
|
208
|
+
this._element.dispatchEvent(
|
|
209
|
+
new CustomEvent('videomodal.ready', {
|
|
210
|
+
bubbles: true,
|
|
211
|
+
detail: {
|
|
212
|
+
provider: 'youtube',
|
|
213
|
+
player: this._youtubePlayer,
|
|
214
|
+
videoId: youtubeId
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
// eslint-disable-next-line no-console
|
|
221
|
+
console.error('[VideoModal] Failed to initialize video player:', error);
|
|
116
222
|
}
|
|
117
223
|
}
|
|
118
224
|
|
|
119
225
|
_onHide() {
|
|
120
226
|
if (this._vimeoPlayer) {
|
|
121
227
|
this._vimeoPlayer.unload().catch(() => {});
|
|
228
|
+
|
|
122
229
|
this._vimeoPlayer = null;
|
|
123
230
|
}
|
|
124
231
|
|
|
@@ -129,17 +236,20 @@ class VideoModal extends BaseComponent {
|
|
|
129
236
|
|
|
130
237
|
delete this._element.__unlkYtPlayer;
|
|
131
238
|
delete this._element.dataset.ytId;
|
|
239
|
+
|
|
132
240
|
delete this._element.__unlkVimeoPlayer;
|
|
133
241
|
delete this._element.dataset.vimeoId;
|
|
134
242
|
}
|
|
135
243
|
|
|
136
244
|
_getYouTubeId(url) {
|
|
137
245
|
const match = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([\w-]{11})/);
|
|
246
|
+
|
|
138
247
|
return match ? match[1] : '';
|
|
139
248
|
}
|
|
140
249
|
|
|
141
250
|
_getVimeoId(url) {
|
|
142
251
|
const match = url.match(/vimeo\.com\/(\d+)/);
|
|
252
|
+
|
|
143
253
|
return match ? match[1] : '';
|
|
144
254
|
}
|
|
145
255
|
|
|
@@ -155,12 +265,18 @@ class VideoModal extends BaseComponent {
|
|
|
155
265
|
}
|
|
156
266
|
|
|
157
267
|
EventHandler.on(document, EVENT_SHOW, SELECTOR_MODAL, function () {
|
|
158
|
-
if (!SelectorEngine.findOne(SELECTOR_DIALOG, this))
|
|
268
|
+
if (!SelectorEngine.findOne(SELECTOR_DIALOG, this)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
159
272
|
VideoModal.getOrCreateInstance(this)._onShow();
|
|
160
273
|
});
|
|
161
274
|
|
|
162
275
|
EventHandler.on(document, EVENT_HIDDEN, SELECTOR_MODAL, function () {
|
|
163
|
-
if (!SelectorEngine.findOne(SELECTOR_DIALOG, this))
|
|
276
|
+
if (!SelectorEngine.findOne(SELECTOR_DIALOG, this)) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
164
280
|
VideoModal.getOrCreateInstance(this)._onHide();
|
|
165
281
|
});
|
|
166
282
|
|
package/package.json
CHANGED
package/scss/theme/_version.scss
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GENERATED FILE – do not edit manually
|
|
2
|
-
$km-version: "1.
|
|
2
|
+
$km-version: "1.7.0" !default;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
input:checked {
|
|
19
19
|
label {
|
|
20
|
+
background-color: $action-25;
|
|
20
21
|
border-color: $action;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
|
|
33
34
|
&:checked {
|
|
34
35
|
~ .form-check-label {
|
|
36
|
+
background-color: $action-25;
|
|
35
37
|
border-color: $action;
|
|
36
|
-
|
|
37
38
|
&::before {
|
|
38
39
|
background-color: $action;
|
|
39
40
|
border-color: $action;
|