var __accessCheck = (obj, member, msg) => { if (!member.has(obj)) throw TypeError("Cannot " + msg); }; var __privateGet = (obj, member, getter) => { __accessCheck(obj, member, "read from private field"); return getter ? getter.call(obj) : member.get(obj); }; var __privateAdd = (obj, member, value) => { if (member.has(obj)) throw TypeError("Cannot add the same private member more than once"); member instanceof WeakSet ? member.add(obj) : member.set(obj, value); }; var __privateSet = (obj, member, value, setter) => { __accessCheck(obj, member, "write to private field"); setter ? setter.call(obj, value) : member.set(obj, value); return value; }; var __privateMethod = (obj, member, method) => { __accessCheck(obj, member, "access private method"); return method; }; var _rootNode, _animation, _boxes, _previewTime, _previewBox, _currentBox, _boxPaddingLeft, _boxPaddingRight, _mediaChaptersCues, _toggleRangeAnimation, toggleRangeAnimation_fn, _shouldRangeAnimate, shouldRangeAnimate_fn, _updateRange, _getElementRects, getElementRects_fn, _getBoxPosition, getBoxPosition_fn, _getBoxShiftPosition, getBoxShiftPosition_fn, _handlePointerMove, handlePointerMove_fn, _previewRequest, previewRequest_fn, _seekRequest, seekRequest_fn; import { globalThis, document } from "./utils/server-safe-globals.js"; import { MediaChromeRange } from "./media-chrome-range.js"; import "./media-preview-thumbnail.js"; import "./media-preview-time-display.js"; import "./media-preview-chapter-display.js"; import { MediaUIEvents, MediaUIAttributes } from "./constants.js"; import { nouns } from "./labels/labels.js"; import { isValidNumber } from "./utils/utils.js"; import { formatAsTimePhrase } from "./utils/time.js"; import { isElementVisible } from "./utils/element-utils.js"; import { RangeAnimation } from "./utils/range-animation.js"; import { getOrInsertCSSRule, containsComposedNode, closestComposedNode, getBooleanAttr, setBooleanAttr, getNumericAttr, setNumericAttr, getStringAttr, setStringAttr } from "./utils/element-utils.js"; const DEFAULT_MISSING_TIME_PHRASE = "video not loaded, unknown time."; const updateAriaValueText = (el) => { const range = el.range; const currentTimePhrase = formatAsTimePhrase(+calcTimeFromRangeValue(el)); const totalTimePhrase = formatAsTimePhrase(+el.mediaSeekableEnd); const fullPhrase = !(currentTimePhrase && totalTimePhrase) ? DEFAULT_MISSING_TIME_PHRASE : `${currentTimePhrase} of ${totalTimePhrase}`; range.setAttribute("aria-valuetext", fullPhrase); }; const template = document.createElement("template"); template.innerHTML = /*html*/ `
${/* Example: add the current time w/ arrow to the playhead
*/ ""}
`; const calcRangeValueFromTime = (el, time = el.mediaCurrentTime) => { const startTime = Number.isFinite(el.mediaSeekableStart) ? el.mediaSeekableStart : 0; const endTime = Number.isFinite(el.mediaDuration) ? el.mediaDuration : el.mediaSeekableEnd; if (Number.isNaN(endTime)) return 0; const value = (time - startTime) / (endTime - startTime); return Math.max(0, Math.min(value, 1)); }; const calcTimeFromRangeValue = (el, value = el.range.valueAsNumber) => { const startTime = Number.isFinite(el.mediaSeekableStart) ? el.mediaSeekableStart : 0; const endTime = Number.isFinite(el.mediaDuration) ? el.mediaDuration : el.mediaSeekableEnd; if (Number.isNaN(endTime)) return 0; return value * (endTime - startTime) + startTime; }; class MediaTimeRange extends MediaChromeRange { constructor() { super(); __privateAdd(this, _toggleRangeAnimation); __privateAdd(this, _shouldRangeAnimate); __privateAdd(this, _getElementRects); /** * Get the position, max and min for the box in percentage. * It's important this is in percentage so when the player is resized * the box will move accordingly. */ __privateAdd(this, _getBoxPosition); __privateAdd(this, _getBoxShiftPosition); __privateAdd(this, _handlePointerMove); __privateAdd(this, _previewRequest); __privateAdd(this, _seekRequest); __privateAdd(this, _rootNode, void 0); __privateAdd(this, _animation, void 0); __privateAdd(this, _boxes, void 0); __privateAdd(this, _previewTime, void 0); __privateAdd(this, _previewBox, void 0); __privateAdd(this, _currentBox, void 0); __privateAdd(this, _boxPaddingLeft, void 0); __privateAdd(this, _boxPaddingRight, void 0); __privateAdd(this, _mediaChaptersCues, void 0); __privateAdd(this, _updateRange, (value) => { if (this.dragging) return; if (isValidNumber(value)) { this.range.valueAsNumber = value; } this.updateBar(); }); this.container.appendChild(template.content.cloneNode(true)); const track = this.shadowRoot.querySelector("#track"); track.insertAdjacentHTML("afterbegin", '
'); __privateSet(this, _boxes, this.shadowRoot.querySelectorAll('[part~="box"]')); __privateSet(this, _previewBox, this.shadowRoot.querySelector('[part~="preview-box"]')); __privateSet(this, _currentBox, this.shadowRoot.querySelector('[part~="current-box"]')); const computedStyle = getComputedStyle(this); __privateSet(this, _boxPaddingLeft, parseInt( computedStyle.getPropertyValue("--media-box-padding-left") )); __privateSet(this, _boxPaddingRight, parseInt( computedStyle.getPropertyValue("--media-box-padding-right") )); __privateSet(this, _animation, new RangeAnimation(this.range, __privateGet(this, _updateRange), 60)); } static get observedAttributes() { return [ ...super.observedAttributes, MediaUIAttributes.MEDIA_PAUSED, MediaUIAttributes.MEDIA_DURATION, MediaUIAttributes.MEDIA_SEEKABLE, MediaUIAttributes.MEDIA_CURRENT_TIME, MediaUIAttributes.MEDIA_PREVIEW_IMAGE, MediaUIAttributes.MEDIA_PREVIEW_TIME, MediaUIAttributes.MEDIA_PREVIEW_CHAPTER, MediaUIAttributes.MEDIA_BUFFERED, MediaUIAttributes.MEDIA_PLAYBACK_RATE, MediaUIAttributes.MEDIA_LOADING, MediaUIAttributes.MEDIA_ENDED ]; } connectedCallback() { var _a; super.connectedCallback(); this.range.setAttribute("aria-label", nouns.SEEK()); __privateMethod(this, _toggleRangeAnimation, toggleRangeAnimation_fn).call(this); __privateSet(this, _rootNode, this.getRootNode()); (_a = __privateGet(this, _rootNode)) == null ? void 0 : _a.addEventListener("transitionstart", this); } disconnectedCallback() { var _a; super.disconnectedCallback(); __privateMethod(this, _toggleRangeAnimation, toggleRangeAnimation_fn).call(this); (_a = __privateGet(this, _rootNode)) == null ? void 0 : _a.removeEventListener("transitionstart", this); __privateSet(this, _rootNode, null); } attributeChangedCallback(attrName, oldValue, newValue) { super.attributeChangedCallback(attrName, oldValue, newValue); if (oldValue == newValue) return; if (attrName === MediaUIAttributes.MEDIA_CURRENT_TIME || attrName === MediaUIAttributes.MEDIA_PAUSED || attrName === MediaUIAttributes.MEDIA_ENDED || attrName === MediaUIAttributes.MEDIA_LOADING || attrName === MediaUIAttributes.MEDIA_DURATION || attrName === MediaUIAttributes.MEDIA_SEEKABLE) { __privateGet(this, _animation).update({ start: calcRangeValueFromTime(this), duration: this.mediaSeekableEnd - this.mediaSeekableStart, playbackRate: this.mediaPlaybackRate }); __privateMethod(this, _toggleRangeAnimation, toggleRangeAnimation_fn).call(this); updateAriaValueText(this); } else if (attrName === MediaUIAttributes.MEDIA_BUFFERED) { this.updateBufferedBar(); } if (attrName === MediaUIAttributes.MEDIA_DURATION || attrName === MediaUIAttributes.MEDIA_SEEKABLE) { this.mediaChaptersCues = __privateGet(this, _mediaChaptersCues); this.updateBar(); } } get mediaChaptersCues() { return __privateGet(this, _mediaChaptersCues); } set mediaChaptersCues(value) { var _a; __privateSet(this, _mediaChaptersCues, value); this.updateSegments( (_a = __privateGet(this, _mediaChaptersCues)) == null ? void 0 : _a.map((c) => ({ start: calcRangeValueFromTime(this, c.startTime), end: calcRangeValueFromTime(this, c.endTime) })) ); } /** * Is the media paused */ get mediaPaused() { return getBooleanAttr(this, MediaUIAttributes.MEDIA_PAUSED); } set mediaPaused(value) { setBooleanAttr(this, MediaUIAttributes.MEDIA_PAUSED, value); } /** * Is the media loading */ get mediaLoading() { return getBooleanAttr(this, MediaUIAttributes.MEDIA_LOADING); } set mediaLoading(value) { setBooleanAttr(this, MediaUIAttributes.MEDIA_LOADING, value); } /** * */ get mediaDuration() { return getNumericAttr(this, MediaUIAttributes.MEDIA_DURATION); } set mediaDuration(value) { setNumericAttr(this, MediaUIAttributes.MEDIA_DURATION, value); } /** * */ get mediaCurrentTime() { return getNumericAttr(this, MediaUIAttributes.MEDIA_CURRENT_TIME); } set mediaCurrentTime(value) { setNumericAttr(this, MediaUIAttributes.MEDIA_CURRENT_TIME, value); } /** * */ get mediaPlaybackRate() { return getNumericAttr(this, MediaUIAttributes.MEDIA_PLAYBACK_RATE, 1); } set mediaPlaybackRate(value) { setNumericAttr(this, MediaUIAttributes.MEDIA_PLAYBACK_RATE, value); } /** * An array of ranges, each range being an array of two numbers. * e.g. [[1, 2], [3, 4]] */ get mediaBuffered() { const buffered = this.getAttribute(MediaUIAttributes.MEDIA_BUFFERED); if (!buffered) return []; return buffered.split(" ").map((timePair) => timePair.split(":").map((timeStr) => +timeStr)); } set mediaBuffered(list) { if (!list) { this.removeAttribute(MediaUIAttributes.MEDIA_BUFFERED); return; } const strVal = list.map((tuple) => tuple.join(":")).join(" "); this.setAttribute(MediaUIAttributes.MEDIA_BUFFERED, strVal); } /** * Range of values that can be seeked to * An array of two numbers [start, end] */ get mediaSeekable() { const seekable = this.getAttribute(MediaUIAttributes.MEDIA_SEEKABLE); if (!seekable) return void 0; return seekable.split(":").map((time) => +time); } set mediaSeekable(range) { if (range == null) { this.removeAttribute(MediaUIAttributes.MEDIA_SEEKABLE); return; } this.setAttribute(MediaUIAttributes.MEDIA_SEEKABLE, range.join(":")); } /** * */ get mediaSeekableEnd() { var _a; const [, end = this.mediaDuration] = (_a = this.mediaSeekable) != null ? _a : []; return end; } get mediaSeekableStart() { var _a; const [start = 0] = (_a = this.mediaSeekable) != null ? _a : []; return start; } /** * The url of the preview image */ get mediaPreviewImage() { return getStringAttr(this, MediaUIAttributes.MEDIA_PREVIEW_IMAGE); } set mediaPreviewImage(value) { setStringAttr(this, MediaUIAttributes.MEDIA_PREVIEW_IMAGE, value); } /** * */ get mediaPreviewTime() { return getNumericAttr(this, MediaUIAttributes.MEDIA_PREVIEW_TIME); } set mediaPreviewTime(value) { setNumericAttr(this, MediaUIAttributes.MEDIA_PREVIEW_TIME, value); } /** * */ get mediaEnded() { return getBooleanAttr(this, MediaUIAttributes.MEDIA_ENDED); } set mediaEnded(value) { setBooleanAttr(this, MediaUIAttributes.MEDIA_ENDED, value); } /* Add a buffered progress bar */ updateBar() { super.updateBar(); this.updateBufferedBar(); this.updateCurrentBox(); } updateBufferedBar() { var _a; const buffered = this.mediaBuffered; if (!buffered.length) { return; } let relativeBufferedEnd; if (!this.mediaEnded) { const currentTime = this.mediaCurrentTime; const [, bufferedEnd = this.mediaSeekableStart] = (_a = buffered.find( ([start, end]) => start <= currentTime && currentTime <= end )) != null ? _a : []; relativeBufferedEnd = calcRangeValueFromTime(this, bufferedEnd); } else { relativeBufferedEnd = 1; } const { style } = getOrInsertCSSRule(this.shadowRoot, "#buffered"); style.setProperty("width", `${relativeBufferedEnd * 100}%`); } updateCurrentBox() { const currentSlot = this.shadowRoot.querySelector( 'slot[name="current"]' ); if (!currentSlot.assignedElements().length) return; const currentRailRule = getOrInsertCSSRule( this.shadowRoot, "#current-rail" ); const currentBoxRule = getOrInsertCSSRule( this.shadowRoot, '[part~="current-box"]' ); const rects = __privateMethod(this, _getElementRects, getElementRects_fn).call(this, __privateGet(this, _currentBox)); const boxPos = __privateMethod(this, _getBoxPosition, getBoxPosition_fn).call(this, rects, this.range.valueAsNumber); const boxShift = __privateMethod(this, _getBoxShiftPosition, getBoxShiftPosition_fn).call(this, rects, this.range.valueAsNumber); currentRailRule.style.transform = `translateX(${boxPos})`; currentRailRule.style.setProperty("--_range-width", `${rects.range.width}`); currentBoxRule.style.setProperty("--_box-shift", `${boxShift}`); currentBoxRule.style.setProperty("--_box-width", `${rects.box.width}px`); currentBoxRule.style.setProperty("visibility", "initial"); } handleEvent(evt) { super.handleEvent(evt); switch (evt.type) { case "input": __privateMethod(this, _seekRequest, seekRequest_fn).call(this); break; case "pointermove": __privateMethod(this, _handlePointerMove, handlePointerMove_fn).call(this, evt); break; case "pointerup": case "pointerleave": __privateMethod(this, _previewRequest, previewRequest_fn).call(this, null); break; case "transitionstart": if (containsComposedNode(evt.target, this)) { setTimeout(() => __privateMethod(this, _toggleRangeAnimation, toggleRangeAnimation_fn).call(this), 0); } break; } } } _rootNode = new WeakMap(); _animation = new WeakMap(); _boxes = new WeakMap(); _previewTime = new WeakMap(); _previewBox = new WeakMap(); _currentBox = new WeakMap(); _boxPaddingLeft = new WeakMap(); _boxPaddingRight = new WeakMap(); _mediaChaptersCues = new WeakMap(); _toggleRangeAnimation = new WeakSet(); toggleRangeAnimation_fn = function() { if (__privateMethod(this, _shouldRangeAnimate, shouldRangeAnimate_fn).call(this)) { __privateGet(this, _animation).start(); } else { __privateGet(this, _animation).stop(); } }; _shouldRangeAnimate = new WeakSet(); shouldRangeAnimate_fn = function() { return this.isConnected && !this.mediaPaused && !this.mediaLoading && !this.mediaEnded && this.mediaSeekableEnd > 0 && isElementVisible(this); }; _updateRange = new WeakMap(); _getElementRects = new WeakSet(); getElementRects_fn = function(box) { var _a; const bounds = (_a = this.getAttribute("bounds") ? closestComposedNode(this, `#${this.getAttribute("bounds")}`) : this.parentElement) != null ? _a : this; const boundsRect = bounds.getBoundingClientRect(); const rangeRect = this.range.getBoundingClientRect(); const width = box.offsetWidth; const min = -(rangeRect.left - boundsRect.left - width / 2); const max = boundsRect.right - rangeRect.left - width / 2; return { box: { width, min, max }, bounds: boundsRect, range: rangeRect }; }; _getBoxPosition = new WeakSet(); getBoxPosition_fn = function(rects, ratio) { let position = `${ratio * 100}%`; const { width, min, max } = rects.box; if (!width) return position; if (!Number.isNaN(min)) { const pad = `var(--media-box-padding-left)`; const minPos = `calc(1 / var(--_range-width) * 100 * ${min}% + ${pad})`; position = `max(${minPos}, ${position})`; } if (!Number.isNaN(max)) { const pad = `var(--media-box-padding-right)`; const maxPos = `calc(1 / var(--_range-width) * 100 * ${max}% - ${pad})`; position = `min(${position}, ${maxPos})`; } return position; }; _getBoxShiftPosition = new WeakSet(); getBoxShiftPosition_fn = function(rects, ratio) { const { width, min, max } = rects.box; const pointerX = ratio * rects.range.width; if (pointerX < min + __privateGet(this, _boxPaddingLeft)) { const offset = rects.range.left - rects.bounds.left - __privateGet(this, _boxPaddingLeft); return `${pointerX - width / 2 + offset}px`; } if (pointerX > max - __privateGet(this, _boxPaddingRight)) { const offset = rects.bounds.right - rects.range.right - __privateGet(this, _boxPaddingRight); return `${pointerX + width / 2 - offset - rects.range.width}px`; } return 0; }; _handlePointerMove = new WeakSet(); handlePointerMove_fn = function(evt) { const isOverBoxes = [...__privateGet(this, _boxes)].some( (b) => evt.composedPath().includes(b) ); if (!this.dragging && (isOverBoxes || !evt.composedPath().includes(this))) { __privateMethod(this, _previewRequest, previewRequest_fn).call(this, null); return; } const duration = this.mediaSeekableEnd; if (!duration) return; const previewRailRule = getOrInsertCSSRule( this.shadowRoot, "#preview-rail" ); const previewBoxRule = getOrInsertCSSRule( this.shadowRoot, '[part~="preview-box"]' ); const rects = __privateMethod(this, _getElementRects, getElementRects_fn).call(this, __privateGet(this, _previewBox)); let pointerRatio = (evt.clientX - rects.range.left) / rects.range.width; pointerRatio = Math.max(0, Math.min(1, pointerRatio)); const boxPos = __privateMethod(this, _getBoxPosition, getBoxPosition_fn).call(this, rects, pointerRatio); const boxShift = __privateMethod(this, _getBoxShiftPosition, getBoxShiftPosition_fn).call(this, rects, pointerRatio); previewRailRule.style.transform = `translateX(${boxPos})`; previewRailRule.style.setProperty("--_range-width", `${rects.range.width}`); previewBoxRule.style.setProperty("--_box-shift", `${boxShift}`); previewBoxRule.style.setProperty("--_box-width", `${rects.box.width}px`); const diff = Math.round(__privateGet(this, _previewTime)) - Math.round(pointerRatio * duration); if (Math.abs(diff) < 1 && pointerRatio > 0.01 && pointerRatio < 0.99) return; __privateSet(this, _previewTime, pointerRatio * duration); __privateMethod(this, _previewRequest, previewRequest_fn).call(this, __privateGet(this, _previewTime)); }; _previewRequest = new WeakSet(); previewRequest_fn = function(detail) { this.dispatchEvent( new globalThis.CustomEvent(MediaUIEvents.MEDIA_PREVIEW_REQUEST, { composed: true, bubbles: true, detail }) ); }; _seekRequest = new WeakSet(); seekRequest_fn = function() { __privateGet(this, _animation).stop(); const detail = calcTimeFromRangeValue(this); this.dispatchEvent( new globalThis.CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, { composed: true, bubbles: true, detail }) ); }; if (!globalThis.customElements.get("media-time-range")) { globalThis.customElements.define("media-time-range", MediaTimeRange); } var media_time_range_default = MediaTimeRange; export { media_time_range_default as default };