@hopeplayer/core 1.0.8 → 1.0.10
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/chunks/{index-CqNAnI1c.js → index-BJxXxr0g.js} +2 -2
- package/dist/chunks/{index-CqNAnI1c.js.map → index-BJxXxr0g.js.map} +1 -1
- package/dist/chunks/{index-hGMg-xMe.js → index-Bqs23yk7.js} +7 -7
- package/dist/chunks/{index-hGMg-xMe.js.map → index-Bqs23yk7.js.map} +1 -1
- package/dist/chunks/{index-5NsIvcG-.js → index-CJ3wYSlU.js} +2 -2
- package/dist/chunks/{index-5NsIvcG-.js.map → index-CJ3wYSlU.js.map} +1 -1
- package/dist/chunks/{index-lKS-jIUb.js → index-DJZjHaLs.js} +2 -2
- package/dist/chunks/{index-lKS-jIUb.js.map → index-DJZjHaLs.js.map} +1 -1
- package/dist/chunks/{index-nL5ba3Fw.js → index-DN077Bbe.js} +2 -2
- package/dist/chunks/{index-nL5ba3Fw.js.map → index-DN077Bbe.js.map} +1 -1
- package/dist/chunks/{index-CmbuiyxF.js → index-i8eKCN8V.js} +2 -2
- package/dist/chunks/{index-CmbuiyxF.js.map → index-i8eKCN8V.js.map} +1 -1
- package/dist/hope-player.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var A = Object.defineProperty;
|
|
2
2
|
var P = (h, t, e) => t in h ? A(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
|
|
3
3
|
var r = (h, t, e) => P(h, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
-
import { c as b } from "./index-
|
|
4
|
+
import { c as b } from "./index-Bqs23yk7.js";
|
|
5
5
|
const L = {
|
|
6
6
|
startIndex: 0,
|
|
7
7
|
loop: !0,
|
|
@@ -319,4 +319,4 @@ export {
|
|
|
319
319
|
N as HopeStory,
|
|
320
320
|
M as installStory
|
|
321
321
|
};
|
|
322
|
-
//# sourceMappingURL=index-
|
|
322
|
+
//# sourceMappingURL=index-CJ3wYSlU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-5NsIvcG-.js","sources":["../../src/plugins/story/HopeStory.ts","../../src/plugins/story/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { PlayableItem, StoryOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface StoryPlayerContext {\n videoEl: HTMLVideoElement;\n hostEl: HTMLElement;\n /** .video-area — story aktifken 9:16'ya çevrilir (data-story attribute ile), destroy'da geri alınır. */\n videoAreaEl: HTMLElement;\n /** Gerçek player gövdesi (.media-layer + chrome + center-play-button) — orta pane'e taşınır, destroy'da geri alınır. */\n playerBodyEls: HTMLElement[];\n loadSource(src: string, type?: string): Promise<void>;\n setPoster(url: string): void;\n requestFreshAd(prerollTag?: string): void;\n i18n: HopeLocaleStrings;\n}\n\ntype Dir = 'next' | 'prev';\n\nconst DEFAULTS: Required<Omit<StoryOptions, 'items' | 'adTagPreroll' | 'gestures' | 'ui'>> = {\n startIndex: 0,\n loop: true,\n autoplayNext: true,\n preferHls: true,\n transitionMs: 380,\n adsOnItemChange: true,\n};\n\n/** Dikey video akışı (Shorts/Story): tek <video>, item değişiminde kaynak değişir. */\nexport class HopeStory {\n private readonly options: Required<Omit<StoryOptions, 'items' | 'adTagPreroll' | 'gestures' | 'ui'>>;\n private readonly gestureOpts: Required<NonNullable<StoryOptions['gestures']>>;\n private readonly uiOpts: Required<NonNullable<StoryOptions['ui']>>;\n private readonly items: PlayableItem[];\n\n private index: number;\n private destroyed = false;\n private switching = false;\n private listOpen = false;\n private userHasStartedPlayback = false;\n private lastAdsIndex: number | null = null;\n private wheelAccum = 0;\n private lastNavAt = 0;\n private drag: { startY: number; startX: number; dy: number; active: boolean; id: number } | null = null;\n\n private readonly els: {\n viewport: HTMLElement;\n track: HTMLElement;\n panePrev: HTMLElement;\n paneCurrent: HTMLElement;\n paneNext: HTMLElement;\n overlay: HTMLElement;\n counter: HTMLElement;\n titleEl: HTMLElement;\n spotEl: HTMLElement;\n dots: HTMLElement;\n progressBar: HTMLElement;\n upBtn: HTMLButtonElement;\n downBtn: HTMLButtonElement;\n listBtn: HTMLButtonElement;\n panel: HTMLElement;\n grid: HTMLElement;\n };\n\n constructor(\n private readonly ctx: StoryPlayerContext,\n private readonly rawOptions: StoryOptions\n ) {\n this.options = { ...DEFAULTS, ...rawOptions };\n this.gestureOpts = {\n swipe: true,\n swipeThreshold: 0.12,\n wheel: true,\n wheelThreshold: 42,\n wheelCooldownMs: 620,\n keyboard: true,\n ...rawOptions.gestures,\n };\n this.uiOpts = { counter: true, title: true, spot: true, dots: true, progress: true, ...rawOptions.ui };\n this.items = Array.isArray(rawOptions.items) ? rawOptions.items : [];\n this.index = this.clamp(this.options.startIndex);\n\n this.els = this.buildDom();\n this.bindEvents();\n this.syncUi();\n this.preloadNeighbours();\n\n if (this.items.length) void this.loadItem(this.index, { isStart: true });\n }\n\n refreshLabels(): void {\n this.els.upBtn.setAttribute('aria-label', this.ctx.i18n.storyPrev);\n this.els.downBtn.setAttribute('aria-label', this.ctx.i18n.storyNext);\n this.els.listBtn.setAttribute('aria-label', this.ctx.i18n.storyAllVideos);\n this.renderList(this.els.grid);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n this.ctx.videoEl.removeEventListener('ended', this.onEnded);\n this.ctx.videoEl.removeEventListener('playing', this.onPlaying);\n this.ctx.videoEl.removeEventListener('play', this.onFirstPlay);\n this.ctx.videoEl.removeEventListener('timeupdate', this.onTimeUpdate);\n this.els.viewport.removeEventListener('wheel', this.onWheel);\n this.els.viewport.removeEventListener('pointerdown', this.onPointerDown);\n window.removeEventListener('pointermove', this.onPointerMove);\n window.removeEventListener('pointerup', this.onPointerUp);\n document.removeEventListener('keydown', this.onKeyDown);\n\n // player gövdesini .video-area'ya geri taşı, story DOM'unu kaldır.\n this.ctx.playerBodyEls.forEach((el) => this.ctx.videoAreaEl.appendChild(el));\n this.ctx.videoAreaEl.removeAttribute('data-story');\n this.els.viewport.remove();\n this.els.panel.remove();\n }\n\n // ── public api ──\n\n next(userInitiated = true): void {\n const target = this.index + 1;\n if (target >= this.items.length) {\n if (!this.options.loop) return;\n void this.goTo(0, 'next', userInitiated);\n return;\n }\n void this.goTo(target, 'next', userInitiated);\n }\n\n prev(userInitiated = true): void {\n const target = this.index - 1;\n if (target < 0) {\n if (!this.options.loop) return;\n void this.goTo(this.items.length - 1, 'prev', userInitiated);\n return;\n }\n void this.goTo(target, 'prev', userInitiated);\n }\n\n async goTo(target: number, direction: Dir | null = null, userInitiated = true): Promise<void> {\n if (this.destroyed || this.switching) return;\n const i = this.clamp(target);\n if (i === this.index) return;\n\n const dir = direction || (i > this.index ? 'next' : 'prev');\n this.switching = true;\n try {\n this.fillPane(dir, this.items[i]);\n await this.animate(dir);\n this.index = i;\n this.syncUi();\n await this.loadItem(i, { userInitiated });\n this.preloadNeighbours();\n } finally {\n this.switching = false;\n }\n }\n\n toggleList(force?: boolean): void {\n this.listOpen = typeof force === 'boolean' ? force : !this.listOpen;\n this.els.panel.setAttribute('aria-hidden', this.listOpen ? 'false' : 'true');\n this.els.listBtn.setAttribute('aria-expanded', this.listOpen ? 'true' : 'false');\n // translate-x/-y utility yerine inline transform: Shadow DOM'da tek eksenli Tailwind translate 'none' oluyor.\n this.els.panel.style.transform = this.listOpen ? 'translateX(0)' : 'translateX(100%)';\n }\n\n // ── dom ──\n\n private buildDom() {\n this.ctx.videoAreaEl.setAttribute('data-story', '1');\n\n const viewport = document.createElement('div');\n viewport.className = 'hp:absolute hp:inset-0 hp:overflow-hidden hp:touch-none';\n viewport.setAttribute('tabindex', '0');\n\n const track = document.createElement('div');\n track.className = 'hp:relative hp:w-full';\n track.style.height = '300%';\n track.style.transform = 'translate3d(0,-33.3333%,0)';\n track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n\n const panePrev = this.createPane();\n const paneCurrent = document.createElement('div');\n paneCurrent.className = 'hp:absolute hp:inset-x-0 hp:h-1/3';\n paneCurrent.style.top = '33.3333%';\n const paneNext = this.createPane();\n paneNext.style.top = '66.6667%';\n\n // Gerçek player gövdesini orta pane'e taşı.\n this.ctx.playerBodyEls.forEach((el) => paneCurrent.appendChild(el));\n\n track.append(panePrev, paneCurrent, paneNext);\n viewport.appendChild(track);\n\n const overlay = document.createElement('div');\n overlay.className = 'hp:absolute hp:inset-0 hp:z-20 hp:pointer-events-none hp:flex hp:flex-col hp:justify-between hp:p-3';\n\n const progressBar = document.createElement('div');\n progressBar.className = 'hp:h-0.5 hp:bg-white/30 hp:rounded-full hp:overflow-hidden';\n const progressFill = document.createElement('div');\n progressFill.className = 'hp:h-full hp:bg-accent hp:w-0';\n progressBar.appendChild(progressFill);\n\n const meta = document.createElement('div');\n meta.className = 'hp:flex hp:flex-col hp:gap-0.5 hp:text-white hp:drop-shadow hp:mt-2 hp:pointer-events-auto';\n const counter = document.createElement('span');\n counter.setAttribute('part', 'story-counter');\n counter.className = 'hp:text-xs hp:opacity-80';\n const titleEl = document.createElement('strong');\n titleEl.className = 'hp:text-sm';\n const spotEl = document.createElement('span');\n spotEl.className = 'hp:text-xs hp:opacity-80';\n meta.append(counter, titleEl, spotEl);\n\n const dots = document.createElement('div');\n dots.className = 'hp:absolute hp:right-2 hp:top-1/2 hp-center-y hp:flex hp:flex-col hp:gap-1 hp:pointer-events-none';\n\n const navWrap = document.createElement('div');\n navWrap.className = 'hp:flex hp:flex-col hp:gap-2 hp:self-end hp:pointer-events-auto hp:mb-16';\n const upBtn = this.navBtn('chevron-left', this.ctx.i18n.storyPrev);\n upBtn.style.transform = 'rotate(90deg)';\n const downBtn = this.navBtn('chevron-left', this.ctx.i18n.storyNext);\n downBtn.style.transform = 'rotate(-90deg)';\n navWrap.append(upBtn, downBtn);\n\n const listBtn = document.createElement('button');\n listBtn.type = 'button';\n listBtn.setAttribute('aria-label', this.ctx.i18n.storyAllVideos);\n listBtn.setAttribute('aria-expanded', 'false');\n // Konum tailwind.css `.hp-story-list-btn` — dar (dikey) player'da üst bara (ayarlar/PiP) taşınan butonların altında.\n listBtn.className =\n 'hp-story-list-btn hp:absolute hp:flex hp:items-center hp:justify-center hp:w-9 hp:h-9 hp:rounded-full ' +\n 'hp:bg-black/40 hp:text-white hp:border-0 hp:cursor-pointer hp:pointer-events-auto';\n listBtn.setAttribute('part', 'story-list-button');\n listBtn.appendChild(createIcon('grid', 'hp:text-base'));\n\n overlay.append(progressBar, meta, dots, navWrap, listBtn);\n\n const panel = document.createElement('aside');\n panel.className =\n 'hp:absolute hp:inset-y-0 hp:right-0 hp:w-72 hp:max-w-[80%] hp:bg-surface-elev hp:z-30 hp:overflow-y-auto hp:p-3 ' +\n 'hp:transition-transform hp:duration-300';\n panel.style.transform = 'translateX(100%)';\n panel.setAttribute('aria-hidden', 'true');\n const grid = document.createElement('div');\n grid.className = 'hp:flex hp:flex-col hp:gap-2 hp:mt-8';\n panel.appendChild(grid);\n\n this.ctx.videoAreaEl.append(viewport, overlay, panel);\n\n this.renderList(grid);\n\n return {\n viewport, track, panePrev, paneCurrent, paneNext, overlay,\n counter, titleEl, spotEl, dots, progressBar: progressFill,\n upBtn, downBtn, listBtn, panel, grid,\n };\n }\n\n private createPane(): HTMLElement {\n const pane = document.createElement('div');\n pane.className = 'hp:absolute hp:inset-x-0 hp:h-1/3 hp:bg-cover hp:bg-center hp:bg-black';\n return pane;\n }\n\n private navBtn(iconName: string, label: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n btn.className =\n 'hp:flex hp:items-center hp:justify-center hp:w-8 hp:h-8 hp:rounded-full hp:bg-black/40 hp:text-white hp:border-0 hp:cursor-pointer';\n btn.appendChild(createIcon(iconName, 'hp:text-sm'));\n return btn;\n }\n\n private fillPane(dir: Dir, item: PlayableItem | undefined): void {\n const pane = dir === 'next' ? this.els.paneNext : this.els.panePrev;\n const img = item?.image || '';\n pane.style.backgroundImage = img ? `url(\"${img}\")` : 'none';\n }\n\n private animate(dir: Dir): Promise<void> {\n const track = this.els.track;\n return new Promise((resolve) => {\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n track.removeEventListener('transitionend', finish);\n clearTimeout(timer);\n track.style.transition = 'none';\n track.style.transform = 'translate3d(0,-33.3333%,0)';\n void track.offsetHeight;\n track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n resolve();\n };\n track.addEventListener('transitionend', finish);\n const timer = setTimeout(finish, this.options.transitionMs + 120);\n track.style.transform = dir === 'next' ? 'translate3d(0,-66.6667%,0)' : 'translate3d(0,0,0)';\n });\n }\n\n // ── source / playback ──\n\n private async loadItem(i: number, opts: { userInitiated?: boolean; isStart?: boolean } = {}): Promise<void> {\n const item = this.items[i];\n if (!item) return;\n\n const mode = this.resolveMode(item);\n const payload = this.buildPayload(item, mode);\n if (!payload) return;\n\n this.ctx.videoEl.pause();\n if (item.image) this.ctx.setPoster(item.image);\n await this.ctx.loadSource(payload.src, payload.type);\n\n if (this.options.adsOnItemChange && this.lastAdsIndex !== i) {\n this.lastAdsIndex = i;\n this.ctx.requestFreshAd(item.adTagPreroll || this.rawOptions.adTagPreroll);\n }\n\n if (this.shouldAutoPlay(opts)) {\n try {\n await this.ctx.videoEl.play();\n } catch {\n /* autoplay engellendi */\n }\n }\n }\n\n private shouldAutoPlay({ userInitiated, isStart }: { userInitiated?: boolean; isStart?: boolean }): boolean {\n if (isStart) return !!this.ctx.videoEl.autoplay;\n if (userInitiated) return true;\n return this.options.autoplayNext && this.userHasStartedPlayback;\n }\n\n private resolveMode(item: PlayableItem): 'hls' | 'mp4' {\n const hasHls = this.hasMode(item, 'hls');\n const hasMp4 = this.hasMode(item, 'mp4');\n if (this.options.preferHls) return hasHls ? 'hls' : hasMp4 ? 'mp4' : 'hls';\n return hasMp4 ? 'mp4' : hasHls ? 'hls' : 'mp4';\n }\n\n private hasMode(item: PlayableItem, mode: 'hls' | 'mp4'): boolean {\n return Array.isArray(item[mode]) && item[mode]!.some((v) => v?.src);\n }\n\n private buildPayload(item: PlayableItem, mode: 'hls' | 'mp4'): { src: string; type?: string } | null {\n const list = (item[mode] || []).filter((v) => v?.src);\n if (!list.length) return null;\n const src = mode === 'mp4' ? list.find((v) => v.selected) || list[0] : list[0];\n return { src: src!.src, type: src!.type || (mode === 'hls' ? 'application/x-mpegURL' : 'video/mp4') };\n }\n\n // ── preload ──\n\n private preloadNeighbours(): void {\n const nav = navigator as Navigator & { connection?: { saveData?: boolean } };\n if (nav.connection?.saveData) return;\n\n [this.wrapIndex(this.index + 1), this.wrapIndex(this.index - 1)].forEach((i) => {\n if (i === this.index) return;\n const item = this.items[i];\n if (item?.image) {\n const img = new Image();\n img.decoding = 'async';\n img.src = item.image;\n }\n });\n }\n\n // ── ui sync ──\n\n private syncUi(): void {\n const item = this.items[this.index];\n const total = this.items.length;\n\n if (this.uiOpts.counter) this.els.counter.textContent = `${this.index + 1} / ${total}`;\n if (this.uiOpts.title) this.els.titleEl.textContent = item?.title || '';\n if (this.uiOpts.spot) this.els.spotEl.textContent = item?.spot || '';\n\n this.fillPane('prev', this.items[this.wrapIndex(this.index - 1)]);\n this.fillPane('next', this.items[this.wrapIndex(this.index + 1)]);\n\n const atStart = !this.options.loop && this.index === 0;\n const atEnd = !this.options.loop && this.index === total - 1;\n this.els.upBtn.disabled = atStart;\n this.els.downBtn.disabled = atEnd;\n\n if (this.uiOpts.dots) {\n if (this.els.dots.children.length !== total) {\n this.els.dots.innerHTML = '';\n for (let i = 0; i < total; i++) {\n const dot = document.createElement('i');\n dot.className = 'hp:block hp:w-1 hp:h-1 hp:rounded-full hp:bg-white/40';\n this.els.dots.appendChild(dot);\n }\n }\n Array.from(this.els.dots.children).forEach((el, i) => {\n (el as HTMLElement).className = `hp:block hp:w-1 hp:h-1 hp:rounded-full ${i === this.index ? 'hp:bg-white' : 'hp:bg-white/40'}`;\n });\n }\n\n this.setActiveThumb();\n this.ctx.hostEl.dispatchEvent(new CustomEvent('hp:itemchange', { detail: { index: this.index, source: 'story' } }));\n }\n\n private renderList(grid: HTMLElement): void {\n grid.innerHTML = '';\n this.items.forEach((item, i) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'hp:flex hp:items-center hp:gap-2 hp:w-full hp:p-1.5 hp:rounded hp:text-left hp:text-on-surface hp:bg-transparent hp:border-0 hp:cursor-pointer hp:hover:bg-on-surface/5';\n btn.dataset.index = String(i);\n if (item.image) {\n const img = document.createElement('img');\n img.src = item.image;\n img.loading = 'lazy';\n img.className = 'hp:w-14 hp:aspect-[9/16] hp:object-cover hp:rounded hp:shrink-0';\n btn.appendChild(img);\n }\n const title = document.createElement('span');\n title.className = 'hp:text-xs hp:truncate';\n title.textContent = item.title || this.ctx.i18n.videoFallbackTitle(i);\n btn.appendChild(title);\n btn.addEventListener('click', () => {\n this.toggleList(false);\n void this.goTo(i);\n });\n grid.appendChild(btn);\n });\n }\n\n private setActiveThumb(): void {\n Array.from(this.els.grid.children).forEach((el, i) => {\n el.classList.toggle('hp:bg-accent/15', i === this.index);\n });\n }\n\n // ── events ──\n\n private bindEvents(): void {\n this.ctx.videoEl.addEventListener('ended', this.onEnded);\n this.ctx.videoEl.addEventListener('playing', this.onPlaying);\n this.ctx.videoEl.addEventListener('play', this.onFirstPlay);\n if (this.uiOpts.progress) this.ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n\n this.els.upBtn.addEventListener('click', () => this.prev());\n this.els.downBtn.addEventListener('click', () => this.next());\n this.els.listBtn.addEventListener('click', () => this.toggleList());\n\n this.els.grid.parentElement?.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') this.toggleList(false);\n });\n\n if (this.gestureOpts.wheel) this.els.viewport.addEventListener('wheel', this.onWheel, { passive: false });\n if (this.gestureOpts.swipe) {\n this.els.viewport.addEventListener('pointerdown', this.onPointerDown);\n window.addEventListener('pointermove', this.onPointerMove, { passive: false });\n window.addEventListener('pointerup', this.onPointerUp);\n }\n if (this.gestureOpts.keyboard) document.addEventListener('keydown', this.onKeyDown);\n }\n\n private readonly onEnded = (): void => {\n if (!this.options.autoplayNext) return;\n if (!this.options.loop && this.index === this.items.length - 1) return;\n this.next(false);\n };\n\n private readonly onPlaying = (): void => {};\n\n private readonly onFirstPlay = (): void => {\n this.userHasStartedPlayback = true;\n };\n\n private readonly onTimeUpdate = (): void => {\n const dur = this.ctx.videoEl.duration || 0;\n const cur = this.ctx.videoEl.currentTime || 0;\n this.els.progressBar.style.width = dur > 0 ? `${Math.min(100, (cur / dur) * 100)}%` : '0%';\n };\n\n private readonly onWheel = (e: WheelEvent): void => {\n if (this.switching) {\n e.preventDefault();\n return;\n }\n const dir: Dir = e.deltaY > 0 ? 'next' : 'prev';\n if (!this.canGo(dir)) return;\n e.preventDefault();\n\n const now = Date.now();\n if (now - this.lastNavAt < this.gestureOpts.wheelCooldownMs) return;\n this.wheelAccum += e.deltaY;\n if (Math.abs(this.wheelAccum) < this.gestureOpts.wheelThreshold) return;\n this.wheelAccum = 0;\n this.lastNavAt = now;\n dir === 'next' ? this.next() : this.prev();\n };\n\n private readonly onPointerDown = (e: PointerEvent): void => {\n if (this.switching || this.listOpen) return;\n if (e.pointerType === 'mouse' && e.button !== 0) return;\n this.drag = { startY: e.clientY, startX: e.clientX, dy: 0, active: false, id: e.pointerId };\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n const drag = this.drag;\n if (!drag || e.pointerId !== drag.id) return;\n const dy = e.clientY - drag.startY;\n const dx = e.clientX - drag.startX;\n\n if (!drag.active) {\n if (Math.abs(dy) < 10 || Math.abs(dy) < Math.abs(dx)) return;\n drag.active = true;\n this.els.track.style.transition = 'none';\n }\n e.preventDefault();\n\n const h = this.els.viewport.clientHeight || 1;\n const resisted = this.canGo(dy < 0 ? 'next' : 'prev') ? dy : dy * 0.25;\n drag.dy = resisted;\n\n const pct = -33.3333 + (resisted / h) * 33.3333;\n this.els.track.style.transform = `translate3d(0, ${pct}%, 0)`;\n };\n\n private readonly onPointerUp = (): void => {\n const drag = this.drag;\n this.drag = null;\n if (!drag?.active) return;\n\n const h = this.els.viewport.clientHeight || 1;\n const ratio = Math.abs(drag.dy) / h;\n const dir: Dir = drag.dy < 0 ? 'next' : 'prev';\n\n this.els.track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n if (ratio >= this.gestureOpts.swipeThreshold && this.canGo(dir)) {\n this.els.track.style.transform = 'translate3d(0,-33.3333%,0)';\n dir === 'next' ? this.next() : this.prev();\n } else {\n this.els.track.style.transform = 'translate3d(0,-33.3333%,0)';\n }\n };\n\n private readonly onKeyDown = (e: KeyboardEvent): void => {\n if (this.destroyed) return;\n if (e.key === 'Escape' && this.listOpen) {\n this.toggleList(false);\n return;\n }\n const tag = document.activeElement?.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA') return;\n if (!this.isInViewport()) return;\n\n if (e.key === 'ArrowDown' || e.key === 'PageDown' || e.key === 'j') {\n e.preventDefault();\n this.next();\n } else if (e.key === 'ArrowUp' || e.key === 'PageUp' || e.key === 'k') {\n e.preventDefault();\n this.prev();\n }\n };\n\n // ── helpers ──\n\n private canGo(dir: Dir): boolean {\n if (this.options.loop) return this.items.length > 1;\n return dir === 'next' ? this.index < this.items.length - 1 : this.index > 0;\n }\n\n private isInViewport(): boolean {\n const r = this.ctx.hostEl.getBoundingClientRect();\n return r.bottom > 0 && r.top < (window.innerHeight || 0);\n }\n\n private clamp(i: number): number {\n if (!Number.isFinite(i) || !this.items.length) return 0;\n return Math.max(0, Math.min(this.items.length - 1, Math.trunc(i)));\n }\n\n private wrapIndex(i: number): number {\n const n = this.items.length;\n if (!n) return 0;\n if (this.options.loop) return ((i % n) + n) % n;\n return this.clamp(i);\n }\n}\n","import { HopeStory, type StoryPlayerContext } from './HopeStory';\nimport type { StoryOptions } from '../../types';\n\nexport function installStory(ctx: StoryPlayerContext, options: StoryOptions): HopeStory {\n return new HopeStory(ctx, options);\n}\n\nexport { HopeStory };\nexport type { StoryPlayerContext };\n"],"names":["DEFAULTS","HopeStory","ctx","rawOptions","__publicField","dur","cur","e","dir","now","drag","dy","dx","h","resisted","pct","ratio","tag","_a","el","userInitiated","target","direction","force","viewport","track","panePrev","paneCurrent","paneNext","overlay","progressBar","progressFill","meta","counter","titleEl","spotEl","dots","navWrap","upBtn","downBtn","listBtn","createIcon","panel","grid","pane","iconName","label","btn","item","img","resolve","done","finish","timer","i","opts","mode","payload","isStart","hasHls","hasMp4","v","list","src","total","atStart","atEnd","dot","title","r","n","installStory","options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-CJ3wYSlU.js","sources":["../../src/plugins/story/HopeStory.ts","../../src/plugins/story/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { PlayableItem, StoryOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface StoryPlayerContext {\n videoEl: HTMLVideoElement;\n hostEl: HTMLElement;\n /** .video-area — story aktifken 9:16'ya çevrilir (data-story attribute ile), destroy'da geri alınır. */\n videoAreaEl: HTMLElement;\n /** Gerçek player gövdesi (.media-layer + chrome + center-play-button) — orta pane'e taşınır, destroy'da geri alınır. */\n playerBodyEls: HTMLElement[];\n loadSource(src: string, type?: string): Promise<void>;\n setPoster(url: string): void;\n requestFreshAd(prerollTag?: string): void;\n i18n: HopeLocaleStrings;\n}\n\ntype Dir = 'next' | 'prev';\n\nconst DEFAULTS: Required<Omit<StoryOptions, 'items' | 'adTagPreroll' | 'gestures' | 'ui'>> = {\n startIndex: 0,\n loop: true,\n autoplayNext: true,\n preferHls: true,\n transitionMs: 380,\n adsOnItemChange: true,\n};\n\n/** Dikey video akışı (Shorts/Story): tek <video>, item değişiminde kaynak değişir. */\nexport class HopeStory {\n private readonly options: Required<Omit<StoryOptions, 'items' | 'adTagPreroll' | 'gestures' | 'ui'>>;\n private readonly gestureOpts: Required<NonNullable<StoryOptions['gestures']>>;\n private readonly uiOpts: Required<NonNullable<StoryOptions['ui']>>;\n private readonly items: PlayableItem[];\n\n private index: number;\n private destroyed = false;\n private switching = false;\n private listOpen = false;\n private userHasStartedPlayback = false;\n private lastAdsIndex: number | null = null;\n private wheelAccum = 0;\n private lastNavAt = 0;\n private drag: { startY: number; startX: number; dy: number; active: boolean; id: number } | null = null;\n\n private readonly els: {\n viewport: HTMLElement;\n track: HTMLElement;\n panePrev: HTMLElement;\n paneCurrent: HTMLElement;\n paneNext: HTMLElement;\n overlay: HTMLElement;\n counter: HTMLElement;\n titleEl: HTMLElement;\n spotEl: HTMLElement;\n dots: HTMLElement;\n progressBar: HTMLElement;\n upBtn: HTMLButtonElement;\n downBtn: HTMLButtonElement;\n listBtn: HTMLButtonElement;\n panel: HTMLElement;\n grid: HTMLElement;\n };\n\n constructor(\n private readonly ctx: StoryPlayerContext,\n private readonly rawOptions: StoryOptions\n ) {\n this.options = { ...DEFAULTS, ...rawOptions };\n this.gestureOpts = {\n swipe: true,\n swipeThreshold: 0.12,\n wheel: true,\n wheelThreshold: 42,\n wheelCooldownMs: 620,\n keyboard: true,\n ...rawOptions.gestures,\n };\n this.uiOpts = { counter: true, title: true, spot: true, dots: true, progress: true, ...rawOptions.ui };\n this.items = Array.isArray(rawOptions.items) ? rawOptions.items : [];\n this.index = this.clamp(this.options.startIndex);\n\n this.els = this.buildDom();\n this.bindEvents();\n this.syncUi();\n this.preloadNeighbours();\n\n if (this.items.length) void this.loadItem(this.index, { isStart: true });\n }\n\n refreshLabels(): void {\n this.els.upBtn.setAttribute('aria-label', this.ctx.i18n.storyPrev);\n this.els.downBtn.setAttribute('aria-label', this.ctx.i18n.storyNext);\n this.els.listBtn.setAttribute('aria-label', this.ctx.i18n.storyAllVideos);\n this.renderList(this.els.grid);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n this.ctx.videoEl.removeEventListener('ended', this.onEnded);\n this.ctx.videoEl.removeEventListener('playing', this.onPlaying);\n this.ctx.videoEl.removeEventListener('play', this.onFirstPlay);\n this.ctx.videoEl.removeEventListener('timeupdate', this.onTimeUpdate);\n this.els.viewport.removeEventListener('wheel', this.onWheel);\n this.els.viewport.removeEventListener('pointerdown', this.onPointerDown);\n window.removeEventListener('pointermove', this.onPointerMove);\n window.removeEventListener('pointerup', this.onPointerUp);\n document.removeEventListener('keydown', this.onKeyDown);\n\n // player gövdesini .video-area'ya geri taşı, story DOM'unu kaldır.\n this.ctx.playerBodyEls.forEach((el) => this.ctx.videoAreaEl.appendChild(el));\n this.ctx.videoAreaEl.removeAttribute('data-story');\n this.els.viewport.remove();\n this.els.panel.remove();\n }\n\n // ── public api ──\n\n next(userInitiated = true): void {\n const target = this.index + 1;\n if (target >= this.items.length) {\n if (!this.options.loop) return;\n void this.goTo(0, 'next', userInitiated);\n return;\n }\n void this.goTo(target, 'next', userInitiated);\n }\n\n prev(userInitiated = true): void {\n const target = this.index - 1;\n if (target < 0) {\n if (!this.options.loop) return;\n void this.goTo(this.items.length - 1, 'prev', userInitiated);\n return;\n }\n void this.goTo(target, 'prev', userInitiated);\n }\n\n async goTo(target: number, direction: Dir | null = null, userInitiated = true): Promise<void> {\n if (this.destroyed || this.switching) return;\n const i = this.clamp(target);\n if (i === this.index) return;\n\n const dir = direction || (i > this.index ? 'next' : 'prev');\n this.switching = true;\n try {\n this.fillPane(dir, this.items[i]);\n await this.animate(dir);\n this.index = i;\n this.syncUi();\n await this.loadItem(i, { userInitiated });\n this.preloadNeighbours();\n } finally {\n this.switching = false;\n }\n }\n\n toggleList(force?: boolean): void {\n this.listOpen = typeof force === 'boolean' ? force : !this.listOpen;\n this.els.panel.setAttribute('aria-hidden', this.listOpen ? 'false' : 'true');\n this.els.listBtn.setAttribute('aria-expanded', this.listOpen ? 'true' : 'false');\n // translate-x/-y utility yerine inline transform: Shadow DOM'da tek eksenli Tailwind translate 'none' oluyor.\n this.els.panel.style.transform = this.listOpen ? 'translateX(0)' : 'translateX(100%)';\n }\n\n // ── dom ──\n\n private buildDom() {\n this.ctx.videoAreaEl.setAttribute('data-story', '1');\n\n const viewport = document.createElement('div');\n viewport.className = 'hp:absolute hp:inset-0 hp:overflow-hidden hp:touch-none';\n viewport.setAttribute('tabindex', '0');\n\n const track = document.createElement('div');\n track.className = 'hp:relative hp:w-full';\n track.style.height = '300%';\n track.style.transform = 'translate3d(0,-33.3333%,0)';\n track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n\n const panePrev = this.createPane();\n const paneCurrent = document.createElement('div');\n paneCurrent.className = 'hp:absolute hp:inset-x-0 hp:h-1/3';\n paneCurrent.style.top = '33.3333%';\n const paneNext = this.createPane();\n paneNext.style.top = '66.6667%';\n\n // Gerçek player gövdesini orta pane'e taşı.\n this.ctx.playerBodyEls.forEach((el) => paneCurrent.appendChild(el));\n\n track.append(panePrev, paneCurrent, paneNext);\n viewport.appendChild(track);\n\n const overlay = document.createElement('div');\n overlay.className = 'hp:absolute hp:inset-0 hp:z-20 hp:pointer-events-none hp:flex hp:flex-col hp:justify-between hp:p-3';\n\n const progressBar = document.createElement('div');\n progressBar.className = 'hp:h-0.5 hp:bg-white/30 hp:rounded-full hp:overflow-hidden';\n const progressFill = document.createElement('div');\n progressFill.className = 'hp:h-full hp:bg-accent hp:w-0';\n progressBar.appendChild(progressFill);\n\n const meta = document.createElement('div');\n meta.className = 'hp:flex hp:flex-col hp:gap-0.5 hp:text-white hp:drop-shadow hp:mt-2 hp:pointer-events-auto';\n const counter = document.createElement('span');\n counter.setAttribute('part', 'story-counter');\n counter.className = 'hp:text-xs hp:opacity-80';\n const titleEl = document.createElement('strong');\n titleEl.className = 'hp:text-sm';\n const spotEl = document.createElement('span');\n spotEl.className = 'hp:text-xs hp:opacity-80';\n meta.append(counter, titleEl, spotEl);\n\n const dots = document.createElement('div');\n dots.className = 'hp:absolute hp:right-2 hp:top-1/2 hp-center-y hp:flex hp:flex-col hp:gap-1 hp:pointer-events-none';\n\n const navWrap = document.createElement('div');\n navWrap.className = 'hp:flex hp:flex-col hp:gap-2 hp:self-end hp:pointer-events-auto hp:mb-16';\n const upBtn = this.navBtn('chevron-left', this.ctx.i18n.storyPrev);\n upBtn.style.transform = 'rotate(90deg)';\n const downBtn = this.navBtn('chevron-left', this.ctx.i18n.storyNext);\n downBtn.style.transform = 'rotate(-90deg)';\n navWrap.append(upBtn, downBtn);\n\n const listBtn = document.createElement('button');\n listBtn.type = 'button';\n listBtn.setAttribute('aria-label', this.ctx.i18n.storyAllVideos);\n listBtn.setAttribute('aria-expanded', 'false');\n // Konum tailwind.css `.hp-story-list-btn` — dar (dikey) player'da üst bara (ayarlar/PiP) taşınan butonların altında.\n listBtn.className =\n 'hp-story-list-btn hp:absolute hp:flex hp:items-center hp:justify-center hp:w-9 hp:h-9 hp:rounded-full ' +\n 'hp:bg-black/40 hp:text-white hp:border-0 hp:cursor-pointer hp:pointer-events-auto';\n listBtn.setAttribute('part', 'story-list-button');\n listBtn.appendChild(createIcon('grid', 'hp:text-base'));\n\n overlay.append(progressBar, meta, dots, navWrap, listBtn);\n\n const panel = document.createElement('aside');\n panel.className =\n 'hp:absolute hp:inset-y-0 hp:right-0 hp:w-72 hp:max-w-[80%] hp:bg-surface-elev hp:z-30 hp:overflow-y-auto hp:p-3 ' +\n 'hp:transition-transform hp:duration-300';\n panel.style.transform = 'translateX(100%)';\n panel.setAttribute('aria-hidden', 'true');\n const grid = document.createElement('div');\n grid.className = 'hp:flex hp:flex-col hp:gap-2 hp:mt-8';\n panel.appendChild(grid);\n\n this.ctx.videoAreaEl.append(viewport, overlay, panel);\n\n this.renderList(grid);\n\n return {\n viewport, track, panePrev, paneCurrent, paneNext, overlay,\n counter, titleEl, spotEl, dots, progressBar: progressFill,\n upBtn, downBtn, listBtn, panel, grid,\n };\n }\n\n private createPane(): HTMLElement {\n const pane = document.createElement('div');\n pane.className = 'hp:absolute hp:inset-x-0 hp:h-1/3 hp:bg-cover hp:bg-center hp:bg-black';\n return pane;\n }\n\n private navBtn(iconName: string, label: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n btn.className =\n 'hp:flex hp:items-center hp:justify-center hp:w-8 hp:h-8 hp:rounded-full hp:bg-black/40 hp:text-white hp:border-0 hp:cursor-pointer';\n btn.appendChild(createIcon(iconName, 'hp:text-sm'));\n return btn;\n }\n\n private fillPane(dir: Dir, item: PlayableItem | undefined): void {\n const pane = dir === 'next' ? this.els.paneNext : this.els.panePrev;\n const img = item?.image || '';\n pane.style.backgroundImage = img ? `url(\"${img}\")` : 'none';\n }\n\n private animate(dir: Dir): Promise<void> {\n const track = this.els.track;\n return new Promise((resolve) => {\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n track.removeEventListener('transitionend', finish);\n clearTimeout(timer);\n track.style.transition = 'none';\n track.style.transform = 'translate3d(0,-33.3333%,0)';\n void track.offsetHeight;\n track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n resolve();\n };\n track.addEventListener('transitionend', finish);\n const timer = setTimeout(finish, this.options.transitionMs + 120);\n track.style.transform = dir === 'next' ? 'translate3d(0,-66.6667%,0)' : 'translate3d(0,0,0)';\n });\n }\n\n // ── source / playback ──\n\n private async loadItem(i: number, opts: { userInitiated?: boolean; isStart?: boolean } = {}): Promise<void> {\n const item = this.items[i];\n if (!item) return;\n\n const mode = this.resolveMode(item);\n const payload = this.buildPayload(item, mode);\n if (!payload) return;\n\n this.ctx.videoEl.pause();\n if (item.image) this.ctx.setPoster(item.image);\n await this.ctx.loadSource(payload.src, payload.type);\n\n if (this.options.adsOnItemChange && this.lastAdsIndex !== i) {\n this.lastAdsIndex = i;\n this.ctx.requestFreshAd(item.adTagPreroll || this.rawOptions.adTagPreroll);\n }\n\n if (this.shouldAutoPlay(opts)) {\n try {\n await this.ctx.videoEl.play();\n } catch {\n /* autoplay engellendi */\n }\n }\n }\n\n private shouldAutoPlay({ userInitiated, isStart }: { userInitiated?: boolean; isStart?: boolean }): boolean {\n if (isStart) return !!this.ctx.videoEl.autoplay;\n if (userInitiated) return true;\n return this.options.autoplayNext && this.userHasStartedPlayback;\n }\n\n private resolveMode(item: PlayableItem): 'hls' | 'mp4' {\n const hasHls = this.hasMode(item, 'hls');\n const hasMp4 = this.hasMode(item, 'mp4');\n if (this.options.preferHls) return hasHls ? 'hls' : hasMp4 ? 'mp4' : 'hls';\n return hasMp4 ? 'mp4' : hasHls ? 'hls' : 'mp4';\n }\n\n private hasMode(item: PlayableItem, mode: 'hls' | 'mp4'): boolean {\n return Array.isArray(item[mode]) && item[mode]!.some((v) => v?.src);\n }\n\n private buildPayload(item: PlayableItem, mode: 'hls' | 'mp4'): { src: string; type?: string } | null {\n const list = (item[mode] || []).filter((v) => v?.src);\n if (!list.length) return null;\n const src = mode === 'mp4' ? list.find((v) => v.selected) || list[0] : list[0];\n return { src: src!.src, type: src!.type || (mode === 'hls' ? 'application/x-mpegURL' : 'video/mp4') };\n }\n\n // ── preload ──\n\n private preloadNeighbours(): void {\n const nav = navigator as Navigator & { connection?: { saveData?: boolean } };\n if (nav.connection?.saveData) return;\n\n [this.wrapIndex(this.index + 1), this.wrapIndex(this.index - 1)].forEach((i) => {\n if (i === this.index) return;\n const item = this.items[i];\n if (item?.image) {\n const img = new Image();\n img.decoding = 'async';\n img.src = item.image;\n }\n });\n }\n\n // ── ui sync ──\n\n private syncUi(): void {\n const item = this.items[this.index];\n const total = this.items.length;\n\n if (this.uiOpts.counter) this.els.counter.textContent = `${this.index + 1} / ${total}`;\n if (this.uiOpts.title) this.els.titleEl.textContent = item?.title || '';\n if (this.uiOpts.spot) this.els.spotEl.textContent = item?.spot || '';\n\n this.fillPane('prev', this.items[this.wrapIndex(this.index - 1)]);\n this.fillPane('next', this.items[this.wrapIndex(this.index + 1)]);\n\n const atStart = !this.options.loop && this.index === 0;\n const atEnd = !this.options.loop && this.index === total - 1;\n this.els.upBtn.disabled = atStart;\n this.els.downBtn.disabled = atEnd;\n\n if (this.uiOpts.dots) {\n if (this.els.dots.children.length !== total) {\n this.els.dots.innerHTML = '';\n for (let i = 0; i < total; i++) {\n const dot = document.createElement('i');\n dot.className = 'hp:block hp:w-1 hp:h-1 hp:rounded-full hp:bg-white/40';\n this.els.dots.appendChild(dot);\n }\n }\n Array.from(this.els.dots.children).forEach((el, i) => {\n (el as HTMLElement).className = `hp:block hp:w-1 hp:h-1 hp:rounded-full ${i === this.index ? 'hp:bg-white' : 'hp:bg-white/40'}`;\n });\n }\n\n this.setActiveThumb();\n this.ctx.hostEl.dispatchEvent(new CustomEvent('hp:itemchange', { detail: { index: this.index, source: 'story' } }));\n }\n\n private renderList(grid: HTMLElement): void {\n grid.innerHTML = '';\n this.items.forEach((item, i) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'hp:flex hp:items-center hp:gap-2 hp:w-full hp:p-1.5 hp:rounded hp:text-left hp:text-on-surface hp:bg-transparent hp:border-0 hp:cursor-pointer hp:hover:bg-on-surface/5';\n btn.dataset.index = String(i);\n if (item.image) {\n const img = document.createElement('img');\n img.src = item.image;\n img.loading = 'lazy';\n img.className = 'hp:w-14 hp:aspect-[9/16] hp:object-cover hp:rounded hp:shrink-0';\n btn.appendChild(img);\n }\n const title = document.createElement('span');\n title.className = 'hp:text-xs hp:truncate';\n title.textContent = item.title || this.ctx.i18n.videoFallbackTitle(i);\n btn.appendChild(title);\n btn.addEventListener('click', () => {\n this.toggleList(false);\n void this.goTo(i);\n });\n grid.appendChild(btn);\n });\n }\n\n private setActiveThumb(): void {\n Array.from(this.els.grid.children).forEach((el, i) => {\n el.classList.toggle('hp:bg-accent/15', i === this.index);\n });\n }\n\n // ── events ──\n\n private bindEvents(): void {\n this.ctx.videoEl.addEventListener('ended', this.onEnded);\n this.ctx.videoEl.addEventListener('playing', this.onPlaying);\n this.ctx.videoEl.addEventListener('play', this.onFirstPlay);\n if (this.uiOpts.progress) this.ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n\n this.els.upBtn.addEventListener('click', () => this.prev());\n this.els.downBtn.addEventListener('click', () => this.next());\n this.els.listBtn.addEventListener('click', () => this.toggleList());\n\n this.els.grid.parentElement?.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Escape') this.toggleList(false);\n });\n\n if (this.gestureOpts.wheel) this.els.viewport.addEventListener('wheel', this.onWheel, { passive: false });\n if (this.gestureOpts.swipe) {\n this.els.viewport.addEventListener('pointerdown', this.onPointerDown);\n window.addEventListener('pointermove', this.onPointerMove, { passive: false });\n window.addEventListener('pointerup', this.onPointerUp);\n }\n if (this.gestureOpts.keyboard) document.addEventListener('keydown', this.onKeyDown);\n }\n\n private readonly onEnded = (): void => {\n if (!this.options.autoplayNext) return;\n if (!this.options.loop && this.index === this.items.length - 1) return;\n this.next(false);\n };\n\n private readonly onPlaying = (): void => {};\n\n private readonly onFirstPlay = (): void => {\n this.userHasStartedPlayback = true;\n };\n\n private readonly onTimeUpdate = (): void => {\n const dur = this.ctx.videoEl.duration || 0;\n const cur = this.ctx.videoEl.currentTime || 0;\n this.els.progressBar.style.width = dur > 0 ? `${Math.min(100, (cur / dur) * 100)}%` : '0%';\n };\n\n private readonly onWheel = (e: WheelEvent): void => {\n if (this.switching) {\n e.preventDefault();\n return;\n }\n const dir: Dir = e.deltaY > 0 ? 'next' : 'prev';\n if (!this.canGo(dir)) return;\n e.preventDefault();\n\n const now = Date.now();\n if (now - this.lastNavAt < this.gestureOpts.wheelCooldownMs) return;\n this.wheelAccum += e.deltaY;\n if (Math.abs(this.wheelAccum) < this.gestureOpts.wheelThreshold) return;\n this.wheelAccum = 0;\n this.lastNavAt = now;\n dir === 'next' ? this.next() : this.prev();\n };\n\n private readonly onPointerDown = (e: PointerEvent): void => {\n if (this.switching || this.listOpen) return;\n if (e.pointerType === 'mouse' && e.button !== 0) return;\n this.drag = { startY: e.clientY, startX: e.clientX, dy: 0, active: false, id: e.pointerId };\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n const drag = this.drag;\n if (!drag || e.pointerId !== drag.id) return;\n const dy = e.clientY - drag.startY;\n const dx = e.clientX - drag.startX;\n\n if (!drag.active) {\n if (Math.abs(dy) < 10 || Math.abs(dy) < Math.abs(dx)) return;\n drag.active = true;\n this.els.track.style.transition = 'none';\n }\n e.preventDefault();\n\n const h = this.els.viewport.clientHeight || 1;\n const resisted = this.canGo(dy < 0 ? 'next' : 'prev') ? dy : dy * 0.25;\n drag.dy = resisted;\n\n const pct = -33.3333 + (resisted / h) * 33.3333;\n this.els.track.style.transform = `translate3d(0, ${pct}%, 0)`;\n };\n\n private readonly onPointerUp = (): void => {\n const drag = this.drag;\n this.drag = null;\n if (!drag?.active) return;\n\n const h = this.els.viewport.clientHeight || 1;\n const ratio = Math.abs(drag.dy) / h;\n const dir: Dir = drag.dy < 0 ? 'next' : 'prev';\n\n this.els.track.style.transition = `transform ${this.options.transitionMs}ms ease`;\n if (ratio >= this.gestureOpts.swipeThreshold && this.canGo(dir)) {\n this.els.track.style.transform = 'translate3d(0,-33.3333%,0)';\n dir === 'next' ? this.next() : this.prev();\n } else {\n this.els.track.style.transform = 'translate3d(0,-33.3333%,0)';\n }\n };\n\n private readonly onKeyDown = (e: KeyboardEvent): void => {\n if (this.destroyed) return;\n if (e.key === 'Escape' && this.listOpen) {\n this.toggleList(false);\n return;\n }\n const tag = document.activeElement?.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA') return;\n if (!this.isInViewport()) return;\n\n if (e.key === 'ArrowDown' || e.key === 'PageDown' || e.key === 'j') {\n e.preventDefault();\n this.next();\n } else if (e.key === 'ArrowUp' || e.key === 'PageUp' || e.key === 'k') {\n e.preventDefault();\n this.prev();\n }\n };\n\n // ── helpers ──\n\n private canGo(dir: Dir): boolean {\n if (this.options.loop) return this.items.length > 1;\n return dir === 'next' ? this.index < this.items.length - 1 : this.index > 0;\n }\n\n private isInViewport(): boolean {\n const r = this.ctx.hostEl.getBoundingClientRect();\n return r.bottom > 0 && r.top < (window.innerHeight || 0);\n }\n\n private clamp(i: number): number {\n if (!Number.isFinite(i) || !this.items.length) return 0;\n return Math.max(0, Math.min(this.items.length - 1, Math.trunc(i)));\n }\n\n private wrapIndex(i: number): number {\n const n = this.items.length;\n if (!n) return 0;\n if (this.options.loop) return ((i % n) + n) % n;\n return this.clamp(i);\n }\n}\n","import { HopeStory, type StoryPlayerContext } from './HopeStory';\nimport type { StoryOptions } from '../../types';\n\nexport function installStory(ctx: StoryPlayerContext, options: StoryOptions): HopeStory {\n return new HopeStory(ctx, options);\n}\n\nexport { HopeStory };\nexport type { StoryPlayerContext };\n"],"names":["DEFAULTS","HopeStory","ctx","rawOptions","__publicField","dur","cur","e","dir","now","drag","dy","dx","h","resisted","pct","ratio","tag","_a","el","userInitiated","target","direction","force","viewport","track","panePrev","paneCurrent","paneNext","overlay","progressBar","progressFill","meta","counter","titleEl","spotEl","dots","navWrap","upBtn","downBtn","listBtn","createIcon","panel","grid","pane","iconName","label","btn","item","img","resolve","done","finish","timer","i","opts","mode","payload","isStart","hasHls","hasMp4","v","list","src","total","atStart","atEnd","dot","title","r","n","installStory","options"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var A = Object.defineProperty;
|
|
2
2
|
var g = (h, t, e) => t in h ? A(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
|
|
3
3
|
var n = (h, t, e) => g(h, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
-
import { c as b } from "./index-
|
|
4
|
+
import { c as b } from "./index-Bqs23yk7.js";
|
|
5
5
|
const P = {
|
|
6
6
|
startMode: "after_current",
|
|
7
7
|
startIndex: 0,
|
|
@@ -233,4 +233,4 @@ export {
|
|
|
233
233
|
I as PlaylistRunner,
|
|
234
234
|
M as installPlaylist
|
|
235
235
|
};
|
|
236
|
-
//# sourceMappingURL=index-
|
|
236
|
+
//# sourceMappingURL=index-DJZjHaLs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-lKS-jIUb.js","sources":["../../src/plugins/playlist/PlaylistRunner.ts","../../src/plugins/playlist/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { PlayableItem, PlaylistOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface PlaylistPlayerContext {\n videoEl: HTMLVideoElement;\n hostEl: HTMLElement;\n /** Liste UI'ının monte edileceği, shadow root içindeki panel. */\n panelEl: HTMLElement;\n loadSource(src: string, type?: string): Promise<void>;\n setPoster(url: string): void;\n /** AdsController varsa retry/VMAP durumunu sıfırlayıp item'ın (varsa) kendi adTagPreroll'üyle yeniden reklam ister — yoksa no-op. */\n requestFreshAd(prerollTag?: string): void;\n i18n: HopeLocaleStrings;\n}\n\ntype SourceMode = 'hls' | 'mp4';\n\ninterface Attempt {\n id: number;\n index: number;\n mode: SourceMode;\n tried: Set<SourceMode>;\n mp4Index: number;\n}\n\nconst DEFAULTS: Required<Omit<PlaylistOptions, 'items' | 'adTagPreroll' | 'ui'>> = {\n startMode: 'after_current',\n startIndex: 0,\n loop: false,\n autoplayNext: true,\n preferHls: true,\n adsOnItemChange: true,\n};\n\n/** Tek player üzerinde sıralı oynatma listesi (startMode, loop, hata fallback zinciri). */\nexport class PlaylistRunner {\n private readonly options: Required<Omit<PlaylistOptions, 'items' | 'adTagPreroll' | 'ui'>>;\n private readonly items: PlayableItem[];\n\n private index: number;\n private destroyed = false;\n private switching = false;\n private playlistActive = false;\n private startPendingAfterCurrent = false;\n private attemptId = 0;\n private attempt: Attempt | null = null;\n private lastErrorAt = 0;\n private lastAdsIndex: number | null = null;\n private lastDispatchedIndex: number | null = null;\n private userHasStartedPlayback = false;\n\n private readonly ui: {\n root: HTMLElement;\n titleEl: HTMLElement;\n listEl: HTMLElement;\n prevBtn: HTMLButtonElement;\n nextBtn: HTMLButtonElement;\n itemEls: Array<{ li: HTMLElement; titleRow: HTMLElement; progressFill: HTMLElement | null; timeEl: HTMLElement | null }>;\n };\n\n constructor(\n private readonly ctx: PlaylistPlayerContext,\n private readonly rawOptions: PlaylistOptions\n ) {\n this.options = { ...DEFAULTS, ...rawOptions };\n this.items = Array.isArray(rawOptions.items) ? rawOptions.items : [];\n this.index = this.clampIndex(this.options.startIndex);\n\n this.ui = this.buildUi();\n this.bindPlayerEvents();\n this.init();\n }\n\n // ── lifecycle ──\n\n private init(): void {\n if (!this.items.length) return;\n\n if (this.options.startMode === 'after_current') {\n this.startPendingAfterCurrent = true;\n this.playlistActive = false;\n this.setActive(this.index);\n return;\n }\n\n this.playlistActive = true;\n void this.loadAndPlay(this.index, { userInitiated: false, isPlaylistStart: true });\n }\n\n refreshLabels(): void {\n this.ui.titleEl.textContent = this.playlistTitleText();\n this.ui.prevBtn.setAttribute('aria-label', this.ctx.i18n.playlistPrev);\n this.ui.nextBtn.setAttribute('aria-label', this.ctx.i18n.playlistNext);\n this.ui.itemEls.forEach((entry, idx) => {\n const item = this.items[idx];\n if (item && !item.title) entry.titleRow.textContent = this.ctx.i18n.videoFallbackTitle(idx);\n });\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.ctx.videoEl.removeEventListener('ended', this.onEnded);\n this.ctx.videoEl.removeEventListener('error', this.onError);\n this.ctx.videoEl.removeEventListener('play', this.onUserPlay);\n this.ui.root.remove();\n }\n\n // ── public api ──\n\n start(opts: { index?: number } = {}): void {\n if (this.destroyed || !this.items.length) return;\n this.startPendingAfterCurrent = false;\n this.playlistActive = true;\n this.index = this.clampIndex(opts.index ?? this.index);\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: false, isPlaylistStart: true });\n }\n\n next(): void {\n if (this.destroyed || !this.items.length) return;\n if (!this.playlistActive) return this.start({ index: this.index });\n\n if (this.index < this.items.length - 1) this.index++;\n else if (this.options.loop) this.index = 0;\n else return;\n\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n prev(): void {\n if (this.destroyed || !this.items.length) return;\n if (!this.playlistActive) return this.start({ index: this.index });\n if (this.index === 0) return;\n this.index--;\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n setIndex(i: number): void {\n if (this.destroyed || !this.items.length) return;\n this.index = this.clampIndex(i);\n this.setActive(this.index);\n if (!this.playlistActive) return this.start({ index: this.index });\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n getIndex(): number {\n return this.index;\n }\n\n isActive(): boolean {\n return this.playlistActive;\n }\n\n // ── player events ──\n\n private bindPlayerEvents(): void {\n this.ctx.videoEl.addEventListener('ended', this.onEnded);\n this.ctx.videoEl.addEventListener('error', this.onError);\n this.ctx.videoEl.addEventListener('play', this.onUserPlay);\n this.ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n }\n\n private readonly onUserPlay = (): void => {\n this.userHasStartedPlayback = true;\n };\n\n private readonly onEnded = (): void => {\n if (this.startPendingAfterCurrent && !this.playlistActive) {\n this.startPendingAfterCurrent = false;\n this.playlistActive = true;\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true, isPlaylistStart: true });\n return;\n }\n if (this.playlistActive) this.next();\n };\n\n private readonly onError = (): void => {\n if (this.destroyed || !this.playlistActive) return;\n const now = Date.now();\n if (now - this.lastErrorAt < 200) return;\n this.lastErrorAt = now;\n void this.fallbackOrSkip();\n };\n\n private readonly onTimeUpdate = (): void => {\n this.updateProgress();\n };\n\n // ── core load ──\n\n private async loadAndPlay(\n i: number,\n opts: { userInitiated?: boolean; forceMode?: SourceMode | null; forceMp4Index?: number | null; isFallback?: boolean; isPlaylistStart?: boolean } = {}\n ): Promise<void> {\n if (this.destroyed || this.switching || !this.playlistActive) return;\n this.switching = true;\n\n const item = this.items[i];\n if (!item) {\n this.switching = false;\n return;\n }\n const attemptId = ++this.attemptId;\n const mode = opts.forceMode ?? this.resolveMode(item);\n\n let tried = new Set<SourceMode>([mode]);\n let mp4Index = opts.forceMp4Index ?? 0;\n if (opts.isFallback && this.attempt?.index === i && this.attempt?.tried) {\n tried = new Set(this.attempt.tried);\n tried.add(mode);\n if (opts.forceMp4Index == null) mp4Index = this.attempt.mp4Index ?? 0;\n }\n this.attempt = { id: attemptId, index: i, mode, tried, mp4Index };\n\n try {\n const payload = this.buildPayload(item, mode, mp4Index);\n if (!payload) throw new Error('No playable source');\n\n this.setActive(i);\n this.ctx.videoEl.pause();\n if (item.image) this.ctx.setPoster(item.image);\n await this.ctx.loadSource(payload.src, payload.type);\n\n if (this.options.adsOnItemChange) this.maybeRequestAds(i, item, !!opts.isFallback);\n\n if (this.shouldAutoPlay(!!opts.userInitiated)) {\n try {\n await this.ctx.videoEl.play();\n } catch {\n /* autoplay engellendi — sessizce geç */\n }\n }\n this.updateProgress();\n } finally {\n this.switching = false;\n }\n }\n\n private async fallbackOrSkip(): Promise<void> {\n if (this.destroyed || !this.attempt) return;\n const item = this.items[this.attempt.index];\n if (!item) return;\n\n if (this.attempt.mode === 'mp4') {\n const mp4List = (item.mp4 || []).filter((v) => v?.src);\n const nextMp4 = (this.attempt.mp4Index ?? 0) + 1;\n if (nextMp4 < mp4List.length) {\n await this.loadAndPlay(this.attempt.index, { forceMode: 'mp4', forceMp4Index: nextMp4, isFallback: true });\n return;\n }\n }\n\n const otherMode: SourceMode = this.attempt.mode === 'hls' ? 'mp4' : 'hls';\n if (this.hasMode(item, otherMode) && !this.attempt.tried.has(otherMode)) {\n await this.loadAndPlay(this.attempt.index, { forceMode: otherMode, isFallback: true });\n return;\n }\n\n this.next();\n }\n\n // ── ads ──\n\n private maybeRequestAds(index: number, item: PlayableItem, _isFallback: boolean): void {\n if (this.lastAdsIndex === index) return;\n this.lastAdsIndex = index;\n this.ctx.requestFreshAd(item.adTagPreroll || this.rawOptions.adTagPreroll);\n }\n\n // ── sources ──\n\n private resolveMode(item: PlayableItem): SourceMode {\n if (this.options.preferHls) return this.hasMode(item, 'hls') ? 'hls' : this.hasMode(item, 'mp4') ? 'mp4' : 'hls';\n return this.hasMode(item, 'mp4') ? 'mp4' : this.hasMode(item, 'hls') ? 'hls' : 'mp4';\n }\n\n private hasMode(item: PlayableItem, mode: SourceMode): boolean {\n return Array.isArray(item[mode]) && item[mode]!.some((v) => v?.src);\n }\n\n private buildPayload(item: PlayableItem, mode: SourceMode, mp4Index = 0): { src: string; type?: string } | null {\n const list = (item[mode] || []).filter((v) => v?.src);\n if (!list.length) return null;\n\n let src;\n if (mode === 'mp4') {\n src = mp4Index > 0 ? list[mp4Index] || list[0] : list.find((v) => v.selected) || list[0];\n } else {\n src = list[0];\n }\n return { src: src!.src, type: src!.type || (mode === 'hls' ? 'application/x-mpegURL' : 'video/mp4') };\n }\n\n private shouldAutoPlay(userInitiated: boolean): boolean {\n if (userInitiated) return true;\n return this.options.autoplayNext && this.userHasStartedPlayback;\n }\n\n private clampIndex(i: number): number {\n if (!Number.isFinite(i) || !this.items.length) return 0;\n return Math.max(0, Math.min(this.items.length - 1, Math.trunc(i)));\n }\n\n // ── ui ──\n\n private buildUi() {\n const ui = this.rawOptions.ui ?? {};\n const root = document.createElement('div');\n root.setAttribute('part', 'playlist-panel');\n root.className = 'hp:flex hp:flex-col hp:gap-2 hp:p-3 hp:bg-surface-elev hp:max-h-80 hp:overflow-y-auto';\n\n const header = document.createElement('div');\n header.className = 'hp:flex hp:items-center hp:justify-between hp:gap-2 hp:mb-1';\n\n const titleEl = document.createElement('div');\n titleEl.className = 'hp:text-sm hp:font-semibold hp:text-on-surface';\n titleEl.textContent = this.playlistTitleText();\n header.appendChild(titleEl);\n\n const navWrap = document.createElement('div');\n navWrap.className = 'hp:flex hp:gap-1';\n const prevBtn = this.navButton('chevron-left', this.ctx.i18n.playlistPrev, () => this.prev());\n const nextBtn = this.navButton('chevron-left', this.ctx.i18n.playlistNext, () => this.next());\n nextBtn.style.transform = 'rotate(180deg)';\n navWrap.append(prevBtn, nextBtn);\n header.appendChild(navWrap);\n\n root.appendChild(header);\n\n const listEl = document.createElement('ul');\n listEl.className = 'hp:flex hp:flex-col hp:gap-1 hp:list-none hp:m-0 hp:p-0';\n root.appendChild(listEl);\n\n const itemEls: Array<{ li: HTMLElement; titleRow: HTMLElement; progressFill: HTMLElement | null; timeEl: HTMLElement | null }> = [];\n this.items.forEach((item, idx) => {\n const li = document.createElement('li');\n li.className =\n 'hp:flex hp:items-center hp:gap-2 hp:p-2 hp:rounded hp:cursor-pointer hp:text-on-surface-muted hp:text-sm ' +\n 'hp:hover:bg-on-surface/5 hp:data-[active=true]:bg-accent/15 hp:data-[active=true]:text-on-surface';\n\n if (item.image) {\n const img = document.createElement('img');\n img.src = item.image;\n img.loading = 'lazy';\n img.className = 'hp:w-16 hp:aspect-video hp:object-cover hp:rounded hp:shrink-0';\n li.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'hp:flex-1 hp:min-w-0';\n const titleRow = document.createElement('div');\n titleRow.className = 'hp:truncate hp:font-medium';\n titleRow.textContent = item.title || this.ctx.i18n.videoFallbackTitle(idx);\n body.appendChild(titleRow);\n\n let progressFill: HTMLElement | null = null;\n let timeEl: HTMLElement | null = null;\n if (ui.showMeta !== false) {\n const metaRow = document.createElement('div');\n metaRow.className = 'hp:flex hp:items-center hp:gap-2 hp:text-xs hp:opacity-70 hp:mt-0.5';\n timeEl = document.createElement('span');\n timeEl.textContent = '';\n metaRow.appendChild(timeEl);\n\n const bar = document.createElement('div');\n bar.className = 'hp:flex-1 hp:h-0.5 hp:bg-on-surface/20 hp:rounded-full hp:overflow-hidden';\n progressFill = document.createElement('div');\n progressFill.className = 'hp:h-full hp:bg-accent';\n progressFill.style.width = '0%';\n bar.appendChild(progressFill);\n metaRow.appendChild(bar);\n body.appendChild(metaRow);\n }\n\n li.appendChild(body);\n li.addEventListener('click', () => this.setIndex(idx));\n listEl.appendChild(li);\n itemEls.push({ li, titleRow, progressFill, timeEl });\n });\n\n this.ctx.panelEl.appendChild(root);\n return { root, titleEl, listEl, prevBtn, nextBtn, itemEls };\n }\n\n private playlistTitleText(): string {\n const ui = this.rawOptions.ui ?? {};\n const title = ui.title || this.ctx.i18n.playlistTitle;\n return ui.showCount === false ? title : `${title} (${this.items.length})`;\n }\n\n private navButton(iconName: string, label: string, onClick: () => void): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n btn.className = 'hp:flex hp:items-center hp:justify-center hp:w-6 hp:h-6 hp:text-on-surface hp:bg-transparent hp:border-0 hp:cursor-pointer';\n btn.appendChild(createIcon(iconName, 'hp:text-sm'));\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private setActive(index: number): void {\n this.ui.itemEls.forEach((entry, idx) => {\n if (idx === index) entry.li.setAttribute('data-active', 'true');\n else entry.li.removeAttribute('data-active');\n });\n // loadAndPlay() hem anlık UI geri bildirimi için önceden (next/prev/setIndex\n // içinden) hem de kaynak gerçekten yüklendiğinde setActive() çağırıyor —\n // aynı index için event'i yalnızca BİR kez yaymak için guard.\n if (this.lastDispatchedIndex !== index) {\n this.lastDispatchedIndex = index;\n this.ctx.hostEl.dispatchEvent(new CustomEvent('hp:itemchange', { detail: { index, source: 'playlist' } }));\n }\n }\n\n private updateProgress(): void {\n const active = this.ui.itemEls[this.index];\n if (!active) return;\n const duration = this.ctx.videoEl.duration;\n const current = this.ctx.videoEl.currentTime;\n if (active.timeEl) active.timeEl.textContent = this.formatTime(current);\n if (active.progressFill && Number.isFinite(duration) && duration > 0) {\n active.progressFill.style.width = `${Math.min(100, (current / duration) * 100)}%`;\n }\n }\n\n private formatTime(sec: number): string {\n const s = Math.max(0, Math.floor(sec || 0));\n const m = Math.floor(s / 60);\n const r = s % 60;\n return `${m}:${String(r).padStart(2, '0')}`;\n }\n}\n","import { PlaylistRunner, type PlaylistPlayerContext } from './PlaylistRunner';\nimport type { PlaylistOptions } from '../../types';\n\nexport function installPlaylist(ctx: PlaylistPlayerContext, options: PlaylistOptions): PlaylistRunner {\n return new PlaylistRunner(ctx, options);\n}\n\nexport { PlaylistRunner };\nexport type { PlaylistPlayerContext };\n"],"names":["DEFAULTS","PlaylistRunner","ctx","rawOptions","__publicField","now","entry","idx","item","opts","i","attemptId","mode","tried","mp4Index","_a","_b","payload","mp4List","v","nextMp4","otherMode","index","_isFallback","list","src","userInitiated","ui","root","header","titleEl","navWrap","prevBtn","nextBtn","listEl","itemEls","li","img","body","titleRow","progressFill","timeEl","metaRow","bar","title","iconName","label","onClick","btn","createIcon","active","duration","current","sec","s","m","r","installPlaylist","options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DJZjHaLs.js","sources":["../../src/plugins/playlist/PlaylistRunner.ts","../../src/plugins/playlist/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { PlayableItem, PlaylistOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface PlaylistPlayerContext {\n videoEl: HTMLVideoElement;\n hostEl: HTMLElement;\n /** Liste UI'ının monte edileceği, shadow root içindeki panel. */\n panelEl: HTMLElement;\n loadSource(src: string, type?: string): Promise<void>;\n setPoster(url: string): void;\n /** AdsController varsa retry/VMAP durumunu sıfırlayıp item'ın (varsa) kendi adTagPreroll'üyle yeniden reklam ister — yoksa no-op. */\n requestFreshAd(prerollTag?: string): void;\n i18n: HopeLocaleStrings;\n}\n\ntype SourceMode = 'hls' | 'mp4';\n\ninterface Attempt {\n id: number;\n index: number;\n mode: SourceMode;\n tried: Set<SourceMode>;\n mp4Index: number;\n}\n\nconst DEFAULTS: Required<Omit<PlaylistOptions, 'items' | 'adTagPreroll' | 'ui'>> = {\n startMode: 'after_current',\n startIndex: 0,\n loop: false,\n autoplayNext: true,\n preferHls: true,\n adsOnItemChange: true,\n};\n\n/** Tek player üzerinde sıralı oynatma listesi (startMode, loop, hata fallback zinciri). */\nexport class PlaylistRunner {\n private readonly options: Required<Omit<PlaylistOptions, 'items' | 'adTagPreroll' | 'ui'>>;\n private readonly items: PlayableItem[];\n\n private index: number;\n private destroyed = false;\n private switching = false;\n private playlistActive = false;\n private startPendingAfterCurrent = false;\n private attemptId = 0;\n private attempt: Attempt | null = null;\n private lastErrorAt = 0;\n private lastAdsIndex: number | null = null;\n private lastDispatchedIndex: number | null = null;\n private userHasStartedPlayback = false;\n\n private readonly ui: {\n root: HTMLElement;\n titleEl: HTMLElement;\n listEl: HTMLElement;\n prevBtn: HTMLButtonElement;\n nextBtn: HTMLButtonElement;\n itemEls: Array<{ li: HTMLElement; titleRow: HTMLElement; progressFill: HTMLElement | null; timeEl: HTMLElement | null }>;\n };\n\n constructor(\n private readonly ctx: PlaylistPlayerContext,\n private readonly rawOptions: PlaylistOptions\n ) {\n this.options = { ...DEFAULTS, ...rawOptions };\n this.items = Array.isArray(rawOptions.items) ? rawOptions.items : [];\n this.index = this.clampIndex(this.options.startIndex);\n\n this.ui = this.buildUi();\n this.bindPlayerEvents();\n this.init();\n }\n\n // ── lifecycle ──\n\n private init(): void {\n if (!this.items.length) return;\n\n if (this.options.startMode === 'after_current') {\n this.startPendingAfterCurrent = true;\n this.playlistActive = false;\n this.setActive(this.index);\n return;\n }\n\n this.playlistActive = true;\n void this.loadAndPlay(this.index, { userInitiated: false, isPlaylistStart: true });\n }\n\n refreshLabels(): void {\n this.ui.titleEl.textContent = this.playlistTitleText();\n this.ui.prevBtn.setAttribute('aria-label', this.ctx.i18n.playlistPrev);\n this.ui.nextBtn.setAttribute('aria-label', this.ctx.i18n.playlistNext);\n this.ui.itemEls.forEach((entry, idx) => {\n const item = this.items[idx];\n if (item && !item.title) entry.titleRow.textContent = this.ctx.i18n.videoFallbackTitle(idx);\n });\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.ctx.videoEl.removeEventListener('ended', this.onEnded);\n this.ctx.videoEl.removeEventListener('error', this.onError);\n this.ctx.videoEl.removeEventListener('play', this.onUserPlay);\n this.ui.root.remove();\n }\n\n // ── public api ──\n\n start(opts: { index?: number } = {}): void {\n if (this.destroyed || !this.items.length) return;\n this.startPendingAfterCurrent = false;\n this.playlistActive = true;\n this.index = this.clampIndex(opts.index ?? this.index);\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: false, isPlaylistStart: true });\n }\n\n next(): void {\n if (this.destroyed || !this.items.length) return;\n if (!this.playlistActive) return this.start({ index: this.index });\n\n if (this.index < this.items.length - 1) this.index++;\n else if (this.options.loop) this.index = 0;\n else return;\n\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n prev(): void {\n if (this.destroyed || !this.items.length) return;\n if (!this.playlistActive) return this.start({ index: this.index });\n if (this.index === 0) return;\n this.index--;\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n setIndex(i: number): void {\n if (this.destroyed || !this.items.length) return;\n this.index = this.clampIndex(i);\n this.setActive(this.index);\n if (!this.playlistActive) return this.start({ index: this.index });\n void this.loadAndPlay(this.index, { userInitiated: true });\n }\n\n getIndex(): number {\n return this.index;\n }\n\n isActive(): boolean {\n return this.playlistActive;\n }\n\n // ── player events ──\n\n private bindPlayerEvents(): void {\n this.ctx.videoEl.addEventListener('ended', this.onEnded);\n this.ctx.videoEl.addEventListener('error', this.onError);\n this.ctx.videoEl.addEventListener('play', this.onUserPlay);\n this.ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n }\n\n private readonly onUserPlay = (): void => {\n this.userHasStartedPlayback = true;\n };\n\n private readonly onEnded = (): void => {\n if (this.startPendingAfterCurrent && !this.playlistActive) {\n this.startPendingAfterCurrent = false;\n this.playlistActive = true;\n this.setActive(this.index);\n void this.loadAndPlay(this.index, { userInitiated: true, isPlaylistStart: true });\n return;\n }\n if (this.playlistActive) this.next();\n };\n\n private readonly onError = (): void => {\n if (this.destroyed || !this.playlistActive) return;\n const now = Date.now();\n if (now - this.lastErrorAt < 200) return;\n this.lastErrorAt = now;\n void this.fallbackOrSkip();\n };\n\n private readonly onTimeUpdate = (): void => {\n this.updateProgress();\n };\n\n // ── core load ──\n\n private async loadAndPlay(\n i: number,\n opts: { userInitiated?: boolean; forceMode?: SourceMode | null; forceMp4Index?: number | null; isFallback?: boolean; isPlaylistStart?: boolean } = {}\n ): Promise<void> {\n if (this.destroyed || this.switching || !this.playlistActive) return;\n this.switching = true;\n\n const item = this.items[i];\n if (!item) {\n this.switching = false;\n return;\n }\n const attemptId = ++this.attemptId;\n const mode = opts.forceMode ?? this.resolveMode(item);\n\n let tried = new Set<SourceMode>([mode]);\n let mp4Index = opts.forceMp4Index ?? 0;\n if (opts.isFallback && this.attempt?.index === i && this.attempt?.tried) {\n tried = new Set(this.attempt.tried);\n tried.add(mode);\n if (opts.forceMp4Index == null) mp4Index = this.attempt.mp4Index ?? 0;\n }\n this.attempt = { id: attemptId, index: i, mode, tried, mp4Index };\n\n try {\n const payload = this.buildPayload(item, mode, mp4Index);\n if (!payload) throw new Error('No playable source');\n\n this.setActive(i);\n this.ctx.videoEl.pause();\n if (item.image) this.ctx.setPoster(item.image);\n await this.ctx.loadSource(payload.src, payload.type);\n\n if (this.options.adsOnItemChange) this.maybeRequestAds(i, item, !!opts.isFallback);\n\n if (this.shouldAutoPlay(!!opts.userInitiated)) {\n try {\n await this.ctx.videoEl.play();\n } catch {\n /* autoplay engellendi — sessizce geç */\n }\n }\n this.updateProgress();\n } finally {\n this.switching = false;\n }\n }\n\n private async fallbackOrSkip(): Promise<void> {\n if (this.destroyed || !this.attempt) return;\n const item = this.items[this.attempt.index];\n if (!item) return;\n\n if (this.attempt.mode === 'mp4') {\n const mp4List = (item.mp4 || []).filter((v) => v?.src);\n const nextMp4 = (this.attempt.mp4Index ?? 0) + 1;\n if (nextMp4 < mp4List.length) {\n await this.loadAndPlay(this.attempt.index, { forceMode: 'mp4', forceMp4Index: nextMp4, isFallback: true });\n return;\n }\n }\n\n const otherMode: SourceMode = this.attempt.mode === 'hls' ? 'mp4' : 'hls';\n if (this.hasMode(item, otherMode) && !this.attempt.tried.has(otherMode)) {\n await this.loadAndPlay(this.attempt.index, { forceMode: otherMode, isFallback: true });\n return;\n }\n\n this.next();\n }\n\n // ── ads ──\n\n private maybeRequestAds(index: number, item: PlayableItem, _isFallback: boolean): void {\n if (this.lastAdsIndex === index) return;\n this.lastAdsIndex = index;\n this.ctx.requestFreshAd(item.adTagPreroll || this.rawOptions.adTagPreroll);\n }\n\n // ── sources ──\n\n private resolveMode(item: PlayableItem): SourceMode {\n if (this.options.preferHls) return this.hasMode(item, 'hls') ? 'hls' : this.hasMode(item, 'mp4') ? 'mp4' : 'hls';\n return this.hasMode(item, 'mp4') ? 'mp4' : this.hasMode(item, 'hls') ? 'hls' : 'mp4';\n }\n\n private hasMode(item: PlayableItem, mode: SourceMode): boolean {\n return Array.isArray(item[mode]) && item[mode]!.some((v) => v?.src);\n }\n\n private buildPayload(item: PlayableItem, mode: SourceMode, mp4Index = 0): { src: string; type?: string } | null {\n const list = (item[mode] || []).filter((v) => v?.src);\n if (!list.length) return null;\n\n let src;\n if (mode === 'mp4') {\n src = mp4Index > 0 ? list[mp4Index] || list[0] : list.find((v) => v.selected) || list[0];\n } else {\n src = list[0];\n }\n return { src: src!.src, type: src!.type || (mode === 'hls' ? 'application/x-mpegURL' : 'video/mp4') };\n }\n\n private shouldAutoPlay(userInitiated: boolean): boolean {\n if (userInitiated) return true;\n return this.options.autoplayNext && this.userHasStartedPlayback;\n }\n\n private clampIndex(i: number): number {\n if (!Number.isFinite(i) || !this.items.length) return 0;\n return Math.max(0, Math.min(this.items.length - 1, Math.trunc(i)));\n }\n\n // ── ui ──\n\n private buildUi() {\n const ui = this.rawOptions.ui ?? {};\n const root = document.createElement('div');\n root.setAttribute('part', 'playlist-panel');\n root.className = 'hp:flex hp:flex-col hp:gap-2 hp:p-3 hp:bg-surface-elev hp:max-h-80 hp:overflow-y-auto';\n\n const header = document.createElement('div');\n header.className = 'hp:flex hp:items-center hp:justify-between hp:gap-2 hp:mb-1';\n\n const titleEl = document.createElement('div');\n titleEl.className = 'hp:text-sm hp:font-semibold hp:text-on-surface';\n titleEl.textContent = this.playlistTitleText();\n header.appendChild(titleEl);\n\n const navWrap = document.createElement('div');\n navWrap.className = 'hp:flex hp:gap-1';\n const prevBtn = this.navButton('chevron-left', this.ctx.i18n.playlistPrev, () => this.prev());\n const nextBtn = this.navButton('chevron-left', this.ctx.i18n.playlistNext, () => this.next());\n nextBtn.style.transform = 'rotate(180deg)';\n navWrap.append(prevBtn, nextBtn);\n header.appendChild(navWrap);\n\n root.appendChild(header);\n\n const listEl = document.createElement('ul');\n listEl.className = 'hp:flex hp:flex-col hp:gap-1 hp:list-none hp:m-0 hp:p-0';\n root.appendChild(listEl);\n\n const itemEls: Array<{ li: HTMLElement; titleRow: HTMLElement; progressFill: HTMLElement | null; timeEl: HTMLElement | null }> = [];\n this.items.forEach((item, idx) => {\n const li = document.createElement('li');\n li.className =\n 'hp:flex hp:items-center hp:gap-2 hp:p-2 hp:rounded hp:cursor-pointer hp:text-on-surface-muted hp:text-sm ' +\n 'hp:hover:bg-on-surface/5 hp:data-[active=true]:bg-accent/15 hp:data-[active=true]:text-on-surface';\n\n if (item.image) {\n const img = document.createElement('img');\n img.src = item.image;\n img.loading = 'lazy';\n img.className = 'hp:w-16 hp:aspect-video hp:object-cover hp:rounded hp:shrink-0';\n li.appendChild(img);\n }\n\n const body = document.createElement('div');\n body.className = 'hp:flex-1 hp:min-w-0';\n const titleRow = document.createElement('div');\n titleRow.className = 'hp:truncate hp:font-medium';\n titleRow.textContent = item.title || this.ctx.i18n.videoFallbackTitle(idx);\n body.appendChild(titleRow);\n\n let progressFill: HTMLElement | null = null;\n let timeEl: HTMLElement | null = null;\n if (ui.showMeta !== false) {\n const metaRow = document.createElement('div');\n metaRow.className = 'hp:flex hp:items-center hp:gap-2 hp:text-xs hp:opacity-70 hp:mt-0.5';\n timeEl = document.createElement('span');\n timeEl.textContent = '';\n metaRow.appendChild(timeEl);\n\n const bar = document.createElement('div');\n bar.className = 'hp:flex-1 hp:h-0.5 hp:bg-on-surface/20 hp:rounded-full hp:overflow-hidden';\n progressFill = document.createElement('div');\n progressFill.className = 'hp:h-full hp:bg-accent';\n progressFill.style.width = '0%';\n bar.appendChild(progressFill);\n metaRow.appendChild(bar);\n body.appendChild(metaRow);\n }\n\n li.appendChild(body);\n li.addEventListener('click', () => this.setIndex(idx));\n listEl.appendChild(li);\n itemEls.push({ li, titleRow, progressFill, timeEl });\n });\n\n this.ctx.panelEl.appendChild(root);\n return { root, titleEl, listEl, prevBtn, nextBtn, itemEls };\n }\n\n private playlistTitleText(): string {\n const ui = this.rawOptions.ui ?? {};\n const title = ui.title || this.ctx.i18n.playlistTitle;\n return ui.showCount === false ? title : `${title} (${this.items.length})`;\n }\n\n private navButton(iconName: string, label: string, onClick: () => void): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n btn.className = 'hp:flex hp:items-center hp:justify-center hp:w-6 hp:h-6 hp:text-on-surface hp:bg-transparent hp:border-0 hp:cursor-pointer';\n btn.appendChild(createIcon(iconName, 'hp:text-sm'));\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private setActive(index: number): void {\n this.ui.itemEls.forEach((entry, idx) => {\n if (idx === index) entry.li.setAttribute('data-active', 'true');\n else entry.li.removeAttribute('data-active');\n });\n // loadAndPlay() hem anlık UI geri bildirimi için önceden (next/prev/setIndex\n // içinden) hem de kaynak gerçekten yüklendiğinde setActive() çağırıyor —\n // aynı index için event'i yalnızca BİR kez yaymak için guard.\n if (this.lastDispatchedIndex !== index) {\n this.lastDispatchedIndex = index;\n this.ctx.hostEl.dispatchEvent(new CustomEvent('hp:itemchange', { detail: { index, source: 'playlist' } }));\n }\n }\n\n private updateProgress(): void {\n const active = this.ui.itemEls[this.index];\n if (!active) return;\n const duration = this.ctx.videoEl.duration;\n const current = this.ctx.videoEl.currentTime;\n if (active.timeEl) active.timeEl.textContent = this.formatTime(current);\n if (active.progressFill && Number.isFinite(duration) && duration > 0) {\n active.progressFill.style.width = `${Math.min(100, (current / duration) * 100)}%`;\n }\n }\n\n private formatTime(sec: number): string {\n const s = Math.max(0, Math.floor(sec || 0));\n const m = Math.floor(s / 60);\n const r = s % 60;\n return `${m}:${String(r).padStart(2, '0')}`;\n }\n}\n","import { PlaylistRunner, type PlaylistPlayerContext } from './PlaylistRunner';\nimport type { PlaylistOptions } from '../../types';\n\nexport function installPlaylist(ctx: PlaylistPlayerContext, options: PlaylistOptions): PlaylistRunner {\n return new PlaylistRunner(ctx, options);\n}\n\nexport { PlaylistRunner };\nexport type { PlaylistPlayerContext };\n"],"names":["DEFAULTS","PlaylistRunner","ctx","rawOptions","__publicField","now","entry","idx","item","opts","i","attemptId","mode","tried","mp4Index","_a","_b","payload","mp4List","v","nextMp4","otherMode","index","_isFallback","list","src","userInitiated","ui","root","header","titleEl","navWrap","prevBtn","nextBtn","listEl","itemEls","li","img","body","titleRow","progressFill","timeEl","metaRow","bar","title","iconName","label","onClick","btn","createIcon","active","duration","current","sec","s","m","r","installPlaylist","options"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var u = Object.defineProperty;
|
|
2
2
|
var m = (p, e, t) => e in p ? u(p, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : p[e] = t;
|
|
3
3
|
var o = (p, e, t) => m(p, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
-
import { c as b } from "./index-
|
|
4
|
+
import { c as b } from "./index-Bqs23yk7.js";
|
|
5
5
|
const f = {
|
|
6
6
|
enabled: !1,
|
|
7
7
|
mode: "rich",
|
|
@@ -202,4 +202,4 @@ export {
|
|
|
202
202
|
v as MarkProgress,
|
|
203
203
|
y as installMarkers
|
|
204
204
|
};
|
|
205
|
-
//# sourceMappingURL=index-
|
|
205
|
+
//# sourceMappingURL=index-DN077Bbe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-nL5ba3Fw.js","sources":["../../src/plugins/markers/MarkProgress.ts","../../src/plugins/markers/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { MarkerItem, MarkerPingOptions, MarkerPopupOptions, MarkersOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface MarkersPlayerContext {\n videoEl: HTMLVideoElement;\n /** Popup overlay'in ekleneceği katman (shadow root içinde, video üzerinde). */\n overlayContainerEl: HTMLElement;\n /** Seek bar üzerinde işaretçi çizmek için — SeekBar.addMarker ile aynı imza. */\n addSeekBarMarker(ratio: number, extraClass?: string): HTMLDivElement;\n i18n: HopeLocaleStrings;\n}\n\ninterface NormalizedMarker extends Required<Pick<MarkerItem, 'time'>> {\n idx: number;\n color?: string;\n label: string;\n pauseOnHit: boolean;\n once: boolean;\n tolerance: number;\n cooldownMs: number;\n action?: MarkerItem['action'];\n popup: MarkerPopupOptions | null;\n pingUrl: string;\n el?: HTMLElement;\n}\n\nconst POPUP_DEFAULTS: Required<MarkerPopupOptions> = {\n enabled: false,\n mode: 'rich',\n text: '',\n imageUrl: '',\n imageAlt: '',\n linkUrl: '',\n // Boş bırakılıyor (sentinel) — gerçek varsayılan metin showPopup()'ta\n // `ctx.i18n.markerLearnMore`/`markerContinue`'dan okunuyor, dil\n // değişimini takip edebilsin diye burada SABİT bir dilde gömülü değil.\n linkText: '',\n continueText: '',\n showContinue: true,\n showClose: true,\n autoplayOnClose: true,\n autoplayOnContinue: true,\n};\n\nconst PING_DEFAULTS: Required<MarkerPingOptions> = { enabled: false, url: '', method: 'GET', includeMarker: false };\n\n/** Zaman tabanlı seek bar işaretçileri — seek/jump koruması, once/cooldown, popup/ping/aksiyon. */\nexport class MarkProgress {\n private readonly markers: NormalizedMarker[];\n private readonly popupGlobal: Required<MarkerPopupOptions>;\n private readonly pingGlobal: Required<MarkerPingOptions>;\n private readonly triggerOnSeek: boolean;\n private readonly seekJumpThresholdSec: number;\n private readonly seekIgnoreWindowMs: number;\n\n private hitSet = new Set<string>();\n private lastHitAt = new Map<string, number>();\n private lastTime = 0;\n private isSeeking = false;\n private ignoreUntilTs = 0;\n private popupEl: HTMLElement | null = null;\n\n constructor(\n private readonly ctx: MarkersPlayerContext,\n options: MarkersOptions\n ) {\n this.triggerOnSeek = options.triggerOnSeek === true;\n this.seekJumpThresholdSec = options.seekJumpThresholdSec ?? 1;\n this.seekIgnoreWindowMs = options.seekIgnoreWindowMs ?? 250;\n this.popupGlobal = { ...POPUP_DEFAULTS, ...options.popup };\n this.pingGlobal = { ...PING_DEFAULTS, ...options.ping };\n\n this.markers = (options.items || [])\n .map((m, idx) => this.normalize(m, idx))\n .filter((m) => Number.isFinite(m.time) && m.time >= 0)\n .sort((a, b) => a.time - b.time);\n\n ctx.videoEl.addEventListener('loadedmetadata', this.onLoadedMetadata);\n ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n ctx.videoEl.addEventListener('seeking', this.onSeeking);\n ctx.videoEl.addEventListener('seeked', this.onSeeked);\n\n if (ctx.videoEl.readyState >= 1) this.onLoadedMetadata();\n }\n\n /** Popup lazy oluşturulup yeniden kullanılıyor (`ensurePopup`) — zaten açıksa close butonunun statik etiketini tazeler. */\n refreshLabels(): void {\n const closeBtn = this.popupEl?.querySelector('button') as HTMLButtonElement | null;\n if (closeBtn) {\n closeBtn.setAttribute('aria-label', this.ctx.i18n.close);\n }\n }\n\n destroy(): void {\n this.ctx.videoEl.removeEventListener('loadedmetadata', this.onLoadedMetadata);\n this.ctx.videoEl.removeEventListener('timeupdate', this.onTimeUpdate);\n this.ctx.videoEl.removeEventListener('seeking', this.onSeeking);\n this.ctx.videoEl.removeEventListener('seeked', this.onSeeked);\n this.markers.forEach((m) => m.el?.remove());\n this.popupEl?.remove();\n this.popupEl = null;\n this.hitSet.clear();\n this.lastHitAt.clear();\n }\n\n private normalize(m: MarkerItem, idx: number): NormalizedMarker {\n return {\n idx,\n time: Number(m.time),\n color: m.color,\n label: m.label ?? '',\n pauseOnHit: m.pauseOnHit ?? true,\n once: m.once ?? true,\n tolerance: m.tolerance ?? 0.25,\n cooldownMs: m.cooldownMs ?? 600,\n action: m.action,\n popup: m.popup ?? null,\n pingUrl: m.pingUrl ?? '',\n };\n }\n\n private readonly onLoadedMetadata = (): void => {\n const duration = this.ctx.videoEl.duration;\n if (!Number.isFinite(duration) || duration <= 0) return;\n\n this.markers.forEach((m) => {\n if (m.el) return;\n const el = this.ctx.addSeekBarMarker(m.time / duration);\n if (m.color) el.style.backgroundColor = m.color;\n if (m.label) el.title = m.label;\n m.el = el;\n });\n\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n };\n\n private readonly onSeeking = (): void => {\n this.isSeeking = true;\n this.ignoreUntilTs = Date.now() + this.seekIgnoreWindowMs;\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n };\n\n private readonly onSeeked = (): void => {\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n this.isSeeking = false;\n this.ignoreUntilTs = Date.now() + this.seekIgnoreWindowMs;\n };\n\n private readonly onTimeUpdate = (): void => {\n const now = Date.now();\n if (now < this.ignoreUntilTs) return;\n if (this.isSeeking) return;\n\n const t = this.ctx.videoEl.currentTime;\n if (!Number.isFinite(t)) return;\n\n const jumped = Math.abs(t - this.lastTime) >= this.seekJumpThresholdSec;\n if (jumped && !this.triggerOnSeek) {\n this.lastTime = t;\n return;\n }\n\n for (const m of this.markers) {\n const key = `${m.time}-${m.idx}`;\n if (m.once && this.hitSet.has(key)) continue;\n if (!m.once) {\n const last = this.lastHitAt.get(key) || 0;\n if (now - last < m.cooldownMs) continue;\n }\n\n const crossed = this.lastTime < m.time && t >= m.time - m.tolerance;\n if (!crossed) continue;\n\n if (m.once) this.hitSet.add(key);\n else this.lastHitAt.set(key, now);\n\n if (m.pauseOnHit) this.ctx.videoEl.pause();\n\n this.firePing(m);\n this.fireAction(m);\n\n const popupCfg = m.popup ? { ...this.popupGlobal, ...m.popup } : this.popupGlobal;\n if (popupCfg.enabled) {\n this.ctx.videoEl.pause();\n this.showPopup(popupCfg);\n }\n break;\n }\n\n this.lastTime = t;\n };\n\n private resolveUrlTemplate(url: string, m: NormalizedMarker): string {\n if (!url) return '';\n return url.replaceAll('{time}', encodeURIComponent(String(m.time))).replaceAll('{label}', encodeURIComponent(m.label));\n }\n\n private firePing(m: NormalizedMarker): void {\n const baseUrl = m.pingUrl || this.pingGlobal.url;\n const url = this.resolveUrlTemplate(baseUrl, m);\n if (!this.pingGlobal.enabled || !url) return;\n\n try {\n if (this.pingGlobal.method === 'POST') {\n fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: this.pingGlobal.includeMarker ? { 'Content-Type': 'application/json' } : undefined,\n body: this.pingGlobal.includeMarker ? JSON.stringify({ time: m.time, label: m.label, ts: Date.now() }) : undefined,\n }).catch(() => {});\n } else {\n fetch(url, { method: 'GET', keepalive: true }).catch(() => {});\n }\n } catch {\n /* no-op */\n }\n }\n\n private fireAction(m: NormalizedMarker): void {\n const a = m.action;\n if (!a) return;\n\n if (a.type === 'log') {\n console.log('[HopePlayer markers]', a.name || 'marker_hit', { time: m.time, label: m.label }, a.payload);\n return;\n }\n\n if (a.type === 'datalayer') {\n try {\n const w = window as unknown as { dataLayer?: unknown[] };\n w.dataLayer = w.dataLayer || [];\n w.dataLayer.push({\n event: 'HopePlayerVideoEvents',\n trigger: a.name || 'marker_hit',\n marker: { time: m.time, label: m.label, color: m.color },\n payload: a.payload ?? {},\n ts: Date.now(),\n });\n } catch {\n /* no-op */\n }\n return;\n }\n\n if (a.type === 'custom') {\n const fn = a.name ? (window as unknown as Record<string, unknown>)[a.name] : null;\n if (typeof fn === 'function') {\n try {\n (fn as (ctx: unknown) => void)({ marker: m, payload: a.payload });\n } catch (err) {\n console.warn('[HopePlayer markers] custom action fonksiyonu hata verdi:', err);\n }\n } else {\n console.warn('[HopePlayer markers] custom action fonksiyonu bulunamadı: window.' + a.name);\n }\n }\n }\n\n private ensurePopup(): HTMLElement {\n if (this.popupEl) return this.popupEl;\n\n const overlay = document.createElement('div');\n overlay.setAttribute('part', 'marker-popup');\n overlay.className = 'hp:hidden hp:absolute hp:inset-0 hp:z-30 hp:items-center hp:justify-center hp:bg-black/70';\n\n const panel = document.createElement('div');\n panel.className = 'hp:relative hp:max-w-[85%] hp:bg-surface-elev hp:text-on-surface hp:rounded-lg hp:p-4 hp:text-center hp:shadow-xl';\n panel.setAttribute('role', 'dialog');\n panel.setAttribute('aria-modal', 'true');\n\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', this.ctx.i18n.close);\n closeBtn.className = 'hp:absolute hp:top-1 hp:right-1 hp:w-6 hp:h-6 hp:flex hp:items-center hp:justify-center hp:bg-transparent hp:border-0 hp:cursor-pointer hp:text-on-surface';\n closeBtn.appendChild(createIcon('close', 'hp:text-sm'));\n\n const mediaWrap = document.createElement('div');\n mediaWrap.className = 'hp:hidden hp:mb-2';\n const img = document.createElement('img');\n img.className = 'hp:max-w-full hp:rounded';\n mediaWrap.appendChild(img);\n\n const textEl = document.createElement('div');\n textEl.className = 'hp:text-sm hp:mb-2';\n\n const linkEl = document.createElement('a');\n linkEl.target = '_blank';\n linkEl.rel = 'noopener noreferrer';\n linkEl.className = 'hp:hidden hp:text-accent hp:underline hp:text-sm hp:block hp:mb-2';\n\n const continueBtn = document.createElement('button');\n continueBtn.type = 'button';\n continueBtn.className = 'hp:hidden hp:px-4 hp:py-1.5 hp:rounded hp:bg-accent hp:text-on-accent hp:border-0 hp:cursor-pointer hp:text-sm';\n\n panel.append(closeBtn, mediaWrap, textEl, linkEl, continueBtn);\n overlay.appendChild(panel);\n this.ctx.overlayContainerEl.appendChild(overlay);\n this.popupEl = overlay;\n return overlay;\n }\n\n private hidePopup(): void {\n this.popupEl?.classList.add('hp:hidden');\n this.popupEl?.classList.remove('hp:flex');\n }\n\n private safePlay(): void {\n void this.ctx.videoEl.play().catch(() => {});\n }\n\n private showPopup(cfg: Required<MarkerPopupOptions>): void {\n const el = this.ensurePopup();\n const textEl = el.querySelector('div.hp\\\\:text-sm.hp\\\\:mb-2:not(.hp\\\\:hidden)') as HTMLElement | null;\n const mediaWrap = el.querySelector('div.hp\\\\:mb-2') as HTMLElement | null;\n const img = el.querySelector('img') as HTMLImageElement | null;\n const linkEl = el.querySelector('a') as HTMLAnchorElement | null;\n const continueBtn = el.querySelectorAll('button')[1] as HTMLButtonElement | null;\n const closeBtn = el.querySelector('button') as HTMLButtonElement | null;\n\n if (closeBtn) closeBtn.style.display = cfg.showClose === false ? 'none' : '';\n if (textEl) textEl.textContent = cfg.text || '';\n\n const isRich = cfg.mode === 'rich';\n if (mediaWrap && img) {\n if (isRich && cfg.imageUrl) {\n mediaWrap.classList.remove('hp:hidden');\n img.src = cfg.imageUrl;\n img.alt = cfg.imageAlt || '';\n } else {\n mediaWrap.classList.add('hp:hidden');\n img.removeAttribute('src');\n }\n }\n\n if (linkEl) {\n if (isRich && cfg.linkUrl) {\n linkEl.classList.remove('hp:hidden');\n linkEl.href = cfg.linkUrl;\n linkEl.textContent = cfg.linkText || this.ctx.i18n.markerLearnMore;\n } else {\n linkEl.classList.add('hp:hidden');\n linkEl.removeAttribute('href');\n }\n }\n\n if (continueBtn) {\n if (cfg.showContinue) {\n continueBtn.classList.remove('hp:hidden');\n continueBtn.textContent = cfg.continueText || this.ctx.i18n.markerContinue;\n } else {\n continueBtn.classList.add('hp:hidden');\n }\n }\n\n const newClose = closeBtn?.cloneNode(true) as HTMLButtonElement | undefined;\n if (closeBtn && newClose) closeBtn.replaceWith(newClose);\n const newCont = continueBtn?.cloneNode(true) as HTMLButtonElement | undefined;\n if (continueBtn && newCont) continueBtn.replaceWith(newCont);\n\n newClose?.addEventListener('click', () => {\n this.hidePopup();\n if (cfg.autoplayOnClose) this.safePlay();\n });\n newCont?.addEventListener('click', () => {\n this.hidePopup();\n if (cfg.autoplayOnContinue) this.safePlay();\n });\n\n el.classList.remove('hp:hidden');\n el.classList.add('hp:flex');\n }\n}\n","import { MarkProgress, type MarkersPlayerContext } from './MarkProgress';\nimport type { MarkersOptions } from '../../types';\n\nexport function installMarkers(ctx: MarkersPlayerContext, options: MarkersOptions): MarkProgress {\n return new MarkProgress(ctx, options);\n}\n\nexport { MarkProgress };\nexport type { MarkersPlayerContext };\n"],"names":["POPUP_DEFAULTS","PING_DEFAULTS","MarkProgress","ctx","options","__publicField","duration","m","el","now","key","last","popupCfg","idx","a","b","closeBtn","_a","url","baseUrl","w","fn","err","overlay","panel","createIcon","mediaWrap","img","textEl","linkEl","continueBtn","_b","cfg","isRich","newClose","newCont","installMarkers"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DN077Bbe.js","sources":["../../src/plugins/markers/MarkProgress.ts","../../src/plugins/markers/index.ts"],"sourcesContent":["import { createIcon } from '../../icons/Icon';\nimport type { MarkerItem, MarkerPingOptions, MarkerPopupOptions, MarkersOptions } from '../../types';\nimport type { HopeLocaleStrings } from '../../i18n/types';\n\nexport interface MarkersPlayerContext {\n videoEl: HTMLVideoElement;\n /** Popup overlay'in ekleneceği katman (shadow root içinde, video üzerinde). */\n overlayContainerEl: HTMLElement;\n /** Seek bar üzerinde işaretçi çizmek için — SeekBar.addMarker ile aynı imza. */\n addSeekBarMarker(ratio: number, extraClass?: string): HTMLDivElement;\n i18n: HopeLocaleStrings;\n}\n\ninterface NormalizedMarker extends Required<Pick<MarkerItem, 'time'>> {\n idx: number;\n color?: string;\n label: string;\n pauseOnHit: boolean;\n once: boolean;\n tolerance: number;\n cooldownMs: number;\n action?: MarkerItem['action'];\n popup: MarkerPopupOptions | null;\n pingUrl: string;\n el?: HTMLElement;\n}\n\nconst POPUP_DEFAULTS: Required<MarkerPopupOptions> = {\n enabled: false,\n mode: 'rich',\n text: '',\n imageUrl: '',\n imageAlt: '',\n linkUrl: '',\n // Boş bırakılıyor (sentinel) — gerçek varsayılan metin showPopup()'ta\n // `ctx.i18n.markerLearnMore`/`markerContinue`'dan okunuyor, dil\n // değişimini takip edebilsin diye burada SABİT bir dilde gömülü değil.\n linkText: '',\n continueText: '',\n showContinue: true,\n showClose: true,\n autoplayOnClose: true,\n autoplayOnContinue: true,\n};\n\nconst PING_DEFAULTS: Required<MarkerPingOptions> = { enabled: false, url: '', method: 'GET', includeMarker: false };\n\n/** Zaman tabanlı seek bar işaretçileri — seek/jump koruması, once/cooldown, popup/ping/aksiyon. */\nexport class MarkProgress {\n private readonly markers: NormalizedMarker[];\n private readonly popupGlobal: Required<MarkerPopupOptions>;\n private readonly pingGlobal: Required<MarkerPingOptions>;\n private readonly triggerOnSeek: boolean;\n private readonly seekJumpThresholdSec: number;\n private readonly seekIgnoreWindowMs: number;\n\n private hitSet = new Set<string>();\n private lastHitAt = new Map<string, number>();\n private lastTime = 0;\n private isSeeking = false;\n private ignoreUntilTs = 0;\n private popupEl: HTMLElement | null = null;\n\n constructor(\n private readonly ctx: MarkersPlayerContext,\n options: MarkersOptions\n ) {\n this.triggerOnSeek = options.triggerOnSeek === true;\n this.seekJumpThresholdSec = options.seekJumpThresholdSec ?? 1;\n this.seekIgnoreWindowMs = options.seekIgnoreWindowMs ?? 250;\n this.popupGlobal = { ...POPUP_DEFAULTS, ...options.popup };\n this.pingGlobal = { ...PING_DEFAULTS, ...options.ping };\n\n this.markers = (options.items || [])\n .map((m, idx) => this.normalize(m, idx))\n .filter((m) => Number.isFinite(m.time) && m.time >= 0)\n .sort((a, b) => a.time - b.time);\n\n ctx.videoEl.addEventListener('loadedmetadata', this.onLoadedMetadata);\n ctx.videoEl.addEventListener('timeupdate', this.onTimeUpdate);\n ctx.videoEl.addEventListener('seeking', this.onSeeking);\n ctx.videoEl.addEventListener('seeked', this.onSeeked);\n\n if (ctx.videoEl.readyState >= 1) this.onLoadedMetadata();\n }\n\n /** Popup lazy oluşturulup yeniden kullanılıyor (`ensurePopup`) — zaten açıksa close butonunun statik etiketini tazeler. */\n refreshLabels(): void {\n const closeBtn = this.popupEl?.querySelector('button') as HTMLButtonElement | null;\n if (closeBtn) {\n closeBtn.setAttribute('aria-label', this.ctx.i18n.close);\n }\n }\n\n destroy(): void {\n this.ctx.videoEl.removeEventListener('loadedmetadata', this.onLoadedMetadata);\n this.ctx.videoEl.removeEventListener('timeupdate', this.onTimeUpdate);\n this.ctx.videoEl.removeEventListener('seeking', this.onSeeking);\n this.ctx.videoEl.removeEventListener('seeked', this.onSeeked);\n this.markers.forEach((m) => m.el?.remove());\n this.popupEl?.remove();\n this.popupEl = null;\n this.hitSet.clear();\n this.lastHitAt.clear();\n }\n\n private normalize(m: MarkerItem, idx: number): NormalizedMarker {\n return {\n idx,\n time: Number(m.time),\n color: m.color,\n label: m.label ?? '',\n pauseOnHit: m.pauseOnHit ?? true,\n once: m.once ?? true,\n tolerance: m.tolerance ?? 0.25,\n cooldownMs: m.cooldownMs ?? 600,\n action: m.action,\n popup: m.popup ?? null,\n pingUrl: m.pingUrl ?? '',\n };\n }\n\n private readonly onLoadedMetadata = (): void => {\n const duration = this.ctx.videoEl.duration;\n if (!Number.isFinite(duration) || duration <= 0) return;\n\n this.markers.forEach((m) => {\n if (m.el) return;\n const el = this.ctx.addSeekBarMarker(m.time / duration);\n if (m.color) el.style.backgroundColor = m.color;\n if (m.label) el.title = m.label;\n m.el = el;\n });\n\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n };\n\n private readonly onSeeking = (): void => {\n this.isSeeking = true;\n this.ignoreUntilTs = Date.now() + this.seekIgnoreWindowMs;\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n };\n\n private readonly onSeeked = (): void => {\n this.lastTime = this.ctx.videoEl.currentTime || 0;\n this.isSeeking = false;\n this.ignoreUntilTs = Date.now() + this.seekIgnoreWindowMs;\n };\n\n private readonly onTimeUpdate = (): void => {\n const now = Date.now();\n if (now < this.ignoreUntilTs) return;\n if (this.isSeeking) return;\n\n const t = this.ctx.videoEl.currentTime;\n if (!Number.isFinite(t)) return;\n\n const jumped = Math.abs(t - this.lastTime) >= this.seekJumpThresholdSec;\n if (jumped && !this.triggerOnSeek) {\n this.lastTime = t;\n return;\n }\n\n for (const m of this.markers) {\n const key = `${m.time}-${m.idx}`;\n if (m.once && this.hitSet.has(key)) continue;\n if (!m.once) {\n const last = this.lastHitAt.get(key) || 0;\n if (now - last < m.cooldownMs) continue;\n }\n\n const crossed = this.lastTime < m.time && t >= m.time - m.tolerance;\n if (!crossed) continue;\n\n if (m.once) this.hitSet.add(key);\n else this.lastHitAt.set(key, now);\n\n if (m.pauseOnHit) this.ctx.videoEl.pause();\n\n this.firePing(m);\n this.fireAction(m);\n\n const popupCfg = m.popup ? { ...this.popupGlobal, ...m.popup } : this.popupGlobal;\n if (popupCfg.enabled) {\n this.ctx.videoEl.pause();\n this.showPopup(popupCfg);\n }\n break;\n }\n\n this.lastTime = t;\n };\n\n private resolveUrlTemplate(url: string, m: NormalizedMarker): string {\n if (!url) return '';\n return url.replaceAll('{time}', encodeURIComponent(String(m.time))).replaceAll('{label}', encodeURIComponent(m.label));\n }\n\n private firePing(m: NormalizedMarker): void {\n const baseUrl = m.pingUrl || this.pingGlobal.url;\n const url = this.resolveUrlTemplate(baseUrl, m);\n if (!this.pingGlobal.enabled || !url) return;\n\n try {\n if (this.pingGlobal.method === 'POST') {\n fetch(url, {\n method: 'POST',\n keepalive: true,\n headers: this.pingGlobal.includeMarker ? { 'Content-Type': 'application/json' } : undefined,\n body: this.pingGlobal.includeMarker ? JSON.stringify({ time: m.time, label: m.label, ts: Date.now() }) : undefined,\n }).catch(() => {});\n } else {\n fetch(url, { method: 'GET', keepalive: true }).catch(() => {});\n }\n } catch {\n /* no-op */\n }\n }\n\n private fireAction(m: NormalizedMarker): void {\n const a = m.action;\n if (!a) return;\n\n if (a.type === 'log') {\n console.log('[HopePlayer markers]', a.name || 'marker_hit', { time: m.time, label: m.label }, a.payload);\n return;\n }\n\n if (a.type === 'datalayer') {\n try {\n const w = window as unknown as { dataLayer?: unknown[] };\n w.dataLayer = w.dataLayer || [];\n w.dataLayer.push({\n event: 'HopePlayerVideoEvents',\n trigger: a.name || 'marker_hit',\n marker: { time: m.time, label: m.label, color: m.color },\n payload: a.payload ?? {},\n ts: Date.now(),\n });\n } catch {\n /* no-op */\n }\n return;\n }\n\n if (a.type === 'custom') {\n const fn = a.name ? (window as unknown as Record<string, unknown>)[a.name] : null;\n if (typeof fn === 'function') {\n try {\n (fn as (ctx: unknown) => void)({ marker: m, payload: a.payload });\n } catch (err) {\n console.warn('[HopePlayer markers] custom action fonksiyonu hata verdi:', err);\n }\n } else {\n console.warn('[HopePlayer markers] custom action fonksiyonu bulunamadı: window.' + a.name);\n }\n }\n }\n\n private ensurePopup(): HTMLElement {\n if (this.popupEl) return this.popupEl;\n\n const overlay = document.createElement('div');\n overlay.setAttribute('part', 'marker-popup');\n overlay.className = 'hp:hidden hp:absolute hp:inset-0 hp:z-30 hp:items-center hp:justify-center hp:bg-black/70';\n\n const panel = document.createElement('div');\n panel.className = 'hp:relative hp:max-w-[85%] hp:bg-surface-elev hp:text-on-surface hp:rounded-lg hp:p-4 hp:text-center hp:shadow-xl';\n panel.setAttribute('role', 'dialog');\n panel.setAttribute('aria-modal', 'true');\n\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', this.ctx.i18n.close);\n closeBtn.className = 'hp:absolute hp:top-1 hp:right-1 hp:w-6 hp:h-6 hp:flex hp:items-center hp:justify-center hp:bg-transparent hp:border-0 hp:cursor-pointer hp:text-on-surface';\n closeBtn.appendChild(createIcon('close', 'hp:text-sm'));\n\n const mediaWrap = document.createElement('div');\n mediaWrap.className = 'hp:hidden hp:mb-2';\n const img = document.createElement('img');\n img.className = 'hp:max-w-full hp:rounded';\n mediaWrap.appendChild(img);\n\n const textEl = document.createElement('div');\n textEl.className = 'hp:text-sm hp:mb-2';\n\n const linkEl = document.createElement('a');\n linkEl.target = '_blank';\n linkEl.rel = 'noopener noreferrer';\n linkEl.className = 'hp:hidden hp:text-accent hp:underline hp:text-sm hp:block hp:mb-2';\n\n const continueBtn = document.createElement('button');\n continueBtn.type = 'button';\n continueBtn.className = 'hp:hidden hp:px-4 hp:py-1.5 hp:rounded hp:bg-accent hp:text-on-accent hp:border-0 hp:cursor-pointer hp:text-sm';\n\n panel.append(closeBtn, mediaWrap, textEl, linkEl, continueBtn);\n overlay.appendChild(panel);\n this.ctx.overlayContainerEl.appendChild(overlay);\n this.popupEl = overlay;\n return overlay;\n }\n\n private hidePopup(): void {\n this.popupEl?.classList.add('hp:hidden');\n this.popupEl?.classList.remove('hp:flex');\n }\n\n private safePlay(): void {\n void this.ctx.videoEl.play().catch(() => {});\n }\n\n private showPopup(cfg: Required<MarkerPopupOptions>): void {\n const el = this.ensurePopup();\n const textEl = el.querySelector('div.hp\\\\:text-sm.hp\\\\:mb-2:not(.hp\\\\:hidden)') as HTMLElement | null;\n const mediaWrap = el.querySelector('div.hp\\\\:mb-2') as HTMLElement | null;\n const img = el.querySelector('img') as HTMLImageElement | null;\n const linkEl = el.querySelector('a') as HTMLAnchorElement | null;\n const continueBtn = el.querySelectorAll('button')[1] as HTMLButtonElement | null;\n const closeBtn = el.querySelector('button') as HTMLButtonElement | null;\n\n if (closeBtn) closeBtn.style.display = cfg.showClose === false ? 'none' : '';\n if (textEl) textEl.textContent = cfg.text || '';\n\n const isRich = cfg.mode === 'rich';\n if (mediaWrap && img) {\n if (isRich && cfg.imageUrl) {\n mediaWrap.classList.remove('hp:hidden');\n img.src = cfg.imageUrl;\n img.alt = cfg.imageAlt || '';\n } else {\n mediaWrap.classList.add('hp:hidden');\n img.removeAttribute('src');\n }\n }\n\n if (linkEl) {\n if (isRich && cfg.linkUrl) {\n linkEl.classList.remove('hp:hidden');\n linkEl.href = cfg.linkUrl;\n linkEl.textContent = cfg.linkText || this.ctx.i18n.markerLearnMore;\n } else {\n linkEl.classList.add('hp:hidden');\n linkEl.removeAttribute('href');\n }\n }\n\n if (continueBtn) {\n if (cfg.showContinue) {\n continueBtn.classList.remove('hp:hidden');\n continueBtn.textContent = cfg.continueText || this.ctx.i18n.markerContinue;\n } else {\n continueBtn.classList.add('hp:hidden');\n }\n }\n\n const newClose = closeBtn?.cloneNode(true) as HTMLButtonElement | undefined;\n if (closeBtn && newClose) closeBtn.replaceWith(newClose);\n const newCont = continueBtn?.cloneNode(true) as HTMLButtonElement | undefined;\n if (continueBtn && newCont) continueBtn.replaceWith(newCont);\n\n newClose?.addEventListener('click', () => {\n this.hidePopup();\n if (cfg.autoplayOnClose) this.safePlay();\n });\n newCont?.addEventListener('click', () => {\n this.hidePopup();\n if (cfg.autoplayOnContinue) this.safePlay();\n });\n\n el.classList.remove('hp:hidden');\n el.classList.add('hp:flex');\n }\n}\n","import { MarkProgress, type MarkersPlayerContext } from './MarkProgress';\nimport type { MarkersOptions } from '../../types';\n\nexport function installMarkers(ctx: MarkersPlayerContext, options: MarkersOptions): MarkProgress {\n return new MarkProgress(ctx, options);\n}\n\nexport { MarkProgress };\nexport type { MarkersPlayerContext };\n"],"names":["POPUP_DEFAULTS","PING_DEFAULTS","MarkProgress","ctx","options","__publicField","duration","m","el","now","key","last","popupCfg","idx","a","b","closeBtn","_a","url","baseUrl","w","fn","err","overlay","panel","createIcon","mediaWrap","img","textEl","linkEl","continueBtn","_b","cfg","isRich","newClose","newCont","installMarkers"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var m = Object.defineProperty;
|
|
2
2
|
var v = (r, t, e) => t in r ? m(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
3
|
var n = (r, t, e) => v(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
-
import { f as E } from "./index-
|
|
4
|
+
import { f as E } from "./index-Bqs23yk7.js";
|
|
5
5
|
class u {
|
|
6
6
|
constructor(t, e) {
|
|
7
7
|
n(this, "chapters", []);
|
|
@@ -60,4 +60,4 @@ export {
|
|
|
60
60
|
u as Segments,
|
|
61
61
|
g as installSegments
|
|
62
62
|
};
|
|
63
|
-
//# sourceMappingURL=index-
|
|
63
|
+
//# sourceMappingURL=index-i8eKCN8V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-CmbuiyxF.js","sources":["../../src/plugins/segments/Segments.ts","../../src/plugins/segments/index.ts"],"sourcesContent":["import type { SegmentItem } from '../../types';\nimport type { SeekBarChapterRange } from '../../controls/SeekBar';\nimport { formatTime } from '../../utils/format-time';\n\nexport interface SegmentsPlayerContext {\n videoEl: HTMLVideoElement;\n /** Chapter modunu destekleyen seek bar — parçalara bölme/zaman eşlemesi onda. */\n seekBar: {\n readonly el: HTMLElement;\n setChapters(ranges: SeekBarChapterRange[]): HTMLElement[];\n suppressHoverTime(key: string, suppressed: boolean): void;\n clearChapters(): void;\n ratioAtClientX(clientX: number): number;\n };\n}\n\ninterface Chapter {\n start: number;\n end: number;\n text?: string;\n color?: string;\n}\n\n/**\n * YouTube tarzı bölümler: seek bar parçalara bölünür (SeekBar.setChapters), hover'da bölüm adı + zaman.\n * Zaman çizgisi her zaman tam kaplanır (boşluklar adsız bölüm, son bölüm sona uzar).\n */\nexport class Segments {\n private chapters: Chapter[] = [];\n private chapterEls: HTMLElement[] = [];\n private hoverIndex = -1;\n private readonly tooltipEl: HTMLDivElement;\n private readonly titleEl: HTMLDivElement;\n private readonly timeEl: HTMLDivElement;\n\n constructor(\n private readonly ctx: SegmentsPlayerContext,\n private readonly segments: SegmentItem[]\n ) {\n this.tooltipEl = document.createElement('div');\n this.tooltipEl.className = 'hp-chapter-tooltip';\n this.tooltipEl.setAttribute('part', 'segment-tooltip');\n this.titleEl = document.createElement('div');\n this.titleEl.className = 'hp-chapter-tooltip-title';\n this.timeEl = document.createElement('div');\n this.timeEl.className = 'hp-chapter-tooltip-time';\n this.tooltipEl.append(this.titleEl, this.timeEl);\n ctx.seekBar.el.appendChild(this.tooltipEl);\n // Popup zamanı zaten gösteriyor — seek bar'ın varsayılan hover zamanı ikinci kez yazmasın.\n ctx.seekBar.suppressHoverTime('segments', true);\n\n ctx.seekBar.el.addEventListener('pointermove', this.onPointerMove);\n ctx.seekBar.el.addEventListener('pointerleave', this.onPointerLeave);\n ctx.videoEl.addEventListener('loadedmetadata', this.onLoadedMetadata);\n if (ctx.videoEl.readyState >= 1) this.onLoadedMetadata();\n }\n\n destroy(): void {\n this.ctx.videoEl.removeEventListener('loadedmetadata', this.onLoadedMetadata);\n this.ctx.seekBar.el.removeEventListener('pointermove', this.onPointerMove);\n this.ctx.seekBar.el.removeEventListener('pointerleave', this.onPointerLeave);\n this.ctx.seekBar.clearChapters();\n this.ctx.seekBar.suppressHoverTime('segments', false);\n this.tooltipEl.remove();\n this.chapterEls = [];\n this.chapters = [];\n }\n\n private readonly onLoadedMetadata = (): void => {\n const duration = this.ctx.videoEl.duration;\n if (!Number.isFinite(duration) || duration <= 0) return;\n\n this.chapters = buildChapters(this.segments, duration);\n this.chapterEls = this.ctx.seekBar.setChapters(\n this.chapters.map((c) => ({ start: c.start / duration, end: c.end / duration, color: c.color }))\n );\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n const duration = this.ctx.videoEl.duration;\n if (!this.chapters.length || !Number.isFinite(duration) || duration <= 0) return;\n\n const time = this.ctx.seekBar.ratioAtClientX(e.clientX) * duration;\n let index = this.chapters.findIndex((c) => time < c.end);\n if (index === -1) index = this.chapters.length - 1;\n this.setHover(index);\n\n const chapter = this.chapters[index]!;\n this.titleEl.textContent = chapter.text ?? '';\n this.titleEl.style.display = chapter.text ? '' : 'none';\n this.timeEl.textContent = formatTime(time);\n\n // sprite-thumbnails önizlemesi de seek bar'ın hemen üstünde açılıyor —\n // görünürse popup onun ÜSTÜNE çıkıyor (üst üste binmesin).\n const sprite = this.ctx.seekBar.el.querySelector<HTMLElement>('[part=\"sprite-tooltip\"]');\n const spriteH = sprite && sprite.hasAttribute('data-visible') && sprite.offsetHeight > 0 ? sprite.offsetHeight + 8 : 0;\n this.tooltipEl.style.bottom = spriteH ? `calc(100% + 6px + ${spriteH}px)` : '';\n // Sprite önizlemesi zamanı zaten kendi altında gösteriyor — aynı zaman iki kez yazılmasın.\n this.timeEl.style.display = spriteH ? 'none' : '';\n\n // Popup imleci ortalar ama seek bar kenarlarından taşmaz.\n const barRect = this.ctx.seekBar.el.getBoundingClientRect();\n this.tooltipEl.setAttribute('data-visible', '');\n const tipWidth = this.tooltipEl.offsetWidth;\n const x = Math.min(Math.max(e.clientX - barRect.left - tipWidth / 2, 0), Math.max(barRect.width - tipWidth, 0));\n this.tooltipEl.style.transform = `translateX(${x}px)`;\n };\n\n private readonly onPointerLeave = (): void => {\n this.setHover(-1);\n this.tooltipEl.removeAttribute('data-visible');\n };\n\n private setHover(index: number): void {\n if (index === this.hoverIndex) return;\n this.chapterEls[this.hoverIndex]?.removeAttribute('data-hover');\n this.hoverIndex = index;\n this.chapterEls[index]?.setAttribute('data-hover', '');\n }\n}\n\n/** Segmentleri sıralar, boşlukları adsız bölümle doldurur, son bölümü video sonuna uzatır — sonuç 0→duration'ı kesintisiz kaplar. */\nfunction buildChapters(segments: SegmentItem[], duration: number): Chapter[] {\n const sorted = segments\n .filter((s) => Number.isFinite(s.start) && s.start < duration)\n .slice()\n .sort((a, b) => a.start - b.start);\n\n const chapters: Chapter[] = [];\n let cursor = 0;\n for (const seg of sorted) {\n const start = Math.max(seg.start, cursor);\n const end = Math.min(Number.isFinite(seg.end) ? seg.end : duration, duration);\n if (end <= start) continue;\n if (start > cursor) chapters.push({ start: cursor, end: start });\n chapters.push({ start, end, text: seg.text, color: seg.color });\n cursor = end;\n }\n if (!chapters.length) return [];\n if (cursor < duration) chapters[chapters.length - 1]!.end = duration;\n return chapters;\n}\n","import { Segments, type SegmentsPlayerContext } from './Segments';\nimport type { SegmentItem } from '../../types';\n\nexport function installSegments(ctx: SegmentsPlayerContext, items: SegmentItem[]): Segments {\n return new Segments(ctx, items);\n}\n\nexport { Segments };\nexport type { SegmentsPlayerContext };\n"],"names":["Segments","ctx","segments","__publicField","duration","buildChapters","c","e","time","index","chapter","formatTime","sprite","spriteH","barRect","tipWidth","x","_a","_b","sorted","a","b","chapters","cursor","seg","start","end","installSegments","items"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-i8eKCN8V.js","sources":["../../src/plugins/segments/Segments.ts","../../src/plugins/segments/index.ts"],"sourcesContent":["import type { SegmentItem } from '../../types';\nimport type { SeekBarChapterRange } from '../../controls/SeekBar';\nimport { formatTime } from '../../utils/format-time';\n\nexport interface SegmentsPlayerContext {\n videoEl: HTMLVideoElement;\n /** Chapter modunu destekleyen seek bar — parçalara bölme/zaman eşlemesi onda. */\n seekBar: {\n readonly el: HTMLElement;\n setChapters(ranges: SeekBarChapterRange[]): HTMLElement[];\n suppressHoverTime(key: string, suppressed: boolean): void;\n clearChapters(): void;\n ratioAtClientX(clientX: number): number;\n };\n}\n\ninterface Chapter {\n start: number;\n end: number;\n text?: string;\n color?: string;\n}\n\n/**\n * YouTube tarzı bölümler: seek bar parçalara bölünür (SeekBar.setChapters), hover'da bölüm adı + zaman.\n * Zaman çizgisi her zaman tam kaplanır (boşluklar adsız bölüm, son bölüm sona uzar).\n */\nexport class Segments {\n private chapters: Chapter[] = [];\n private chapterEls: HTMLElement[] = [];\n private hoverIndex = -1;\n private readonly tooltipEl: HTMLDivElement;\n private readonly titleEl: HTMLDivElement;\n private readonly timeEl: HTMLDivElement;\n\n constructor(\n private readonly ctx: SegmentsPlayerContext,\n private readonly segments: SegmentItem[]\n ) {\n this.tooltipEl = document.createElement('div');\n this.tooltipEl.className = 'hp-chapter-tooltip';\n this.tooltipEl.setAttribute('part', 'segment-tooltip');\n this.titleEl = document.createElement('div');\n this.titleEl.className = 'hp-chapter-tooltip-title';\n this.timeEl = document.createElement('div');\n this.timeEl.className = 'hp-chapter-tooltip-time';\n this.tooltipEl.append(this.titleEl, this.timeEl);\n ctx.seekBar.el.appendChild(this.tooltipEl);\n // Popup zamanı zaten gösteriyor — seek bar'ın varsayılan hover zamanı ikinci kez yazmasın.\n ctx.seekBar.suppressHoverTime('segments', true);\n\n ctx.seekBar.el.addEventListener('pointermove', this.onPointerMove);\n ctx.seekBar.el.addEventListener('pointerleave', this.onPointerLeave);\n ctx.videoEl.addEventListener('loadedmetadata', this.onLoadedMetadata);\n if (ctx.videoEl.readyState >= 1) this.onLoadedMetadata();\n }\n\n destroy(): void {\n this.ctx.videoEl.removeEventListener('loadedmetadata', this.onLoadedMetadata);\n this.ctx.seekBar.el.removeEventListener('pointermove', this.onPointerMove);\n this.ctx.seekBar.el.removeEventListener('pointerleave', this.onPointerLeave);\n this.ctx.seekBar.clearChapters();\n this.ctx.seekBar.suppressHoverTime('segments', false);\n this.tooltipEl.remove();\n this.chapterEls = [];\n this.chapters = [];\n }\n\n private readonly onLoadedMetadata = (): void => {\n const duration = this.ctx.videoEl.duration;\n if (!Number.isFinite(duration) || duration <= 0) return;\n\n this.chapters = buildChapters(this.segments, duration);\n this.chapterEls = this.ctx.seekBar.setChapters(\n this.chapters.map((c) => ({ start: c.start / duration, end: c.end / duration, color: c.color }))\n );\n };\n\n private readonly onPointerMove = (e: PointerEvent): void => {\n const duration = this.ctx.videoEl.duration;\n if (!this.chapters.length || !Number.isFinite(duration) || duration <= 0) return;\n\n const time = this.ctx.seekBar.ratioAtClientX(e.clientX) * duration;\n let index = this.chapters.findIndex((c) => time < c.end);\n if (index === -1) index = this.chapters.length - 1;\n this.setHover(index);\n\n const chapter = this.chapters[index]!;\n this.titleEl.textContent = chapter.text ?? '';\n this.titleEl.style.display = chapter.text ? '' : 'none';\n this.timeEl.textContent = formatTime(time);\n\n // sprite-thumbnails önizlemesi de seek bar'ın hemen üstünde açılıyor —\n // görünürse popup onun ÜSTÜNE çıkıyor (üst üste binmesin).\n const sprite = this.ctx.seekBar.el.querySelector<HTMLElement>('[part=\"sprite-tooltip\"]');\n const spriteH = sprite && sprite.hasAttribute('data-visible') && sprite.offsetHeight > 0 ? sprite.offsetHeight + 8 : 0;\n this.tooltipEl.style.bottom = spriteH ? `calc(100% + 6px + ${spriteH}px)` : '';\n // Sprite önizlemesi zamanı zaten kendi altında gösteriyor — aynı zaman iki kez yazılmasın.\n this.timeEl.style.display = spriteH ? 'none' : '';\n\n // Popup imleci ortalar ama seek bar kenarlarından taşmaz.\n const barRect = this.ctx.seekBar.el.getBoundingClientRect();\n this.tooltipEl.setAttribute('data-visible', '');\n const tipWidth = this.tooltipEl.offsetWidth;\n const x = Math.min(Math.max(e.clientX - barRect.left - tipWidth / 2, 0), Math.max(barRect.width - tipWidth, 0));\n this.tooltipEl.style.transform = `translateX(${x}px)`;\n };\n\n private readonly onPointerLeave = (): void => {\n this.setHover(-1);\n this.tooltipEl.removeAttribute('data-visible');\n };\n\n private setHover(index: number): void {\n if (index === this.hoverIndex) return;\n this.chapterEls[this.hoverIndex]?.removeAttribute('data-hover');\n this.hoverIndex = index;\n this.chapterEls[index]?.setAttribute('data-hover', '');\n }\n}\n\n/** Segmentleri sıralar, boşlukları adsız bölümle doldurur, son bölümü video sonuna uzatır — sonuç 0→duration'ı kesintisiz kaplar. */\nfunction buildChapters(segments: SegmentItem[], duration: number): Chapter[] {\n const sorted = segments\n .filter((s) => Number.isFinite(s.start) && s.start < duration)\n .slice()\n .sort((a, b) => a.start - b.start);\n\n const chapters: Chapter[] = [];\n let cursor = 0;\n for (const seg of sorted) {\n const start = Math.max(seg.start, cursor);\n const end = Math.min(Number.isFinite(seg.end) ? seg.end : duration, duration);\n if (end <= start) continue;\n if (start > cursor) chapters.push({ start: cursor, end: start });\n chapters.push({ start, end, text: seg.text, color: seg.color });\n cursor = end;\n }\n if (!chapters.length) return [];\n if (cursor < duration) chapters[chapters.length - 1]!.end = duration;\n return chapters;\n}\n","import { Segments, type SegmentsPlayerContext } from './Segments';\nimport type { SegmentItem } from '../../types';\n\nexport function installSegments(ctx: SegmentsPlayerContext, items: SegmentItem[]): Segments {\n return new Segments(ctx, items);\n}\n\nexport { Segments };\nexport type { SegmentsPlayerContext };\n"],"names":["Segments","ctx","segments","__publicField","duration","buildChapters","c","e","time","index","chapter","formatTime","sprite","spriteH","barRect","tipWidth","x","_a","_b","sorted","a","b","chapters","cursor","seg","start","end","installSegments","items"],"mappings":"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"}
|
package/dist/hope-player.js
CHANGED