import { globalThis, document } from "../../utils/server-safe-globals.js";
import { MediaUIEvents, MediaUIAttributes } from "../../constants.js";
const template = document.createElement("template");
const HANDLE_W = 8;
const Z = {
100: 100,
200: 200,
300: 300
};
function lockBetweenZeroAndOne(num) {
return Math.max(0, Math.min(1, num));
}
template.innerHTML = `
`;
class MediaClipSelector extends globalThis.HTMLElement {
static get observedAttributes() {
return [
"thumbnails",
MediaUIAttributes.MEDIA_DURATION,
MediaUIAttributes.MEDIA_CURRENT_TIME
];
}
constructor() {
var _a, _b, _c;
super();
if (!this.shadowRoot) {
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
this.draggingEl = null;
this.wrapper = this.shadowRoot.querySelector("#selectorContainer");
this.selection = this.shadowRoot.querySelector("#selection");
this.playhead = this.shadowRoot.querySelector("#playhead");
this.leftTrim = this.shadowRoot.querySelector("#leftTrim");
this.spacerFirst = this.shadowRoot.querySelector("#spacerFirst");
this.startHandle = this.shadowRoot.querySelector("#startHandle");
this.spacerMiddle = this.shadowRoot.querySelector("#spacerMiddle");
this.endHandle = this.shadowRoot.querySelector("#endHandle");
this.spacerLast = this.shadowRoot.querySelector("#spacerLast");
this._clickHandler = this.handleClick.bind(this);
this._dragStart = this.dragStart.bind(this);
this._dragEnd = this.dragEnd.bind(this);
this._drag = this.drag.bind(this);
this.wrapper.addEventListener("click", this._clickHandler, false);
this.wrapper.addEventListener("touchstart", this._dragStart, false);
(_a = globalThis.window) == null ? void 0 : _a.addEventListener("touchend", this._dragEnd, false);
this.wrapper.addEventListener("touchmove", this._drag, false);
this.wrapper.addEventListener("mousedown", this._dragStart, false);
(_b = globalThis.window) == null ? void 0 : _b.addEventListener("mouseup", this._dragEnd, false);
(_c = globalThis.window) == null ? void 0 : _c.addEventListener("mousemove", this._drag, false);
this.enableThumbnails();
}
get mediaDuration() {
return +this.getAttribute(MediaUIAttributes.MEDIA_DURATION);
}
get mediaCurrentTime() {
return +this.getAttribute(MediaUIAttributes.MEDIA_CURRENT_TIME);
}
/*
* pass in a mouse event (evt.clientX)
* calculates the percentage progress based on the bounding rectang
* converts the percentage progress into a duration in seconds
*/
getPlayheadBasedOnMouseEvent(evt) {
const duration = this.mediaDuration;
if (!duration)
return;
const mousePercent = lockBetweenZeroAndOne(this.getMousePercent(evt));
return mousePercent * duration;
}
getXPositionFromMouse(evt) {
let clientX;
if (["touchstart", "touchmove"].includes(evt.type)) {
clientX = evt.touches[0].clientX;
}
return clientX || evt.clientX;
}
getMousePercent(evt) {
const rangeRect = this.wrapper.getBoundingClientRect();
const mousePercent = (this.getXPositionFromMouse(evt) - rangeRect.left) / rangeRect.width;
return lockBetweenZeroAndOne(mousePercent);
}
dragStart(evt) {
if (evt.target === this.startHandle) {
this.draggingEl = this.startHandle;
}
if (evt.target === this.endHandle) {
this.draggingEl = this.endHandle;
}
this.initialX = this.getXPositionFromMouse(evt);
}
dragEnd() {
this.initialX = null;
this.draggingEl = null;
}
setSelectionWidth(selectionPercent, fullTimelineWidth) {
let percent = selectionPercent;
const minWidthPx = HANDLE_W * 3;
const minWidthPercent = lockBetweenZeroAndOne(
minWidthPx / fullTimelineWidth
);
if (percent < minWidthPercent) {
percent = minWidthPercent;
}
this.selection.style.width = `${percent * 100}%`;
}
drag(evt) {
if (!this.draggingEl) {
return;
}
evt.preventDefault();
const rangeRect = this.wrapper.getBoundingClientRect();
const fullTimelineWidth = rangeRect.width;
const endXPosition = this.getXPositionFromMouse(evt);
const xDelta = endXPosition - this.initialX;
const percent = this.getMousePercent(evt);
const selectionW = this.selection.getBoundingClientRect().width;
if (this.draggingEl === this.startHandle) {
this.initialX = this.getXPositionFromMouse(evt);
this.leftTrim.style.width = `${percent * 100}%`;
const selectionPercent = lockBetweenZeroAndOne(
(selectionW - xDelta) / fullTimelineWidth
);
this.setSelectionWidth(selectionPercent, fullTimelineWidth);
}
if (this.draggingEl === this.endHandle) {
this.initialX = this.getXPositionFromMouse(evt);
const selectionPercent = lockBetweenZeroAndOne(
(selectionW + xDelta) / fullTimelineWidth
);
this.setSelectionWidth(selectionPercent, fullTimelineWidth);
}
this.dispatchUpdate();
}
dispatchUpdate() {
const updateEvent = new CustomEvent("update", {
detail: this.getCurrentClipBounds()
});
this.dispatchEvent(updateEvent);
}
getCurrentClipBounds() {
const rangeRect = this.wrapper.getBoundingClientRect();
const leftTrimRect = this.leftTrim.getBoundingClientRect();
const selectionRect = this.selection.getBoundingClientRect();
const percentStart = lockBetweenZeroAndOne(
leftTrimRect.width / rangeRect.width
);
const percentEnd = lockBetweenZeroAndOne(
(leftTrimRect.width + selectionRect.width) / rangeRect.width
);
return {
startTime: Math.round(percentStart * this.mediaDuration),
endTime: Math.round(percentEnd * this.mediaDuration)
};
}
isTimestampInBounds(timestamp) {
const { startTime, endTime } = this.getCurrentClipBounds();
return startTime <= timestamp && endTime >= timestamp;
}
handleClick(evt) {
const mousePercent = this.getMousePercent(evt);
const timestampForClick = mousePercent * this.mediaDuration;
if (this.isTimestampInBounds(timestampForClick)) {
this.dispatchEvent(
new globalThis.CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, {
composed: true,
bubbles: true,
detail: timestampForClick
})
);
}
}
mediaCurrentTimeSet() {
const percentComplete = lockBetweenZeroAndOne(
this.mediaCurrentTime / this.mediaDuration
);
this.playhead.style.left = `${percentComplete * 100}%`;
this.playhead.style.display = "block";
if (!this.mediaPaused) {
const { startTime, endTime } = this.getCurrentClipBounds();
if (this.mediaCurrentTime < startTime || this.mediaCurrentTime > endTime) {
this.dispatchEvent(
new globalThis.CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, {
composed: true,
bubbles: true,
detail: startTime
})
);
}
}
}
mediaUnsetCallback(media) {
var _a, _b;
super.mediaUnsetCallback(media);
this.wrapper.removeEventListener("touchstart", this._dragStart);
this.wrapper.removeEventListener("touchend", this._dragEnd);
this.wrapper.removeEventListener("touchmove", this._drag);
this.wrapper.removeEventListener("mousedown", this._dragStart);
(_a = globalThis.window) == null ? void 0 : _a.removeEventListener("mouseup", this._dragEnd);
(_b = globalThis.window) == null ? void 0 : _b.removeEventListener("mousemove", this._drag);
}
/*
* This was copied over from media-time-range, we should have a way of making
* this code shared between the two components
*/
enableThumbnails() {
this.thumbnailPreview = this.shadowRoot.querySelector(
"media-preview-thumbnail"
);
const thumbnailContainer = this.shadowRoot.querySelector(
"#thumbnailContainer"
);
thumbnailContainer.classList.add("enabled");
let mouseMoveHandler;
const trackMouse = () => {
var _a;
mouseMoveHandler = (evt) => {
const duration = this.mediaDuration;
if (!duration)
return;
const rangeRect = this.wrapper.getBoundingClientRect();
const mousePercent = this.getMousePercent(evt);
const leftPadding = rangeRect.left - this.getBoundingClientRect().left;
const thumbnailLeft = leftPadding + mousePercent * rangeRect.width;
this.thumbnailPreview.style.left = `${thumbnailLeft}px`;
this.dispatchEvent(
new globalThis.CustomEvent(MediaUIEvents.MEDIA_PREVIEW_REQUEST, {
composed: true,
bubbles: true,
detail: mousePercent * duration
})
);
};
(_a = globalThis.window) == null ? void 0 : _a.addEventListener("mousemove", mouseMoveHandler, false);
};
const stopTrackingMouse = () => {
var _a;
(_a = globalThis.window) == null ? void 0 : _a.removeEventListener("mousemove", mouseMoveHandler);
};
let rangeEntered = false;
const rangeMouseMoveHander = () => {
var _a;
if (!rangeEntered && this.mediaDuration) {
rangeEntered = true;
this.thumbnailPreview.style.display = "block";
trackMouse();
const offRangeHandler = (evt) => {
var _a2;
if (evt.target != this && !this.contains(evt.target)) {
this.thumbnailPreview.style.display = "none";
(_a2 = globalThis.window) == null ? void 0 : _a2.removeEventListener(
"mousemove",
offRangeHandler
);
rangeEntered = false;
stopTrackingMouse();
}
};
(_a = globalThis.window) == null ? void 0 : _a.addEventListener(
"mousemove",
offRangeHandler,
false
);
}
if (!this.mediaDuration) {
this.thumbnailPreview.style.display = "none";
}
};
this.addEventListener("mousemove", rangeMouseMoveHander, false);
}
disableThumbnails() {
const thumbnailContainer = this.shadowRoot.querySelector(
"#thumbnailContainer"
);
thumbnailContainer.classList.remove("enabled");
}
}
if (!globalThis.customElements.get("media-clip-selector")) {
globalThis.customElements.define("media-clip-selector", MediaClipSelector);
}
var media_clip_selector_default = MediaClipSelector;
export {
media_clip_selector_default as default
};