@scarlett-player/embed 1.8.0 → 1.8.1
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/README.md +91 -22
- package/dist/create-embed.d.ts +9 -0
- package/dist/create-embed.d.ts.map +1 -1
- package/dist/embed.audio.index.js.map +1 -1
- package/dist/embed.audio.js +42 -6
- package/dist/embed.audio.js.map +1 -1
- package/dist/embed.audio.umd.cjs +1 -1
- package/dist/embed.audio.umd.cjs.map +1 -1
- package/dist/embed.js +935 -74
- package/dist/embed.js.map +1 -1
- package/dist/embed.umd.cjs +1 -1
- package/dist/embed.umd.cjs.map +1 -1
- package/dist/embed.video.js +946 -80
- package/dist/embed.video.js.map +1 -1
- package/dist/embed.video.umd.cjs +1 -1
- package/dist/embed.video.umd.cjs.map +1 -1
- package/dist/index-audio.d.ts.map +1 -1
- package/dist/index-video.d.ts +1 -0
- package/dist/index-video.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/iframe.html +19 -0
- package/package.json +13 -12
package/dist/embed.js
CHANGED
|
@@ -2895,7 +2895,7 @@ function createValidatingPlaylistLoader(Hls) {
|
|
|
2895
2895
|
}
|
|
2896
2896
|
};
|
|
2897
2897
|
}
|
|
2898
|
-
var PKG_VERSION$
|
|
2898
|
+
var PKG_VERSION$b = "1.8.1";
|
|
2899
2899
|
var DEFAULT_CONFIG$4 = {
|
|
2900
2900
|
debug: false,
|
|
2901
2901
|
autoStartLoad: true,
|
|
@@ -3228,7 +3228,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
3228
3228
|
if (api) {
|
|
3229
3229
|
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api);
|
|
3230
3230
|
}
|
|
3231
|
-
return new Promise((
|
|
3231
|
+
return new Promise((resolve2, reject) => {
|
|
3232
3232
|
let watchdog = null;
|
|
3233
3233
|
let settled = false;
|
|
3234
3234
|
const settle = () => {
|
|
@@ -3287,7 +3287,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
3287
3287
|
};
|
|
3288
3288
|
api?.setState("source", { src, type: "application/x-mpegURL" });
|
|
3289
3289
|
api?.emit("media:loaded", { src, type: "application/x-mpegURL" });
|
|
3290
|
-
|
|
3290
|
+
resolve2();
|
|
3291
3291
|
};
|
|
3292
3292
|
const onError = () => {
|
|
3293
3293
|
if (settled) return;
|
|
@@ -3321,7 +3321,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
3321
3321
|
if (api) {
|
|
3322
3322
|
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api);
|
|
3323
3323
|
}
|
|
3324
|
-
return new Promise((
|
|
3324
|
+
return new Promise((resolve2, reject) => {
|
|
3325
3325
|
if (!hls || !api) {
|
|
3326
3326
|
reject(new Error("HLS not initialized"));
|
|
3327
3327
|
return;
|
|
@@ -3356,7 +3356,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
3356
3356
|
hasPlayedContent = true;
|
|
3357
3357
|
api?.setState("source", { src, type: "application/x-mpegURL" });
|
|
3358
3358
|
api?.emit("media:loaded", { src, type: "application/x-mpegURL" });
|
|
3359
|
-
|
|
3359
|
+
resolve2();
|
|
3360
3360
|
}
|
|
3361
3361
|
},
|
|
3362
3362
|
onLevelSwitched: () => {
|
|
@@ -3512,7 +3512,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
3512
3512
|
const plugin = {
|
|
3513
3513
|
id: "hls-provider",
|
|
3514
3514
|
name: variant.name,
|
|
3515
|
-
version: PKG_VERSION$
|
|
3515
|
+
version: PKG_VERSION$b,
|
|
3516
3516
|
type: "provider",
|
|
3517
3517
|
description: variant.description,
|
|
3518
3518
|
canPlay(src) {
|
|
@@ -3859,7 +3859,7 @@ function createHLSPlugin(config) {
|
|
|
3859
3859
|
config
|
|
3860
3860
|
);
|
|
3861
3861
|
}
|
|
3862
|
-
var PKG_VERSION$
|
|
3862
|
+
var PKG_VERSION$a = "1.8.1";
|
|
3863
3863
|
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "mkv", "ogv", "m4v"];
|
|
3864
3864
|
var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "weba"];
|
|
3865
3865
|
var SUPPORTED_EXTENSIONS = [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS];
|
|
@@ -4098,7 +4098,7 @@ function createNativePlugin(config) {
|
|
|
4098
4098
|
const plugin = {
|
|
4099
4099
|
id: "native-provider",
|
|
4100
4100
|
name: "Native Media Provider",
|
|
4101
|
-
version: PKG_VERSION$
|
|
4101
|
+
version: PKG_VERSION$a,
|
|
4102
4102
|
type: "provider",
|
|
4103
4103
|
description: "Native HTML5 playback for video (MP4, WebM, MOV) and audio (MP3, WAV, FLAC, AAC)",
|
|
4104
4104
|
canPlay(src) {
|
|
@@ -4196,7 +4196,7 @@ function createNativePlugin(config) {
|
|
|
4196
4196
|
videoEl.style.display = isAudio ? "none" : "block";
|
|
4197
4197
|
applyPoster();
|
|
4198
4198
|
cleanupEvents = setupEventListeners(videoEl);
|
|
4199
|
-
return new Promise((
|
|
4199
|
+
return new Promise((resolve2, reject) => {
|
|
4200
4200
|
let watchdog = null;
|
|
4201
4201
|
const settle = () => {
|
|
4202
4202
|
videoEl.removeEventListener("loadedmetadata", onLoaded);
|
|
@@ -4216,7 +4216,7 @@ function createNativePlugin(config) {
|
|
|
4216
4216
|
api?.setState("playbackState", "ready");
|
|
4217
4217
|
api?.setState("buffering", false);
|
|
4218
4218
|
api?.emit("media:loaded", { src, type: mimeType });
|
|
4219
|
-
|
|
4219
|
+
resolve2();
|
|
4220
4220
|
};
|
|
4221
4221
|
const onError = () => {
|
|
4222
4222
|
settle();
|
|
@@ -4314,7 +4314,7 @@ function planFit(items, available, gap, overflowButtonWidth) {
|
|
|
4314
4314
|
hidden: ids.filter((id) => hidden.has(id))
|
|
4315
4315
|
};
|
|
4316
4316
|
}
|
|
4317
|
-
var styles$
|
|
4317
|
+
var styles$3 = `
|
|
4318
4318
|
/* ============================================
|
|
4319
4319
|
Container & Base
|
|
4320
4320
|
============================================ */
|
|
@@ -5403,7 +5403,7 @@ var styles$2 = `
|
|
|
5403
5403
|
--sp-icon-size: 24px;
|
|
5404
5404
|
}
|
|
5405
5405
|
`;
|
|
5406
|
-
var icons = {
|
|
5406
|
+
var icons$1 = {
|
|
5407
5407
|
play: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`,
|
|
5408
5408
|
pause: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/></svg>`,
|
|
5409
5409
|
replay: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/></svg>`,
|
|
@@ -5496,7 +5496,7 @@ var PlayButton = class {
|
|
|
5496
5496
|
this.toggle();
|
|
5497
5497
|
};
|
|
5498
5498
|
this.api = api;
|
|
5499
|
-
this.el = createButton("sp-play", "Play", icons.play);
|
|
5499
|
+
this.el = createButton("sp-play", "Play", icons$1.play);
|
|
5500
5500
|
this.el.addEventListener("click", this.clickHandler);
|
|
5501
5501
|
}
|
|
5502
5502
|
render() {
|
|
@@ -5508,13 +5508,13 @@ var PlayButton = class {
|
|
|
5508
5508
|
let icon;
|
|
5509
5509
|
let label;
|
|
5510
5510
|
if (ended) {
|
|
5511
|
-
icon = icons.replay;
|
|
5511
|
+
icon = icons$1.replay;
|
|
5512
5512
|
label = "Replay";
|
|
5513
5513
|
} else if (playing) {
|
|
5514
|
-
icon = icons.pause;
|
|
5514
|
+
icon = icons$1.pause;
|
|
5515
5515
|
label = "Pause";
|
|
5516
5516
|
} else {
|
|
5517
|
-
icon = icons.play;
|
|
5517
|
+
icon = icons$1.play;
|
|
5518
5518
|
label = "Play";
|
|
5519
5519
|
}
|
|
5520
5520
|
setHTML(this.el, icon);
|
|
@@ -5558,7 +5558,7 @@ var BigPlayButton = class {
|
|
|
5558
5558
|
btn.className = "sp-big-play";
|
|
5559
5559
|
btn.setAttribute("type", "button");
|
|
5560
5560
|
btn.setAttribute("aria-label", "Play");
|
|
5561
|
-
setHTML(btn, icons.play);
|
|
5561
|
+
setHTML(btn, icons$1.play);
|
|
5562
5562
|
btn.addEventListener("click", this.clickHandler);
|
|
5563
5563
|
this.el = btn;
|
|
5564
5564
|
}
|
|
@@ -5595,7 +5595,7 @@ var BigPlayButton = class {
|
|
|
5595
5595
|
} else {
|
|
5596
5596
|
visible = playbackState === "idle" || playbackState === "ready";
|
|
5597
5597
|
}
|
|
5598
|
-
setHTML(this.el, ended ? icons.replay : icons.play);
|
|
5598
|
+
setHTML(this.el, ended ? icons$1.replay : icons$1.play);
|
|
5599
5599
|
setAttr(this.el, "aria-label", ended ? "Replay" : "Play");
|
|
5600
5600
|
this.el.classList.toggle("sp-big-play--visible", visible);
|
|
5601
5601
|
}
|
|
@@ -6143,7 +6143,7 @@ var VolumeControl = class {
|
|
|
6143
6143
|
"aria-label": "Mute",
|
|
6144
6144
|
type: "button"
|
|
6145
6145
|
});
|
|
6146
|
-
this.btn.innerHTML = icons.volumeHigh;
|
|
6146
|
+
this.btn.innerHTML = icons$1.volumeHigh;
|
|
6147
6147
|
this.btn.onclick = () => this.toggleMute();
|
|
6148
6148
|
const sliderWrap = createElement("div", { className: "sp-volume__slider-wrap" });
|
|
6149
6149
|
this.slider = createElement("div", { className: "sp-volume__slider" });
|
|
@@ -6175,13 +6175,13 @@ var VolumeControl = class {
|
|
|
6175
6175
|
let icon;
|
|
6176
6176
|
let label;
|
|
6177
6177
|
if (muted || volume === 0) {
|
|
6178
|
-
icon = icons.volumeMute;
|
|
6178
|
+
icon = icons$1.volumeMute;
|
|
6179
6179
|
label = "Unmute";
|
|
6180
6180
|
} else if (volume < 0.5) {
|
|
6181
|
-
icon = icons.volumeLow;
|
|
6181
|
+
icon = icons$1.volumeLow;
|
|
6182
6182
|
label = "Mute";
|
|
6183
6183
|
} else {
|
|
6184
|
-
icon = icons.volumeHigh;
|
|
6184
|
+
icon = icons$1.volumeHigh;
|
|
6185
6185
|
label = "Mute";
|
|
6186
6186
|
}
|
|
6187
6187
|
setHTML(this.btn, icon);
|
|
@@ -6288,7 +6288,7 @@ var QualityMenu = class {
|
|
|
6288
6288
|
this.lastQualitiesJson = "";
|
|
6289
6289
|
this.api = api;
|
|
6290
6290
|
this.el = createElement("div", { className: "sp-quality" });
|
|
6291
|
-
this.btn = createButton("sp-quality__btn", "Quality", icons.settings);
|
|
6291
|
+
this.btn = createButton("sp-quality__btn", "Quality", icons$1.settings);
|
|
6292
6292
|
this.btnLabel = createElement("span", { className: "sp-quality__label" });
|
|
6293
6293
|
this.btnLabel.textContent = "Auto";
|
|
6294
6294
|
this.btn.appendChild(this.btnLabel);
|
|
@@ -6400,7 +6400,7 @@ var CastButton = class {
|
|
|
6400
6400
|
this.api = api;
|
|
6401
6401
|
this.type = type;
|
|
6402
6402
|
this.supported = type === "chromecast" ? isChromecastSupported() : isAirPlaySupported();
|
|
6403
|
-
const icon = type === "chromecast" ? icons.chromecast : icons.airplay;
|
|
6403
|
+
const icon = type === "chromecast" ? icons$1.chromecast : icons$1.airplay;
|
|
6404
6404
|
const label = type === "chromecast" ? "Cast" : "AirPlay";
|
|
6405
6405
|
this.el = createButton(`sp-cast sp-cast--${type}`, label, icon);
|
|
6406
6406
|
this.el.addEventListener("click", () => this.handleClick());
|
|
@@ -6424,10 +6424,10 @@ var CastButton = class {
|
|
|
6424
6424
|
this.el.classList.toggle("sp-cast--active", !!active);
|
|
6425
6425
|
this.el.classList.toggle("sp-cast--unavailable", !available && !active);
|
|
6426
6426
|
if (active) {
|
|
6427
|
-
setHTML(this.el, icons.chromecastConnected);
|
|
6427
|
+
setHTML(this.el, icons$1.chromecastConnected);
|
|
6428
6428
|
setAttr(this.el, "aria-label", "Stop casting");
|
|
6429
6429
|
} else {
|
|
6430
|
-
setHTML(this.el, icons.chromecast);
|
|
6430
|
+
setHTML(this.el, icons$1.chromecast);
|
|
6431
6431
|
setAttr(this.el, "aria-label", available ? "Cast" : "No Cast devices found");
|
|
6432
6432
|
}
|
|
6433
6433
|
} else {
|
|
@@ -6478,7 +6478,7 @@ var PipButton = class {
|
|
|
6478
6478
|
this.api = api;
|
|
6479
6479
|
const probe = document.createElement("video");
|
|
6480
6480
|
this.supported = "pictureInPictureEnabled" in document || "webkitSetPresentationMode" in probe;
|
|
6481
|
-
this.el = createButton("sp-pip", "Picture-in-Picture", icons.pip);
|
|
6481
|
+
this.el = createButton("sp-pip", "Picture-in-Picture", icons$1.pip);
|
|
6482
6482
|
this.el.addEventListener("click", this.clickHandler);
|
|
6483
6483
|
if (!this.supported) {
|
|
6484
6484
|
this.el.style.display = "none";
|
|
@@ -6501,7 +6501,7 @@ var PipButton = class {
|
|
|
6501
6501
|
const enabled = pip || this.isMediaReady();
|
|
6502
6502
|
this.el.disabled = !enabled;
|
|
6503
6503
|
setAttr(this.el, "aria-disabled", String(!enabled));
|
|
6504
|
-
setHTML(this.el, pip ? icons.exitPip : icons.pip);
|
|
6504
|
+
setHTML(this.el, pip ? icons$1.exitPip : icons$1.pip);
|
|
6505
6505
|
setAttr(this.el, "aria-label", pip ? "Exit Picture-in-Picture" : "Picture-in-Picture");
|
|
6506
6506
|
this.el.classList.toggle("sp-pip--active", pip);
|
|
6507
6507
|
}
|
|
@@ -6550,7 +6550,7 @@ var FullscreenButton = class {
|
|
|
6550
6550
|
this.toggle();
|
|
6551
6551
|
};
|
|
6552
6552
|
this.api = api;
|
|
6553
|
-
this.el = createButton("sp-fullscreen", "Fullscreen", icons.fullscreen);
|
|
6553
|
+
this.el = createButton("sp-fullscreen", "Fullscreen", icons$1.fullscreen);
|
|
6554
6554
|
this.el.addEventListener("click", this.clickHandler);
|
|
6555
6555
|
}
|
|
6556
6556
|
render() {
|
|
@@ -6559,10 +6559,10 @@ var FullscreenButton = class {
|
|
|
6559
6559
|
update() {
|
|
6560
6560
|
const fullscreen = this.api.getState("fullscreen");
|
|
6561
6561
|
if (fullscreen) {
|
|
6562
|
-
setHTML(this.el, icons.exitFullscreen);
|
|
6562
|
+
setHTML(this.el, icons$1.exitFullscreen);
|
|
6563
6563
|
setAttr(this.el, "aria-label", "Exit fullscreen");
|
|
6564
6564
|
} else {
|
|
6565
|
-
setHTML(this.el, icons.fullscreen);
|
|
6565
|
+
setHTML(this.el, icons$1.fullscreen);
|
|
6566
6566
|
setAttr(this.el, "aria-label", "Fullscreen");
|
|
6567
6567
|
}
|
|
6568
6568
|
}
|
|
@@ -6672,7 +6672,7 @@ var ErrorOverlay = class {
|
|
|
6672
6672
|
content.className = "sp-error-overlay__content";
|
|
6673
6673
|
const iconEl = document.createElement("div");
|
|
6674
6674
|
iconEl.className = "sp-error-overlay__icon";
|
|
6675
|
-
iconEl.innerHTML = icons.error;
|
|
6675
|
+
iconEl.innerHTML = icons$1.error;
|
|
6676
6676
|
const messageEl = document.createElement("p");
|
|
6677
6677
|
messageEl.className = "sp-error-overlay__message";
|
|
6678
6678
|
messageEl.textContent = "Something went wrong.";
|
|
@@ -6778,7 +6778,7 @@ var SettingsMenu = class {
|
|
|
6778
6778
|
this.lastQualitiesJson = "";
|
|
6779
6779
|
this.api = api;
|
|
6780
6780
|
this.el = createElement("div", { className: "sp-settings" });
|
|
6781
|
-
this.btn = createButton("sp-settings__btn", "Settings", icons.settings);
|
|
6781
|
+
this.btn = createButton("sp-settings__btn", "Settings", icons$1.settings);
|
|
6782
6782
|
this.btn.setAttribute("aria-haspopup", "true");
|
|
6783
6783
|
this.btn.setAttribute("aria-expanded", "false");
|
|
6784
6784
|
this.btn.addEventListener("click", (e) => {
|
|
@@ -6923,7 +6923,7 @@ var SettingsMenu = class {
|
|
|
6923
6923
|
const rightSide = createElement("span", { className: "sp-settings-panel__value" });
|
|
6924
6924
|
rightSide.textContent = value;
|
|
6925
6925
|
const arrow = createElement("span", { className: "sp-settings-panel__arrow" });
|
|
6926
|
-
arrow.innerHTML = icons.chevronDown;
|
|
6926
|
+
arrow.innerHTML = icons$1.chevronDown;
|
|
6927
6927
|
rightSide.appendChild(arrow);
|
|
6928
6928
|
row.appendChild(labelEl);
|
|
6929
6929
|
row.appendChild(rightSide);
|
|
@@ -7023,7 +7023,7 @@ var SettingsMenu = class {
|
|
|
7023
7023
|
header.setAttribute("role", "menuitem");
|
|
7024
7024
|
header.setAttribute("tabindex", "0");
|
|
7025
7025
|
const backArrow = createElement("span", { className: "sp-settings-panel__back" });
|
|
7026
|
-
backArrow.innerHTML = icons.chevronUp;
|
|
7026
|
+
backArrow.innerHTML = icons$1.chevronUp;
|
|
7027
7027
|
const label = createElement("span", { className: "sp-settings-panel__header-label" });
|
|
7028
7028
|
label.textContent = title;
|
|
7029
7029
|
header.appendChild(backArrow);
|
|
@@ -7050,7 +7050,7 @@ var SettingsMenu = class {
|
|
|
7050
7050
|
const labelEl = createElement("span");
|
|
7051
7051
|
labelEl.textContent = label;
|
|
7052
7052
|
const check = createElement("span", { className: "sp-settings-panel__check" });
|
|
7053
|
-
check.innerHTML = icons.checkmark;
|
|
7053
|
+
check.innerHTML = icons$1.checkmark;
|
|
7054
7054
|
item.appendChild(labelEl);
|
|
7055
7055
|
item.appendChild(check);
|
|
7056
7056
|
item.addEventListener("keydown", (e) => {
|
|
@@ -7144,7 +7144,7 @@ var SkipButton = class {
|
|
|
7144
7144
|
this.api = api;
|
|
7145
7145
|
this.direction = direction;
|
|
7146
7146
|
this.seconds = seconds;
|
|
7147
|
-
const icon = direction === "backward" ? icons.replay10 : icons.forward10;
|
|
7147
|
+
const icon = direction === "backward" ? icons$1.replay10 : icons$1.forward10;
|
|
7148
7148
|
const label = direction === "backward" ? `Rewind ${seconds} seconds` : `Forward ${seconds} seconds`;
|
|
7149
7149
|
this.el = createButton(
|
|
7150
7150
|
`sp-skip sp-skip--${direction}`,
|
|
@@ -7206,7 +7206,7 @@ var CaptionsButton = class {
|
|
|
7206
7206
|
this.toggle();
|
|
7207
7207
|
};
|
|
7208
7208
|
this.api = api;
|
|
7209
|
-
this.el = createButton("sp-captions", "Captions", icons.captionsOff);
|
|
7209
|
+
this.el = createButton("sp-captions", "Captions", icons$1.captionsOff);
|
|
7210
7210
|
this.el.addEventListener("click", this.clickHandler);
|
|
7211
7211
|
}
|
|
7212
7212
|
render() {
|
|
@@ -7221,11 +7221,11 @@ var CaptionsButton = class {
|
|
|
7221
7221
|
}
|
|
7222
7222
|
this.el.style.display = "";
|
|
7223
7223
|
if (currentTrack) {
|
|
7224
|
-
setHTML(this.el, icons.captions);
|
|
7224
|
+
setHTML(this.el, icons$1.captions);
|
|
7225
7225
|
setAttr(this.el, "aria-label", `Captions: ${currentTrack.label}`);
|
|
7226
7226
|
this.el.classList.add("sp-captions--active");
|
|
7227
7227
|
} else {
|
|
7228
|
-
setHTML(this.el, icons.captionsOff);
|
|
7228
|
+
setHTML(this.el, icons$1.captionsOff);
|
|
7229
7229
|
setAttr(this.el, "aria-label", "Captions");
|
|
7230
7230
|
this.el.classList.remove("sp-captions--active");
|
|
7231
7231
|
}
|
|
@@ -7288,7 +7288,7 @@ var OverflowTray = class {
|
|
|
7288
7288
|
this.isOpen ? this.close() : this.open();
|
|
7289
7289
|
};
|
|
7290
7290
|
this.el = createElement("div", { className: "sp-overflow" });
|
|
7291
|
-
this.btn = createButton("sp-overflow__btn", "More controls", icons.more);
|
|
7291
|
+
this.btn = createButton("sp-overflow__btn", "More controls", icons$1.more);
|
|
7292
7292
|
this.btn.setAttribute("aria-haspopup", "true");
|
|
7293
7293
|
this.btn.setAttribute("aria-expanded", "false");
|
|
7294
7294
|
this.btn.addEventListener("click", this.toggleHandler);
|
|
@@ -7440,7 +7440,7 @@ function onControlRegistered(listener) {
|
|
|
7440
7440
|
listeners.delete(listener);
|
|
7441
7441
|
};
|
|
7442
7442
|
}
|
|
7443
|
-
var PKG_VERSION$
|
|
7443
|
+
var PKG_VERSION$9 = "1.8.1";
|
|
7444
7444
|
var DEFAULT_LAYOUT = [
|
|
7445
7445
|
"play",
|
|
7446
7446
|
"skip-backward",
|
|
@@ -7874,11 +7874,11 @@ function uiPlugin(config = {}) {
|
|
|
7874
7874
|
id: "ui-controls",
|
|
7875
7875
|
name: "UI Controls",
|
|
7876
7876
|
type: "ui",
|
|
7877
|
-
version: PKG_VERSION$
|
|
7877
|
+
version: PKG_VERSION$9,
|
|
7878
7878
|
async init(pluginApi) {
|
|
7879
7879
|
api = pluginApi;
|
|
7880
7880
|
styleEl = document.createElement("style");
|
|
7881
|
-
styleEl.textContent = styles$
|
|
7881
|
+
styleEl.textContent = styles$3;
|
|
7882
7882
|
document.head.appendChild(styleEl);
|
|
7883
7883
|
if (config.theme) {
|
|
7884
7884
|
this.setTheme(config.theme);
|
|
@@ -7898,7 +7898,7 @@ function uiPlugin(config = {}) {
|
|
|
7898
7898
|
container.appendChild(gradient);
|
|
7899
7899
|
bufferingIndicator = document.createElement("div");
|
|
7900
7900
|
bufferingIndicator.className = "sp-buffering";
|
|
7901
|
-
bufferingIndicator.innerHTML = icons.spinner;
|
|
7901
|
+
bufferingIndicator.innerHTML = icons$1.spinner;
|
|
7902
7902
|
bufferingIndicator.setAttribute("aria-hidden", "true");
|
|
7903
7903
|
container.appendChild(bufferingIndicator);
|
|
7904
7904
|
errorOverlay = new ErrorOverlay(api);
|
|
@@ -8060,14 +8060,14 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
8060
8060
|
formatLiveTime,
|
|
8061
8061
|
formatTime: formatTime$1,
|
|
8062
8062
|
getControlFactory,
|
|
8063
|
-
icons,
|
|
8063
|
+
icons: icons$1,
|
|
8064
8064
|
planFit,
|
|
8065
8065
|
registerControl,
|
|
8066
8066
|
resolveFitItems,
|
|
8067
|
-
styles: styles$
|
|
8067
|
+
styles: styles$3,
|
|
8068
8068
|
uiPlugin
|
|
8069
8069
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
8070
|
-
var PKG_VERSION$
|
|
8070
|
+
var PKG_VERSION$8 = "1.8.1";
|
|
8071
8071
|
var DEFAULT_THEME = {
|
|
8072
8072
|
primary: "#6366f1",
|
|
8073
8073
|
background: "#18181b",
|
|
@@ -8694,7 +8694,7 @@ function createAudioUIPlugin(config) {
|
|
|
8694
8694
|
const plugin = {
|
|
8695
8695
|
id: "audio-ui",
|
|
8696
8696
|
name: "Audio UI",
|
|
8697
|
-
version: PKG_VERSION$
|
|
8697
|
+
version: PKG_VERSION$8,
|
|
8698
8698
|
type: "ui",
|
|
8699
8699
|
description: "Compact audio player interface",
|
|
8700
8700
|
async init(pluginApi) {
|
|
@@ -8999,8 +8999,20 @@ function safeStringify(data) {
|
|
|
8999
8999
|
return "{}";
|
|
9000
9000
|
}
|
|
9001
9001
|
}
|
|
9002
|
-
|
|
9003
|
-
|
|
9002
|
+
function isHttpsUrl(url) {
|
|
9003
|
+
if (!url || typeof url !== "string" || !url.trim()) {
|
|
9004
|
+
return false;
|
|
9005
|
+
}
|
|
9006
|
+
try {
|
|
9007
|
+
const base = typeof window !== "undefined" && window.location?.href ? window.location.href : void 0;
|
|
9008
|
+
const parsed = base ? new URL(url, base) : new URL(url);
|
|
9009
|
+
return parsed.protocol === "https:";
|
|
9010
|
+
} catch {
|
|
9011
|
+
return false;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
var PKG_VERSION$7 = "1.8.1";
|
|
9015
|
+
var PLUGIN_VERSION = PKG_VERSION$7;
|
|
9004
9016
|
var PLUGIN_NAME = "scarlett-player";
|
|
9005
9017
|
var DEFAULT_CONFIG$2 = {
|
|
9006
9018
|
heartbeatInterval: 1e4,
|
|
@@ -9094,11 +9106,14 @@ function createAnalyticsPlugin(config) {
|
|
|
9094
9106
|
});
|
|
9095
9107
|
navigator.sendBeacon(mergedConfig.beaconUrl, blob);
|
|
9096
9108
|
} else {
|
|
9109
|
+
const shouldAttachApiKey = Boolean(
|
|
9110
|
+
mergedConfig.apiKey && isHttpsUrl(mergedConfig.beaconUrl)
|
|
9111
|
+
);
|
|
9097
9112
|
fetch(mergedConfig.beaconUrl, {
|
|
9098
9113
|
method: "POST",
|
|
9099
9114
|
headers: {
|
|
9100
9115
|
"Content-Type": "application/json",
|
|
9101
|
-
...
|
|
9116
|
+
...shouldAttachApiKey ? { "X-API-Key": mergedConfig.apiKey } : {}
|
|
9102
9117
|
},
|
|
9103
9118
|
body: safeStringify(payload),
|
|
9104
9119
|
keepalive: true
|
|
@@ -9538,8 +9553,8 @@ var PlaylistPanel = class {
|
|
|
9538
9553
|
});
|
|
9539
9554
|
}
|
|
9540
9555
|
};
|
|
9541
|
-
var STYLE_ID$
|
|
9542
|
-
var styles$
|
|
9556
|
+
var STYLE_ID$2 = "sp-playlist-styles";
|
|
9557
|
+
var styles$2 = `
|
|
9543
9558
|
.sp-playlist-skip[disabled] {
|
|
9544
9559
|
opacity: 0.4;
|
|
9545
9560
|
cursor: default;
|
|
@@ -9639,16 +9654,16 @@ var styles$1 = `
|
|
|
9639
9654
|
}
|
|
9640
9655
|
`;
|
|
9641
9656
|
function injectStyles() {
|
|
9642
|
-
if (typeof document === "undefined" || document.getElementById(STYLE_ID$
|
|
9657
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID$2)) {
|
|
9643
9658
|
return null;
|
|
9644
9659
|
}
|
|
9645
9660
|
const el = document.createElement("style");
|
|
9646
|
-
el.id = STYLE_ID$
|
|
9647
|
-
el.textContent = styles$
|
|
9661
|
+
el.id = STYLE_ID$2;
|
|
9662
|
+
el.textContent = styles$2;
|
|
9648
9663
|
document.head.appendChild(el);
|
|
9649
9664
|
return el;
|
|
9650
9665
|
}
|
|
9651
|
-
var PKG_VERSION$
|
|
9666
|
+
var PKG_VERSION$6 = "1.8.1";
|
|
9652
9667
|
var DEFAULT_CONFIG$1 = {
|
|
9653
9668
|
autoAdvance: true,
|
|
9654
9669
|
preloadNext: true,
|
|
@@ -9807,7 +9822,7 @@ function createPlaylistPlugin(config) {
|
|
|
9807
9822
|
const plugin = {
|
|
9808
9823
|
id: "playlist",
|
|
9809
9824
|
name: "Playlist",
|
|
9810
|
-
version: PKG_VERSION$
|
|
9825
|
+
version: PKG_VERSION$6,
|
|
9811
9826
|
type: "feature",
|
|
9812
9827
|
description: "Playlist management with shuffle, repeat, and gapless playback",
|
|
9813
9828
|
async init(pluginApi) {
|
|
@@ -10063,7 +10078,7 @@ function createPlaylistPlugin(config) {
|
|
|
10063
10078
|
};
|
|
10064
10079
|
return plugin;
|
|
10065
10080
|
}
|
|
10066
|
-
var PKG_VERSION$
|
|
10081
|
+
var PKG_VERSION$5 = "1.8.1";
|
|
10067
10082
|
var DEFAULT_CONFIG = {
|
|
10068
10083
|
enablePlayPause: true,
|
|
10069
10084
|
enableSeek: true,
|
|
@@ -10211,7 +10226,7 @@ function createMediaSessionPlugin(config) {
|
|
|
10211
10226
|
const plugin = {
|
|
10212
10227
|
id: "media-session",
|
|
10213
10228
|
name: "Media Session",
|
|
10214
|
-
version: PKG_VERSION$
|
|
10229
|
+
version: PKG_VERSION$5,
|
|
10215
10230
|
type: "feature",
|
|
10216
10231
|
description: "Media Session API integration for system-level media controls",
|
|
10217
10232
|
async init(pluginApi) {
|
|
@@ -10322,7 +10337,7 @@ function createMediaSessionPlugin(config) {
|
|
|
10322
10337
|
};
|
|
10323
10338
|
return plugin;
|
|
10324
10339
|
}
|
|
10325
|
-
var PKG_VERSION$
|
|
10340
|
+
var PKG_VERSION$4 = "1.8.1";
|
|
10326
10341
|
var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right", "center"];
|
|
10327
10342
|
function getPositionStyles(padding, bottomPadding) {
|
|
10328
10343
|
return {
|
|
@@ -10432,7 +10447,7 @@ function createWatermarkPlugin(config = {}) {
|
|
|
10432
10447
|
return {
|
|
10433
10448
|
id: "watermark",
|
|
10434
10449
|
name: "Watermark",
|
|
10435
|
-
version: PKG_VERSION$
|
|
10450
|
+
version: PKG_VERSION$4,
|
|
10436
10451
|
type: "feature",
|
|
10437
10452
|
description: "Anti-piracy watermark overlay with text/image support and dynamic repositioning",
|
|
10438
10453
|
init(pluginApi) {
|
|
@@ -10517,7 +10532,7 @@ function createWatermarkPlugin(config = {}) {
|
|
|
10517
10532
|
}
|
|
10518
10533
|
};
|
|
10519
10534
|
}
|
|
10520
|
-
var PKG_VERSION$
|
|
10535
|
+
var PKG_VERSION$3 = "1.8.1";
|
|
10521
10536
|
var HLS_SUBTITLE_TRACKS_UPDATED = "hlsSubtitleTracksUpdated";
|
|
10522
10537
|
var HLS_INSTANCE_RETRY_MS = 500;
|
|
10523
10538
|
function createCaptionsPlugin(config = {}) {
|
|
@@ -10688,7 +10703,7 @@ function createCaptionsPlugin(config = {}) {
|
|
|
10688
10703
|
return {
|
|
10689
10704
|
id: "captions",
|
|
10690
10705
|
name: "Captions",
|
|
10691
|
-
version: PKG_VERSION$
|
|
10706
|
+
version: PKG_VERSION$3,
|
|
10692
10707
|
type: "feature",
|
|
10693
10708
|
description: "WebVTT subtitles and closed captions with HLS extraction",
|
|
10694
10709
|
init(pluginApi) {
|
|
@@ -10883,8 +10898,8 @@ function createRecognizer(options = {}) {
|
|
|
10883
10898
|
}
|
|
10884
10899
|
};
|
|
10885
10900
|
}
|
|
10886
|
-
var STYLE_ID = "sp-gestures-styles";
|
|
10887
|
-
var styles = `
|
|
10901
|
+
var STYLE_ID$1 = "sp-gestures-styles";
|
|
10902
|
+
var styles$1 = `
|
|
10888
10903
|
.sp-gestures {
|
|
10889
10904
|
position: absolute;
|
|
10890
10905
|
top: 0;
|
|
@@ -11047,14 +11062,14 @@ var GestureOverlay = class {
|
|
|
11047
11062
|
return { zone, label };
|
|
11048
11063
|
}
|
|
11049
11064
|
injectStyles() {
|
|
11050
|
-
if (document.getElementById(STYLE_ID)) return;
|
|
11065
|
+
if (document.getElementById(STYLE_ID$1)) return;
|
|
11051
11066
|
this.styleEl = document.createElement("style");
|
|
11052
|
-
this.styleEl.id = STYLE_ID;
|
|
11053
|
-
this.styleEl.textContent = styles;
|
|
11067
|
+
this.styleEl.id = STYLE_ID$1;
|
|
11068
|
+
this.styleEl.textContent = styles$1;
|
|
11054
11069
|
document.head.appendChild(this.styleEl);
|
|
11055
11070
|
}
|
|
11056
11071
|
};
|
|
11057
|
-
var PKG_VERSION$
|
|
11072
|
+
var PKG_VERSION$2 = "1.8.1";
|
|
11058
11073
|
function hasCoarsePointer() {
|
|
11059
11074
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
11060
11075
|
return false;
|
|
@@ -11178,7 +11193,7 @@ function createGesturesPlugin(config = {}) {
|
|
|
11178
11193
|
return {
|
|
11179
11194
|
id: "gestures",
|
|
11180
11195
|
name: "Gestures",
|
|
11181
|
-
version: PKG_VERSION$
|
|
11196
|
+
version: PKG_VERSION$2,
|
|
11182
11197
|
type: "feature",
|
|
11183
11198
|
init(pluginApi) {
|
|
11184
11199
|
api = pluginApi;
|
|
@@ -11224,6 +11239,818 @@ function createGesturesPlugin(config = {}) {
|
|
|
11224
11239
|
}
|
|
11225
11240
|
};
|
|
11226
11241
|
}
|
|
11242
|
+
function resolve(value, fallback) {
|
|
11243
|
+
if (typeof value === "function") {
|
|
11244
|
+
return value();
|
|
11245
|
+
}
|
|
11246
|
+
if (typeof value === "string" && value.length > 0) {
|
|
11247
|
+
return value;
|
|
11248
|
+
}
|
|
11249
|
+
return fallback();
|
|
11250
|
+
}
|
|
11251
|
+
function resolveBaseUrl(config) {
|
|
11252
|
+
return resolve(
|
|
11253
|
+
config.url,
|
|
11254
|
+
() => typeof window === "undefined" ? "" : window.location.href
|
|
11255
|
+
);
|
|
11256
|
+
}
|
|
11257
|
+
function resolveTitle(config) {
|
|
11258
|
+
return resolve(config.title, () => typeof document === "undefined" ? "" : document.title);
|
|
11259
|
+
}
|
|
11260
|
+
function applyTimestamp(baseUrl, currentTime, isLive, config) {
|
|
11261
|
+
const enabled = config.withTimestamp !== false;
|
|
11262
|
+
if (!enabled || isLive || !Number.isFinite(currentTime) || currentTime <= 0) {
|
|
11263
|
+
return baseUrl;
|
|
11264
|
+
}
|
|
11265
|
+
const param = config.timestampParam ?? "t";
|
|
11266
|
+
const rounded = config.roundTimestamp === false ? currentTime : Math.floor(currentTime);
|
|
11267
|
+
if (rounded <= 0) {
|
|
11268
|
+
return baseUrl;
|
|
11269
|
+
}
|
|
11270
|
+
try {
|
|
11271
|
+
const base = typeof window === "undefined" ? void 0 : window.location.href;
|
|
11272
|
+
const url = new URL(baseUrl, base);
|
|
11273
|
+
url.searchParams.set(param, String(rounded));
|
|
11274
|
+
return url.toString();
|
|
11275
|
+
} catch {
|
|
11276
|
+
return baseUrl;
|
|
11277
|
+
}
|
|
11278
|
+
}
|
|
11279
|
+
var icons = {
|
|
11280
|
+
/**
|
|
11281
|
+
* Three connected nodes. The one share glyph that reads the same everywhere:
|
|
11282
|
+
* it is the Material/Android share icon, and it carries no platform baggage.
|
|
11283
|
+
* The tray-and-arrow below is native on iOS but reads as "upload" or "export"
|
|
11284
|
+
* to everyone else, which is why it is no longer the default.
|
|
11285
|
+
*/
|
|
11286
|
+
share: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.6" y1="10.6" x2="15.4" y2="6.4"/><line x1="8.6" y1="13.4" x2="15.4" y2="17.6"/></svg>',
|
|
11287
|
+
/** iOS-style tray and arrow. Kept for hosts targeting an iOS-heavy audience. */
|
|
11288
|
+
upload: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></svg>',
|
|
11289
|
+
native: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></svg>',
|
|
11290
|
+
copy: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
|
|
11291
|
+
embed: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
|
|
11292
|
+
x: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.9 2H22l-7.1 8.1L23 22h-6.6l-5.2-6.8L5.3 22H2.2l7.6-8.7L1.7 2h6.8l4.7 6.2L18.9 2Zm-1.1 18h1.7L7.3 3.8H5.5L17.8 20Z"/></svg>',
|
|
11293
|
+
facebook: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M22 12a10 10 0 1 0-11.6 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7A10 10 0 0 0 22 12Z"/></svg>',
|
|
11294
|
+
linkedin: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4.98 3.5A2.5 2.5 0 1 1 0 3.5a2.5 2.5 0 0 1 4.98 0ZM.2 8.3h4.6V24H.2V8.3Zm7.6 0h4.4v2.1h.1a4.8 4.8 0 0 1 4.3-2.4c4.6 0 5.4 3 5.4 6.9V24h-4.6v-7.6c0-1.8 0-4.1-2.5-4.1s-2.9 2-2.9 4V24H7.8V8.3Z"/></svg>',
|
|
11295
|
+
whatsapp: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2a10 10 0 0 0-8.5 15.2L2 22l4.9-1.4A10 10 0 1 0 12 2Zm5.1 14.1c-.2.6-1.2 1.2-1.7 1.2-.5.1-1 .1-1.6-.1-.4-.1-.9-.3-1.5-.6a11 11 0 0 1-4.2-3.8c-.3-.5-.7-1.2-.7-2 0-.8.4-1.2.6-1.4.2-.2.4-.3.6-.3h.4c.1 0 .3 0 .5.4l.7 1.6c0 .1.1.3 0 .4l-.3.4-.2.3c-.1.1-.2.2 0 .5.2.3.7 1.1 1.4 1.8.9.8 1.6 1 1.9 1.2.2.1.4.1.5-.1l.6-.7c.2-.2.3-.2.5-.1l1.6.8c.2.1.4.2.4.3.1.1.1.5-.1 1Z"/></svg>',
|
|
11296
|
+
telegram: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M21.9 4.3 18.6 20c-.2 1.1-.9 1.4-1.8.9l-4.9-3.6-2.4 2.3c-.3.3-.5.5-1 .5l.4-5 9.1-8.2c.4-.4-.1-.6-.6-.2L6.2 13.1l-4.8-1.5c-1-.3-1-1 .2-1.5l18.9-7.3c.9-.3 1.6.2 1.4 1.5Z"/></svg>',
|
|
11297
|
+
reddit: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M22 12a2.1 2.1 0 0 0-3.6-1.5 10.4 10.4 0 0 0-5.4-1.7l.9-4.2 3 .6a1.8 1.8 0 1 0 .2-1.2l-3.6-.8a.6.6 0 0 0-.7.5L11.7 8.8a10.4 10.4 0 0 0-5.5 1.7A2.1 2.1 0 1 0 3.6 14a4 4 0 0 0 0 .6c0 3.1 3.7 5.6 8.4 5.6s8.4-2.5 8.4-5.6a4 4 0 0 0 0-.6A2.1 2.1 0 0 0 22 12ZM7.9 13.5a1.5 1.5 0 1 1 1.5 1.5 1.5 1.5 0 0 1-1.5-1.5Zm8.3 4.1a5.6 5.6 0 0 1-4.2 1.3 5.6 5.6 0 0 1-4.2-1.3.5.5 0 0 1 .7-.7 4.7 4.7 0 0 0 3.5 1 4.7 4.7 0 0 0 3.5-1 .5.5 0 1 1 .7.7Zm-.6-2.6a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1-1.5 1.5Z"/></svg>',
|
|
11298
|
+
email: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 6L2 7"/></svg>'
|
|
11299
|
+
};
|
|
11300
|
+
var encode = encodeURIComponent;
|
|
11301
|
+
var BUILTIN_TARGETS = {
|
|
11302
|
+
native: { id: "native", label: "Share", icon: icons.native },
|
|
11303
|
+
copy: { id: "copy", label: "Copy link", icon: icons.copy },
|
|
11304
|
+
embed: { id: "embed", label: "Embed", icon: icons.embed },
|
|
11305
|
+
x: {
|
|
11306
|
+
id: "x",
|
|
11307
|
+
label: "X",
|
|
11308
|
+
icon: icons.x,
|
|
11309
|
+
href: (c) => `https://x.com/intent/tweet?url=${encode(c.url)}&text=${encode(c.title)}`
|
|
11310
|
+
},
|
|
11311
|
+
facebook: {
|
|
11312
|
+
id: "facebook",
|
|
11313
|
+
label: "Facebook",
|
|
11314
|
+
icon: icons.facebook,
|
|
11315
|
+
href: (c) => `https://www.facebook.com/sharer/sharer.php?u=${encode(c.url)}`
|
|
11316
|
+
},
|
|
11317
|
+
linkedin: {
|
|
11318
|
+
id: "linkedin",
|
|
11319
|
+
label: "LinkedIn",
|
|
11320
|
+
icon: icons.linkedin,
|
|
11321
|
+
href: (c) => `https://www.linkedin.com/sharing/share-offsite/?url=${encode(c.url)}`
|
|
11322
|
+
},
|
|
11323
|
+
whatsapp: {
|
|
11324
|
+
id: "whatsapp",
|
|
11325
|
+
label: "WhatsApp",
|
|
11326
|
+
icon: icons.whatsapp,
|
|
11327
|
+
href: (c) => `https://api.whatsapp.com/send?text=${encode(`${c.title} ${c.url}`)}`
|
|
11328
|
+
},
|
|
11329
|
+
telegram: {
|
|
11330
|
+
id: "telegram",
|
|
11331
|
+
label: "Telegram",
|
|
11332
|
+
icon: icons.telegram,
|
|
11333
|
+
href: (c) => `https://t.me/share/url?url=${encode(c.url)}&text=${encode(c.title)}`
|
|
11334
|
+
},
|
|
11335
|
+
reddit: {
|
|
11336
|
+
id: "reddit",
|
|
11337
|
+
label: "Reddit",
|
|
11338
|
+
icon: icons.reddit,
|
|
11339
|
+
href: (c) => `https://www.reddit.com/submit?url=${encode(c.url)}&title=${encode(c.title)}`
|
|
11340
|
+
},
|
|
11341
|
+
email: {
|
|
11342
|
+
id: "email",
|
|
11343
|
+
label: "Email",
|
|
11344
|
+
icon: icons.email,
|
|
11345
|
+
href: (c) => `mailto:?subject=${encode(c.title)}&body=${encode(c.url)}`
|
|
11346
|
+
}
|
|
11347
|
+
};
|
|
11348
|
+
var DEFAULT_TARGETS = ["native", "copy", "embed"];
|
|
11349
|
+
function resolveTargets(configured, onUnknown) {
|
|
11350
|
+
const resolved = [];
|
|
11351
|
+
for (const entry of configured) {
|
|
11352
|
+
if (typeof entry !== "string") {
|
|
11353
|
+
resolved.push({ ...entry, icon: entry.icon ?? icons[entry.id] });
|
|
11354
|
+
continue;
|
|
11355
|
+
}
|
|
11356
|
+
const builtin = BUILTIN_TARGETS[entry];
|
|
11357
|
+
if (builtin) {
|
|
11358
|
+
resolved.push(builtin);
|
|
11359
|
+
} else {
|
|
11360
|
+
onUnknown?.(entry);
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11363
|
+
return resolved;
|
|
11364
|
+
}
|
|
11365
|
+
function canUseNativeShare() {
|
|
11366
|
+
return typeof navigator !== "undefined" && typeof navigator.share === "function";
|
|
11367
|
+
}
|
|
11368
|
+
async function nativeShare(context) {
|
|
11369
|
+
try {
|
|
11370
|
+
await navigator.share({ title: context.title, url: context.url });
|
|
11371
|
+
return true;
|
|
11372
|
+
} catch (error) {
|
|
11373
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
11374
|
+
return false;
|
|
11375
|
+
}
|
|
11376
|
+
throw error;
|
|
11377
|
+
}
|
|
11378
|
+
}
|
|
11379
|
+
async function copyText(text) {
|
|
11380
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
11381
|
+
try {
|
|
11382
|
+
await navigator.clipboard.writeText(text);
|
|
11383
|
+
return true;
|
|
11384
|
+
} catch {
|
|
11385
|
+
}
|
|
11386
|
+
}
|
|
11387
|
+
if (typeof document === "undefined") {
|
|
11388
|
+
return false;
|
|
11389
|
+
}
|
|
11390
|
+
const textarea = document.createElement("textarea");
|
|
11391
|
+
textarea.value = text;
|
|
11392
|
+
textarea.setAttribute("readonly", "");
|
|
11393
|
+
textarea.style.position = "fixed";
|
|
11394
|
+
textarea.style.top = "-9999px";
|
|
11395
|
+
textarea.style.opacity = "0";
|
|
11396
|
+
document.body.appendChild(textarea);
|
|
11397
|
+
try {
|
|
11398
|
+
textarea.select();
|
|
11399
|
+
textarea.setSelectionRange(0, textarea.value.length);
|
|
11400
|
+
return document.execCommand("copy");
|
|
11401
|
+
} catch {
|
|
11402
|
+
return false;
|
|
11403
|
+
} finally {
|
|
11404
|
+
textarea.remove();
|
|
11405
|
+
}
|
|
11406
|
+
}
|
|
11407
|
+
function buildEmbedSnippet(context, config) {
|
|
11408
|
+
if (config.embedSnippet) {
|
|
11409
|
+
return config.embedSnippet(context);
|
|
11410
|
+
}
|
|
11411
|
+
if (!config.embedBaseUrl) {
|
|
11412
|
+
return null;
|
|
11413
|
+
}
|
|
11414
|
+
try {
|
|
11415
|
+
const base = typeof window === "undefined" ? void 0 : window.location.href;
|
|
11416
|
+
const embedUrl = new URL(config.embedBaseUrl, base);
|
|
11417
|
+
if (context.currentTime > 0 && !context.isLive) {
|
|
11418
|
+
embedUrl.searchParams.set("startTime", String(Math.floor(context.currentTime)));
|
|
11419
|
+
}
|
|
11420
|
+
embedUrl.searchParams.set("shareUrl", context.url);
|
|
11421
|
+
return `<iframe src="${embedUrl.toString()}" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen loading="lazy"></iframe>`;
|
|
11422
|
+
} catch {
|
|
11423
|
+
return null;
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
var TOAST_MS = 1800;
|
|
11427
|
+
var ShareSheet = class {
|
|
11428
|
+
constructor(api, callbacks) {
|
|
11429
|
+
this.api = api;
|
|
11430
|
+
this.callbacks = callbacks;
|
|
11431
|
+
this.backdrop = null;
|
|
11432
|
+
this.sheet = null;
|
|
11433
|
+
this.toast = null;
|
|
11434
|
+
this.toastTimer = null;
|
|
11435
|
+
this.previouslyFocused = null;
|
|
11436
|
+
this.open = false;
|
|
11437
|
+
this.keydownHandler = (event) => {
|
|
11438
|
+
if (event.key === "Escape") {
|
|
11439
|
+
event.stopPropagation();
|
|
11440
|
+
this.close();
|
|
11441
|
+
return;
|
|
11442
|
+
}
|
|
11443
|
+
if (event.key === "Tab") {
|
|
11444
|
+
this.trapFocus(event);
|
|
11445
|
+
}
|
|
11446
|
+
};
|
|
11447
|
+
}
|
|
11448
|
+
/** Whether the sheet is currently showing. */
|
|
11449
|
+
isOpen() {
|
|
11450
|
+
return this.open;
|
|
11451
|
+
}
|
|
11452
|
+
/**
|
|
11453
|
+
* Show the sheet for a set of targets.
|
|
11454
|
+
*/
|
|
11455
|
+
show(targets) {
|
|
11456
|
+
if (this.open) {
|
|
11457
|
+
return;
|
|
11458
|
+
}
|
|
11459
|
+
this.previouslyFocused = document.activeElement;
|
|
11460
|
+
this.open = true;
|
|
11461
|
+
this.backdrop = document.createElement("div");
|
|
11462
|
+
this.backdrop.className = "sp-share-backdrop";
|
|
11463
|
+
this.backdrop.addEventListener("click", () => this.close());
|
|
11464
|
+
this.sheet = document.createElement("div");
|
|
11465
|
+
this.sheet.className = "sp-share-sheet";
|
|
11466
|
+
this.sheet.setAttribute("role", "dialog");
|
|
11467
|
+
this.sheet.setAttribute("aria-modal", "true");
|
|
11468
|
+
this.sheet.setAttribute("aria-label", "Share");
|
|
11469
|
+
const grip = document.createElement("div");
|
|
11470
|
+
grip.className = "sp-share-grip";
|
|
11471
|
+
this.sheet.appendChild(grip);
|
|
11472
|
+
const heading = document.createElement("p");
|
|
11473
|
+
heading.className = "sp-share-title";
|
|
11474
|
+
heading.textContent = "Share";
|
|
11475
|
+
this.sheet.appendChild(heading);
|
|
11476
|
+
const list = document.createElement("ul");
|
|
11477
|
+
list.className = "sp-share-targets";
|
|
11478
|
+
for (const target of targets) {
|
|
11479
|
+
list.appendChild(this.renderTarget(target));
|
|
11480
|
+
}
|
|
11481
|
+
this.sheet.appendChild(list);
|
|
11482
|
+
this.api.container.appendChild(this.backdrop);
|
|
11483
|
+
this.api.container.appendChild(this.sheet);
|
|
11484
|
+
requestAnimationFrame(() => {
|
|
11485
|
+
this.backdrop?.classList.add("sp-share-backdrop--open");
|
|
11486
|
+
this.sheet?.classList.add("sp-share-sheet--open");
|
|
11487
|
+
});
|
|
11488
|
+
document.addEventListener("keydown", this.keydownHandler, true);
|
|
11489
|
+
const first = this.sheet.querySelector(".sp-share-target");
|
|
11490
|
+
first?.focus();
|
|
11491
|
+
}
|
|
11492
|
+
/** Hide the sheet and restore focus. */
|
|
11493
|
+
close() {
|
|
11494
|
+
if (!this.open) {
|
|
11495
|
+
return;
|
|
11496
|
+
}
|
|
11497
|
+
this.open = false;
|
|
11498
|
+
document.removeEventListener("keydown", this.keydownHandler, true);
|
|
11499
|
+
this.backdrop?.classList.remove("sp-share-backdrop--open");
|
|
11500
|
+
this.sheet?.classList.remove("sp-share-sheet--open");
|
|
11501
|
+
const backdrop = this.backdrop;
|
|
11502
|
+
const sheet = this.sheet;
|
|
11503
|
+
this.backdrop = null;
|
|
11504
|
+
this.sheet = null;
|
|
11505
|
+
setTimeout(() => {
|
|
11506
|
+
backdrop?.remove();
|
|
11507
|
+
sheet?.remove();
|
|
11508
|
+
}, 220);
|
|
11509
|
+
this.previouslyFocused?.focus();
|
|
11510
|
+
this.previouslyFocused = null;
|
|
11511
|
+
this.callbacks.onClose();
|
|
11512
|
+
}
|
|
11513
|
+
/**
|
|
11514
|
+
* Show a transient confirmation, e.g. after copying.
|
|
11515
|
+
*/
|
|
11516
|
+
showToast(message) {
|
|
11517
|
+
if (this.toastTimer) {
|
|
11518
|
+
clearTimeout(this.toastTimer);
|
|
11519
|
+
this.toastTimer = null;
|
|
11520
|
+
}
|
|
11521
|
+
if (!this.toast) {
|
|
11522
|
+
this.toast = document.createElement("div");
|
|
11523
|
+
this.toast.className = "sp-share-toast";
|
|
11524
|
+
this.toast.setAttribute("role", "status");
|
|
11525
|
+
this.toast.setAttribute("aria-live", "polite");
|
|
11526
|
+
this.api.container.appendChild(this.toast);
|
|
11527
|
+
}
|
|
11528
|
+
this.toast.textContent = message;
|
|
11529
|
+
requestAnimationFrame(() => this.toast?.classList.add("sp-share-toast--visible"));
|
|
11530
|
+
this.toastTimer = setTimeout(() => {
|
|
11531
|
+
this.toast?.classList.remove("sp-share-toast--visible");
|
|
11532
|
+
this.toastTimer = null;
|
|
11533
|
+
}, TOAST_MS);
|
|
11534
|
+
}
|
|
11535
|
+
/**
|
|
11536
|
+
* Last-resort clipboard fallback: show the text for manual copying.
|
|
11537
|
+
*/
|
|
11538
|
+
showManualCopy(text) {
|
|
11539
|
+
if (!this.sheet) {
|
|
11540
|
+
return;
|
|
11541
|
+
}
|
|
11542
|
+
const row = document.createElement("div");
|
|
11543
|
+
row.className = "sp-share-fallback";
|
|
11544
|
+
const input = document.createElement("input");
|
|
11545
|
+
input.type = "text";
|
|
11546
|
+
input.readOnly = true;
|
|
11547
|
+
input.value = text;
|
|
11548
|
+
input.setAttribute("aria-label", "Link to copy");
|
|
11549
|
+
row.appendChild(input);
|
|
11550
|
+
this.sheet.appendChild(row);
|
|
11551
|
+
input.focus();
|
|
11552
|
+
input.select();
|
|
11553
|
+
}
|
|
11554
|
+
/** Remove everything and detach listeners. */
|
|
11555
|
+
destroy() {
|
|
11556
|
+
document.removeEventListener("keydown", this.keydownHandler, true);
|
|
11557
|
+
if (this.toastTimer) {
|
|
11558
|
+
clearTimeout(this.toastTimer);
|
|
11559
|
+
this.toastTimer = null;
|
|
11560
|
+
}
|
|
11561
|
+
this.backdrop?.remove();
|
|
11562
|
+
this.sheet?.remove();
|
|
11563
|
+
this.toast?.remove();
|
|
11564
|
+
this.backdrop = null;
|
|
11565
|
+
this.sheet = null;
|
|
11566
|
+
this.toast = null;
|
|
11567
|
+
this.open = false;
|
|
11568
|
+
this.previouslyFocused = null;
|
|
11569
|
+
}
|
|
11570
|
+
/** Build one target button. */
|
|
11571
|
+
renderTarget(target) {
|
|
11572
|
+
const item = document.createElement("li");
|
|
11573
|
+
const button = document.createElement("button");
|
|
11574
|
+
button.type = "button";
|
|
11575
|
+
button.className = `sp-share-target sp-share-target--${target.id}`;
|
|
11576
|
+
button.setAttribute("aria-label", target.label);
|
|
11577
|
+
const icon = document.createElement("span");
|
|
11578
|
+
icon.className = "sp-share-icon";
|
|
11579
|
+
icon.setAttribute("aria-hidden", "true");
|
|
11580
|
+
if (target.icon) {
|
|
11581
|
+
icon.innerHTML = target.icon;
|
|
11582
|
+
}
|
|
11583
|
+
const label = document.createElement("span");
|
|
11584
|
+
label.className = "sp-share-label";
|
|
11585
|
+
label.textContent = target.label;
|
|
11586
|
+
button.appendChild(icon);
|
|
11587
|
+
button.appendChild(label);
|
|
11588
|
+
button.addEventListener("click", () => this.callbacks.onSelect(target));
|
|
11589
|
+
item.appendChild(button);
|
|
11590
|
+
return item;
|
|
11591
|
+
}
|
|
11592
|
+
/** Keep Tab inside the dialog while it is open. */
|
|
11593
|
+
trapFocus(event) {
|
|
11594
|
+
if (!this.sheet) {
|
|
11595
|
+
return;
|
|
11596
|
+
}
|
|
11597
|
+
const focusable = this.sheet.querySelectorAll('button, input, [tabindex]:not([tabindex="-1"])');
|
|
11598
|
+
if (focusable.length === 0) {
|
|
11599
|
+
return;
|
|
11600
|
+
}
|
|
11601
|
+
const first = focusable[0];
|
|
11602
|
+
const last = focusable[focusable.length - 1];
|
|
11603
|
+
const active = document.activeElement;
|
|
11604
|
+
if (event.shiftKey && active === first) {
|
|
11605
|
+
event.preventDefault();
|
|
11606
|
+
last?.focus();
|
|
11607
|
+
} else if (!event.shiftKey && active === last) {
|
|
11608
|
+
event.preventDefault();
|
|
11609
|
+
first?.focus();
|
|
11610
|
+
}
|
|
11611
|
+
}
|
|
11612
|
+
};
|
|
11613
|
+
var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
|
|
11614
|
+
"svg",
|
|
11615
|
+
"g",
|
|
11616
|
+
"path",
|
|
11617
|
+
"circle",
|
|
11618
|
+
"ellipse",
|
|
11619
|
+
"line",
|
|
11620
|
+
"polyline",
|
|
11621
|
+
"polygon",
|
|
11622
|
+
"rect",
|
|
11623
|
+
"defs",
|
|
11624
|
+
"lineargradient",
|
|
11625
|
+
"radialgradient",
|
|
11626
|
+
"stop",
|
|
11627
|
+
"clippath",
|
|
11628
|
+
"mask",
|
|
11629
|
+
"use",
|
|
11630
|
+
"symbol",
|
|
11631
|
+
"title",
|
|
11632
|
+
"desc"
|
|
11633
|
+
]);
|
|
11634
|
+
function sanitizeIcon(markup) {
|
|
11635
|
+
if (typeof markup !== "string" || markup.trim() === "") {
|
|
11636
|
+
return null;
|
|
11637
|
+
}
|
|
11638
|
+
if (typeof DOMParser === "undefined") {
|
|
11639
|
+
return null;
|
|
11640
|
+
}
|
|
11641
|
+
const doc = new DOMParser().parseFromString(markup, "text/html");
|
|
11642
|
+
const svg = doc.body.querySelector("svg");
|
|
11643
|
+
if (!svg) {
|
|
11644
|
+
return null;
|
|
11645
|
+
}
|
|
11646
|
+
for (const element of Array.from(svg.querySelectorAll("*"))) {
|
|
11647
|
+
if (!ALLOWED_ELEMENTS.has(element.nodeName.toLowerCase())) {
|
|
11648
|
+
element.remove();
|
|
11649
|
+
continue;
|
|
11650
|
+
}
|
|
11651
|
+
stripDangerousAttributes(element);
|
|
11652
|
+
}
|
|
11653
|
+
stripDangerousAttributes(svg);
|
|
11654
|
+
return svg.outerHTML;
|
|
11655
|
+
}
|
|
11656
|
+
function stripDangerousAttributes(element) {
|
|
11657
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
11658
|
+
const name = attribute.name.toLowerCase();
|
|
11659
|
+
if (name.startsWith("on")) {
|
|
11660
|
+
element.removeAttribute(attribute.name);
|
|
11661
|
+
continue;
|
|
11662
|
+
}
|
|
11663
|
+
if (name === "href" || name === "xlink:href" || name === "src") {
|
|
11664
|
+
const value = attribute.value.replace(/\s+/g, "").toLowerCase();
|
|
11665
|
+
if (value.startsWith("javascript:") || value.startsWith("data:text/html")) {
|
|
11666
|
+
element.removeAttribute(attribute.name);
|
|
11667
|
+
}
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
}
|
|
11671
|
+
var ShareButton = class {
|
|
11672
|
+
constructor(_api, onActivate, options = {}) {
|
|
11673
|
+
this.onActivate = onActivate;
|
|
11674
|
+
this.clickHandler = () => {
|
|
11675
|
+
this.onActivate();
|
|
11676
|
+
};
|
|
11677
|
+
const label = options.label ?? "Share";
|
|
11678
|
+
this.el = document.createElement("button");
|
|
11679
|
+
this.el.type = "button";
|
|
11680
|
+
this.el.className = "sp-share sp-control";
|
|
11681
|
+
this.el.setAttribute("aria-label", label);
|
|
11682
|
+
this.el.setAttribute("title", label);
|
|
11683
|
+
this.el.setAttribute("aria-haspopup", "dialog");
|
|
11684
|
+
const icon = options.icon ? sanitizeIcon(options.icon) : null;
|
|
11685
|
+
this.el.innerHTML = icon ?? icons.share ?? "";
|
|
11686
|
+
this.el.addEventListener("click", this.clickHandler);
|
|
11687
|
+
}
|
|
11688
|
+
render() {
|
|
11689
|
+
return this.el;
|
|
11690
|
+
}
|
|
11691
|
+
update() {
|
|
11692
|
+
}
|
|
11693
|
+
destroy() {
|
|
11694
|
+
this.el.removeEventListener("click", this.clickHandler);
|
|
11695
|
+
this.el.remove();
|
|
11696
|
+
}
|
|
11697
|
+
};
|
|
11698
|
+
var styles = `
|
|
11699
|
+
.sp-share-backdrop {
|
|
11700
|
+
position: absolute;
|
|
11701
|
+
inset: 0;
|
|
11702
|
+
background: rgba(0, 0, 0, 0.55);
|
|
11703
|
+
opacity: 0;
|
|
11704
|
+
transition: opacity 180ms ease;
|
|
11705
|
+
z-index: 30;
|
|
11706
|
+
}
|
|
11707
|
+
.sp-share-backdrop--open { opacity: 1; }
|
|
11708
|
+
|
|
11709
|
+
.sp-share-sheet {
|
|
11710
|
+
position: absolute;
|
|
11711
|
+
left: 0;
|
|
11712
|
+
right: 0;
|
|
11713
|
+
bottom: 0;
|
|
11714
|
+
z-index: 31;
|
|
11715
|
+
background: #1c1c1e;
|
|
11716
|
+
color: #fff;
|
|
11717
|
+
border-radius: 14px 14px 0 0;
|
|
11718
|
+
padding: 8px 12px calc(12px + env(safe-area-inset-bottom, 0px));
|
|
11719
|
+
box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.45);
|
|
11720
|
+
transform: translateY(100%);
|
|
11721
|
+
transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
11722
|
+
max-height: 80%;
|
|
11723
|
+
overflow-y: auto;
|
|
11724
|
+
-webkit-overflow-scrolling: touch;
|
|
11725
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11726
|
+
}
|
|
11727
|
+
.sp-share-sheet--open { transform: translateY(0); }
|
|
11728
|
+
|
|
11729
|
+
/* Grab handle - the affordance that says "this drags/dismisses" on a phone. */
|
|
11730
|
+
.sp-share-grip {
|
|
11731
|
+
width: 36px;
|
|
11732
|
+
height: 4px;
|
|
11733
|
+
border-radius: 2px;
|
|
11734
|
+
background: rgba(255, 255, 255, 0.3);
|
|
11735
|
+
margin: 4px auto 10px;
|
|
11736
|
+
}
|
|
11737
|
+
|
|
11738
|
+
.sp-share-title {
|
|
11739
|
+
font-size: 13px;
|
|
11740
|
+
font-weight: 600;
|
|
11741
|
+
letter-spacing: 0.02em;
|
|
11742
|
+
text-transform: uppercase;
|
|
11743
|
+
color: rgba(255, 255, 255, 0.55);
|
|
11744
|
+
padding: 0 8px 8px;
|
|
11745
|
+
margin: 0;
|
|
11746
|
+
}
|
|
11747
|
+
|
|
11748
|
+
.sp-share-targets {
|
|
11749
|
+
display: grid;
|
|
11750
|
+
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
|
11751
|
+
gap: 4px;
|
|
11752
|
+
list-style: none;
|
|
11753
|
+
margin: 0;
|
|
11754
|
+
padding: 0;
|
|
11755
|
+
}
|
|
11756
|
+
|
|
11757
|
+
.sp-share-target {
|
|
11758
|
+
display: flex;
|
|
11759
|
+
flex-direction: column;
|
|
11760
|
+
align-items: center;
|
|
11761
|
+
justify-content: center;
|
|
11762
|
+
gap: 8px;
|
|
11763
|
+
/* 44px is the WCAG 2.5.5 floor; 72px is a comfortable thumb target. */
|
|
11764
|
+
min-height: 72px;
|
|
11765
|
+
min-width: 44px;
|
|
11766
|
+
padding: 10px 6px;
|
|
11767
|
+
border: 0;
|
|
11768
|
+
border-radius: 10px;
|
|
11769
|
+
background: transparent;
|
|
11770
|
+
color: inherit;
|
|
11771
|
+
font: inherit;
|
|
11772
|
+
font-size: 12px;
|
|
11773
|
+
cursor: pointer;
|
|
11774
|
+
-webkit-tap-highlight-color: transparent;
|
|
11775
|
+
transition: background-color 120ms ease, transform 120ms ease;
|
|
11776
|
+
}
|
|
11777
|
+
.sp-share-target:active { transform: scale(0.94); background: rgba(255, 255, 255, 0.14); }
|
|
11778
|
+
.sp-share-target:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
|
|
11779
|
+
@media (hover: hover) {
|
|
11780
|
+
.sp-share-target:hover { background: rgba(255, 255, 255, 0.1); }
|
|
11781
|
+
}
|
|
11782
|
+
|
|
11783
|
+
.sp-share-target svg {
|
|
11784
|
+
width: 24px;
|
|
11785
|
+
height: 24px;
|
|
11786
|
+
display: block;
|
|
11787
|
+
}
|
|
11788
|
+
|
|
11789
|
+
.sp-share-icon {
|
|
11790
|
+
display: flex;
|
|
11791
|
+
align-items: center;
|
|
11792
|
+
justify-content: center;
|
|
11793
|
+
width: 44px;
|
|
11794
|
+
height: 44px;
|
|
11795
|
+
border-radius: 50%;
|
|
11796
|
+
background: rgba(255, 255, 255, 0.12);
|
|
11797
|
+
}
|
|
11798
|
+
|
|
11799
|
+
.sp-share-label {
|
|
11800
|
+
line-height: 1.2;
|
|
11801
|
+
text-align: center;
|
|
11802
|
+
max-width: 100%;
|
|
11803
|
+
overflow: hidden;
|
|
11804
|
+
text-overflow: ellipsis;
|
|
11805
|
+
white-space: nowrap;
|
|
11806
|
+
}
|
|
11807
|
+
|
|
11808
|
+
/* Copy confirmation, announced politely to assistive tech. */
|
|
11809
|
+
.sp-share-toast {
|
|
11810
|
+
position: absolute;
|
|
11811
|
+
left: 50%;
|
|
11812
|
+
bottom: 16px;
|
|
11813
|
+
transform: translateX(-50%) translateY(8px);
|
|
11814
|
+
z-index: 32;
|
|
11815
|
+
background: rgba(28, 28, 30, 0.95);
|
|
11816
|
+
color: #fff;
|
|
11817
|
+
font-size: 13px;
|
|
11818
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
11819
|
+
padding: 10px 16px;
|
|
11820
|
+
border-radius: 20px;
|
|
11821
|
+
opacity: 0;
|
|
11822
|
+
pointer-events: none;
|
|
11823
|
+
transition: opacity 160ms ease, transform 160ms ease;
|
|
11824
|
+
}
|
|
11825
|
+
.sp-share-toast--visible { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
11826
|
+
|
|
11827
|
+
/* Fallback when the clipboard is unavailable: show the URL to copy by hand. */
|
|
11828
|
+
.sp-share-fallback {
|
|
11829
|
+
display: flex;
|
|
11830
|
+
gap: 8px;
|
|
11831
|
+
padding: 8px;
|
|
11832
|
+
}
|
|
11833
|
+
.sp-share-fallback input {
|
|
11834
|
+
flex: 1;
|
|
11835
|
+
min-width: 0;
|
|
11836
|
+
/* 16px keeps iOS Safari from zooming the viewport on focus. */
|
|
11837
|
+
font-size: 16px;
|
|
11838
|
+
padding: 10px 12px;
|
|
11839
|
+
border-radius: 8px;
|
|
11840
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
11841
|
+
background: rgba(255, 255, 255, 0.08);
|
|
11842
|
+
color: #fff;
|
|
11843
|
+
}
|
|
11844
|
+
|
|
11845
|
+
@media (min-width: 640px) {
|
|
11846
|
+
.sp-share-backdrop { background: rgba(0, 0, 0, 0.25); }
|
|
11847
|
+
|
|
11848
|
+
.sp-share-sheet {
|
|
11849
|
+
left: auto;
|
|
11850
|
+
right: 12px;
|
|
11851
|
+
bottom: 64px;
|
|
11852
|
+
width: 320px;
|
|
11853
|
+
border-radius: 12px;
|
|
11854
|
+
padding: 10px;
|
|
11855
|
+
transform: translateY(8px) scale(0.98);
|
|
11856
|
+
opacity: 0;
|
|
11857
|
+
transition: opacity 140ms ease, transform 140ms ease;
|
|
11858
|
+
max-height: 60%;
|
|
11859
|
+
}
|
|
11860
|
+
.sp-share-sheet--open { transform: translateY(0) scale(1); opacity: 1; }
|
|
11861
|
+
|
|
11862
|
+
.sp-share-grip { display: none; }
|
|
11863
|
+
.sp-share-targets { grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); }
|
|
11864
|
+
}
|
|
11865
|
+
|
|
11866
|
+
@media (prefers-reduced-motion: reduce) {
|
|
11867
|
+
.sp-share-backdrop,
|
|
11868
|
+
.sp-share-sheet,
|
|
11869
|
+
.sp-share-target,
|
|
11870
|
+
.sp-share-toast {
|
|
11871
|
+
transition: none;
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
`;
|
|
11875
|
+
var PKG_VERSION$1 = "1.8.1";
|
|
11876
|
+
var STYLE_ID = "sp-share-styles";
|
|
11877
|
+
function createSharePlugin(config = {}) {
|
|
11878
|
+
let api = null;
|
|
11879
|
+
let sheet = null;
|
|
11880
|
+
let styleEl = null;
|
|
11881
|
+
const configuredTargets = config.targets ?? DEFAULT_TARGETS;
|
|
11882
|
+
const buildContext = () => {
|
|
11883
|
+
const currentTime = api?.getState("currentTime") ?? 0;
|
|
11884
|
+
const isLive = Boolean(api?.getState("live"));
|
|
11885
|
+
const baseUrl = resolveBaseUrl(config);
|
|
11886
|
+
return {
|
|
11887
|
+
url: applyTimestamp(baseUrl, currentTime, isLive, config),
|
|
11888
|
+
title: resolveTitle(config),
|
|
11889
|
+
currentTime,
|
|
11890
|
+
isLive
|
|
11891
|
+
};
|
|
11892
|
+
};
|
|
11893
|
+
const reportError = (error) => {
|
|
11894
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
11895
|
+
api?.logger.error("Share failed", { error: err });
|
|
11896
|
+
config.onError?.(err);
|
|
11897
|
+
};
|
|
11898
|
+
const reportShared = (targetId, url) => {
|
|
11899
|
+
config.onShare?.(targetId, url);
|
|
11900
|
+
api?.emit("share:completed", { targetId, url });
|
|
11901
|
+
};
|
|
11902
|
+
const availableTargets = (context) => resolveTargets(configuredTargets, (id) => api?.logger.warn(`Unknown share target: ${id}`)).filter(
|
|
11903
|
+
(target) => {
|
|
11904
|
+
if (target.id === "native") {
|
|
11905
|
+
return canUseNativeShare();
|
|
11906
|
+
}
|
|
11907
|
+
if (target.id === "embed") {
|
|
11908
|
+
return buildEmbedSnippet(context, config) !== null;
|
|
11909
|
+
}
|
|
11910
|
+
return true;
|
|
11911
|
+
}
|
|
11912
|
+
);
|
|
11913
|
+
const runTarget = async (target, context) => {
|
|
11914
|
+
if (target.id === "native") {
|
|
11915
|
+
const shared = await nativeShare(context);
|
|
11916
|
+
if (shared) {
|
|
11917
|
+
reportShared(target.id, context.url);
|
|
11918
|
+
}
|
|
11919
|
+
return;
|
|
11920
|
+
}
|
|
11921
|
+
if (target.id === "copy") {
|
|
11922
|
+
const copied = await copyText(context.url);
|
|
11923
|
+
if (copied) {
|
|
11924
|
+
sheet?.showToast("Link copied");
|
|
11925
|
+
reportShared(target.id, context.url);
|
|
11926
|
+
} else {
|
|
11927
|
+
sheet?.showManualCopy(context.url);
|
|
11928
|
+
}
|
|
11929
|
+
return;
|
|
11930
|
+
}
|
|
11931
|
+
if (target.id === "embed") {
|
|
11932
|
+
const snippet = buildEmbedSnippet(context, config);
|
|
11933
|
+
if (!snippet) {
|
|
11934
|
+
return;
|
|
11935
|
+
}
|
|
11936
|
+
const copied = await copyText(snippet);
|
|
11937
|
+
if (copied) {
|
|
11938
|
+
sheet?.showToast("Embed code copied");
|
|
11939
|
+
reportShared(target.id, context.url);
|
|
11940
|
+
} else {
|
|
11941
|
+
sheet?.showManualCopy(snippet);
|
|
11942
|
+
}
|
|
11943
|
+
return;
|
|
11944
|
+
}
|
|
11945
|
+
if (target.href) {
|
|
11946
|
+
window.open(target.href(context), "_blank", "noopener,noreferrer");
|
|
11947
|
+
reportShared(target.id, context.url);
|
|
11948
|
+
}
|
|
11949
|
+
};
|
|
11950
|
+
const openSheet = () => {
|
|
11951
|
+
if (!sheet) {
|
|
11952
|
+
return;
|
|
11953
|
+
}
|
|
11954
|
+
const context = buildContext();
|
|
11955
|
+
const targets = availableTargets(context);
|
|
11956
|
+
if (targets.length === 0) {
|
|
11957
|
+
api?.logger.warn("No share targets available");
|
|
11958
|
+
return;
|
|
11959
|
+
}
|
|
11960
|
+
sheet.show(targets);
|
|
11961
|
+
api?.emit("share:opened", void 0);
|
|
11962
|
+
};
|
|
11963
|
+
const activate = async () => {
|
|
11964
|
+
const hostChoseTargets = config.targets !== void 0;
|
|
11965
|
+
if (!hostChoseTargets && canUseNativeShare()) {
|
|
11966
|
+
const context = buildContext();
|
|
11967
|
+
try {
|
|
11968
|
+
const shared = await nativeShare(context);
|
|
11969
|
+
if (shared) {
|
|
11970
|
+
reportShared("native", context.url);
|
|
11971
|
+
}
|
|
11972
|
+
} catch (error) {
|
|
11973
|
+
reportError(error);
|
|
11974
|
+
}
|
|
11975
|
+
return;
|
|
11976
|
+
}
|
|
11977
|
+
openSheet();
|
|
11978
|
+
};
|
|
11979
|
+
return {
|
|
11980
|
+
id: "share",
|
|
11981
|
+
name: "Share",
|
|
11982
|
+
version: PKG_VERSION$1,
|
|
11983
|
+
type: "feature",
|
|
11984
|
+
description: "Native share sheet, copy link, timestamps and embed codes",
|
|
11985
|
+
init(pluginApi) {
|
|
11986
|
+
api = pluginApi;
|
|
11987
|
+
api.logger.debug("Share plugin initialized");
|
|
11988
|
+
if (!document.getElementById(STYLE_ID)) {
|
|
11989
|
+
styleEl = document.createElement("style");
|
|
11990
|
+
styleEl.id = STYLE_ID;
|
|
11991
|
+
styleEl.textContent = styles;
|
|
11992
|
+
document.head.appendChild(styleEl);
|
|
11993
|
+
}
|
|
11994
|
+
sheet = new ShareSheet(api, {
|
|
11995
|
+
onSelect: (target) => {
|
|
11996
|
+
const context = buildContext();
|
|
11997
|
+
if (target.id !== "copy" && target.id !== "embed") {
|
|
11998
|
+
sheet?.close();
|
|
11999
|
+
}
|
|
12000
|
+
void runTarget(target, context).catch(reportError);
|
|
12001
|
+
},
|
|
12002
|
+
onClose: () => {
|
|
12003
|
+
api?.emit("share:closed", void 0);
|
|
12004
|
+
}
|
|
12005
|
+
});
|
|
12006
|
+
void Promise.resolve().then(() => index).then(({ registerControl: registerControl2 }) => {
|
|
12007
|
+
registerControl2(
|
|
12008
|
+
"share",
|
|
12009
|
+
(controlApi) => new ShareButton(controlApi, () => void activate(), {
|
|
12010
|
+
icon: config.buttonIcon,
|
|
12011
|
+
label: config.buttonLabel
|
|
12012
|
+
})
|
|
12013
|
+
);
|
|
12014
|
+
}).catch(() => {
|
|
12015
|
+
api?.logger.debug("@scarlett-player/ui not present, share control not registered");
|
|
12016
|
+
});
|
|
12017
|
+
api.onDestroy(() => {
|
|
12018
|
+
sheet?.destroy();
|
|
12019
|
+
sheet = null;
|
|
12020
|
+
});
|
|
12021
|
+
},
|
|
12022
|
+
destroy() {
|
|
12023
|
+
sheet?.destroy();
|
|
12024
|
+
sheet = null;
|
|
12025
|
+
styleEl?.remove();
|
|
12026
|
+
styleEl = null;
|
|
12027
|
+
api = null;
|
|
12028
|
+
},
|
|
12029
|
+
async share(targetId) {
|
|
12030
|
+
if (!targetId) {
|
|
12031
|
+
await activate();
|
|
12032
|
+
return;
|
|
12033
|
+
}
|
|
12034
|
+
const context = buildContext();
|
|
12035
|
+
const target = availableTargets(context).find((t) => t.id === targetId);
|
|
12036
|
+
if (!target) {
|
|
12037
|
+
reportError(new Error(`Unknown or unavailable share target: ${targetId}`));
|
|
12038
|
+
return;
|
|
12039
|
+
}
|
|
12040
|
+
try {
|
|
12041
|
+
await runTarget(target, context);
|
|
12042
|
+
} catch (error) {
|
|
12043
|
+
reportError(error);
|
|
12044
|
+
}
|
|
12045
|
+
},
|
|
12046
|
+
getShareUrl() {
|
|
12047
|
+
return buildContext().url;
|
|
12048
|
+
},
|
|
12049
|
+
close() {
|
|
12050
|
+
sheet?.close();
|
|
12051
|
+
}
|
|
12052
|
+
};
|
|
12053
|
+
}
|
|
11227
12054
|
function getAttr(element, ...names) {
|
|
11228
12055
|
for (const name of names) {
|
|
11229
12056
|
const value = element.getAttribute(name);
|
|
@@ -11289,6 +12116,14 @@ function parseDataAttributes(element) {
|
|
|
11289
12116
|
if (album) {
|
|
11290
12117
|
config.album = album;
|
|
11291
12118
|
}
|
|
12119
|
+
const shareUrl = getAttr(element, "data-share-url", "share-url");
|
|
12120
|
+
if (shareUrl) {
|
|
12121
|
+
config.shareUrl = shareUrl;
|
|
12122
|
+
}
|
|
12123
|
+
const embedBaseUrl = getAttr(element, "data-embed-base-url", "embed-base-url");
|
|
12124
|
+
if (embedBaseUrl) {
|
|
12125
|
+
config.embedBaseUrl = embedBaseUrl;
|
|
12126
|
+
}
|
|
11292
12127
|
const brandColor = getAttr(element, "data-brand-color", "data-color", "color");
|
|
11293
12128
|
if (brandColor) {
|
|
11294
12129
|
config.brandColor = brandColor;
|
|
@@ -11391,6 +12226,23 @@ function applyContainerStyles(container, config) {
|
|
|
11391
12226
|
container.style.width = container.style.width || "100%";
|
|
11392
12227
|
}
|
|
11393
12228
|
}
|
|
12229
|
+
const SHARE_CONTROL_LAYOUT = [
|
|
12230
|
+
"play",
|
|
12231
|
+
"skip-backward",
|
|
12232
|
+
"skip-forward",
|
|
12233
|
+
"volume",
|
|
12234
|
+
"time",
|
|
12235
|
+
"live-indicator",
|
|
12236
|
+
"bandwidth-indicator",
|
|
12237
|
+
"spacer",
|
|
12238
|
+
"share",
|
|
12239
|
+
"settings",
|
|
12240
|
+
"captions",
|
|
12241
|
+
"chromecast",
|
|
12242
|
+
"airplay",
|
|
12243
|
+
"pip",
|
|
12244
|
+
"fullscreen"
|
|
12245
|
+
];
|
|
11394
12246
|
async function createEmbedPlayer(container, config, pluginCreators2, availableTypes) {
|
|
11395
12247
|
const type = config.type || "video";
|
|
11396
12248
|
if (!availableTypes.includes(type)) {
|
|
@@ -11442,6 +12294,13 @@ async function createEmbedPlayer(container, config, pluginCreators2, availableTy
|
|
|
11442
12294
|
if (type === "video" && pluginCreators2.gestures && config.gestures !== false) {
|
|
11443
12295
|
plugins.push(pluginCreators2.gestures({}));
|
|
11444
12296
|
}
|
|
12297
|
+
const shareEnabled = type === "video" && Boolean(config.shareUrl) && config.controls !== false;
|
|
12298
|
+
if (shareEnabled && pluginCreators2.share) {
|
|
12299
|
+
const shareConfig = { url: config.shareUrl };
|
|
12300
|
+
if (config.title) shareConfig.title = config.title;
|
|
12301
|
+
if (config.embedBaseUrl) shareConfig.embedBaseUrl = config.embedBaseUrl;
|
|
12302
|
+
plugins.push(pluginCreators2.share(shareConfig));
|
|
12303
|
+
}
|
|
11445
12304
|
if (pluginCreators2.analytics && config.analytics?.beaconUrl) {
|
|
11446
12305
|
plugins.push(pluginCreators2.analytics({
|
|
11447
12306
|
beaconUrl: config.analytics.beaconUrl,
|
|
@@ -11455,6 +12314,7 @@ async function createEmbedPlayer(container, config, pluginCreators2, availableTy
|
|
|
11455
12314
|
if (Object.keys(theme).length > 0) uiConfig.theme = theme;
|
|
11456
12315
|
if (config.hideDelay !== void 0) uiConfig.hideDelay = config.hideDelay;
|
|
11457
12316
|
if (config.bigPlayButton !== void 0) uiConfig.bigPlayButton = config.bigPlayButton;
|
|
12317
|
+
if (shareEnabled && pluginCreators2.share) uiConfig.controls = SHARE_CONTROL_LAYOUT;
|
|
11458
12318
|
plugins.push(pluginCreators2.videoUI(uiConfig));
|
|
11459
12319
|
} else if ((type === "audio" || type === "audio-mini") && pluginCreators2.audioUI) {
|
|
11460
12320
|
plugins.push(pluginCreators2.audioUI({
|
|
@@ -11560,7 +12420,7 @@ function setupAutoInit(pluginCreators2, availableTypes) {
|
|
|
11560
12420
|
}
|
|
11561
12421
|
}
|
|
11562
12422
|
}
|
|
11563
|
-
const PKG_VERSION = "1.
|
|
12423
|
+
const PKG_VERSION = "1.8.1";
|
|
11564
12424
|
const VERSION = PKG_VERSION;
|
|
11565
12425
|
const AVAILABLE_TYPES = ["video", "audio", "audio-mini"];
|
|
11566
12426
|
const pluginCreators = {
|
|
@@ -11573,7 +12433,8 @@ const pluginCreators = {
|
|
|
11573
12433
|
mediaSession: createMediaSessionPlugin,
|
|
11574
12434
|
watermark: createWatermarkPlugin,
|
|
11575
12435
|
captions: createCaptionsPlugin,
|
|
11576
|
-
gestures: createGesturesPlugin
|
|
12436
|
+
gestures: createGesturesPlugin,
|
|
12437
|
+
share: createSharePlugin
|
|
11577
12438
|
};
|
|
11578
12439
|
const ScarlettPlayerAPI = createScarlettPlayerAPI(
|
|
11579
12440
|
pluginCreators,
|