2021-10-05 22:40:07 +02:00
|
|
|
|
/*
|
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
|
*/
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
// * zoom part from WebZ
|
|
|
|
|
// * https://github.com/Ajaxy/telegram-tt/blob/069f4f5b2f2c7c22529ccced876842e7f9cb81f4/src/components/mediaViewer/MediaViewerSlides.tsx
|
|
|
|
|
|
2023-02-01 09:53:35 +01:00
|
|
|
|
import type {MyDocument} from '../lib/appManagers/appDocsManager';
|
|
|
|
|
import type {MyPhoto} from '../lib/appManagers/appPhotosManager';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
import deferredPromise from '../helpers/cancellablePromise';
|
|
|
|
|
import mediaSizes from '../helpers/mediaSizes';
|
|
|
|
|
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
2023-01-16 20:14:20 +01:00
|
|
|
|
import {IS_MOBILE, IS_MOBILE_SAFARI, IS_SAFARI} from '../environment/userAgent';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
import {logger} from '../lib/logger';
|
|
|
|
|
import VideoPlayer from '../lib/mediaPlayer';
|
|
|
|
|
import rootScope from '../lib/rootScope';
|
|
|
|
|
import animationIntersector from './animationIntersector';
|
|
|
|
|
import appMediaPlaybackController, {AppMediaPlaybackController} from './appMediaPlaybackController';
|
|
|
|
|
import AvatarElement from './avatar';
|
|
|
|
|
import ButtonIcon from './buttonIcon';
|
|
|
|
|
import {ButtonMenuItemOptions} from './buttonMenu';
|
|
|
|
|
import ButtonMenuToggle from './buttonMenuToggle';
|
|
|
|
|
import ProgressivePreloader from './preloader';
|
2023-02-17 16:15:35 +01:00
|
|
|
|
import SwipeHandler, {ZoomDetails} from './swipeHandler';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
import {formatFullSentTime} from '../helpers/date';
|
|
|
|
|
import appNavigationController, {NavigationItem} from './appNavigationController';
|
2023-02-17 16:15:35 +01:00
|
|
|
|
import {Message, PhotoSize} from '../layer';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
import findUpClassName from '../helpers/dom/findUpClassName';
|
|
|
|
|
import renderImageFromUrl, {renderImageFromUrlPromise} from '../helpers/dom/renderImageFromUrl';
|
|
|
|
|
import getVisibleRect from '../helpers/dom/getVisibleRect';
|
|
|
|
|
import cancelEvent from '../helpers/dom/cancelEvent';
|
|
|
|
|
import fillPropertyValue from '../helpers/fillPropertyValue';
|
|
|
|
|
import generatePathData from '../helpers/generatePathData';
|
|
|
|
|
import replaceContent from '../helpers/dom/replaceContent';
|
|
|
|
|
import {doubleRaf, fastRaf} from '../helpers/schedulers';
|
|
|
|
|
import RangeSelector from './rangeSelector';
|
|
|
|
|
import windowSize from '../helpers/windowSize';
|
|
|
|
|
import ListLoader from '../helpers/listLoader';
|
|
|
|
|
import EventListenerBase from '../helpers/eventListenerBase';
|
|
|
|
|
import {MyMessage} from '../lib/appManagers/appMessagesManager';
|
|
|
|
|
import {NULL_PEER_ID} from '../lib/mtproto/mtproto_config';
|
|
|
|
|
import {isFullScreen} from '../helpers/dom/fullScreen';
|
2023-02-27 14:11:37 +01:00
|
|
|
|
import {attachClickEvent, hasMouseMovedSinceDown} from '../helpers/dom/clickEvent';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
import SearchListLoader from '../helpers/searchListLoader';
|
|
|
|
|
import createVideo from '../helpers/dom/createVideo';
|
|
|
|
|
import {AppManagers} from '../lib/appManagers/managers';
|
|
|
|
|
import getStrippedThumbIfNeeded from '../helpers/getStrippedThumbIfNeeded';
|
|
|
|
|
import setAttachmentSize from '../helpers/setAttachmentSize';
|
|
|
|
|
import wrapEmojiText from '../lib/richTextProcessor/wrapEmojiText';
|
|
|
|
|
import LazyLoadQueueBase from './lazyLoadQueueBase';
|
|
|
|
|
import overlayCounter from '../helpers/overlayCounter';
|
|
|
|
|
import appDownloadManager from '../lib/appManagers/appDownloadManager';
|
|
|
|
|
import wrapPeerTitle from './wrappers/peerTitle';
|
2023-01-16 20:14:20 +01:00
|
|
|
|
import {toastNew} from './toast';
|
2023-02-17 16:15:35 +01:00
|
|
|
|
import clamp from '../helpers/number/clamp';
|
|
|
|
|
import debounce from '../helpers/schedulers/debounce';
|
|
|
|
|
import isBetween from '../helpers/number/isBetween';
|
2023-02-27 14:11:37 +01:00
|
|
|
|
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
2023-03-01 11:20:49 +01:00
|
|
|
|
import liteMode from '../helpers/liteMode';
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
const ZOOM_STEP = 0.5;
|
|
|
|
|
const ZOOM_INITIAL_VALUE = 1;
|
|
|
|
|
const ZOOM_MIN_VALUE = 0.5;
|
|
|
|
|
const ZOOM_MAX_VALUE = 4;
|
|
|
|
|
|
|
|
|
|
export const MEDIA_VIEWER_CLASSNAME = 'media-viewer';
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
type Transform = {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
scale: number;
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
export default class AppMediaViewerBase<
|
2022-08-04 08:49:54 +02:00
|
|
|
|
ContentAdditionType extends string,
|
|
|
|
|
ButtonsAdditionType extends string,
|
2021-10-05 22:40:07 +02:00
|
|
|
|
TargetType extends {element: HTMLElement
|
|
|
|
|
}> extends EventListenerBase<{
|
|
|
|
|
setMoverBefore: () => void,
|
|
|
|
|
setMoverAfter: () => void
|
|
|
|
|
}> {
|
|
|
|
|
protected wholeDiv: HTMLElement;
|
|
|
|
|
protected overlaysDiv: HTMLElement;
|
|
|
|
|
protected author: {[k in 'container' | 'avatarEl' | 'nameEl' | 'date']: HTMLElement} = {} as any;
|
|
|
|
|
protected content: {[k in 'main' | 'container' | 'media' | 'mover' | ContentAdditionType]: HTMLElement} = {} as any;
|
|
|
|
|
protected buttons: {[k in 'download' | 'close' | 'prev' | 'next' | 'mobile-close' | 'zoom' | ButtonsAdditionType]: HTMLElement} = {} as any;
|
|
|
|
|
protected topbar: HTMLElement;
|
|
|
|
|
protected moversContainer: HTMLElement;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
protected tempId = 0;
|
|
|
|
|
protected preloader: ProgressivePreloader = null;
|
|
|
|
|
protected preloaderStreamable: ProgressivePreloader = null;
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
protected log: ReturnType<typeof logger>;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
protected isFirstOpen = true;
|
|
|
|
|
|
|
|
|
|
protected pageEl = document.getElementById('page-chats') as HTMLDivElement;
|
|
|
|
|
|
|
|
|
|
protected setMoverPromise: Promise<void>;
|
|
|
|
|
protected setMoverAnimationPromise: Promise<void>;
|
|
|
|
|
|
|
|
|
|
protected lazyLoadQueue: LazyLoadQueueBase;
|
|
|
|
|
|
|
|
|
|
protected highlightSwitchersTimeout: number;
|
|
|
|
|
|
|
|
|
|
protected onDownloadClick: (e: MouseEvent) => void;
|
|
|
|
|
protected onPrevClick: (target: TargetType) => void;
|
|
|
|
|
protected onNextClick: (target: TargetType) => void;
|
|
|
|
|
|
|
|
|
|
protected videoPlayer: VideoPlayer;
|
|
|
|
|
|
|
|
|
|
protected zoomElements: {
|
|
|
|
|
container: HTMLElement,
|
|
|
|
|
btnOut: HTMLElement,
|
|
|
|
|
btnIn: HTMLElement,
|
|
|
|
|
rangeSelector: RangeSelector
|
|
|
|
|
} = {} as any;
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected transform: Transform = {x: 0, y: 0, scale: ZOOM_INITIAL_VALUE};
|
|
|
|
|
protected isZooming: boolean;
|
|
|
|
|
protected isGesturingNow: boolean;
|
|
|
|
|
protected isZoomingNow: boolean;
|
|
|
|
|
protected draggingType: 'wheel' | 'touchmove' | 'mousemove';
|
|
|
|
|
protected initialContentRect: DOMRect;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
protected ctrlKeyDown: boolean;
|
2022-04-15 20:04:56 +02:00
|
|
|
|
protected releaseSingleMedia: ReturnType<AppMediaPlaybackController['setSingleMedia']>;
|
2022-04-15 23:19:57 +02:00
|
|
|
|
protected navigationItem: NavigationItem;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-04-25 16:54:30 +02:00
|
|
|
|
protected managers: AppManagers;
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected swipeHandler: SwipeHandler;
|
|
|
|
|
protected closing: boolean;
|
|
|
|
|
|
|
|
|
|
protected lastTransform: Transform = this.transform;
|
|
|
|
|
protected lastZoomCenter: {x: number, y: number} = this.transform;
|
|
|
|
|
protected lastDragOffset: {x: number, y: number} = this.transform;
|
|
|
|
|
protected lastDragDelta: {x: number, y: number} = this.transform;
|
|
|
|
|
protected lastGestureTime: number;
|
|
|
|
|
protected clampZoomDebounced: ReturnType<typeof debounce<() => void>>;
|
2023-02-27 14:11:37 +01:00
|
|
|
|
ignoreNextClick: boolean;
|
2022-04-25 16:54:30 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
get target() {
|
|
|
|
|
return this.listLoader.current;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set target(value) {
|
|
|
|
|
this.listLoader.current = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor(
|
2022-08-04 08:49:54 +02:00
|
|
|
|
protected listLoader: ListLoader<TargetType, any>,
|
2021-10-05 22:40:07 +02:00
|
|
|
|
topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>
|
|
|
|
|
) {
|
|
|
|
|
super(false);
|
|
|
|
|
|
2022-04-25 16:54:30 +02:00
|
|
|
|
this.managers = rootScope.managers;
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.log = logger('AMV');
|
|
|
|
|
this.preloader = new ProgressivePreloader();
|
|
|
|
|
this.preloaderStreamable = new ProgressivePreloader({
|
|
|
|
|
cancelable: false,
|
|
|
|
|
streamable: true
|
|
|
|
|
});
|
|
|
|
|
this.preloader.construct();
|
|
|
|
|
this.preloaderStreamable.construct();
|
|
|
|
|
this.lazyLoadQueue = new LazyLoadQueueBase();
|
|
|
|
|
|
|
|
|
|
this.wholeDiv = document.createElement('div');
|
|
|
|
|
this.wholeDiv.classList.add(MEDIA_VIEWER_CLASSNAME + '-whole');
|
|
|
|
|
|
|
|
|
|
this.overlaysDiv = document.createElement('div');
|
|
|
|
|
this.overlaysDiv.classList.add('overlays');
|
|
|
|
|
|
|
|
|
|
const mainDiv = document.createElement('div');
|
|
|
|
|
mainDiv.classList.add(MEDIA_VIEWER_CLASSNAME);
|
|
|
|
|
|
|
|
|
|
const topbar = this.topbar = document.createElement('div');
|
|
|
|
|
topbar.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar', MEDIA_VIEWER_CLASSNAME + '-appear');
|
|
|
|
|
|
|
|
|
|
const topbarLeft = document.createElement('div');
|
|
|
|
|
topbarLeft.classList.add(MEDIA_VIEWER_CLASSNAME + '-topbar-left');
|
|
|
|
|
|
|
|
|
|
this.buttons['mobile-close'] = ButtonIcon('close', {onlyMobile: true});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// * author
|
|
|
|
|
this.author.container = document.createElement('div');
|
|
|
|
|
this.author.container.classList.add(MEDIA_VIEWER_CLASSNAME + '-author', 'no-select');
|
|
|
|
|
const authorRight = document.createElement('div');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.author.avatarEl = new AvatarElement();
|
|
|
|
|
this.author.avatarEl.classList.add(MEDIA_VIEWER_CLASSNAME + '-userpic', 'avatar-44');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.author.nameEl = document.createElement('div');
|
|
|
|
|
this.author.nameEl.classList.add(MEDIA_VIEWER_CLASSNAME + '-name');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.author.date = document.createElement('div');
|
|
|
|
|
this.author.date.classList.add(MEDIA_VIEWER_CLASSNAME + '-date');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
authorRight.append(this.author.nameEl, this.author.date);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.author.container.append(this.author.avatarEl, authorRight);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// * buttons
|
|
|
|
|
const buttonsDiv = document.createElement('div');
|
|
|
|
|
buttonsDiv.classList.add(MEDIA_VIEWER_CLASSNAME + '-buttons');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
topButtons.concat(['download', 'zoom', 'close']).forEach((name) => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const button = ButtonIcon(name, {noRipple: true});
|
|
|
|
|
this.buttons[name] = button;
|
|
|
|
|
buttonsDiv.append(button);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.buttons.zoom.classList.add('zoom-in');
|
|
|
|
|
|
|
|
|
|
// * zoom
|
|
|
|
|
this.zoomElements.container = document.createElement('div');
|
|
|
|
|
this.zoomElements.container.classList.add('zoom-container');
|
|
|
|
|
|
|
|
|
|
this.zoomElements.btnOut = ButtonIcon('zoomout', {noRipple: true});
|
2023-02-17 16:15:35 +01:00
|
|
|
|
attachClickEvent(this.zoomElements.btnOut, () => this.addZoomStep(false));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.zoomElements.btnIn = ButtonIcon('zoomin', {noRipple: true});
|
2023-02-17 16:15:35 +01:00
|
|
|
|
attachClickEvent(this.zoomElements.btnIn, () => this.addZoomStep(true));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.zoomElements.rangeSelector = new RangeSelector({
|
2023-02-17 16:15:35 +01:00
|
|
|
|
step: 0.01,
|
2022-08-04 08:49:54 +02:00
|
|
|
|
min: ZOOM_MIN_VALUE,
|
|
|
|
|
max: ZOOM_MAX_VALUE,
|
2021-10-05 22:40:07 +02:00
|
|
|
|
withTransition: true
|
|
|
|
|
}, ZOOM_INITIAL_VALUE);
|
|
|
|
|
this.zoomElements.rangeSelector.setListeners();
|
|
|
|
|
this.zoomElements.rangeSelector.setHandlers({
|
2023-02-27 14:11:37 +01:00
|
|
|
|
onScrub: (value) => {
|
|
|
|
|
const add = value - this.transform.scale;
|
|
|
|
|
this.addZoom(add);
|
|
|
|
|
this.clampZoomDebounced?.clearTimeout();
|
|
|
|
|
},
|
|
|
|
|
onMouseDown: () => {
|
|
|
|
|
this.moversContainer.classList.add('no-transition');
|
|
|
|
|
this.zoomElements.rangeSelector.container.classList.remove('with-transition');
|
|
|
|
|
},
|
|
|
|
|
onMouseUp: () => {
|
|
|
|
|
this.moversContainer.classList.remove('no-transition');
|
|
|
|
|
this.zoomElements.rangeSelector.container.classList.add('with-transition');
|
|
|
|
|
this.setZoomValue();
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.zoomElements.container.append(this.zoomElements.btnOut, this.zoomElements.rangeSelector.container, this.zoomElements.btnIn);
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
if(!IS_TOUCH_SUPPORTED) {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.wholeDiv.append(this.zoomElements.container);
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
// * content
|
|
|
|
|
this.content.main = document.createElement('div');
|
|
|
|
|
this.content.main.classList.add(MEDIA_VIEWER_CLASSNAME + '-content');
|
|
|
|
|
|
|
|
|
|
this.content.container = document.createElement('div');
|
|
|
|
|
this.content.container.classList.add(MEDIA_VIEWER_CLASSNAME + '-container');
|
|
|
|
|
|
|
|
|
|
this.content.media = document.createElement('div');
|
|
|
|
|
this.content.media.classList.add(MEDIA_VIEWER_CLASSNAME + '-media');
|
|
|
|
|
|
|
|
|
|
this.content.container.append(this.content.media);
|
|
|
|
|
|
|
|
|
|
this.content.main.append(this.content.container);
|
|
|
|
|
mainDiv.append(this.content.main);
|
|
|
|
|
this.overlaysDiv.append(mainDiv);
|
|
|
|
|
// * overlays end
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
topbarLeft.append(this.buttons['mobile-close'], this.author.container);
|
|
|
|
|
topbar.append(topbarLeft, buttonsDiv);
|
|
|
|
|
|
|
|
|
|
this.buttons.prev = document.createElement('div');
|
|
|
|
|
this.buttons.prev.className = `${MEDIA_VIEWER_CLASSNAME}-switcher ${MEDIA_VIEWER_CLASSNAME}-switcher-left`;
|
|
|
|
|
this.buttons.prev.innerHTML = `<span class="tgico-down ${MEDIA_VIEWER_CLASSNAME}-prev-button"></span>`;
|
|
|
|
|
|
|
|
|
|
this.buttons.next = document.createElement('div');
|
|
|
|
|
this.buttons.next.className = `${MEDIA_VIEWER_CLASSNAME}-switcher ${MEDIA_VIEWER_CLASSNAME}-switcher-right`;
|
|
|
|
|
this.buttons.next.innerHTML = `<span class="tgico-down ${MEDIA_VIEWER_CLASSNAME}-next-button"></span>`;
|
|
|
|
|
|
|
|
|
|
this.moversContainer = document.createElement('div');
|
|
|
|
|
this.moversContainer.classList.add(MEDIA_VIEWER_CLASSNAME + '-movers');
|
|
|
|
|
|
2023-02-01 13:47:24 +01:00
|
|
|
|
this.moversContainer.append(this.buttons.prev, this.buttons.next);
|
|
|
|
|
|
|
|
|
|
this.wholeDiv.append(this.overlaysDiv, /* this.buttons.prev, this.buttons.next, */ this.topbar, this.moversContainer);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
// * constructing html end
|
|
|
|
|
|
2021-12-16 21:21:36 +01:00
|
|
|
|
this.listLoader.onLoadedMore = () => {
|
|
|
|
|
this.buttons.prev.classList.toggle('hide', !this.listLoader.previous.length);
|
|
|
|
|
this.buttons.next.classList.toggle('hide', !this.listLoader.next.length);
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.setNewMover();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected setListeners() {
|
2022-02-11 15:36:00 +01:00
|
|
|
|
attachClickEvent(this.buttons.download, this.onDownloadClick);
|
2022-06-17 18:01:43 +02:00
|
|
|
|
[this.buttons.close, this.buttons['mobile-close'], this.preloaderStreamable.preloader].forEach((el) => {
|
2022-02-11 15:36:00 +01:00
|
|
|
|
attachClickEvent(el, this.close.bind(this));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
([[-1, this.buttons.prev], [1, this.buttons.next]] as [number, HTMLElement][]).forEach(([moveLength, button]) => {
|
2022-02-11 16:15:49 +01:00
|
|
|
|
// attachClickEvent(button, (e) => {
|
|
|
|
|
button.addEventListener('click', (e) => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
cancelEvent(e);
|
|
|
|
|
if(this.setMoverPromise) return;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.listLoader.go(moveLength);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-11 15:36:00 +01:00
|
|
|
|
attachClickEvent(this.buttons.zoom, () => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
if(this.isZooming) this.resetZoom();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
else {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.addZoomStep(true);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-11 16:05:05 +01:00
|
|
|
|
// ! cannot use the function because it'll cancel slide event on touch devices
|
|
|
|
|
// attachClickEvent(this.wholeDiv, this.onClick);
|
|
|
|
|
this.wholeDiv.addEventListener('click', this.onClick);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.listLoader.onJump = (item, older) => {
|
|
|
|
|
if(older) this.onNextClick(item);
|
|
|
|
|
else this.onPrevClick(item);
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const adjustPosition = (xDiff: number, yDiff: number) => {
|
|
|
|
|
const [x, y] = [xDiff - this.lastDragOffset.x, yDiff - this.lastDragOffset.y];
|
|
|
|
|
const [transform, inBoundsX, inBoundsY] = this.calculateOffsetBoundaries({
|
|
|
|
|
x: this.transform.x + x,
|
|
|
|
|
y: this.transform.y + y,
|
|
|
|
|
scale: this.transform.scale
|
|
|
|
|
});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.lastDragDelta = {
|
|
|
|
|
x,
|
|
|
|
|
y
|
|
|
|
|
};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.lastDragOffset = {
|
|
|
|
|
x: xDiff,
|
|
|
|
|
y: yDiff
|
|
|
|
|
};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.setTransform(transform);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
return {inBoundsX, inBoundsY};
|
|
|
|
|
};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const setLastGestureTime = debounce(() => {
|
|
|
|
|
this.lastGestureTime = Date.now();
|
|
|
|
|
}, 500, false, true);
|
|
|
|
|
|
|
|
|
|
this.clampZoomDebounced = debounce(() => {
|
|
|
|
|
this.onSwipeReset();
|
|
|
|
|
}, 300, false, true);
|
|
|
|
|
|
|
|
|
|
this.swipeHandler = new SwipeHandler({
|
|
|
|
|
element: this.wholeDiv,
|
|
|
|
|
onReset: this.onSwipeReset,
|
2023-02-27 14:11:37 +01:00
|
|
|
|
onFirstSwipe: this.onSwipeFirst as any,
|
2023-02-17 16:15:35 +01:00
|
|
|
|
onSwipe: (xDiff, yDiff, e, cancelDrag) => {
|
|
|
|
|
if(isFullScreen()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.isZooming && !this.isZoomingNow) {
|
|
|
|
|
setLastGestureTime();
|
|
|
|
|
|
|
|
|
|
this.draggingType = e.type as any;
|
|
|
|
|
const {inBoundsX, inBoundsY} = adjustPosition(xDiff, yDiff);
|
|
|
|
|
cancelDrag?.(!inBoundsX, !inBoundsY);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.isZoomingNow || !IS_TOUCH_SUPPORTED) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const percents = Math.abs(xDiff) / windowSize.width;
|
|
|
|
|
if(percents > .2 || Math.abs(xDiff) > 125) {
|
|
|
|
|
if(xDiff > 0) {
|
|
|
|
|
this.buttons.prev.click();
|
|
|
|
|
} else {
|
|
|
|
|
this.buttons.next.click();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-02-17 16:15:35 +01:00
|
|
|
|
|
|
|
|
|
const percentsY = Math.abs(yDiff) / windowSize.height;
|
|
|
|
|
if(percentsY > .2 || Math.abs(yDiff) > 125) {
|
|
|
|
|
this.close();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
onZoom: this.onZoom,
|
|
|
|
|
onDoubleClick: ({centerX, centerY}) => {
|
|
|
|
|
if(this.isZooming) {
|
|
|
|
|
this.resetZoom();
|
|
|
|
|
} else {
|
|
|
|
|
const scale = ZOOM_INITIAL_VALUE + 2;
|
|
|
|
|
this.changeZoomByPosition(centerX, centerY, scale);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
verifyTouchTarget: (e) => {
|
|
|
|
|
// * Fix for seek input
|
2023-02-20 20:39:43 +01:00
|
|
|
|
if(isFullScreen() ||
|
2023-02-27 14:11:37 +01:00
|
|
|
|
findUpAsChild(e.target as HTMLElement, this.zoomElements.container) ||
|
2023-02-20 20:39:43 +01:00
|
|
|
|
findUpClassName(e.target, 'ckin__controls') ||
|
2023-02-17 16:15:35 +01:00
|
|
|
|
findUpClassName(e.target, 'media-viewer-caption') ||
|
|
|
|
|
(findUpClassName(e.target, 'media-viewer-topbar') && e.type !== 'wheel')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
cursor: ''
|
|
|
|
|
// cursor: 'move'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
protected onSwipeFirst = (e: MouseEvent | TouchEvent | WheelEvent) => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.lastDragOffset = this.lastDragDelta = {x: 0, y: 0};
|
|
|
|
|
this.lastTransform = {...this.transform};
|
2023-02-27 14:11:37 +01:00
|
|
|
|
if(e.type !== 'wheel' || !this.ctrlKeyDown) { // keep transition for real mouse wheel
|
|
|
|
|
this.moversContainer.classList.add('no-transition');
|
|
|
|
|
this.zoomElements.rangeSelector.container.classList.remove('with-transition');
|
|
|
|
|
}
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.isGesturingNow = true;
|
|
|
|
|
this.lastGestureTime = Date.now();
|
|
|
|
|
this.clampZoomDebounced.clearTimeout();
|
|
|
|
|
|
|
|
|
|
if(!this.lastTransform.x && !this.lastTransform.y && !this.isZooming) {
|
|
|
|
|
this.initialContentRect = this.content.media.getBoundingClientRect();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2023-02-17 16:15:35 +01:00
|
|
|
|
};
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
protected onSwipeReset = (e?: Event) => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
// move
|
|
|
|
|
this.moversContainer.classList.remove('no-transition');
|
2023-02-27 14:11:37 +01:00
|
|
|
|
this.zoomElements.rangeSelector.container.classList.add('with-transition');
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.clampZoomDebounced.clearTimeout();
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
if(e?.type === 'mouseup' && this.draggingType === 'mousemove') {
|
|
|
|
|
this.ignoreNextClick = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const {draggingType} = this;
|
|
|
|
|
this.isZoomingNow = false;
|
|
|
|
|
this.isGesturingNow = false;
|
|
|
|
|
this.draggingType = undefined;
|
|
|
|
|
|
|
|
|
|
if(this.closing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.transform.scale > ZOOM_INITIAL_VALUE) {
|
|
|
|
|
// Get current content boundaries
|
|
|
|
|
const s1 = Math.min(this.transform.scale, ZOOM_MAX_VALUE);
|
|
|
|
|
const scaleFactor = s1 / this.transform.scale;
|
|
|
|
|
|
|
|
|
|
// Calculate new position based on the last zoom center to keep the zoom center
|
|
|
|
|
// at the same position when bouncing back from max zoom
|
|
|
|
|
let x1 = this.transform.x * scaleFactor + (this.lastZoomCenter.x - scaleFactor * this.lastZoomCenter.x);
|
|
|
|
|
let y1 = this.transform.y * scaleFactor + (this.lastZoomCenter.y - scaleFactor * this.lastZoomCenter.y);
|
|
|
|
|
|
|
|
|
|
// If scale didn't change, we need to add inertia to pan gesture
|
|
|
|
|
if(draggingType && draggingType !== 'wheel' && this.lastTransform.scale === this.transform.scale) {
|
|
|
|
|
// Arbitrary pan velocity coefficient
|
|
|
|
|
const k = 0.1;
|
|
|
|
|
|
|
|
|
|
// Calculate user gesture velocity
|
|
|
|
|
const elapsedTime = Math.max(1, Date.now() - this.lastGestureTime);
|
|
|
|
|
const Vx = Math.abs(this.lastDragOffset.x) / elapsedTime;
|
|
|
|
|
const Vy = Math.abs(this.lastDragOffset.y) / elapsedTime;
|
|
|
|
|
|
|
|
|
|
// Add extra distance based on gesture velocity and last pan delta
|
|
|
|
|
x1 -= Math.abs(this.lastDragOffset.x) * Vx * k * -this.lastDragDelta.x;
|
|
|
|
|
y1 -= Math.abs(this.lastDragOffset.y) * Vy * k * -this.lastDragDelta.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [transform] = this.calculateOffsetBoundaries({x: x1, y: y1, scale: s1});
|
|
|
|
|
this.lastTransform = transform;
|
|
|
|
|
this.setTransform(transform);
|
|
|
|
|
} else if(this.transform.scale < ZOOM_INITIAL_VALUE) {
|
|
|
|
|
this.resetZoom();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected onZoom = ({
|
|
|
|
|
initialCenterX,
|
|
|
|
|
initialCenterY,
|
|
|
|
|
zoom,
|
|
|
|
|
zoomAdd,
|
|
|
|
|
currentCenterX,
|
|
|
|
|
currentCenterY,
|
|
|
|
|
dragOffsetX,
|
|
|
|
|
dragOffsetY,
|
|
|
|
|
zoomFactor
|
|
|
|
|
}: ZoomDetails) => {
|
|
|
|
|
initialCenterX ||= windowSize.width / 2;
|
|
|
|
|
initialCenterY ||= windowSize.height / 2;
|
|
|
|
|
currentCenterX ||= windowSize.width / 2;
|
|
|
|
|
currentCenterY ||= windowSize.height / 2;
|
|
|
|
|
|
|
|
|
|
this.isZoomingNow = true;
|
|
|
|
|
|
|
|
|
|
const zoomMaxBounceValue = ZOOM_MAX_VALUE * 3;
|
2023-02-27 14:11:37 +01:00
|
|
|
|
const scale = zoomAdd !== undefined ? clamp(this.lastTransform.scale + zoomAdd, ZOOM_MIN_VALUE, zoomMaxBounceValue) : (zoom ?? clamp(this.lastTransform.scale * zoomFactor, ZOOM_MIN_VALUE, zoomMaxBounceValue));
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const scaleFactor = scale / this.lastTransform.scale;
|
|
|
|
|
const offsetX = Math.abs(Math.min(this.lastTransform.x, 0));
|
|
|
|
|
const offsetY = Math.abs(Math.min(this.lastTransform.y, 0));
|
|
|
|
|
|
|
|
|
|
// Save last zoom center for bounce back effect
|
|
|
|
|
this.lastZoomCenter = {
|
|
|
|
|
x: currentCenterX,
|
|
|
|
|
y: currentCenterY
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Calculate new center relative to the shifted image
|
|
|
|
|
const scaledCenterX = offsetX + initialCenterX;
|
|
|
|
|
const scaledCenterY = offsetY + initialCenterY;
|
|
|
|
|
|
|
|
|
|
const {scaleOffsetX, scaleOffsetY} = this.calculateScaleOffset({x: scaledCenterX, y: scaledCenterY, scale: scaleFactor});
|
|
|
|
|
|
|
|
|
|
const [transform] = this.calculateOffsetBoundaries({
|
|
|
|
|
x: this.lastTransform.x + scaleOffsetX + dragOffsetX,
|
|
|
|
|
y: this.lastTransform.y + scaleOffsetY + dragOffsetY,
|
|
|
|
|
scale
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.setTransform(transform);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected changeZoomByPosition(x: number, y: number, scale: number) {
|
|
|
|
|
const {scaleOffsetX, scaleOffsetY} = this.calculateScaleOffset({x, y, scale});
|
|
|
|
|
const transform = this.calculateOffsetBoundaries({
|
|
|
|
|
x: scaleOffsetX,
|
|
|
|
|
y: scaleOffsetY,
|
|
|
|
|
scale
|
|
|
|
|
})[0];
|
|
|
|
|
|
|
|
|
|
this.setTransform(transform);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected setTransform(transform: Transform) {
|
|
|
|
|
this.transform = transform;
|
|
|
|
|
this.changeZoom(transform.scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate how much we need to shift the image to keep the zoom center at the same position
|
|
|
|
|
protected calculateScaleOffset({x, y, scale}: {
|
|
|
|
|
x: number,
|
|
|
|
|
y: number,
|
|
|
|
|
scale: number
|
|
|
|
|
}) {
|
|
|
|
|
return {
|
|
|
|
|
scaleOffsetX: x - scale * x,
|
|
|
|
|
scaleOffsetY: y - scale * y
|
|
|
|
|
};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected toggleZoom(enable?: boolean) {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const isVisible = this.isZooming;
|
|
|
|
|
const auto = enable === undefined;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(this.zoomElements.rangeSelector.mousedown || this.ctrlKeyDown) {
|
|
|
|
|
enable = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
enable ??= !isVisible;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
if(isVisible === enable) {
|
|
|
|
|
return;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.buttons.zoom.classList.toggle('zoom-in', !enable);
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.zoomElements.container.classList.toggle('is-visible', this.isZooming = enable);
|
|
|
|
|
this.wholeDiv.classList.toggle('is-zooming', enable);
|
|
|
|
|
|
|
|
|
|
if(auto || !enable) {
|
|
|
|
|
const zoomValue = enable ? this.transform.scale : ZOOM_INITIAL_VALUE;
|
|
|
|
|
this.setZoomValue(zoomValue);
|
|
|
|
|
this.zoomElements.rangeSelector.setProgress(zoomValue);
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
if(this.videoPlayer) {
|
|
|
|
|
this.videoPlayer.lockControls(enable ? false : undefined);
|
|
|
|
|
}
|
2023-02-17 16:15:35 +01:00
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected addZoomStep(add: boolean) {
|
|
|
|
|
this.addZoom(ZOOM_STEP * (add ? 1 : -1));
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected resetZoom() {
|
|
|
|
|
this.setTransform({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
scale: ZOOM_INITIAL_VALUE
|
|
|
|
|
});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected changeZoom(value = this.transform.scale) {
|
|
|
|
|
this.transform.scale = value;
|
|
|
|
|
this.zoomElements.rangeSelector.setProgress(value);
|
|
|
|
|
this.setZoomValue(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected addZoom(value: number) {
|
|
|
|
|
this.lastTransform = this.transform;
|
|
|
|
|
this.onZoom({
|
|
|
|
|
zoomAdd: value,
|
|
|
|
|
currentCenterX: 0,
|
|
|
|
|
currentCenterY: 0,
|
|
|
|
|
initialCenterX: 0,
|
|
|
|
|
initialCenterY: 0,
|
|
|
|
|
dragOffsetX: 0,
|
|
|
|
|
dragOffsetY: 0
|
|
|
|
|
});
|
|
|
|
|
this.lastTransform = this.transform;
|
|
|
|
|
this.clampZoomDebounced();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected getZoomBounce() {
|
|
|
|
|
return this.isGesturingNow && IS_TOUCH_SUPPORTED ? 50 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected calculateOffsetBoundaries = (
|
|
|
|
|
{x, y, scale}: Transform,
|
|
|
|
|
offsetTop = 0
|
|
|
|
|
): [Transform, boolean, boolean] => {
|
|
|
|
|
if(!this.initialContentRect) return [{x, y, scale}, true, true];
|
|
|
|
|
// Get current content boundaries
|
|
|
|
|
let inBoundsX = true;
|
|
|
|
|
let inBoundsY = true;
|
|
|
|
|
|
|
|
|
|
const {minX, maxX, minY, maxY} = this.getZoomBoundaries(scale, offsetTop);
|
|
|
|
|
|
|
|
|
|
inBoundsX = isBetween(x, maxX, minX);
|
|
|
|
|
x = clamp(x, maxX, minX);
|
|
|
|
|
|
|
|
|
|
inBoundsY = isBetween(y, maxY, minY);
|
|
|
|
|
y = clamp(y, maxY, minY);
|
|
|
|
|
|
|
|
|
|
return [{x, y, scale}, inBoundsX, inBoundsY];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected getZoomBoundaries(scale = this.transform.scale, offsetTop = 0) {
|
|
|
|
|
if(!this.initialContentRect) {
|
|
|
|
|
return {minX: 0, maxX: 0, minY: 0, maxY: 0};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const centerX = (windowSize.width - windowSize.width * scale) / 2;
|
|
|
|
|
const centerY = (windowSize.height - windowSize.height * scale) / 2;
|
|
|
|
|
|
|
|
|
|
// If content is outside window we calculate offset boundaries
|
|
|
|
|
// based on initial content rect and current scale
|
|
|
|
|
const minX = Math.max(-this.initialContentRect.left * scale, centerX);
|
|
|
|
|
const maxX = windowSize.width - this.initialContentRect.right * scale;
|
|
|
|
|
|
|
|
|
|
const minY = Math.max(-this.initialContentRect.top * scale + offsetTop, centerY);
|
|
|
|
|
const maxY = windowSize.height - this.initialContentRect.bottom * scale;
|
|
|
|
|
|
|
|
|
|
return {minX, maxX, minY, maxY};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
protected setZoomValue = (value = this.transform.scale) => {
|
|
|
|
|
this.initialContentRect ??= this.content.media.getBoundingClientRect();
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// this.zoomValue = value;
|
|
|
|
|
if(value === ZOOM_INITIAL_VALUE) {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.transform.x = 0;
|
|
|
|
|
this.transform.y = 0;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.moversContainer.style.transform = `translate3d(${this.transform.x.toFixed(3)}px, ${this.transform.y.toFixed(3)}px, 0px) scale(${value.toFixed(3)})`;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
this.zoomElements.btnOut.classList.toggle('inactive', value <= ZOOM_MIN_VALUE);
|
|
|
|
|
this.zoomElements.btnIn.classList.toggle('inactive', value >= ZOOM_MAX_VALUE);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.toggleZoom(value !== ZOOM_INITIAL_VALUE);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected setBtnMenuToggle(buttons: ButtonMenuItemOptions[]) {
|
2023-01-06 20:27:29 +01:00
|
|
|
|
const btnMenuToggle = ButtonMenuToggle({buttonOptions: {onlyMobile: true}, direction: 'bottom-left', buttons});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.topbar.append(btnMenuToggle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public close(e?: MouseEvent) {
|
|
|
|
|
if(e) {
|
|
|
|
|
cancelEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(this.setMoverAnimationPromise) return Promise.reject();
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.closing = true;
|
|
|
|
|
this.swipeHandler?.removeListeners();
|
|
|
|
|
|
2022-04-15 23:19:57 +02:00
|
|
|
|
if(this.navigationItem) {
|
|
|
|
|
appNavigationController.removeItem(this.navigationItem);
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.lazyLoadQueue.clear();
|
|
|
|
|
|
|
|
|
|
const promise = this.setMoverToTarget(this.target?.element, true).then(({onAnimationEnd}) => onAnimationEnd);
|
|
|
|
|
|
|
|
|
|
this.listLoader.reset();
|
2022-04-15 20:04:56 +02:00
|
|
|
|
(this.listLoader as SearchListLoader<any>).cleanup && (this.listLoader as SearchListLoader<any>).cleanup();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.setMoverPromise = null;
|
|
|
|
|
this.tempId = -1;
|
2022-04-15 20:04:56 +02:00
|
|
|
|
if((window as any).appMediaViewer === this) {
|
|
|
|
|
(window as any).appMediaViewer = undefined;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if(appSidebarRight.historyTabIDs.slice(-1)[0] === AppSidebarRight.SLIDERITEMSIDS.forward) {
|
|
|
|
|
promise.then(() => {
|
|
|
|
|
appSidebarRight.forwardTab.closeBtn.click();
|
|
|
|
|
});
|
|
|
|
|
} */
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.removeGlobalListeners();
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
promise.finally(() => {
|
|
|
|
|
this.wholeDiv.remove();
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.toggleOverlay(false);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
protected toggleOverlay(active: boolean) {
|
2022-06-17 18:01:43 +02:00
|
|
|
|
overlayCounter.isOverlayActive = active;
|
2022-11-06 14:48:41 +01:00
|
|
|
|
animationIntersector.checkAnimations2(active);
|
2022-04-15 20:04:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected toggleGlobalListeners(active: boolean) {
|
|
|
|
|
if(active) this.setGlobalListeners();
|
|
|
|
|
else this.removeGlobalListeners();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected removeGlobalListeners() {
|
|
|
|
|
window.removeEventListener('keydown', this.onKeyDown);
|
|
|
|
|
window.removeEventListener('keyup', this.onKeyUp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected setGlobalListeners() {
|
|
|
|
|
window.addEventListener('keydown', this.onKeyDown);
|
|
|
|
|
window.addEventListener('keyup', this.onKeyUp);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
onClick = (e: MouseEvent) => {
|
2023-02-27 14:11:37 +01:00
|
|
|
|
if(this.ignoreNextClick) {
|
|
|
|
|
this.ignoreNextClick = undefined;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(this.setMoverAnimationPromise) return;
|
|
|
|
|
|
|
|
|
|
const target = e.target as HTMLElement;
|
|
|
|
|
if(target.tagName === 'A') return;
|
|
|
|
|
cancelEvent(e);
|
|
|
|
|
|
|
|
|
|
if(IS_TOUCH_SUPPORTED) {
|
|
|
|
|
if(this.highlightSwitchersTimeout) {
|
|
|
|
|
clearTimeout(this.highlightSwitchersTimeout);
|
|
|
|
|
} else {
|
|
|
|
|
this.wholeDiv.classList.add('highlight-switchers');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.highlightSwitchersTimeout = window.setTimeout(() => {
|
|
|
|
|
this.wholeDiv.classList.remove('highlight-switchers');
|
|
|
|
|
this.highlightSwitchersTimeout = 0;
|
|
|
|
|
}, 3e3);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
if(hasMouseMovedSinceDown(e)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isZooming = this.isZooming && false;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
let mover: HTMLElement = null;
|
|
|
|
|
const classNames = ['ckin__player', 'media-viewer-buttons', 'media-viewer-author', 'media-viewer-caption', 'zoom-container'];
|
|
|
|
|
if(isZooming) {
|
|
|
|
|
classNames.push('media-viewer-movers');
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
classNames.find((s) => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
try {
|
|
|
|
|
mover = findUpClassName(target, s);
|
|
|
|
|
if(mover) return true;
|
|
|
|
|
} catch(err) {return false;}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(/* target === this.mediaViewerDiv */!mover || (!isZooming && (target.tagName === 'IMG' || target.tagName === 'image'))) {
|
2022-02-11 16:54:51 +01:00
|
|
|
|
this.close();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private onKeyDown = (e: KeyboardEvent) => {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// this.log('onKeyDown', e);
|
2022-06-17 18:01:43 +02:00
|
|
|
|
if(overlayCounter.overlaysActive > 1) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-24 14:40:18 +01:00
|
|
|
|
|
2021-12-01 13:46:58 +01:00
|
|
|
|
const key = e.key;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
let good = true;
|
2021-11-24 14:40:18 +01:00
|
|
|
|
if(key === 'ArrowRight') {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
!this.isZooming && this.buttons.next.click();
|
2021-11-24 14:40:18 +01:00
|
|
|
|
} else if(key === 'ArrowLeft') {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
!this.isZooming && this.buttons.prev.click();
|
2021-11-24 14:40:18 +01:00
|
|
|
|
} else if(key === '-' || key === '=') {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(this.ctrlKeyDown) {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.addZoomStep(key === '=');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
good = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(e.ctrlKey || e.metaKey) {
|
|
|
|
|
this.ctrlKeyDown = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(good) {
|
|
|
|
|
cancelEvent(e);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private onKeyUp = (e: KeyboardEvent) => {
|
2022-06-17 18:01:43 +02:00
|
|
|
|
if(overlayCounter.overlaysActive > 1) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!(e.ctrlKey || e.metaKey)) {
|
|
|
|
|
this.ctrlKeyDown = false;
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
if(this.isZooming) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.setZoomValue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
protected async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
|
|
|
|
|
this.dispatchEvent('setMoverBefore');
|
|
|
|
|
|
|
|
|
|
const mover = this.content.mover;
|
|
|
|
|
|
|
|
|
|
if(!closing) {
|
|
|
|
|
mover.innerHTML = '';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// mover.append(this.buttons.prev, this.buttons.next);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const zoomValue = this.isZooming && closing /* && false */ ? this.transform.scale : ZOOM_INITIAL_VALUE;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
/* if(!(zoomValue > 1 && closing)) */ this.removeCenterFromMover(mover);
|
|
|
|
|
|
|
|
|
|
const wasActive = fromRight !== 0;
|
|
|
|
|
|
2023-03-01 11:20:49 +01:00
|
|
|
|
const delay = liteMode.isAvailable('animations') ? (wasActive ? 350 : 200) : 0;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// let delay = wasActive ? 350 : 10000;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
/* if(wasActive) {
|
|
|
|
|
this.moveTheMover(mover);
|
|
|
|
|
mover = this.setNewMover();
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
/* if(DEBUG) {
|
|
|
|
|
this.log('setMoverToTarget', target, closing, wasActive, fromRight);
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
let realParent: HTMLElement;
|
|
|
|
|
|
|
|
|
|
let rect: DOMRect;
|
|
|
|
|
if(target) {
|
|
|
|
|
if(target instanceof AvatarElement || target.classList.contains('grid-item')/* || target.classList.contains('document-ico') */) {
|
|
|
|
|
realParent = target;
|
|
|
|
|
rect = target.getBoundingClientRect();
|
|
|
|
|
} else if(target instanceof SVGImageElement || target.parentElement instanceof SVGForeignObjectElement) {
|
|
|
|
|
realParent = findUpClassName(target, 'attachment');
|
|
|
|
|
rect = realParent.getBoundingClientRect();
|
|
|
|
|
} else if(target.classList.contains('profile-avatars-avatar')) {
|
|
|
|
|
realParent = findUpClassName(target, 'profile-avatars-container');
|
|
|
|
|
rect = realParent.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
// * if not active avatar
|
|
|
|
|
if(closing && target.getBoundingClientRect().left !== rect.left) {
|
|
|
|
|
target = realParent = rect = undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!target) {
|
|
|
|
|
target = this.content.media;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!rect) {
|
|
|
|
|
realParent = target.parentElement as HTMLElement;
|
|
|
|
|
rect = target.getBoundingClientRect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let needOpacity = false;
|
|
|
|
|
if(target !== this.content.media && !target.classList.contains('profile-avatars-avatar')) {
|
|
|
|
|
const overflowElement = findUpClassName(realParent, 'scrollable');
|
2022-01-08 13:52:14 +01:00
|
|
|
|
const visibleRect = getVisibleRect(realParent, overflowElement, true);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
if(closing && (!visibleRect || visibleRect.overflow.vertical === 2 || visibleRect.overflow.horizontal === 2)) {
|
|
|
|
|
target = this.content.media;
|
|
|
|
|
realParent = target.parentElement as HTMLElement;
|
|
|
|
|
rect = target.getBoundingClientRect();
|
|
|
|
|
} else if(visibleRect && (visibleRect.overflow.vertical === 1 || visibleRect.overflow.horizontal === 1)) {
|
|
|
|
|
needOpacity = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const containerRect = this.content.media.getBoundingClientRect();
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
let transform = '';
|
|
|
|
|
let left: number;
|
|
|
|
|
let top: number;
|
|
|
|
|
|
|
|
|
|
if(wasActive) {
|
2021-12-11 17:37:08 +01:00
|
|
|
|
left = fromRight === 1 ? windowSize.width : -containerRect.width;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
top = containerRect.top;
|
|
|
|
|
} else {
|
|
|
|
|
left = rect.left;
|
|
|
|
|
top = rect.top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if(zoomValue > 1) { // 33
|
|
|
|
|
// const diffX = (rect.width * zoomValue - rect.width) / 4;
|
|
|
|
|
const diffX = (rect.width * zoomValue - rect.width) / 2;
|
|
|
|
|
const diffY = (rect.height * zoomValue - rect.height) / 4;
|
|
|
|
|
// left -= diffX;
|
|
|
|
|
// top += diffY;
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
transform += `translate3d(${left}px,${top}px,0) `;
|
|
|
|
|
|
|
|
|
|
/* if(wasActive) {
|
|
|
|
|
left = fromRight === 1 ? appPhotosManager.windowW / 2 : -(containerRect.width + appPhotosManager.windowW / 2);
|
|
|
|
|
transform += `translate(${left}px,-50%) `;
|
|
|
|
|
} else {
|
|
|
|
|
left = rect.left - (appPhotosManager.windowW / 2);
|
|
|
|
|
top = rect.top - (appPhotosManager.windowH / 2);
|
|
|
|
|
transform += `translate(${left}px,${top}px) `;
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
let aspecter: HTMLDivElement;
|
|
|
|
|
if(target instanceof HTMLImageElement || target instanceof HTMLVideoElement || target.tagName === 'DIV') {
|
|
|
|
|
if(mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter')) {
|
|
|
|
|
aspecter = mover.firstElementChild as HTMLDivElement;
|
|
|
|
|
|
|
|
|
|
const player = aspecter.querySelector('.ckin__player');
|
|
|
|
|
if(player) {
|
|
|
|
|
const video = player.firstElementChild as HTMLVideoElement;
|
|
|
|
|
aspecter.append(video);
|
|
|
|
|
player.remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!aspecter.style.cssText) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
|
|
|
|
|
mover.classList.remove('active');
|
|
|
|
|
this.setFullAspect(aspecter, containerRect, rect);
|
|
|
|
|
void mover.offsetLeft; // reflow
|
|
|
|
|
mover.classList.add('active');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
aspecter = document.createElement('div');
|
|
|
|
|
aspecter.classList.add('media-viewer-aspecter'/* , 'disable-hover' */);
|
|
|
|
|
mover.prepend(aspecter);
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
aspecter.style.cssText = `width: ${rect.width}px; height: ${rect.height}px; transform: scale3d(${containerRect.width / rect.width}, ${containerRect.height / rect.height}, 1);`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mover.style.width = containerRect.width + 'px';
|
|
|
|
|
mover.style.height = containerRect.height + 'px';
|
|
|
|
|
|
|
|
|
|
// const scaleX = rect.width / (containerRect.width * zoomValue);
|
|
|
|
|
// const scaleY = rect.height / (containerRect.height * zoomValue);
|
|
|
|
|
const scaleX = rect.width / containerRect.width;
|
|
|
|
|
const scaleY = rect.height / containerRect.height;
|
|
|
|
|
if(!wasActive) {
|
|
|
|
|
transform += `scale3d(${scaleX},${scaleY},1) `;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let borderRadius = window.getComputedStyle(realParent).getPropertyValue('border-radius');
|
|
|
|
|
const brSplitted = fillPropertyValue(borderRadius) as string[];
|
2022-06-17 18:01:43 +02:00
|
|
|
|
borderRadius = brSplitted.map((r) => (parseInt(r) / scaleX) + 'px').join(' ');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(!wasActive) {
|
|
|
|
|
mover.style.borderRadius = borderRadius;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// let borderRadius = '0px 0px 0px 0px';
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
if(closing && zoomValue !== 1) {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const left = rect.left - (windowSize.width * scaleX - rect.width) / 2;
|
|
|
|
|
const top = rect.top - (windowSize.height * scaleY - rect.height) / 2;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.moversContainer.style.transform = `matrix(${scaleX}, 0, 0, ${scaleY}, ${left}, ${top})`;
|
|
|
|
|
} else {
|
|
|
|
|
mover.style.transform = transform;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
needOpacity && (mover.style.opacity = '0'/* !closing ? '0' : '' */);
|
|
|
|
|
|
|
|
|
|
/* if(wasActive) {
|
|
|
|
|
this.log('setMoverToTarget', mover.style.transform);
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
let path: SVGPathElement;
|
|
|
|
|
const isOut = target.classList.contains('is-out');
|
|
|
|
|
|
|
|
|
|
const deferred = this.setMoverAnimationPromise = deferredPromise<void>();
|
|
|
|
|
const ret = {onAnimationEnd: deferred};
|
|
|
|
|
|
|
|
|
|
const timeout = setTimeout(() => {
|
|
|
|
|
if(!deferred.isFulfilled && !deferred.isRejected) {
|
|
|
|
|
deferred.resolve();
|
|
|
|
|
}
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
|
|
|
|
deferred.finally(() => {
|
|
|
|
|
this.dispatchEvent('setMoverAfter');
|
|
|
|
|
|
|
|
|
|
if(this.setMoverAnimationPromise === deferred) {
|
|
|
|
|
this.setMoverAnimationPromise = null;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(!closing) {
|
2022-08-28 18:50:04 +02:00
|
|
|
|
let mediaElement: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
let src: string;
|
|
|
|
|
|
2022-08-28 18:50:04 +02:00
|
|
|
|
// if(target instanceof HTMLVideoElement) {
|
|
|
|
|
const selector = 'video, img, .canvas-thumbnail';
|
|
|
|
|
const queryFrom = target.matches(selector) ? target.parentElement : target;
|
|
|
|
|
const elements = Array.from(queryFrom.querySelectorAll(selector)) as HTMLImageElement[];
|
|
|
|
|
if(elements.length) {
|
|
|
|
|
target = elements.pop();
|
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
|
const context = canvas.getContext('2d');
|
|
|
|
|
if(target instanceof HTMLImageElement) {
|
|
|
|
|
canvas.width = target.naturalWidth;
|
|
|
|
|
canvas.height = target.naturalHeight;
|
|
|
|
|
} else if(target instanceof HTMLVideoElement) {
|
|
|
|
|
canvas.width = target.videoWidth;
|
|
|
|
|
canvas.height = target.videoHeight;
|
|
|
|
|
} else if(target instanceof HTMLCanvasElement) {
|
|
|
|
|
canvas.width = target.width;
|
|
|
|
|
canvas.height = target.height;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-28 18:50:04 +02:00
|
|
|
|
|
|
|
|
|
canvas.className = 'canvas-thumbnail thumbnail media-photo';
|
|
|
|
|
context.drawImage(target as HTMLImageElement | HTMLCanvasElement, 0, 0);
|
|
|
|
|
target = canvas;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-28 18:50:04 +02:00
|
|
|
|
// }
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
if(target.tagName === 'DIV' || target.tagName === 'AVATAR-ELEMENT') { // useContainerAsTarget
|
|
|
|
|
const images = Array.from(target.querySelectorAll('img')) as HTMLImageElement[];
|
|
|
|
|
const image = images.pop();
|
|
|
|
|
if(image) {
|
|
|
|
|
mediaElement = new Image();
|
|
|
|
|
src = image.src;
|
|
|
|
|
mover.append(mediaElement);
|
|
|
|
|
}
|
|
|
|
|
/* mediaElement = new Image();
|
|
|
|
|
src = target.style.backgroundImage.slice(5, -2); */
|
|
|
|
|
} else if(target instanceof HTMLImageElement) {
|
|
|
|
|
mediaElement = new Image();
|
|
|
|
|
src = target.src;
|
|
|
|
|
} else if(target instanceof HTMLVideoElement) {
|
2022-04-15 20:04:56 +02:00
|
|
|
|
mediaElement = createVideo();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
mediaElement.src = target.src;
|
|
|
|
|
} else if(target instanceof SVGSVGElement) {
|
|
|
|
|
const clipId = target.dataset.clipId;
|
|
|
|
|
const newClipId = clipId + '-mv';
|
|
|
|
|
|
|
|
|
|
const {width, height} = containerRect;
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
const newSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
newSvg.setAttributeNS(null, 'width', '' + width);
|
|
|
|
|
newSvg.setAttributeNS(null, 'height', '' + height);
|
|
|
|
|
|
|
|
|
|
// нижние два свойства для масштабирования
|
|
|
|
|
newSvg.setAttributeNS(null, 'viewBox', `0 0 ${width} ${height}`);
|
|
|
|
|
newSvg.setAttributeNS(null, 'preserveAspectRatio', 'xMidYMid meet');
|
|
|
|
|
|
|
|
|
|
newSvg.insertAdjacentHTML('beforeend', target.firstElementChild.outerHTML.replace(clipId, newClipId));
|
|
|
|
|
newSvg.insertAdjacentHTML('beforeend', target.lastElementChild.outerHTML.replace(clipId, newClipId));
|
|
|
|
|
|
|
|
|
|
// теперь надо выставить новую позицию для хвостика
|
|
|
|
|
const defs = newSvg.firstElementChild;
|
|
|
|
|
const use = defs.firstElementChild.firstElementChild as SVGUseElement;
|
|
|
|
|
if(use instanceof SVGUseElement) {
|
|
|
|
|
let transform = use.getAttributeNS(null, 'transform');
|
|
|
|
|
transform = transform.replace(/translate\((.+?), (.+?)\) scale\((.+?), (.+?)\)/, (match, x, y, sX, sY) => {
|
|
|
|
|
x = +x;
|
|
|
|
|
if(x !== 2) {
|
|
|
|
|
x = width - (2 / scaleX);
|
|
|
|
|
} else {
|
|
|
|
|
x = 2 / scaleX;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
y = height;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
return `translate(${x}, ${y}) scale(${+sX / scaleX}, ${+sY / scaleY})`;
|
|
|
|
|
});
|
|
|
|
|
use.setAttributeNS(null, 'transform', transform);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// и новый RECT
|
|
|
|
|
path = defs.firstElementChild.lastElementChild as SVGPathElement;
|
|
|
|
|
|
|
|
|
|
// код ниже нужен только чтобы скрыть моргание до момента как сработает таймаут
|
|
|
|
|
let d: string;
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const br: [number, number, number, number] = borderRadius.split(' ').map((v) => parseInt(v)) as any;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(isOut) d = generatePathData(0, 0, width - 9 / scaleX, height, ...br);
|
|
|
|
|
else d = generatePathData(9 / scaleX, 0, width - 9 / scaleX, height, ...br);
|
|
|
|
|
path.setAttributeNS(null, 'd', d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const foreignObject = newSvg.lastElementChild;
|
|
|
|
|
foreignObject.setAttributeNS(null, 'width', '' + containerRect.width);
|
|
|
|
|
foreignObject.setAttributeNS(null, 'height', '' + containerRect.height);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
mover.prepend(newSvg);
|
2022-08-28 18:50:04 +02:00
|
|
|
|
} else if(target instanceof HTMLCanvasElement) {
|
|
|
|
|
mediaElement = target;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(aspecter) {
|
|
|
|
|
aspecter.style.borderRadius = borderRadius;
|
|
|
|
|
|
|
|
|
|
if(mediaElement) {
|
|
|
|
|
aspecter.append(mediaElement);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mediaElement = mover.querySelector('video, img');
|
|
|
|
|
if(mediaElement instanceof HTMLImageElement) {
|
|
|
|
|
mediaElement.classList.add('thumbnail');
|
|
|
|
|
if(!aspecter) {
|
|
|
|
|
mediaElement.style.width = containerRect.width + 'px';
|
|
|
|
|
mediaElement.style.height = containerRect.height + 'px';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src) {
|
|
|
|
|
await renderImageFromUrlPromise(mediaElement, src);
|
|
|
|
|
}
|
|
|
|
|
}/* else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && ((mediaElement.firstElementChild as HTMLSourceElement).src || src)) {
|
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
|
mediaElement.addEventListener('loadeddata', resolve);
|
|
|
|
|
|
|
|
|
|
if(src) {
|
|
|
|
|
(mediaElement.firstElementChild as HTMLSourceElement).src = src;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} */
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
mover.style.display = '';
|
|
|
|
|
|
|
|
|
|
fastRaf(() => {
|
|
|
|
|
mover.classList.add(wasActive ? 'moving' : 'active');
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
/* if(mover.classList.contains('center')) {
|
|
|
|
|
mover.classList.remove('center');
|
|
|
|
|
void mover.offsetLeft; // reflow
|
|
|
|
|
} */
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(target instanceof SVGSVGElement) {
|
|
|
|
|
path = mover.querySelector('path');
|
|
|
|
|
|
|
|
|
|
if(path) {
|
|
|
|
|
this.sizeTailPath(path, containerRect, scaleX, delay, false, isOut, borderRadius);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(target.classList.contains('media-viewer-media')) {
|
|
|
|
|
mover.classList.add('hiding');
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.toggleWholeActive(false);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// return ret;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
mover.style.borderRadius = borderRadius;
|
|
|
|
|
|
|
|
|
|
if(mover.firstElementChild) {
|
|
|
|
|
(mover.firstElementChild as HTMLElement).style.borderRadius = borderRadius;
|
|
|
|
|
}
|
|
|
|
|
}, delay / 2);
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
mover.innerHTML = '';
|
|
|
|
|
mover.classList.remove('moving', 'active', 'hiding');
|
|
|
|
|
mover.style.cssText = 'display: none;';
|
|
|
|
|
|
|
|
|
|
deferred.resolve();
|
|
|
|
|
}, delay);
|
|
|
|
|
|
|
|
|
|
mover.classList.remove('opening');
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mover.classList.add('opening');
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// await new Promise((resolve) => setTimeout(resolve, 0));
|
|
|
|
|
// await new Promise((resolve) => window.requestAnimationFrame(resolve));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// * одного RAF'а недостаточно, иногда анимация с одним не срабатывает (преимущественно на мобильных)
|
|
|
|
|
await doubleRaf();
|
|
|
|
|
|
|
|
|
|
// чтобы проверить установленную позицию - раскомментировать
|
|
|
|
|
// throw '';
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// await new Promise((resolve) => setTimeout(resolve, 5e3));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
mover.style.transform = `translate3d(${containerRect.left}px,${containerRect.top}px,0) scale3d(1,1,1)`;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// mover.style.transform = `translate(-50%,-50%) scale(1,1)`;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
needOpacity && (mover.style.opacity = ''/* closing ? '0' : '' */);
|
|
|
|
|
|
|
|
|
|
if(aspecter) {
|
|
|
|
|
this.setFullAspect(aspecter, containerRect, rect);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// throw '';
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
mover.style.borderRadius = '';
|
|
|
|
|
|
|
|
|
|
if(mover.firstElementChild) {
|
|
|
|
|
(mover.firstElementChild as HTMLElement).style.borderRadius = '';
|
|
|
|
|
}
|
|
|
|
|
}, 0/* delay / 2 */);
|
|
|
|
|
|
|
|
|
|
mover.dataset.timeout = '' + setTimeout(() => {
|
|
|
|
|
mover.classList.remove('moving', 'opening');
|
|
|
|
|
|
|
|
|
|
if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать
|
|
|
|
|
if(mover.querySelector('video') || true) {
|
|
|
|
|
mover.classList.remove('active');
|
|
|
|
|
aspecter.style.cssText = '';
|
|
|
|
|
void mover.offsetLeft; // reflow
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// aspecter.classList.remove('disable-hover');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// эти строки нужны для установки центральной позиции, в случае ресайза это будет нужно
|
|
|
|
|
mover.classList.add('center', 'no-transition');
|
|
|
|
|
/* mover.style.left = mover.style.top = '50%';
|
|
|
|
|
mover.style.transform = 'translate(-50%, -50%)';
|
|
|
|
|
void mover.offsetLeft; // reflow */
|
|
|
|
|
|
|
|
|
|
// это уже нужно для будущих анимаций
|
|
|
|
|
mover.classList.add('active');
|
|
|
|
|
delete mover.dataset.timeout;
|
|
|
|
|
|
|
|
|
|
deferred.resolve();
|
|
|
|
|
}, delay);
|
|
|
|
|
|
|
|
|
|
if(path) {
|
|
|
|
|
this.sizeTailPath(path, containerRect, scaleX, delay, true, isOut, borderRadius);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
protected toggleWholeActive(active: boolean) {
|
|
|
|
|
if(active) {
|
|
|
|
|
this.wholeDiv.classList.add('active');
|
|
|
|
|
} else {
|
|
|
|
|
this.wholeDiv.classList.add('backwards');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.wholeDiv.classList.remove('active');
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
protected setFullAspect(aspecter: HTMLDivElement, containerRect: DOMRect, rect: DOMRect) {
|
|
|
|
|
/* let media = aspecter.firstElementChild;
|
|
|
|
|
let proportion: number;
|
|
|
|
|
if(media instanceof HTMLImageElement) {
|
|
|
|
|
proportion = media.naturalWidth / media.naturalHeight;
|
|
|
|
|
} else if(media instanceof HTMLVideoElement) {
|
|
|
|
|
proportion = media.videoWidth / media.videoHeight;
|
|
|
|
|
} */
|
|
|
|
|
const proportion = containerRect.width / containerRect.height;
|
|
|
|
|
|
|
|
|
|
let {width, height} = rect;
|
|
|
|
|
/* if(proportion === 1) {
|
|
|
|
|
aspecter.style.cssText = '';
|
|
|
|
|
} else { */
|
2022-08-04 08:49:54 +02:00
|
|
|
|
if(proportion > 0) {
|
|
|
|
|
width = height * proportion;
|
|
|
|
|
} else {
|
|
|
|
|
height = width * proportion;
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// this.log('will set style aspecter:', `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
aspecter.style.cssText = `width: ${width}px; height: ${height}px; transform: scale3d(${containerRect.width / width}, ${containerRect.height / height}, 1);`;
|
|
|
|
|
// }
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected sizeTailPath(path: SVGPathElement, rect: DOMRect, scaleX: number, delay: number, upscale: boolean, isOut: boolean, borderRadius: string) {
|
|
|
|
|
const start = Date.now();
|
|
|
|
|
const {width, height} = rect;
|
|
|
|
|
delay = delay / 2;
|
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const br = borderRadius.split(' ').map((v) => parseInt(v));
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
const step = () => {
|
|
|
|
|
const diff = Date.now() - start;
|
|
|
|
|
|
|
|
|
|
let progress = delay ? diff / delay : 1;
|
|
|
|
|
if(progress > 1) progress = 1;
|
|
|
|
|
if(upscale) progress = 1 - progress;
|
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const _br: [number, number, number, number] = br.map((v) => v * progress) as any;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
let d: string;
|
|
|
|
|
if(isOut) d = generatePathData(0, 0, width - (9 / scaleX * progress), height, ..._br);
|
|
|
|
|
else d = generatePathData(9 / scaleX * progress, 0, width/* width - (9 / scaleX * progress) */, height, ..._br);
|
|
|
|
|
path.setAttributeNS(null, 'd', d);
|
|
|
|
|
|
|
|
|
|
if(diff < delay) fastRaf(step);
|
|
|
|
|
};
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// window.requestAnimationFrame(step);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
step();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected removeCenterFromMover(mover: HTMLElement) {
|
|
|
|
|
if(mover.classList.contains('center')) {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// const rect = mover.getBoundingClientRect();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const rect = this.content.media.getBoundingClientRect();
|
|
|
|
|
mover.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`;
|
|
|
|
|
mover.classList.remove('center');
|
|
|
|
|
void mover.offsetLeft; // reflow
|
|
|
|
|
mover.classList.remove('no-transition');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected moveTheMover(mover: HTMLElement, toLeft = true) {
|
2021-12-11 17:37:08 +01:00
|
|
|
|
const windowW = windowSize.width;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.removeCenterFromMover(mover);
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// mover.classList.remove('active');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
mover.classList.add('moving');
|
|
|
|
|
|
|
|
|
|
if(mover.dataset.timeout) { // и это тоже всё из-за скейла видео, так бы это не нужно было
|
|
|
|
|
clearTimeout(+mover.dataset.timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const rect = mover.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
const newTransform = mover.style.transform.replace(/translate3d\((.+?),/, (match, p1) => {
|
|
|
|
|
const x = toLeft ? -rect.width : windowW;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// const x = toLeft ? -(rect.right + (rect.width / 2)) : windowW / 2;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
return match.replace(p1, x + 'px');
|
|
|
|
|
});
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// //////this.log('set newTransform:', newTransform, mover.style.transform, toLeft);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
mover.style.transform = newTransform;
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
mover.remove();
|
|
|
|
|
}, 350);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected setNewMover() {
|
|
|
|
|
const newMover = document.createElement('div');
|
|
|
|
|
newMover.classList.add('media-viewer-mover');
|
|
|
|
|
newMover.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
if(this.content.mover) {
|
|
|
|
|
const oldMover = this.content.mover;
|
|
|
|
|
oldMover.parentElement.append(newMover);
|
|
|
|
|
} else {
|
|
|
|
|
this.moversContainer.append(newMover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.content.mover = newMover;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// if(target instanceof SVGSVGElement) {
|
|
|
|
|
const el = target.tagName.toLowerCase() === tagName ? target : target.querySelector(tagName) as HTMLElement;
|
|
|
|
|
if(el && !findUpClassName(target, 'document')) {
|
|
|
|
|
if(findUpClassName(target, 'attachment')) {
|
|
|
|
|
// two parentElements because element can be contained in aspecter
|
|
|
|
|
const preloader = target.parentElement.parentElement.querySelector('.preloader-container') as HTMLElement;
|
|
|
|
|
if(preloader) {
|
|
|
|
|
if(tagName === 'video') {
|
|
|
|
|
if(preloader.classList.contains('manual')) {
|
|
|
|
|
preloader.click();
|
|
|
|
|
// return;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
return;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
preloader.remove();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-09-16 19:44:10 +02:00
|
|
|
|
if((el as HTMLImageElement).src !== url) {
|
|
|
|
|
renderImageFromUrl(el, url);
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// ! костыль, но он тут даже и не нужен
|
|
|
|
|
if(el.classList.contains('thumbnail') && el.parentElement.classList.contains('media-container-aspecter')) {
|
|
|
|
|
el.classList.remove('thumbnail');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
/* } else {
|
|
|
|
|
|
|
|
|
|
} */
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-04 15:38:17 +01:00
|
|
|
|
protected setAuthorInfo(fromId: PeerId | string, timestamp: number) {
|
|
|
|
|
const isPeerId = fromId.isPeerId();
|
2022-06-17 18:01:43 +02:00
|
|
|
|
let wrapTitlePromise: Promise<HTMLElement> | HTMLElement;
|
2021-11-04 15:38:17 +01:00
|
|
|
|
if(isPeerId) {
|
2022-06-17 18:01:43 +02:00
|
|
|
|
wrapTitlePromise = wrapPeerTitle({
|
2021-11-04 15:38:17 +01:00
|
|
|
|
peerId: fromId as PeerId,
|
|
|
|
|
dialog: false,
|
|
|
|
|
onlyFirstName: false,
|
|
|
|
|
plainText: false
|
2022-06-17 18:01:43 +02:00
|
|
|
|
})
|
2021-11-04 15:38:17 +01:00
|
|
|
|
} else {
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const title = wrapTitlePromise = document.createElement('span');
|
2022-04-25 16:54:30 +02:00
|
|
|
|
title.append(wrapEmojiText(fromId));
|
2021-11-04 15:38:17 +01:00
|
|
|
|
title.classList.add('peer-title');
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
const oldAvatar = this.author.avatarEl;
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const newAvatar = this.author.avatarEl = (oldAvatar.cloneNode() as AvatarElement);
|
|
|
|
|
|
|
|
|
|
return Promise.all([
|
|
|
|
|
(this.author.avatarEl as AvatarElement).updateWithOptions({
|
|
|
|
|
peerId: fromId as PeerId || NULL_PEER_ID,
|
|
|
|
|
peerTitle: isPeerId ? undefined : '' + fromId
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
wrapTitlePromise
|
|
|
|
|
]).then(([_, title]) => {
|
|
|
|
|
if(this.author.avatarEl !== newAvatar) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-04 15:38:17 +01:00
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
replaceContent(this.author.date, formatFullSentTime(timestamp));
|
|
|
|
|
replaceContent(this.author.nameEl, title);
|
|
|
|
|
oldAvatar.replaceWith(this.author.avatarEl);
|
|
|
|
|
});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
protected async _openMedia(
|
2022-08-04 08:49:54 +02:00
|
|
|
|
media: MyDocument | MyPhoto,
|
|
|
|
|
timestamp: number,
|
|
|
|
|
fromId: PeerId | string,
|
|
|
|
|
fromRight: number,
|
|
|
|
|
target?: HTMLElement,
|
|
|
|
|
reverse = false,
|
|
|
|
|
prevTargets: TargetType[] = [],
|
|
|
|
|
nextTargets: TargetType[] = [],
|
2021-11-04 15:38:17 +01:00
|
|
|
|
message?: MyMessage
|
2021-10-05 22:40:07 +02:00
|
|
|
|
/* , needLoadMore = true */
|
|
|
|
|
) {
|
|
|
|
|
if(this.setMoverPromise) return this.setMoverPromise;
|
|
|
|
|
|
|
|
|
|
/* if(DEBUG) {
|
|
|
|
|
this.log('openMedia:', media, fromId, prevTargets, nextTargets);
|
|
|
|
|
} */
|
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const setAuthorPromise = this.setAuthorInfo(fromId, timestamp);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const isDocument = media._ === 'document';
|
|
|
|
|
const isVideo = isDocument && media.mime_type && ((['video', 'gif'] as MyDocument['type'][]).includes(media.type) || media.mime_type.indexOf('video/') === 0);
|
|
|
|
|
|
|
|
|
|
if(this.isFirstOpen) {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// this.targetContainer = targetContainer;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// this.needLoadMore = needLoadMore;
|
|
|
|
|
this.isFirstOpen = false;
|
|
|
|
|
this.listLoader.setTargets(prevTargets, nextTargets, reverse);
|
|
|
|
|
(window as any).appMediaViewer = this;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// this.loadMore = loadMore;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
/* if(appSidebarRight.historyTabIDs.slice(-1)[0] === AppSidebarRight.SLIDERITEMSIDS.forward) {
|
|
|
|
|
appSidebarRight.forwardTab.closeBtn.click();
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
|
|
|
} */
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 21:21:36 +01:00
|
|
|
|
if(this.listLoader.next.length < 10) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.listLoader.load(true);
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// if(prevTarget && (!prevTarget.parentElement || !this.isElementVisible(this.targetContainer, prevTarget))) prevTarget = null;
|
|
|
|
|
// if(nextTarget && (!nextTarget.parentElement || !this.isElementVisible(this.targetContainer, nextTarget))) nextTarget = null;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
this.buttons.prev.classList.toggle('hide', !this.listLoader.previous.length);
|
|
|
|
|
this.buttons.next.classList.toggle('hide', !this.listLoader.next.length);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const container = this.content.media;
|
|
|
|
|
const useContainerAsTarget = !target || target === container;
|
|
|
|
|
if(useContainerAsTarget) target = container;
|
|
|
|
|
|
|
|
|
|
this.target = {element: target} as any;
|
|
|
|
|
const tempId = ++this.tempId;
|
|
|
|
|
|
|
|
|
|
if(container.firstElementChild) {
|
|
|
|
|
container.innerHTML = '';
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// ok set
|
|
|
|
|
|
|
|
|
|
const wasActive = fromRight !== 0;
|
|
|
|
|
if(wasActive) {
|
|
|
|
|
this.moveTheMover(this.content.mover, fromRight === 1);
|
|
|
|
|
this.setNewMover();
|
|
|
|
|
} else {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
this.navigationItem = {
|
|
|
|
|
type: 'media',
|
|
|
|
|
onPop: (canAnimate) => {
|
|
|
|
|
if(this.setMoverAnimationPromise) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!canAnimate && IS_MOBILE_SAFARI) {
|
|
|
|
|
this.wholeDiv.remove();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.close();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
appNavigationController.pushItem(this.navigationItem);
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.toggleOverlay(true);
|
|
|
|
|
this.setGlobalListeners();
|
2022-06-17 18:01:43 +02:00
|
|
|
|
await setAuthorPromise;
|
2022-04-15 20:04:56 +02:00
|
|
|
|
|
|
|
|
|
if(!this.wholeDiv.parentElement) {
|
|
|
|
|
this.pageEl.insertBefore(this.wholeDiv, document.getElementById('main-columns'));
|
|
|
|
|
void this.wholeDiv.offsetLeft; // reflow
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.toggleWholeActive(true);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// //////this.log('wasActive:', wasActive);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
const mover = this.content.mover;
|
|
|
|
|
|
2021-12-11 17:37:08 +01:00
|
|
|
|
const maxWidth = windowSize.width;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// const maxWidth = this.pageEl.scrollWidth;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// TODO: const maxHeight = mediaSizes.isMobile ? appPhotosManager.windowH : appPhotosManager.windowH - 100;
|
|
|
|
|
let padding = 0;
|
2021-12-11 17:37:08 +01:00
|
|
|
|
const windowH = windowSize.height;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(windowH < 1000000 && !mediaSizes.isMobile) {
|
|
|
|
|
padding = 120;
|
|
|
|
|
}
|
|
|
|
|
const maxHeight = windowH - 120 - padding;
|
|
|
|
|
let thumbPromise: Promise<any> = Promise.resolve();
|
2022-04-25 16:54:30 +02:00
|
|
|
|
const size = setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true, undefined, !!(isDocument && media.w && media.h)).photoSize;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(useContainerAsTarget) {
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const cacheContext = await this.managers.thumbsStorage.getCacheContext(media, size.type);
|
2022-04-21 17:55:00 +02:00
|
|
|
|
let img: HTMLImageElement | HTMLCanvasElement;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(cacheContext.downloaded) {
|
|
|
|
|
img = new Image();
|
|
|
|
|
img.src = cacheContext.url;
|
|
|
|
|
} else {
|
2022-04-25 16:54:30 +02:00
|
|
|
|
const gotThumb = getStrippedThumbIfNeeded(media, cacheContext, true);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(gotThumb) {
|
|
|
|
|
thumbPromise = gotThumb.loadPromise;
|
|
|
|
|
img = gotThumb.image;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(img) {
|
|
|
|
|
img.classList.add('thumbnail');
|
|
|
|
|
container.append(img);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// need after setAttachmentSize
|
|
|
|
|
/* if(useContainerAsTarget) {
|
|
|
|
|
target = target.querySelector('img, video') || target;
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
const supportsStreaming: boolean = !!(isDocument && media.supportsStreaming);
|
|
|
|
|
const preloader = supportsStreaming ? this.preloaderStreamable : this.preloader;
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const getCacheContext = (type = size?.type) => {
|
|
|
|
|
return this.managers.thumbsStorage.getCacheContext(media, type);
|
2022-06-17 18:01:43 +02:00
|
|
|
|
};
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
let setMoverPromise: Promise<void>;
|
|
|
|
|
if(isVideo) {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// //////this.log('will wrap video', media, size);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
|
|
|
|
// потому что для safari нужно создать элемент из event'а
|
|
|
|
|
// const video = document.createElement('video');
|
|
|
|
|
const useController = message && media.type !== 'gif';
|
2022-08-04 08:49:54 +02:00
|
|
|
|
const video = /* useController ?
|
|
|
|
|
appMediaPlaybackController.addMedia(message, false, true) as HTMLVideoElement :
|
2022-04-15 20:04:56 +02:00
|
|
|
|
*/createVideo({pip: useController});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-11-06 14:48:41 +01:00
|
|
|
|
if(this.wholeDiv.classList.contains('no-forwards')) {
|
|
|
|
|
video.addEventListener('contextmenu', cancelEvent);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const set = () => this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// return; // set and don't move
|
|
|
|
|
// if(wasActive) return;
|
|
|
|
|
// return;
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// const video = mover.querySelector('video') || document.createElement('video');
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const moverVideo = mover.querySelector('video');
|
|
|
|
|
if(moverVideo) {
|
|
|
|
|
moverVideo.remove();
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// video.src = '';
|
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
video.setAttribute('playsinline', 'true');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// * fix for playing video if viewer is closed (https://contest.com/javascript-web-bonus/entry1425#issue11629)
|
|
|
|
|
video.addEventListener('timeupdate', () => {
|
|
|
|
|
if(this.tempId !== tempId) {
|
|
|
|
|
video.pause();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.addEventListener('setMoverAfter', () => {
|
|
|
|
|
video.src = '';
|
|
|
|
|
video.load();
|
|
|
|
|
}, {once: true});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(IS_SAFARI) {
|
|
|
|
|
// test stream
|
|
|
|
|
// video.controls = true;
|
|
|
|
|
video.autoplay = true;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(media.type === 'gif') {
|
|
|
|
|
video.muted = true;
|
|
|
|
|
video.autoplay = true;
|
|
|
|
|
video.loop = true;
|
2022-02-09 14:37:16 +01:00
|
|
|
|
} else if(media.duration < 60) {
|
|
|
|
|
video.loop = true;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// if(!video.parentElement) {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
div.append(video);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// }
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const canPlayThrough = new Promise((resolve) => {
|
|
|
|
|
video.addEventListener('canplay', resolve, {once: true});
|
|
|
|
|
});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const createPlayer = () => {
|
|
|
|
|
if(media.type !== 'gif') {
|
|
|
|
|
video.dataset.ckin = 'default';
|
|
|
|
|
video.dataset.overlay = '1';
|
|
|
|
|
|
|
|
|
|
Promise.all([canPlayThrough, onAnimationEnd]).then(() => {
|
|
|
|
|
if(this.tempId !== tempId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// const play = useController ? appMediaPlaybackController.willBePlayedMedia === video : true;
|
|
|
|
|
const play = true;
|
2022-02-11 15:36:00 +01:00
|
|
|
|
const player = this.videoPlayer = new VideoPlayer({
|
2022-08-04 08:49:54 +02:00
|
|
|
|
video,
|
|
|
|
|
play,
|
2022-02-11 15:36:00 +01:00
|
|
|
|
streamable: supportsStreaming,
|
|
|
|
|
onPlaybackRackMenuToggle: (open) => {
|
|
|
|
|
this.wholeDiv.classList.toggle('hide-caption', !!open);
|
2022-04-15 20:04:56 +02:00
|
|
|
|
},
|
|
|
|
|
onPip: (pip) => {
|
2022-04-21 18:15:40 +02:00
|
|
|
|
const otherMediaViewer = (window as any).appMediaViewer;
|
|
|
|
|
if(!pip && otherMediaViewer && otherMediaViewer !== this) {
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.releaseSingleMedia = undefined;
|
|
|
|
|
this.close();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const mover = this.moversContainer.lastElementChild as HTMLElement;
|
|
|
|
|
mover.classList.toggle('hiding', pip);
|
|
|
|
|
this.toggleWholeActive(!pip);
|
|
|
|
|
this.toggleOverlay(!pip);
|
|
|
|
|
this.toggleGlobalListeners(!pip);
|
|
|
|
|
|
2022-04-15 23:19:57 +02:00
|
|
|
|
if(this.navigationItem) {
|
|
|
|
|
if(pip) appNavigationController.removeItem(this.navigationItem);
|
|
|
|
|
else appNavigationController.pushItem(this.navigationItem);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 20:04:56 +02:00
|
|
|
|
if(useController) {
|
|
|
|
|
if(pip) {
|
|
|
|
|
// appMediaPlaybackController.toggleSwitchers(true);
|
|
|
|
|
|
|
|
|
|
this.releaseSingleMedia(false);
|
|
|
|
|
this.releaseSingleMedia = undefined;
|
|
|
|
|
|
|
|
|
|
appMediaPlaybackController.setPictureInPicture(video);
|
|
|
|
|
} else {
|
|
|
|
|
this.releaseSingleMedia = appMediaPlaybackController.setSingleMedia(video, message as Message.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-17 00:39:01 +02:00
|
|
|
|
},
|
|
|
|
|
onPipClose: () => {
|
|
|
|
|
// this.target = undefined;
|
|
|
|
|
// this.toggleWholeActive(false);
|
|
|
|
|
// this.toggleOverlay(false);
|
|
|
|
|
this.close();
|
2022-02-11 15:36:00 +01:00
|
|
|
|
}
|
|
|
|
|
});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
player.addEventListener('toggleControls', (show) => {
|
|
|
|
|
this.wholeDiv.classList.toggle('has-video-controls', show);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.addEventListener('setMoverBefore', () => {
|
|
|
|
|
this.wholeDiv.classList.remove('has-video-controls');
|
2022-04-15 20:04:56 +02:00
|
|
|
|
this.videoPlayer.cleanup();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.videoPlayer = undefined;
|
|
|
|
|
}, {once: true});
|
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
if(this.isZooming) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.videoPlayer.lockControls(false);
|
|
|
|
|
}
|
|
|
|
|
/* div.append(video);
|
|
|
|
|
mover.append(player.wrapper); */
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(supportsStreaming) {
|
|
|
|
|
onAnimationEnd.then(() => {
|
|
|
|
|
if(video.readyState < video.HAVE_FUTURE_DATA) {
|
2022-08-21 13:52:00 +02:00
|
|
|
|
// console.log('ppp 1');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
preloader.attach(mover, true);
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
/* canPlayThrough.then(() => {
|
|
|
|
|
preloader.detach();
|
|
|
|
|
}); */
|
|
|
|
|
});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
const attachCanPlay = () => {
|
|
|
|
|
video.addEventListener('canplay', () => {
|
2022-08-21 13:52:00 +02:00
|
|
|
|
// console.log('ppp 2');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
preloader.detach();
|
|
|
|
|
video.parentElement.classList.remove('is-buffering');
|
|
|
|
|
}, {once: true});
|
|
|
|
|
};
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
video.addEventListener('waiting', () => {
|
|
|
|
|
const loading = video.networkState === video.NETWORK_LOADING;
|
|
|
|
|
const isntEnoughData = video.readyState < video.HAVE_FUTURE_DATA;
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// this.log('video waiting for progress', loading, isntEnoughData);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(loading && isntEnoughData) {
|
|
|
|
|
attachCanPlay();
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2022-08-21 13:52:00 +02:00
|
|
|
|
// console.log('ppp 3');
|
2021-10-05 22:40:07 +02:00
|
|
|
|
preloader.attach(mover, true);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
// поставлю класс для плеера, чтобы убрать большую иконку пока прелоадер на месте
|
|
|
|
|
video.parentElement.classList.add('is-buffering');
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-08 13:52:14 +01:00
|
|
|
|
|
|
|
|
|
if(this.wholeDiv.classList.contains('no-forwards')) {
|
|
|
|
|
video.addEventListener('contextmenu', (e) => {
|
|
|
|
|
cancelEvent(e);
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
attachCanPlay();
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// if(!video.src || media.url !== video.src) {
|
|
|
|
|
const load = async() => {
|
|
|
|
|
/* if(useController) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
appMediaPlaybackController.resolveWaitingForLoadMedia(message.peerId, message.mid, message.pFlags.is_scheduled);
|
|
|
|
|
} */
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
const promise: Promise<any> = supportsStreaming ? Promise.resolve() : appDownloadManager.downloadMediaURL({media});
|
|
|
|
|
|
|
|
|
|
if(!supportsStreaming) {
|
|
|
|
|
onAnimationEnd.then(async() => {
|
|
|
|
|
if(!(await getCacheContext()).url) {
|
2022-08-21 13:52:00 +02:00
|
|
|
|
// console.log('ppp 4');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
preloader.attach(mover, true, promise);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Promise.all([promise, onAnimationEnd]).then(async() => {
|
|
|
|
|
if(this.tempId !== tempId) {
|
|
|
|
|
this.log.warn('media viewer changed video');
|
|
|
|
|
return;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
const url = (await getCacheContext()).url;
|
|
|
|
|
|
|
|
|
|
video.addEventListener('error', () => {
|
2023-01-16 20:14:20 +01:00
|
|
|
|
toastNew({
|
|
|
|
|
langPackKey: IS_MOBILE ? 'Video.Unsupported.Mobile' : 'Video.Unsupported.Desktop'
|
|
|
|
|
});
|
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
if(video.error.code !== 4) {
|
|
|
|
|
this.log.error('Error ' + video.error.code + '; details: ' + video.error.message);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 20:14:20 +01:00
|
|
|
|
preloader?.detach();
|
2022-08-04 08:49:54 +02:00
|
|
|
|
}, {once: true});
|
2022-06-26 21:41:43 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
if(target instanceof SVGSVGElement/* && (video.parentElement || !isSafari) */) { // if video exists
|
|
|
|
|
// if(!video.parentElement) {
|
|
|
|
|
div.firstElementChild.lastElementChild.append(video);
|
|
|
|
|
// }
|
|
|
|
|
} else {
|
|
|
|
|
renderImageFromUrl(video, url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// * have to set options (especially playbackRate) after src
|
|
|
|
|
// * https://github.com/videojs/video.js/issues/2516
|
|
|
|
|
if(useController) {
|
|
|
|
|
this.releaseSingleMedia = appMediaPlaybackController.setSingleMedia(video, message as Message.message);
|
|
|
|
|
|
|
|
|
|
this.addEventListener('setMoverBefore', () => {
|
|
|
|
|
if(this.releaseSingleMedia) {
|
|
|
|
|
this.releaseSingleMedia();
|
|
|
|
|
this.releaseSingleMedia = undefined;
|
2022-06-26 21:41:43 +02:00
|
|
|
|
}
|
|
|
|
|
}, {once: true});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
}
|
2022-06-26 21:41:43 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
this.updateMediaSource(target, url, 'video');
|
2022-02-09 14:34:34 +01:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
createPlayer();
|
|
|
|
|
});
|
2022-02-09 14:34:34 +01:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
return promise;
|
|
|
|
|
};
|
2021-10-05 22:40:07 +02:00
|
|
|
|
|
2022-08-04 08:49:54 +02:00
|
|
|
|
this.lazyLoadQueue.unshift({load});
|
|
|
|
|
// } else createPlayer();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setMoverPromise = thumbPromise.then(set);
|
|
|
|
|
} else {
|
|
|
|
|
const set = () => this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// return; // set and don't move
|
|
|
|
|
// if(wasActive) return;
|
|
|
|
|
// return;
|
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const load = async() => {
|
|
|
|
|
const cancellablePromise = isDocument ? appDownloadManager.downloadMediaURL({media}) : appDownloadManager.downloadMediaURL({media, thumb: size});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const photoSizes = !isDocument && media.sizes.slice().filter((size) => (size as PhotoSize.photoSize).w) as PhotoSize.photoSize[];
|
2023-02-27 14:11:37 +01:00
|
|
|
|
photoSizes && photoSizes.sort((a, b) => b.size - a.size);
|
|
|
|
|
const fullPhotoSize = photoSizes?.[0];
|
|
|
|
|
const cancellableFullPromise = !isDocument && fullPhotoSize !== size && appDownloadManager.downloadMediaURL({media, thumb: fullPhotoSize});
|
2023-02-17 16:15:35 +01:00
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
onAnimationEnd.then(async() => {
|
|
|
|
|
if(!(await getCacheContext()).url) {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.preloader.attachPromise(cancellablePromise);
|
2022-08-04 08:49:54 +02:00
|
|
|
|
// this.preloader.attach(mover, true, cancellablePromise);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
Promise.all([onAnimationEnd, cancellablePromise]).then(async() => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(this.tempId !== tempId) {
|
|
|
|
|
this.log.warn('media viewer changed photo');
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2022-06-17 18:01:43 +02:00
|
|
|
|
const url = (await getCacheContext()).url;
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(target instanceof SVGSVGElement) {
|
|
|
|
|
this.updateMediaSource(target, url, 'img');
|
|
|
|
|
this.updateMediaSource(mover, url, 'img');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
if(mediaSizes.isMobile) {
|
|
|
|
|
const imgs = mover.querySelectorAll('img');
|
2023-02-17 16:15:35 +01:00
|
|
|
|
imgs.forEach((img) => {
|
|
|
|
|
img.classList.remove('thumbnail'); // может здесь это вообще не нужно
|
|
|
|
|
});
|
2021-10-05 22:40:07 +02:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const haveImage = ['CANVAS', 'IMG'].includes(div.firstElementChild?.tagName) ? div.firstElementChild as HTMLElement : null;
|
|
|
|
|
if((haveImage as HTMLImageElement)?.src !== url) {
|
2022-08-04 08:49:54 +02:00
|
|
|
|
const image = new Image();
|
2021-10-05 22:40:07 +02:00
|
|
|
|
image.classList.add('thumbnail');
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
renderImageFromUrl(image, url, () => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
fastRaf(() => {
|
|
|
|
|
this.updateMediaSource(target, url, 'img');
|
|
|
|
|
|
|
|
|
|
if(haveImage) {
|
|
|
|
|
fastRaf(() => {
|
|
|
|
|
haveImage.remove();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2023-02-17 16:15:35 +01:00
|
|
|
|
div.append(image);
|
|
|
|
|
});
|
|
|
|
|
}, false);
|
|
|
|
|
|
2023-02-27 14:11:37 +01:00
|
|
|
|
cancellableFullPromise && cancellableFullPromise.then((url) => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
const fullImage = new Image();
|
|
|
|
|
fullImage.classList.add('thumbnail');
|
|
|
|
|
renderImageFromUrl(fullImage, url, () => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
fastRaf(() => {
|
2023-02-17 16:15:35 +01:00
|
|
|
|
image.replaceWith(fullImage);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
2023-02-17 16:15:35 +01:00
|
|
|
|
}, false);
|
2021-10-05 22:40:07 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
|
|
|
|
// this.preloader.detach();
|
2022-06-17 18:01:43 +02:00
|
|
|
|
}).catch((err) => {
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.log.error(err);
|
|
|
|
|
this.preloader.attach(mover);
|
|
|
|
|
this.preloader.setManual();
|
|
|
|
|
});
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
return cancellablePromise;
|
|
|
|
|
};
|
2022-08-04 08:49:54 +02:00
|
|
|
|
|
2021-10-05 22:40:07 +02:00
|
|
|
|
this.lazyLoadQueue.unshift({load});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setMoverPromise = thumbPromise.then(set);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.setMoverPromise = setMoverPromise.catch(() => {
|
|
|
|
|
this.setMoverAnimationPromise = null;
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
this.setMoverPromise = null;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|