@oddin-gg/havik-player 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -224,6 +224,14 @@ export declare interface CreatePlayerOptions {
224
224
  * 'custom'.
225
225
  */
226
226
  statusOverlays?: boolean;
227
+ /**
228
+ * Your page's Content-Security-Policy nonce, for the `<style>` tag the
229
+ * custom control bar injects. Needed only when `style-src` has no
230
+ * `'unsafe-inline'`: without the nonce the browser blocks the tag and the
231
+ * controls render unstyled. Pass the same value as your policy's
232
+ * `'nonce-…'` source. Ignored unless controls is 'custom'.
233
+ */
234
+ cspNonce?: string;
227
235
  /** Message on the end-of-stream card. Default: "This stream has ended". */
228
236
  endedMessage?: string;
229
237
  /**
package/dist/index.d.ts CHANGED
@@ -224,6 +224,14 @@ export declare interface CreatePlayerOptions {
224
224
  * 'custom'.
225
225
  */
226
226
  statusOverlays?: boolean;
227
+ /**
228
+ * Your page's Content-Security-Policy nonce, for the `<style>` tag the
229
+ * custom control bar injects. Needed only when `style-src` has no
230
+ * `'unsafe-inline'`: without the nonce the browser blocks the tag and the
231
+ * controls render unstyled. Pass the same value as your policy's
232
+ * `'nonce-…'` source. Ignored unless controls is 'custom'.
233
+ */
234
+ cspNonce?: string;
227
235
  /** Message on the end-of-stream card. Default: "This stream has ended". */
228
236
  endedMessage?: string;
229
237
  /**
package/dist/index.js CHANGED
@@ -2260,7 +2260,7 @@ async function $t() {
2260
2260
  }
2261
2261
  //#endregion
2262
2262
  //#region src/core/version.ts
2263
- var en = "2.13.0", tn = {
2263
+ var en = "2.14.0", tn = {
2264
2264
  accent: "#E1B600",
2265
2265
  accentText: "#1B1C23",
2266
2266
  background: "#0e0f13",
@@ -2293,15 +2293,16 @@ function rn(e, t) {
2293
2293
  }
2294
2294
  //#endregion
2295
2295
  //#region src/ui/styles.ts
2296
- var an = "havik-player-styles", on = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n";
2297
- function sn(e = document) {
2298
- if (e.getElementById(an)) return;
2299
- let t = e.createElement("style");
2300
- t.id = an, t.textContent = on, e.head.appendChild(t);
2296
+ var an = "havik-player-styles", on = "\n.havik-player{position:relative;display:block;width:100%;background:var(--havik-bg,#0e0f13);\n color:var(--havik-text,#fff);font-family:var(--havik-font,system-ui,sans-serif);\n overflow:hidden;line-height:1.4;-webkit-tap-highlight-color:transparent}\n.havik-player video{display:block;width:100%;height:auto;aspect-ratio:16/9;\n object-fit:contain;background:#000;outline:none}\n.havik-player:focus{outline:none}\n.havik-player *{box-sizing:border-box}\n\n.havik-player:fullscreen,.havik-player:-webkit-full-screen{display:flex;align-items:center;\n justify-content:center;width:100vw;height:100vh;aspect-ratio:auto}\n.havik-player:fullscreen video,.havik-player:-webkit-full-screen video{width:100%;height:100%;\n aspect-ratio:auto;object-fit:contain}\n\n.havik-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;cursor:pointer}\n.havik-gradient{position:absolute;left:0;right:0;bottom:0;height:120px;pointer-events:none;\n background:linear-gradient(to top,rgba(0,0,0,.78),transparent);\n opacity:1;transition:opacity .25s}\n.havik-player.havik-hide .havik-gradient,\n.havik-player.havik-hide .havik-bar{opacity:0;pointer-events:none}\n.havik-player.havik-hide{cursor:none}\n\n.havik-center{width:74px;height:74px;border-radius:50%;border:0;cursor:pointer;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);\n display:flex;align-items:center;justify-content:center;\n box-shadow:0 6px 24px rgba(0,0,0,.45);transition:transform .15s,opacity .15s}\n.havik-center:hover{transform:scale(1.06)}\n.havik-center svg{width:38px;height:38px;margin-left:2px}\n.havik-center.havik-gone{opacity:0;pointer-events:none;transform:scale(.8)}\n\n/* Absolutely centred, NOT a flex sibling of .havik-center: the hidden center\n button (.havik-gone) still occupies its 74px in the row, which pushed an\n in-flow spinner 37px right of centre. */\n.havik-spinner{position:absolute;inset:0;margin:auto;width:56px;height:56px;border-radius:50%;\n border:4px solid rgba(255,255,255,.2);border-top-color:var(--havik-accent,#E1B600);\n animation:havik-spin .8s linear infinite}\n@keyframes havik-spin{to{transform:rotate(360deg)}}\n\n/* Status card — branded full-bleed overlay for the stopped/pre-play states\n (ended / fatal error / pre-play / armed-waiting). */\n.havik-status{position:absolute;inset:0;display:flex;flex-direction:column;gap:16px;\n align-items:center;justify-content:center;background:rgba(14,15,19,.82);text-align:center;padding:24px}\n.havik-status__logo{width:128px;height:46px;background:var(--havik-logo) center/contain no-repeat;opacity:.95}\n.havik-status__msg{color:var(--havik-text,#fff);font-size:15px;font-weight:600;max-width:480px}\n.havik-status__btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;\n padding:10px 18px;border-radius:var(--havik-radius,8px);font-weight:700;font-size:14px;\n background:var(--havik-accent,#E1B600);color:var(--havik-accent-text,#1B1C23);transition:transform .15s}\n.havik-status__btn:hover{transform:scale(1.04)}\n.havik-status__btn svg{width:18px;height:18px}\n/* Pre-play: lighter scrim so the poster shows through, and a round play CTA. */\n.havik-status--preplay{background:rgba(14,15,19,.4)}\n.havik-status--preplay .havik-status__btn--play{width:74px;height:74px;border-radius:50%;padding:0;\n justify-content:center;box-shadow:0 6px 24px rgba(0,0,0,.45)}\n.havik-status--preplay .havik-status__btn--play svg{width:38px;height:38px;margin-left:3px}\n\n.havik-watermark{position:absolute;top:12px;right:14px;height:22px;opacity:.85;pointer-events:none;\n content:'';background:var(--havik-logo) center/contain no-repeat;width:90px}\n/* A status card carries its own logo — the corner watermark would duplicate it. */\n.havik-player.havik-has-status .havik-watermark{display:none}\n\n.havik-bar{position:absolute;left:0;right:0;bottom:0;padding:0 12px 8px;\n opacity:1;transition:opacity .25s;z-index:2}\n.havik-seek{position:relative;height:16px;display:flex;align-items:center;cursor:pointer;margin-bottom:2px}\n.havik-seek__track{position:relative;width:100%;height:4px;border-radius:3px;background:rgba(255,255,255,.25)}\n.havik-seek__buffered{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:rgba(255,255,255,.4)}\n.havik-seek__played{position:absolute;left:0;top:0;height:100%;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-seek__thumb{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;\n background:var(--havik-accent,#E1B600);transform:translate(-50%,-50%) scale(0);transition:transform .1s}\n.havik-seek:hover .havik-seek__thumb{transform:translate(-50%,-50%) scale(1)}\n\n.havik-controls{display:flex;align-items:center;gap:6px;height:40px}\n.havik-spacer{flex:1}\n.havik-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;border-radius:var(--havik-radius,8px);\n padding:0;transition:background .12s,color .12s}\n.havik-btn:hover{background:rgba(255,255,255,.12)}\n.havik-btn:focus-visible{outline:2px solid var(--havik-accent,#E1B600);outline-offset:1px}\n.havik-btn svg{width:22px;height:22px}\n\n/* The volume level is filled in accent up to the thumb, like the seek bar's\n played span — a native range input paints no \"elapsed\" side, which left the\n level readable only from the dot's position. --havik-vol (0..1) is set by\n syncVolume(); the track width and thumb size live here as variables because\n the WebKit fill has to compute the thumb centre from both. */\n.havik-vol{display:flex;align-items:center}\n.havik-vol__slider{--havik-vol-w:70px;--havik-vol-thumb:12px;--havik-vol:1;\n --havik-vol-pos:calc(var(--havik-vol-thumb) / 2 + var(--havik-vol) * (var(--havik-vol-w) - var(--havik-vol-thumb)));\n width:0;opacity:0;transition:width .18s,opacity .18s;height:4px;cursor:pointer;\n appearance:none;-webkit-appearance:none;background:transparent;border-radius:3px;margin:0 4px}\n.havik-vol:hover .havik-vol__slider,.havik-vol:focus-within .havik-vol__slider{width:var(--havik-vol-w);opacity:1}\n/* WebKit/Blink have no progress pseudo-element: paint the fill as a hard-stop\n gradient that ends at the thumb centre. */\n.havik-vol__slider::-webkit-slider-runnable-track{height:4px;border-radius:3px;\n background:linear-gradient(to right,var(--havik-accent,#E1B600) var(--havik-vol-pos),\n rgba(255,255,255,.3) var(--havik-vol-pos))}\n/* Firefox fills natively, and tracks the thumb exactly. */\n.havik-vol__slider::-moz-range-track{height:4px;border-radius:3px;background:rgba(255,255,255,.3)}\n.havik-vol__slider::-moz-range-progress{height:4px;border-radius:3px;background:var(--havik-accent,#E1B600)}\n.havik-vol__slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--havik-vol-thumb);\n height:var(--havik-vol-thumb);border-radius:50%;background:var(--havik-accent,#E1B600);\n margin-top:calc((4px - var(--havik-vol-thumb)) / 2)}\n.havik-vol__slider::-moz-range-thumb{width:var(--havik-vol-thumb);height:var(--havik-vol-thumb);\n border:0;border-radius:50%;background:var(--havik-accent,#E1B600)}\n\n.havik-time{font-size:12px;color:var(--havik-text-muted,#9aa0ad);\n font-variant-numeric:tabular-nums;padding:0 6px;white-space:nowrap}\n.havik-live{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;\n letter-spacing:.4px;padding:0 8px;color:var(--havik-text,#fff)}\n.havik-live svg{width:9px;height:9px;color:var(--havik-text-muted,#9aa0ad)}\n.havik-live.is-live svg{color:var(--havik-accent,#E1B600)}\n.havik-live__go{margin-left:2px;border:0;cursor:pointer;background:transparent;color:var(--havik-text-muted,#9aa0ad);\n font:inherit;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;padding:2px 6px;border-radius:6px}\n.havik-live.is-live .havik-live__go{display:none}\n.havik-live__go:hover{color:var(--havik-accent,#E1B600)}\n\n.havik-menu{position:absolute;bottom:54px;right:12px;min-width:170px;max-height:230px;overflow:auto;\n background:var(--havik-surface,#1B1C23);border:1px solid var(--havik-border,rgba(255,255,255,.1));\n border-radius:var(--havik-radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.5);padding:6px;z-index:3}\n.havik-menu[hidden]{display:none}\n.havik-menu__title{font-size:11px;text-transform:uppercase;letter-spacing:.6px;\n color:var(--havik-text-muted,#9aa0ad);padding:6px 8px 4px}\n.havik-menu__item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;\n border:0;background:transparent;color:var(--havik-text,#fff);cursor:pointer;font:inherit;font-size:13px;\n text-align:left;padding:7px 8px;border-radius:6px}\n.havik-menu__item:hover{background:var(--havik-surface-muted,#2a2c36)}\n.havik-menu__item[aria-checked=\"true\"]{color:var(--havik-accent,#E1B600);font-weight:600}\n.havik-menu__item[aria-checked=\"true\"]::after{content:'✓'}\n", sn = /* @__PURE__ */ new WeakMap();
2297
+ function cn(e = document, t) {
2298
+ let n = e.querySelector(`style#${an}`);
2299
+ if (n && (!t || sn.get(n) === t)) return;
2300
+ let r = e.createElement("style");
2301
+ r.id = an, t && (r.nonce = t, r.setAttribute("nonce", t)), r.textContent = on, sn.set(r, t), n ? n.replaceWith(r) : e.head.appendChild(r);
2301
2302
  }
2302
2303
  //#endregion
2303
2304
  //#region src/ui/icons.ts
2304
- var cn = {
2305
+ var ln = {
2305
2306
  play: "<path d=\"M8 5v14l11-7z\"/>",
2306
2307
  pause: "<path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/>",
2307
2308
  volumeHigh: "<path d=\"M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4zm-2.5-9v2.06A7 7 0 0 1 14 19v2a9 9 0 0 0 0-18z\"/>",
@@ -2315,20 +2316,20 @@ var cn = {
2315
2316
  retry: "<path d=\"M17.65 6.35A8 8 0 1 0 19.73 13h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>"
2316
2317
  };
2317
2318
  function Z(e) {
2318
- return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${cn[e]}</svg>`;
2319
+ return `<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">${ln[e]}</svg>`;
2319
2320
  }
2320
2321
  //#endregion
2321
2322
  //#region src/ui/controls.ts
2322
- var ln = 3e3, un = /* @__PURE__ */ new Set([
2323
+ var un = 3e3, dn = /* @__PURE__ */ new Set([
2323
2324
  "GONE",
2324
2325
  "NOT_FOUND",
2325
2326
  "INVALID_URN",
2326
2327
  "UNAUTHORIZED"
2327
2328
  ]);
2328
- function dn(e) {
2329
- return new fn(e);
2329
+ function fn(e) {
2330
+ return new pn(e);
2330
2331
  }
2331
- var fn = class {
2332
+ var pn = class {
2332
2333
  root;
2333
2334
  video;
2334
2335
  player;
@@ -2370,7 +2371,7 @@ var fn = class {
2370
2371
  scrubbing = !1;
2371
2372
  offFns = [];
2372
2373
  constructor(e) {
2373
- this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? ln, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, sn(this.doc), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2374
+ this.root = e.root, this.video = e.video, this.player = e.player, this.doc = e.root.ownerDocument ?? document, this.autoHideMs = e.autoHideMs ?? un, this.logoUrl = e.logoUrl, this.statusOverlays = e.statusOverlays !== !1, this.endedMessage = e.endedMessage ?? "This stream has ended", this.interruptedMessage = e.interruptedMessage ?? "Reconnecting — please stand by", this.errorMessage = e.errorMessage, cn(this.doc, e.cspNonce), this.root.classList.add("havik-player"), this.root.hasAttribute("tabindex") || (this.root.tabIndex = 0), this.root.setAttribute("role", "region"), this.root.setAttribute("aria-label", "Video player");
2374
2375
  let t = (e, t, n) => {
2375
2376
  let r = this.doc.createElement(e);
2376
2377
  if (r.className = t, n) for (let [e, t] of Object.entries(n)) r.setAttribute(e, t);
@@ -2461,7 +2462,7 @@ var fn = class {
2461
2462
  e.type = "button", e.className = "havik-status__btn havik-status__btn--play", e.setAttribute("aria-label", "Play"), e.innerHTML = Z("play"), e.addEventListener("click", () => void this.player.play()), n.append(e);
2462
2463
  } else {
2463
2464
  let r = this.doc.createElement("div");
2464
- if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !un.has(this.lastError.code)) {
2465
+ if (r.className = "havik-status__msg", r.textContent = t ?? "", n.append(r), e === "error" && this.lastError && !dn.has(this.lastError.code)) {
2465
2466
  let e = this.doc.createElement("button");
2466
2467
  e.type = "button", e.className = "havik-status__btn", e.innerHTML = `${Z("retry")}<span>Retry</span>`, e.addEventListener("click", () => void this.player.retry()), n.append(e);
2467
2468
  }
@@ -2502,7 +2503,7 @@ var fn = class {
2502
2503
  let n = r.end(r.length - 1), i = Math.min(1, Math.max(0, (n - e.start) / t));
2503
2504
  this.seekBuffered.style.width = `${(i * 100).toFixed(2)}%`;
2504
2505
  }
2505
- !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${Q(this.video.currentTime)} / ${Q(this.video.duration)}`);
2506
+ !this.player.getStats().isLive && Number.isFinite(this.video.duration) && (this.time.textContent = `${mn(this.video.currentTime)} / ${mn(this.video.duration)}`);
2506
2507
  }
2507
2508
  syncLive() {
2508
2509
  let e = this.player.getStats(), t = e.isLive !== !1;
@@ -2552,17 +2553,17 @@ var fn = class {
2552
2553
  renderQualityMenu() {
2553
2554
  let e = this.player.getQualityLevels(), t = this.player.getCurrentQuality(), n = this.doc.createElement("div");
2554
2555
  n.className = "havik-menu__title", n.textContent = "Quality", this.menu.replaceChildren(n, this.menuItem("Auto", t === -1, () => this.player.setQuality("auto")));
2555
- for (let n of e) this.menu.append(this.menuItem(pn(n), t === n.index, () => this.player.setQuality(n.index)));
2556
+ for (let n of e) this.menu.append(this.menuItem(hn(n), t === n.index, () => this.player.setQuality(n.index)));
2556
2557
  }
2557
2558
  renderAudioMenu() {
2558
2559
  let e = this.player.getAudioTracks(), t = this.doc.createElement("div");
2559
2560
  t.className = "havik-menu__title", t.textContent = "Audio", this.menu.replaceChildren(t);
2560
- for (let t of e) this.menu.append(this.menuItem(mn(t), t.default, () => this.player.setAudioTrack(t.id)));
2561
+ for (let t of e) this.menu.append(this.menuItem(gn(t), t.default, () => this.player.setAudioTrack(t.id)));
2561
2562
  }
2562
2563
  renderCcMenu() {
2563
2564
  let e = this.player.getTextTracks(), t = this.doc.createElement("div");
2564
2565
  t.className = "havik-menu__title", t.textContent = "Subtitles", this.menu.replaceChildren(t, this.menuItem("Off", !1, () => this.player.setTextTrack(-1)));
2565
- for (let t of e) this.menu.append(this.menuItem(hn(t), !1, () => this.player.setTextTrack(t.id)));
2566
+ for (let t of e) this.menu.append(this.menuItem(_n(t), !1, () => this.player.setTextTrack(t.id)));
2566
2567
  }
2567
2568
  onTracksChanged() {
2568
2569
  this.openMenu && this.renderMenu(this.openMenu);
@@ -2662,41 +2663,41 @@ var fn = class {
2662
2663
  this.root.classList.remove("havik-player", "havik-hide", "havik-has-status");
2663
2664
  }
2664
2665
  };
2665
- function Q(e) {
2666
+ function mn(e) {
2666
2667
  (!Number.isFinite(e) || e < 0) && (e = 0);
2667
2668
  let t = Math.floor(e % 60), n = Math.floor(e / 60 % 60), r = Math.floor(e / 3600), i = r > 0 ? String(n).padStart(2, "0") : String(n), a = String(t).padStart(2, "0");
2668
2669
  return r > 0 ? `${r}:${i}:${a}` : `${i}:${a}`;
2669
2670
  }
2670
- function pn(e) {
2671
+ function hn(e) {
2671
2672
  return e.height ? `${e.height}p` : `${Math.round(e.bitrate / 1e3)} kbps`;
2672
2673
  }
2673
- function mn(e) {
2674
+ function gn(e) {
2674
2675
  return e.name || e.lang || `Track ${e.id}`;
2675
2676
  }
2676
- function hn(e) {
2677
+ function _n(e) {
2677
2678
  return e.name || e.lang || `Track ${e.id}`;
2678
2679
  }
2679
2680
  //#endregion
2680
2681
  //#region src/managed/index.ts
2681
- var gn = 6e3, _n = 45e3, vn = 2e3, yn = 3, bn = 2e3, xn = 48e4;
2682
- function Sn(e) {
2682
+ var vn = 6e3, yn = 45e3, Q = 2e3, bn = 3, xn = 2e3, Sn = 48e4;
2683
+ function Cn(e) {
2683
2684
  if (e.env !== void 0 && e.baseUrl !== void 0) throw new n("INTERNAL", 0, `createPlayer: pass env OR baseUrl, not both (got env=${JSON.stringify(e.env)} and baseUrl=${JSON.stringify(e.baseUrl)})`);
2684
2685
  if (e.env !== void 0) return B(e.env);
2685
2686
  if (!e.baseUrl) throw new n("INTERNAL", 0, "createPlayer: env or baseUrl is required (env: 'integration' | 'production')");
2686
2687
  return e.baseUrl;
2687
2688
  }
2688
- async function Cn(e) {
2689
+ async function wn(e) {
2689
2690
  if (!e.video || typeof e.video.play != "function") throw new n("INTERNAL", 0, "createPlayer: opts.video must be an HTMLVideoElement");
2690
- let t = Sn(e);
2691
+ let t = Cn(e);
2691
2692
  if (!e.matchUrn) throw new n("INVALID_URN", 0, "createPlayer: matchUrn is required");
2692
2693
  if (!e.credential) throw new n("INTERNAL", 0, "createPlayer: credential is required");
2693
- let r = new wn({
2694
+ let r = new Tn({
2694
2695
  ...e,
2695
2696
  baseUrl: t
2696
2697
  });
2697
2698
  return await r.start(), r;
2698
2699
  }
2699
- var wn = class {
2700
+ var Tn = class {
2700
2701
  opts;
2701
2702
  abort = new AbortController();
2702
2703
  listeners = {};
@@ -2784,7 +2785,7 @@ var wn = class {
2784
2785
  setTimeout(() => {
2785
2786
  a || this.destroyed || e !== this.loadGen || this.emit("waiting", {
2786
2787
  phase: i ? "unavailable" : "tooEarly",
2787
- retryInMs: _n,
2788
+ retryInMs: yn,
2788
2789
  attempt: 1,
2789
2790
  elapsedMs: 0,
2790
2791
  ...!i && f !== void 0 && f > Date.now() ? { ttkMs: f - Date.now() } : {}
@@ -2823,13 +2824,13 @@ var wn = class {
2823
2824
  !a && !p() && h();
2824
2825
  }, e);
2825
2826
  };
2826
- _(gn), this.armingSub = T({
2827
+ _(vn), this.armingSub = T({
2827
2828
  eventsBaseUrl: t,
2828
2829
  matchUrn: this.opts.matchUrn,
2829
2830
  credential: this.opts.credential,
2830
- onAlive: () => _(_n),
2831
+ onAlive: () => _(yn),
2831
2832
  onState: (e) => {
2832
- a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * vn);
2833
+ a || p() || (e.state === "live" || e.state === "gone") && setTimeout(g, Math.random() * Q);
2833
2834
  },
2834
2835
  onError: () => h()
2835
2836
  });
@@ -2988,7 +2989,7 @@ var wn = class {
2988
2989
  r = 0, i = !1;
2989
2990
  },
2990
2991
  onError: () => {
2991
- this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= yn && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
2992
+ this.destroyed || e !== this.loadGen || i || this.endedHandled || (r += 1, r >= bn && (i = !0, this.emit("warning", new n("NETWORK", 0, `live-state events unreachable at ${t} after ${r} attempts — end-of-stream detection degraded to the HLS staleness watchdog. Check the events host, the api-key's allowed origins, and CSP connect-src.`))));
2992
2993
  }
2993
2994
  });
2994
2995
  }
@@ -3010,7 +3011,7 @@ var wn = class {
3010
3011
  scheduleRejoin(e) {
3011
3012
  this.rejoinEnabled() && !this.rejoinTimer && (this.rejoinTimer = setTimeout(() => {
3012
3013
  this.rejoinTimer = null, !(this.destroyed || e !== this.loadGen || this._state !== "ended") && this.rejoin();
3013
- }, Math.random() * vn));
3014
+ }, Math.random() * Q));
3014
3015
  }
3015
3016
  cancelRejoin() {
3016
3017
  this.rejoinTimer &&= (clearTimeout(this.rejoinTimer), null);
@@ -3034,7 +3035,7 @@ var wn = class {
3034
3035
  }
3035
3036
  t.controls = !1;
3036
3037
  let n = (t.ownerDocument ?? document).createElement("div"), r = t.parentNode;
3037
- r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = dn({
3038
+ r && r.insertBefore(n, t), n.appendChild(t), rn(n, this.opts.theme), this.container = n, this.controlBar = fn({
3038
3039
  root: n,
3039
3040
  video: t,
3040
3041
  player: this,
@@ -3042,7 +3043,8 @@ var wn = class {
3042
3043
  statusOverlays: this.opts.statusOverlays,
3043
3044
  endedMessage: this.opts.endedMessage,
3044
3045
  interruptedMessage: this.opts.interruptedMessage,
3045
- errorMessage: this.opts.errorMessage
3046
+ errorMessage: this.opts.errorMessage,
3047
+ cspNonce: this.opts.cspNonce
3046
3048
  });
3047
3049
  }
3048
3050
  unmountControls() {
@@ -3064,7 +3066,7 @@ var wn = class {
3064
3066
  }
3065
3067
  startStats() {
3066
3068
  this.stopStats();
3067
- let e = Math.max(500, this.opts.statsIntervalMs ?? bn);
3069
+ let e = Math.max(500, this.opts.statsIntervalMs ?? xn);
3068
3070
  this.statsTimer = setInterval(() => {
3069
3071
  if (!this.engine) return;
3070
3072
  let e = this.engine.getStats();
@@ -3076,7 +3078,7 @@ var wn = class {
3076
3078
  }
3077
3079
  scheduleLicenseRefresh() {
3078
3080
  this.stopRefresh();
3079
- let e = this.opts.licenseRefreshMs ?? xn;
3081
+ let e = this.opts.licenseRefreshMs ?? Sn;
3080
3082
  e && this._descriptor?.drmEnabled && (this.refreshTimer = setInterval(() => void this.refreshLicense(), e));
3081
3083
  }
3082
3084
  stopRefresh() {
@@ -3231,7 +3233,7 @@ function $(e) {
3231
3233
  }
3232
3234
  //#endregion
3233
3235
  //#region src/embed/host.ts
3234
- function Tn(e) {
3236
+ function En(e) {
3235
3237
  if (!e.container || typeof e.container.appendChild != "function") throw Error("mountEmbed: opts.container must be a DOM element");
3236
3238
  if (!e.src) throw Error("mountEmbed: opts.src is required");
3237
3239
  if (!e.baseUrl || !e.matchUrn) throw Error("mountEmbed: opts.baseUrl and opts.matchUrn are required");
@@ -3290,15 +3292,15 @@ function Tn(e) {
3290
3292
  }
3291
3293
  //#endregion
3292
3294
  //#region src/embed/page.ts
3293
- var En = /^[A-Za-z0-9:_.-]{1,128}$/;
3294
- function Dn(e) {
3295
+ var Dn = /^[A-Za-z0-9:_.-]{1,128}$/;
3296
+ function On(e) {
3295
3297
  if (typeof e == "boolean") return e;
3296
3298
  if (typeof e == "object" && e) {
3297
3299
  let t = e.endpoint;
3298
3300
  return typeof t == "string" ? { endpoint: t } : {};
3299
3301
  }
3300
3302
  }
3301
- function On() {
3303
+ function kn() {
3302
3304
  let e = window.HAVIK_EMBED_CONFIG;
3303
3305
  if (e) return {
3304
3306
  baseUrl: e.baseUrl ?? "",
@@ -3308,7 +3310,7 @@ function On() {
3308
3310
  autoplay: e.autoplay ?? !0,
3309
3311
  muted: e.muted ?? !0,
3310
3312
  waitForLive: e.waitForLive ?? !0,
3311
- analytics: Dn(e.analytics)
3313
+ analytics: On(e.analytics)
3312
3314
  };
3313
3315
  let t = new URLSearchParams(location.search);
3314
3316
  return {
@@ -3321,9 +3323,9 @@ function On() {
3321
3323
  waitForLive: !0
3322
3324
  };
3323
3325
  }
3324
- async function kn() {
3325
- let e = On();
3326
- jn();
3326
+ async function An() {
3327
+ let e = kn();
3328
+ Mn();
3327
3329
  let t = document.createElement("video");
3328
3330
  t.setAttribute("playsinline", ""), t.muted = e.muted ?? !0, t.style.cssText = "width:100%;height:100%;object-fit:contain;background:#000;aspect-ratio:auto", document.body.appendChild(t);
3329
3331
  let r = e.parentOrigin && e.parentOrigin !== "null" ? e.parentOrigin : null;
@@ -3378,12 +3380,12 @@ async function kn() {
3378
3380
  code: e.code,
3379
3381
  message: e.message
3380
3382
  })), e.on("autoplayblocked", () => {
3381
- i({ type: "oddin:autoplayblocked" }), An(t, e);
3383
+ i({ type: "oddin:autoplayblocked" }), jn(t, e);
3382
3384
  });
3383
3385
  }, s = async (r) => {
3384
3386
  a?.destroy(), a = null;
3385
3387
  try {
3386
- let n = await Cn({
3388
+ let n = await wn({
3387
3389
  video: t,
3388
3390
  baseUrl: e.baseUrl,
3389
3391
  matchUrn: r,
@@ -3445,7 +3447,7 @@ async function kn() {
3445
3447
  a?.retry();
3446
3448
  break;
3447
3449
  case "oddin:load":
3448
- typeof e.matchUrn == "string" && En.test(e.matchUrn) && s(e.matchUrn);
3450
+ typeof e.matchUrn == "string" && Dn.test(e.matchUrn) && s(e.matchUrn);
3449
3451
  break;
3450
3452
  case "oddin:destroy": c();
3451
3453
  }
@@ -3457,18 +3459,18 @@ async function kn() {
3457
3459
  protocol: 1
3458
3460
  });
3459
3461
  }
3460
- function An(e, t) {
3462
+ function jn(e, t) {
3461
3463
  if (document.getElementById("havik-tap")) return;
3462
3464
  let n = document.createElement("button");
3463
3465
  n.id = "havik-tap", n.textContent = "▶ Tap to play", n.style.cssText = "position:fixed;inset:0;width:100%;height:100%;border:0;background:rgba(0,0,0,.55);color:#fff;font:600 18px system-ui,sans-serif;cursor:pointer;z-index:9", n.addEventListener("click", () => {
3464
3466
  n.remove(), t.play();
3465
3467
  }), document.body.appendChild(n);
3466
3468
  }
3467
- function jn() {
3469
+ function Mn() {
3468
3470
  let e = document.createElement("style");
3469
3471
  e.textContent = "html,body{margin:0;height:100%;background:#000;overflow:hidden}body{display:flex}", document.head.appendChild(e);
3470
3472
  }
3471
3473
  //#endregion
3472
- export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, B as baseUrlForEnv, kn as bootEmbed, Te as clearDeviceId, Cn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, Tn as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3474
+ export { tn as ODDIN_THEME, n as PlaybackError, rn as applyTheme, B as baseUrlForEnv, An as bootEmbed, Te as clearDeviceId, wn as createPlayer, E as deriveEventsBaseUrl, He as detectDrmSupport, C as enterVideoFullscreen, w as exitVideoFullscreen, _e as fetchCatalog, ye as fetchTournament, xe as flattenMatches, k as getDeviceId, We as isLowLatencyManifest, $ as isOddinMessage, De as licenseRequestHeaders, En as mountEmbed, de as pollToLive, y as resolveStream, T as subscribeLiveState, S as videoFullscreenActive, Ce as watchStatus };
3473
3475
 
3474
3476
  //# sourceMappingURL=index.js.map