stormcloud-video-player 0.2.9 → 0.2.11
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/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +469 -118
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +469 -118
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +206 -66
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +206 -66
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +206 -66
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +116 -31
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +469 -118
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
|
@@ -76,9 +76,22 @@ function createImaController(video, options) {
|
|
|
76
76
|
'script[data-ima="true"]'
|
|
77
77
|
);
|
|
78
78
|
if (existing) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
if (window.google?.ima) {
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const timeout = setTimeout(() => {
|
|
84
|
+
reject(new Error("IMA SDK load timeout"));
|
|
85
|
+
}, 1e4);
|
|
86
|
+
existing.addEventListener("load", () => {
|
|
87
|
+
clearTimeout(timeout);
|
|
88
|
+
resolve();
|
|
89
|
+
});
|
|
90
|
+
existing.addEventListener("error", () => {
|
|
91
|
+
clearTimeout(timeout);
|
|
92
|
+
reject(new Error("IMA SDK load failed"));
|
|
93
|
+
});
|
|
94
|
+
});
|
|
82
95
|
}
|
|
83
96
|
return new Promise((resolve, reject) => {
|
|
84
97
|
const script = document.createElement("script");
|
|
@@ -107,6 +120,17 @@ function createImaController(video, options) {
|
|
|
107
120
|
adsRequest.adTagUrl = vastTagUrl;
|
|
108
121
|
adsLoader.requestAds(adsRequest);
|
|
109
122
|
}
|
|
123
|
+
function destroyAdsManager() {
|
|
124
|
+
if (adsManager) {
|
|
125
|
+
try {
|
|
126
|
+
console.log("[IMA] Destroying existing ads manager");
|
|
127
|
+
adsManager.destroy();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.warn("[IMA] Error destroying ads manager:", error);
|
|
130
|
+
}
|
|
131
|
+
adsManager = void 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
110
134
|
return {
|
|
111
135
|
initialize() {
|
|
112
136
|
ensureImaLoaded().then(() => {
|
|
@@ -139,9 +163,22 @@ function createImaController(video, options) {
|
|
|
139
163
|
},
|
|
140
164
|
async requestAds(vastTagUrl) {
|
|
141
165
|
console.log("[IMA] Requesting ads:", vastTagUrl);
|
|
166
|
+
if (adPlaying) {
|
|
167
|
+
console.warn(
|
|
168
|
+
"[IMA] Cannot request new ads while an ad is playing. Call stop() first."
|
|
169
|
+
);
|
|
170
|
+
return Promise.reject(
|
|
171
|
+
new Error("Ad already playing - cannot request new ads")
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
destroyAdsManager();
|
|
175
|
+
adsLoadedReject = void 0;
|
|
176
|
+
adsLoadedResolve = void 0;
|
|
177
|
+
let currentReject;
|
|
142
178
|
adsLoadedPromise = new Promise((resolve, reject) => {
|
|
143
179
|
adsLoadedResolve = resolve;
|
|
144
180
|
adsLoadedReject = reject;
|
|
181
|
+
currentReject = reject;
|
|
145
182
|
setTimeout(() => {
|
|
146
183
|
if (adsLoadedReject) {
|
|
147
184
|
adsLoadedReject(new Error("Ad request timeout"));
|
|
@@ -163,7 +200,7 @@ function createImaController(video, options) {
|
|
|
163
200
|
container.style.top = "0";
|
|
164
201
|
container.style.right = "0";
|
|
165
202
|
container.style.bottom = "0";
|
|
166
|
-
container.style.display = "
|
|
203
|
+
container.style.display = "none";
|
|
167
204
|
container.style.alignItems = "center";
|
|
168
205
|
container.style.justifyContent = "center";
|
|
169
206
|
container.style.pointerEvents = "none";
|
|
@@ -203,14 +240,14 @@ function createImaController(video, options) {
|
|
|
203
240
|
AdErrorEvent.AD_ERROR,
|
|
204
241
|
(errorEvent) => {
|
|
205
242
|
console.error("[IMA] Ad error:", errorEvent.getError());
|
|
206
|
-
|
|
207
|
-
adsManager?.destroy?.();
|
|
208
|
-
} catch {
|
|
209
|
-
}
|
|
243
|
+
destroyAdsManager();
|
|
210
244
|
adPlaying = false;
|
|
211
245
|
video.muted = originalMutedState;
|
|
212
|
-
if (adContainerEl)
|
|
246
|
+
if (adContainerEl) {
|
|
213
247
|
adContainerEl.style.pointerEvents = "none";
|
|
248
|
+
adContainerEl.style.display = "none";
|
|
249
|
+
console.log("[IMA] Ad container hidden after error");
|
|
250
|
+
}
|
|
214
251
|
if (adsLoadedReject) {
|
|
215
252
|
adsLoadedReject(new Error("Ad playback error"));
|
|
216
253
|
adsLoadedReject = void 0;
|
|
@@ -233,8 +270,11 @@ function createImaController(video, options) {
|
|
|
233
270
|
);
|
|
234
271
|
emit("ad_error");
|
|
235
272
|
if (!options?.continueLiveStreamDuringAds) {
|
|
236
|
-
video.
|
|
237
|
-
|
|
273
|
+
if (video.paused) {
|
|
274
|
+
console.log("[IMA] Resuming paused video after ad error");
|
|
275
|
+
video.play()?.catch(() => {
|
|
276
|
+
});
|
|
277
|
+
}
|
|
238
278
|
}
|
|
239
279
|
}
|
|
240
280
|
}
|
|
@@ -243,10 +283,6 @@ function createImaController(video, options) {
|
|
|
243
283
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
244
284
|
() => {
|
|
245
285
|
console.log("[IMA] Content pause requested");
|
|
246
|
-
if (!adPlaying) {
|
|
247
|
-
originalMutedState = video.muted;
|
|
248
|
-
}
|
|
249
|
-
video.muted = true;
|
|
250
286
|
if (!options?.continueLiveStreamDuringAds) {
|
|
251
287
|
video.pause();
|
|
252
288
|
console.log("[IMA] Video paused (VOD mode)");
|
|
@@ -255,20 +291,34 @@ function createImaController(video, options) {
|
|
|
255
291
|
"[IMA] Video continues playing but muted (Live mode)"
|
|
256
292
|
);
|
|
257
293
|
}
|
|
294
|
+
video.muted = true;
|
|
258
295
|
adPlaying = true;
|
|
259
|
-
if (adContainerEl)
|
|
260
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
261
296
|
emit("content_pause");
|
|
262
297
|
}
|
|
263
298
|
);
|
|
299
|
+
adsManager.addEventListener(AdEvent.STARTED, () => {
|
|
300
|
+
console.log("[IMA] Ad started playing");
|
|
301
|
+
if (adContainerEl) {
|
|
302
|
+
adContainerEl.style.pointerEvents = "auto";
|
|
303
|
+
adContainerEl.style.display = "flex";
|
|
304
|
+
console.log(
|
|
305
|
+
"[IMA] Ad container visibility set to flex with pointer events enabled"
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
264
309
|
adsManager.addEventListener(
|
|
265
310
|
AdEvent.CONTENT_RESUME_REQUESTED,
|
|
266
311
|
() => {
|
|
267
312
|
console.log("[IMA] Content resume requested");
|
|
268
313
|
adPlaying = false;
|
|
269
314
|
video.muted = originalMutedState;
|
|
270
|
-
if (adContainerEl)
|
|
315
|
+
if (adContainerEl) {
|
|
271
316
|
adContainerEl.style.pointerEvents = "none";
|
|
317
|
+
adContainerEl.style.display = "none";
|
|
318
|
+
console.log(
|
|
319
|
+
"[IMA] Ad container hidden - pointer events disabled"
|
|
320
|
+
);
|
|
321
|
+
}
|
|
272
322
|
if (!options?.continueLiveStreamDuringAds) {
|
|
273
323
|
video.play()?.catch(() => {
|
|
274
324
|
});
|
|
@@ -285,7 +335,13 @@ function createImaController(video, options) {
|
|
|
285
335
|
console.log("[IMA] All ads completed");
|
|
286
336
|
adPlaying = false;
|
|
287
337
|
video.muted = originalMutedState;
|
|
288
|
-
if (adContainerEl)
|
|
338
|
+
if (adContainerEl) {
|
|
339
|
+
adContainerEl.style.pointerEvents = "none";
|
|
340
|
+
adContainerEl.style.display = "none";
|
|
341
|
+
console.log(
|
|
342
|
+
"[IMA] Ad container hidden after all ads completed"
|
|
343
|
+
);
|
|
344
|
+
}
|
|
289
345
|
if (!options?.continueLiveStreamDuringAds) {
|
|
290
346
|
video.play().catch(() => {
|
|
291
347
|
});
|
|
@@ -309,10 +365,17 @@ function createImaController(video, options) {
|
|
|
309
365
|
console.error("[IMA] Error setting up ads manager:", e);
|
|
310
366
|
adPlaying = false;
|
|
311
367
|
video.muted = originalMutedState;
|
|
312
|
-
if (adContainerEl)
|
|
368
|
+
if (adContainerEl) {
|
|
369
|
+
adContainerEl.style.pointerEvents = "none";
|
|
370
|
+
adContainerEl.style.display = "none";
|
|
371
|
+
console.log("[IMA] Ad container hidden after setup error");
|
|
372
|
+
}
|
|
313
373
|
if (!options?.continueLiveStreamDuringAds) {
|
|
314
|
-
video.
|
|
315
|
-
|
|
374
|
+
if (video.paused) {
|
|
375
|
+
console.log("[IMA] Resuming paused video after setup error");
|
|
376
|
+
video.play().catch(() => {
|
|
377
|
+
});
|
|
378
|
+
}
|
|
316
379
|
}
|
|
317
380
|
if (adsLoadedReject) {
|
|
318
381
|
adsLoadedReject(new Error("Failed to setup ads manager"));
|
|
@@ -328,6 +391,20 @@ function createImaController(video, options) {
|
|
|
328
391
|
google.ima.AdErrorEvent.Type.AD_ERROR,
|
|
329
392
|
(adErrorEvent) => {
|
|
330
393
|
console.error("[IMA] Ads loader error:", adErrorEvent.getError());
|
|
394
|
+
adPlaying = false;
|
|
395
|
+
video.muted = originalMutedState;
|
|
396
|
+
if (adContainerEl) {
|
|
397
|
+
adContainerEl.style.pointerEvents = "none";
|
|
398
|
+
adContainerEl.style.display = "none";
|
|
399
|
+
console.log("[IMA] Ad container hidden after loader error");
|
|
400
|
+
}
|
|
401
|
+
if (!options?.continueLiveStreamDuringAds) {
|
|
402
|
+
if (video.paused) {
|
|
403
|
+
console.log("[IMA] Resuming paused video after loader error");
|
|
404
|
+
video.play().catch(() => {
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
331
408
|
if (adsLoadedReject) {
|
|
332
409
|
adsLoadedReject(new Error("Ads loader error"));
|
|
333
410
|
adsLoadedReject = void 0;
|
|
@@ -343,11 +420,9 @@ function createImaController(video, options) {
|
|
|
343
420
|
return adsLoadedPromise;
|
|
344
421
|
} catch (error) {
|
|
345
422
|
console.error("[IMA] Failed to request ads:", error);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
adsLoadedResolve = void 0;
|
|
350
|
-
}
|
|
423
|
+
currentReject?.(error);
|
|
424
|
+
adsLoadedReject = void 0;
|
|
425
|
+
adsLoadedResolve = void 0;
|
|
351
426
|
return Promise.reject(error);
|
|
352
427
|
}
|
|
353
428
|
},
|
|
@@ -384,10 +459,16 @@ function createImaController(video, options) {
|
|
|
384
459
|
async stop() {
|
|
385
460
|
adPlaying = false;
|
|
386
461
|
video.muted = originalMutedState;
|
|
462
|
+
if (adContainerEl) {
|
|
463
|
+
adContainerEl.style.pointerEvents = "none";
|
|
464
|
+
adContainerEl.style.display = "none";
|
|
465
|
+
console.log("[IMA] Ad container hidden after stop");
|
|
466
|
+
}
|
|
387
467
|
try {
|
|
388
468
|
adsManager?.stop?.();
|
|
389
469
|
} catch {
|
|
390
470
|
}
|
|
471
|
+
destroyAdsManager();
|
|
391
472
|
if (!options?.continueLiveStreamDuringAds) {
|
|
392
473
|
video.play().catch(() => {
|
|
393
474
|
});
|
|
@@ -397,12 +478,13 @@ function createImaController(video, options) {
|
|
|
397
478
|
}
|
|
398
479
|
},
|
|
399
480
|
destroy() {
|
|
400
|
-
|
|
401
|
-
adsManager?.destroy?.();
|
|
402
|
-
} catch {
|
|
403
|
-
}
|
|
481
|
+
destroyAdsManager();
|
|
404
482
|
adPlaying = false;
|
|
405
483
|
video.muted = originalMutedState;
|
|
484
|
+
if (adContainerEl) {
|
|
485
|
+
adContainerEl.style.pointerEvents = "none";
|
|
486
|
+
adContainerEl.style.display = "none";
|
|
487
|
+
}
|
|
406
488
|
try {
|
|
407
489
|
adsLoader?.destroy?.();
|
|
408
490
|
} catch {
|
|
@@ -410,6 +492,9 @@ function createImaController(video, options) {
|
|
|
410
492
|
if (adContainerEl?.parentElement) {
|
|
411
493
|
adContainerEl.parentElement.removeChild(adContainerEl);
|
|
412
494
|
}
|
|
495
|
+
adContainerEl = void 0;
|
|
496
|
+
adDisplayContainer = void 0;
|
|
497
|
+
adsLoader = void 0;
|
|
413
498
|
},
|
|
414
499
|
isAdPlaying() {
|
|
415
500
|
return adPlaying;
|
|
@@ -887,32 +972,33 @@ var StormcloudVideoPlayer = class {
|
|
|
887
972
|
this.video.muted = !!this.config.muted;
|
|
888
973
|
this.ima.initialize();
|
|
889
974
|
this.ima.on("all_ads_completed", () => {
|
|
890
|
-
if (
|
|
891
|
-
|
|
892
|
-
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
893
|
-
const next = this.adPodQueue.shift();
|
|
894
|
-
this.currentAdIndex++;
|
|
895
|
-
this.playSingleAd(next).catch(() => {
|
|
896
|
-
});
|
|
897
|
-
} else {
|
|
898
|
-
this.currentAdIndex = 0;
|
|
899
|
-
this.totalAdsInBreak = 0;
|
|
900
|
-
this.showAds = false;
|
|
975
|
+
if (this.config.debugAdTiming) {
|
|
976
|
+
console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
|
|
901
977
|
}
|
|
902
978
|
});
|
|
903
979
|
this.ima.on("ad_error", () => {
|
|
904
980
|
if (this.config.debugAdTiming) {
|
|
905
981
|
console.log("[StormcloudVideoPlayer] IMA ad_error event received");
|
|
906
982
|
}
|
|
907
|
-
if (
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
983
|
+
if (this.showAds) {
|
|
984
|
+
if (this.inAdBreak) {
|
|
985
|
+
const remaining = this.getRemainingAdMs();
|
|
986
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
987
|
+
const next = this.adPodQueue.shift();
|
|
988
|
+
this.currentAdIndex++;
|
|
989
|
+
this.playSingleAd(next).catch(() => {
|
|
990
|
+
});
|
|
991
|
+
} else {
|
|
992
|
+
this.handleAdFailure();
|
|
993
|
+
}
|
|
994
|
+
} else {
|
|
995
|
+
if (this.config.debugAdTiming) {
|
|
996
|
+
console.log(
|
|
997
|
+
"[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
this.handleAdFailure();
|
|
1001
|
+
}
|
|
916
1002
|
}
|
|
917
1003
|
});
|
|
918
1004
|
this.ima.on("content_pause", () => {
|
|
@@ -928,6 +1014,26 @@ var StormcloudVideoPlayer = class {
|
|
|
928
1014
|
);
|
|
929
1015
|
}
|
|
930
1016
|
this.clearAdFailsafeTimer();
|
|
1017
|
+
if (!this.inAdBreak) return;
|
|
1018
|
+
const remaining = this.getRemainingAdMs();
|
|
1019
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
1020
|
+
const next = this.adPodQueue.shift();
|
|
1021
|
+
this.currentAdIndex++;
|
|
1022
|
+
if (this.config.debugAdTiming) {
|
|
1023
|
+
console.log(
|
|
1024
|
+
`[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
this.playSingleAd(next).catch(() => {
|
|
1028
|
+
});
|
|
1029
|
+
} else {
|
|
1030
|
+
if (this.config.debugAdTiming) {
|
|
1031
|
+
console.log("[StormcloudVideoPlayer] Ad pod completed");
|
|
1032
|
+
}
|
|
1033
|
+
this.currentAdIndex = 0;
|
|
1034
|
+
this.totalAdsInBreak = 0;
|
|
1035
|
+
this.showAds = false;
|
|
1036
|
+
}
|
|
931
1037
|
});
|
|
932
1038
|
this.video.addEventListener("timeupdate", () => {
|
|
933
1039
|
this.onTimeUpdate(this.video.currentTime);
|
|
@@ -1473,9 +1579,20 @@ var StormcloudVideoPlayer = class {
|
|
|
1473
1579
|
return;
|
|
1474
1580
|
}
|
|
1475
1581
|
if (vastTagUrl) {
|
|
1582
|
+
this.inAdBreak = true;
|
|
1476
1583
|
this.showAds = true;
|
|
1477
1584
|
this.currentAdIndex++;
|
|
1478
|
-
|
|
1585
|
+
try {
|
|
1586
|
+
await this.playSingleAd(vastTagUrl);
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
if (this.config.debugAdTiming) {
|
|
1589
|
+
console.error(
|
|
1590
|
+
"[StormcloudVideoPlayer] Ad playback failed in handleAdStart:",
|
|
1591
|
+
error
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
this.handleAdFailure();
|
|
1595
|
+
}
|
|
1479
1596
|
}
|
|
1480
1597
|
if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
|
|
1481
1598
|
this.expectedAdBreakDurationMs = scheduled.durationMs;
|
|
@@ -1577,21 +1694,21 @@ var StormcloudVideoPlayer = class {
|
|
|
1577
1694
|
if (this.config.debugAdTiming) {
|
|
1578
1695
|
console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
|
|
1579
1696
|
}
|
|
1580
|
-
this.ima.
|
|
1581
|
-
if (!this.shouldContinueLiveStreamDuringAds()) {
|
|
1582
|
-
if (this.config.debugAdTiming) {
|
|
1583
|
-
console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
|
|
1584
|
-
}
|
|
1585
|
-
this.video.pause();
|
|
1586
|
-
} else {
|
|
1697
|
+
if (this.ima.isAdPlaying()) {
|
|
1587
1698
|
if (this.config.debugAdTiming) {
|
|
1588
|
-
console.
|
|
1699
|
+
console.warn(
|
|
1700
|
+
"[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
|
|
1701
|
+
);
|
|
1589
1702
|
}
|
|
1590
|
-
|
|
1703
|
+
return;
|
|
1591
1704
|
}
|
|
1705
|
+
this.ima.updateOriginalMutedState(this.video.muted);
|
|
1592
1706
|
this.startAdFailsafeTimer();
|
|
1593
1707
|
try {
|
|
1594
1708
|
await this.ima.requestAds(vastTagUrl);
|
|
1709
|
+
if (this.config.debugAdTiming) {
|
|
1710
|
+
console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
|
|
1711
|
+
}
|
|
1595
1712
|
await this.ima.play();
|
|
1596
1713
|
if (this.config.debugAdTiming) {
|
|
1597
1714
|
console.log("[StormcloudVideoPlayer] Ad playback started successfully");
|
|
@@ -1606,7 +1723,13 @@ var StormcloudVideoPlayer = class {
|
|
|
1606
1723
|
handleAdFailure() {
|
|
1607
1724
|
if (this.config.debugAdTiming) {
|
|
1608
1725
|
console.log(
|
|
1609
|
-
"[StormcloudVideoPlayer] Handling ad failure - resuming content"
|
|
1726
|
+
"[StormcloudVideoPlayer] Handling ad failure - resuming content",
|
|
1727
|
+
{
|
|
1728
|
+
inAdBreak: this.inAdBreak,
|
|
1729
|
+
showAds: this.showAds,
|
|
1730
|
+
videoPaused: this.video.paused,
|
|
1731
|
+
adPlaying: this.ima.isAdPlaying()
|
|
1732
|
+
}
|
|
1610
1733
|
);
|
|
1611
1734
|
}
|
|
1612
1735
|
this.inAdBreak = false;
|
|
@@ -1619,14 +1742,29 @@ var StormcloudVideoPlayer = class {
|
|
|
1619
1742
|
this.showAds = false;
|
|
1620
1743
|
this.currentAdIndex = 0;
|
|
1621
1744
|
this.totalAdsInBreak = 0;
|
|
1745
|
+
const originalMutedState = this.ima.getOriginalMutedState();
|
|
1746
|
+
this.video.muted = originalMutedState;
|
|
1747
|
+
if (this.config.debugAdTiming) {
|
|
1748
|
+
console.log(
|
|
1749
|
+
`[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1622
1752
|
if (this.video.paused) {
|
|
1623
|
-
this.
|
|
1753
|
+
if (this.config.debugAdTiming) {
|
|
1754
|
+
console.log("[StormcloudVideoPlayer] Resuming paused video");
|
|
1755
|
+
}
|
|
1756
|
+
this.video.play()?.catch((error) => {
|
|
1624
1757
|
if (this.config.debugAdTiming) {
|
|
1625
1758
|
console.error(
|
|
1626
|
-
"[StormcloudVideoPlayer] Failed to resume video after ad failure"
|
|
1759
|
+
"[StormcloudVideoPlayer] Failed to resume video after ad failure:",
|
|
1760
|
+
error
|
|
1627
1761
|
);
|
|
1628
1762
|
}
|
|
1629
1763
|
});
|
|
1764
|
+
} else {
|
|
1765
|
+
if (this.config.debugAdTiming) {
|
|
1766
|
+
console.log("[StormcloudVideoPlayer] Video is already playing, no resume needed");
|
|
1767
|
+
}
|
|
1630
1768
|
}
|
|
1631
1769
|
}
|
|
1632
1770
|
startAdFailsafeTimer() {
|
|
@@ -1638,10 +1776,12 @@ var StormcloudVideoPlayer = class {
|
|
|
1638
1776
|
);
|
|
1639
1777
|
}
|
|
1640
1778
|
this.adFailsafeTimerId = window.setTimeout(() => {
|
|
1641
|
-
|
|
1779
|
+
const shouldTrigger = this.video.paused || this.showAds && !this.ima.isAdPlaying();
|
|
1780
|
+
if (shouldTrigger) {
|
|
1642
1781
|
if (this.config.debugAdTiming) {
|
|
1643
1782
|
console.warn(
|
|
1644
|
-
"[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume"
|
|
1783
|
+
"[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume",
|
|
1784
|
+
{ paused: this.video.paused, showAds: this.showAds, adPlaying: this.ima.isAdPlaying() }
|
|
1645
1785
|
);
|
|
1646
1786
|
}
|
|
1647
1787
|
this.handleAdFailure();
|
|
@@ -2562,39 +2702,144 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
|
|
|
2562
2702
|
left: "50%",
|
|
2563
2703
|
transform: "translateX(-50%)",
|
|
2564
2704
|
marginBottom: "4px",
|
|
2565
|
-
background: "linear-gradient(135deg, rgba(0, 0, 0, 0.
|
|
2705
|
+
background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
|
|
2566
2706
|
backdropFilter: "blur(15px)",
|
|
2567
|
-
padding: "
|
|
2568
|
-
borderRadius: "
|
|
2569
|
-
border: "1px solid rgba(255, 255, 255, 0.
|
|
2707
|
+
padding: "10px 14px",
|
|
2708
|
+
borderRadius: "14px",
|
|
2709
|
+
border: "1px solid rgba(255, 255, 255, 0.15)",
|
|
2570
2710
|
display: "flex",
|
|
2571
2711
|
flexDirection: "column",
|
|
2572
2712
|
alignItems: "center",
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2713
|
+
justifyContent: "center",
|
|
2714
|
+
height: "128px",
|
|
2715
|
+
boxShadow: "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)",
|
|
2716
|
+
zIndex: 10,
|
|
2717
|
+
transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
|
|
2576
2718
|
},
|
|
2577
|
-
onMouseEnter: () =>
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
"
|
|
2719
|
+
onMouseEnter: (e) => {
|
|
2720
|
+
setShowVolumeSlider(true);
|
|
2721
|
+
e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
|
|
2722
|
+
e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.6), 0 6px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 0 24px rgba(59, 130, 246, 0.3)";
|
|
2723
|
+
e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
|
|
2724
|
+
},
|
|
2725
|
+
onMouseLeave: (e) => {
|
|
2726
|
+
setShowVolumeSlider(false);
|
|
2727
|
+
e.currentTarget.style.transform = "translateX(-50%)";
|
|
2728
|
+
e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)";
|
|
2729
|
+
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
|
|
2730
|
+
},
|
|
2731
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
2732
|
+
"div",
|
|
2581
2733
|
{
|
|
2582
|
-
type: "range",
|
|
2583
|
-
min: "0",
|
|
2584
|
-
max: "1",
|
|
2585
|
-
step: "0.01",
|
|
2586
|
-
value: isMuted ? 0 : volume,
|
|
2587
|
-
onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
|
|
2588
2734
|
style: {
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
}
|
|
2735
|
+
position: "relative",
|
|
2736
|
+
width: "8px",
|
|
2737
|
+
height: "104px",
|
|
2738
|
+
cursor: "pointer",
|
|
2739
|
+
transition: "transform 0.2s ease-in-out"
|
|
2740
|
+
},
|
|
2741
|
+
onMouseEnter: (e) => {
|
|
2742
|
+
e.currentTarget.style.transform = "scaleX(1.2)";
|
|
2743
|
+
},
|
|
2744
|
+
onMouseLeave: (e) => {
|
|
2745
|
+
e.currentTarget.style.transform = "scaleX(1)";
|
|
2746
|
+
},
|
|
2747
|
+
onMouseDown: (e) => {
|
|
2748
|
+
e.preventDefault();
|
|
2749
|
+
const sliderElement = e.currentTarget;
|
|
2750
|
+
const handleMouseMove = (moveEvent) => {
|
|
2751
|
+
if (!sliderElement) return;
|
|
2752
|
+
const rect2 = sliderElement.getBoundingClientRect();
|
|
2753
|
+
const y2 = moveEvent.clientY - rect2.top;
|
|
2754
|
+
const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
|
|
2755
|
+
handleVolumeChange(percentage2);
|
|
2756
|
+
};
|
|
2757
|
+
const handleMouseUp = () => {
|
|
2758
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
2759
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
2760
|
+
};
|
|
2761
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
2762
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
2763
|
+
const rect = sliderElement.getBoundingClientRect();
|
|
2764
|
+
const y = e.clientY - rect.top;
|
|
2765
|
+
const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
|
|
2766
|
+
handleVolumeChange(percentage);
|
|
2767
|
+
},
|
|
2768
|
+
onClick: (e) => {
|
|
2769
|
+
e.stopPropagation();
|
|
2770
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2771
|
+
const y = e.clientY - rect.top;
|
|
2772
|
+
const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
|
|
2773
|
+
handleVolumeChange(percentage);
|
|
2774
|
+
},
|
|
2775
|
+
children: [
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2777
|
+
"div",
|
|
2778
|
+
{
|
|
2779
|
+
style: {
|
|
2780
|
+
position: "absolute",
|
|
2781
|
+
bottom: "0",
|
|
2782
|
+
left: "0",
|
|
2783
|
+
width: "100%",
|
|
2784
|
+
height: "100%",
|
|
2785
|
+
background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
|
|
2786
|
+
borderRadius: "4px",
|
|
2787
|
+
boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
),
|
|
2791
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2792
|
+
"div",
|
|
2793
|
+
{
|
|
2794
|
+
style: {
|
|
2795
|
+
position: "absolute",
|
|
2796
|
+
bottom: "0",
|
|
2797
|
+
left: "0",
|
|
2798
|
+
width: "100%",
|
|
2799
|
+
height: `${(isMuted ? 0 : volume) * 100}%`,
|
|
2800
|
+
background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
|
|
2801
|
+
borderRadius: "4px",
|
|
2802
|
+
transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
|
|
2803
|
+
boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
),
|
|
2807
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2808
|
+
"div",
|
|
2809
|
+
{
|
|
2810
|
+
style: {
|
|
2811
|
+
position: "absolute",
|
|
2812
|
+
bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
|
|
2813
|
+
left: "50%",
|
|
2814
|
+
transform: "translateX(-50%)",
|
|
2815
|
+
width: "14px",
|
|
2816
|
+
height: "14px",
|
|
2817
|
+
background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
|
|
2818
|
+
borderRadius: "50%",
|
|
2819
|
+
boxShadow: "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)",
|
|
2820
|
+
transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
|
|
2821
|
+
cursor: "grab"
|
|
2822
|
+
},
|
|
2823
|
+
onMouseEnter: (e) => {
|
|
2824
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
|
|
2825
|
+
e.currentTarget.style.boxShadow = "0 3px 10px rgba(0, 0, 0, 0.4), 0 0 0 3px rgba(59, 130, 246, 0.5), 0 0 20px rgba(59, 130, 246, 0.6)";
|
|
2826
|
+
e.currentTarget.style.cursor = "grab";
|
|
2827
|
+
},
|
|
2828
|
+
onMouseLeave: (e) => {
|
|
2829
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1)";
|
|
2830
|
+
e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)";
|
|
2831
|
+
},
|
|
2832
|
+
onMouseDown: (e) => {
|
|
2833
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
|
|
2834
|
+
e.currentTarget.style.cursor = "grabbing";
|
|
2835
|
+
},
|
|
2836
|
+
onMouseUp: (e) => {
|
|
2837
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
|
|
2838
|
+
e.currentTarget.style.cursor = "grab";
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
)
|
|
2842
|
+
]
|
|
2598
2843
|
}
|
|
2599
2844
|
)
|
|
2600
2845
|
}
|
|
@@ -2911,40 +3156,146 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
|
|
|
2911
3156
|
left: "50%",
|
|
2912
3157
|
transform: "translateX(-50%)",
|
|
2913
3158
|
marginBottom: "4px",
|
|
2914
|
-
background: "linear-gradient(135deg, rgba(0, 0, 0, 0.
|
|
3159
|
+
background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
|
|
2915
3160
|
backdropFilter: "blur(20px)",
|
|
2916
|
-
padding: "
|
|
2917
|
-
borderRadius: "
|
|
2918
|
-
border: "2px solid rgba(255, 255, 255, 0.
|
|
3161
|
+
padding: "10px 14px",
|
|
3162
|
+
borderRadius: "14px",
|
|
3163
|
+
border: "2px solid rgba(255, 255, 255, 0.7)",
|
|
2919
3164
|
display: "flex",
|
|
2920
3165
|
flexDirection: "column",
|
|
2921
3166
|
alignItems: "center",
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
3167
|
+
justifyContent: "center",
|
|
3168
|
+
height: "128px",
|
|
3169
|
+
boxShadow: "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)",
|
|
3170
|
+
zIndex: 10,
|
|
3171
|
+
transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
|
|
2925
3172
|
},
|
|
2926
|
-
onMouseEnter: () =>
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
"
|
|
3173
|
+
onMouseEnter: (e) => {
|
|
3174
|
+
setShowVolumeSlider(true);
|
|
3175
|
+
e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
|
|
3176
|
+
e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.9), 0 6px 16px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 24px rgba(96, 165, 250, 0.4)";
|
|
3177
|
+
e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
|
|
3178
|
+
},
|
|
3179
|
+
onMouseLeave: (e) => {
|
|
3180
|
+
setShowVolumeSlider(false);
|
|
3181
|
+
e.currentTarget.style.transform = "translateX(-50%)";
|
|
3182
|
+
e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)";
|
|
3183
|
+
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
|
|
3184
|
+
},
|
|
3185
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
3186
|
+
"div",
|
|
2930
3187
|
{
|
|
2931
|
-
type: "range",
|
|
2932
|
-
min: "0",
|
|
2933
|
-
max: "1",
|
|
2934
|
-
step: "0.01",
|
|
2935
|
-
value: isMuted ? 0 : volume,
|
|
2936
|
-
onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
|
|
2937
3188
|
style: {
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
height: "90px",
|
|
2942
|
-
background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
|
|
2943
|
-
borderRadius: "3px",
|
|
2944
|
-
outline: "none",
|
|
3189
|
+
position: "relative",
|
|
3190
|
+
width: "8px",
|
|
3191
|
+
height: "104px",
|
|
2945
3192
|
cursor: "pointer",
|
|
2946
|
-
|
|
2947
|
-
}
|
|
3193
|
+
transition: "transform 0.2s ease-in-out"
|
|
3194
|
+
},
|
|
3195
|
+
onMouseEnter: (e) => {
|
|
3196
|
+
e.currentTarget.style.transform = "scaleX(1.2)";
|
|
3197
|
+
},
|
|
3198
|
+
onMouseLeave: (e) => {
|
|
3199
|
+
e.currentTarget.style.transform = "scaleX(1)";
|
|
3200
|
+
},
|
|
3201
|
+
onMouseDown: (e) => {
|
|
3202
|
+
e.preventDefault();
|
|
3203
|
+
const sliderElement = e.currentTarget;
|
|
3204
|
+
const handleMouseMove = (moveEvent) => {
|
|
3205
|
+
if (!sliderElement) return;
|
|
3206
|
+
const rect2 = sliderElement.getBoundingClientRect();
|
|
3207
|
+
const y2 = moveEvent.clientY - rect2.top;
|
|
3208
|
+
const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
|
|
3209
|
+
handleVolumeChange(percentage2);
|
|
3210
|
+
};
|
|
3211
|
+
const handleMouseUp = () => {
|
|
3212
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
3213
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
3214
|
+
};
|
|
3215
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
3216
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
3217
|
+
const rect = sliderElement.getBoundingClientRect();
|
|
3218
|
+
const y = e.clientY - rect.top;
|
|
3219
|
+
const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
|
|
3220
|
+
handleVolumeChange(percentage);
|
|
3221
|
+
},
|
|
3222
|
+
onClick: (e) => {
|
|
3223
|
+
e.stopPropagation();
|
|
3224
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
3225
|
+
const y = e.clientY - rect.top;
|
|
3226
|
+
const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
|
|
3227
|
+
handleVolumeChange(percentage);
|
|
3228
|
+
},
|
|
3229
|
+
children: [
|
|
3230
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3231
|
+
"div",
|
|
3232
|
+
{
|
|
3233
|
+
style: {
|
|
3234
|
+
position: "absolute",
|
|
3235
|
+
bottom: "0",
|
|
3236
|
+
left: "0",
|
|
3237
|
+
width: "100%",
|
|
3238
|
+
height: "100%",
|
|
3239
|
+
background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
|
|
3240
|
+
borderRadius: "4px",
|
|
3241
|
+
border: "1px solid rgba(255, 255, 255, 0.4)",
|
|
3242
|
+
boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
),
|
|
3246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3247
|
+
"div",
|
|
3248
|
+
{
|
|
3249
|
+
style: {
|
|
3250
|
+
position: "absolute",
|
|
3251
|
+
bottom: "0",
|
|
3252
|
+
left: "0",
|
|
3253
|
+
width: "100%",
|
|
3254
|
+
height: `${(isMuted ? 0 : volume) * 100}%`,
|
|
3255
|
+
background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
|
|
3256
|
+
borderRadius: "4px",
|
|
3257
|
+
transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
|
|
3258
|
+
boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
),
|
|
3262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
3263
|
+
"div",
|
|
3264
|
+
{
|
|
3265
|
+
style: {
|
|
3266
|
+
position: "absolute",
|
|
3267
|
+
bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
|
|
3268
|
+
left: "50%",
|
|
3269
|
+
transform: "translateX(-50%)",
|
|
3270
|
+
width: "16px",
|
|
3271
|
+
height: "16px",
|
|
3272
|
+
background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
|
|
3273
|
+
borderRadius: "50%",
|
|
3274
|
+
border: "2px solid rgba(96, 165, 250, 0.9)",
|
|
3275
|
+
boxShadow: "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)",
|
|
3276
|
+
transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
|
|
3277
|
+
cursor: "grab"
|
|
3278
|
+
},
|
|
3279
|
+
onMouseEnter: (e) => {
|
|
3280
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
|
|
3281
|
+
e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(96, 165, 250, 0.6), 0 0 24px rgba(96, 165, 250, 0.7)";
|
|
3282
|
+
e.currentTarget.style.cursor = "grab";
|
|
3283
|
+
},
|
|
3284
|
+
onMouseLeave: (e) => {
|
|
3285
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1)";
|
|
3286
|
+
e.currentTarget.style.boxShadow = "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)";
|
|
3287
|
+
},
|
|
3288
|
+
onMouseDown: (e) => {
|
|
3289
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
|
|
3290
|
+
e.currentTarget.style.cursor = "grabbing";
|
|
3291
|
+
},
|
|
3292
|
+
onMouseUp: (e) => {
|
|
3293
|
+
e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
|
|
3294
|
+
e.currentTarget.style.cursor = "grab";
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
)
|
|
3298
|
+
]
|
|
2948
3299
|
}
|
|
2949
3300
|
)
|
|
2950
3301
|
}
|