forked from akai/readest
270 lines
6.2 KiB
TypeScript
270 lines
6.2 KiB
TypeScript
import { DOUBLE_CLICK_INTERVAL_THRESHOLD_MS, LONG_HOLD_THRESHOLD } from '@/services/constants';
|
|
import { eventDispatcher } from '@/utils/event';
|
|
|
|
let lastClickTime = 0;
|
|
let longHoldTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
let keyboardState = {
|
|
key: '',
|
|
code: '',
|
|
ctrlKey: false,
|
|
shiftKey: false,
|
|
altKey: false,
|
|
metaKey: false,
|
|
};
|
|
|
|
const getKeyStatus = (event?: MouseEvent | WheelEvent | TouchEvent) => {
|
|
if (event && 'ctrlKey' in event) {
|
|
return {
|
|
key: keyboardState.key,
|
|
code: keyboardState.code,
|
|
ctrlKey: event.ctrlKey,
|
|
shiftKey: event.shiftKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey,
|
|
};
|
|
}
|
|
return {
|
|
...keyboardState,
|
|
};
|
|
};
|
|
|
|
export const handleKeydown = (bookKey: string, event: KeyboardEvent) => {
|
|
keyboardState = {
|
|
key: event.key,
|
|
code: event.code,
|
|
ctrlKey: event.ctrlKey,
|
|
shiftKey: event.shiftKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey,
|
|
};
|
|
|
|
if (['Backspace'].includes(event.key)) {
|
|
event.preventDefault();
|
|
}
|
|
if (event.ctrlKey && event.key.toLowerCase() === 'f') {
|
|
event.preventDefault();
|
|
}
|
|
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-keydown',
|
|
bookKey,
|
|
key: event.key,
|
|
code: event.code,
|
|
ctrlKey: event.ctrlKey,
|
|
shiftKey: event.shiftKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey,
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleKeyup = (bookKey: string, event: KeyboardEvent) => {
|
|
keyboardState = {
|
|
key: '',
|
|
code: '',
|
|
ctrlKey: event.ctrlKey,
|
|
shiftKey: event.shiftKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey,
|
|
};
|
|
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-keyup',
|
|
bookKey,
|
|
key: event.key,
|
|
code: event.code,
|
|
ctrlKey: event.ctrlKey,
|
|
shiftKey: event.shiftKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey,
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleMousedown = (bookKey: string, event: MouseEvent) => {
|
|
longHoldTimeout = setTimeout(() => {
|
|
longHoldTimeout = null;
|
|
}, LONG_HOLD_THRESHOLD);
|
|
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-mousedown',
|
|
bookKey,
|
|
button: event.button,
|
|
screenX: event.screenX,
|
|
screenY: event.screenY,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
offsetX: event.offsetX,
|
|
offsetY: event.offsetY,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleMouseup = (bookKey: string, event: MouseEvent) => {
|
|
// we will handle mouse back and forward buttons ourselves
|
|
if ([3, 4].includes(event.button)) {
|
|
event.preventDefault();
|
|
}
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-mouseup',
|
|
bookKey,
|
|
button: event.button,
|
|
screenX: event.screenX,
|
|
screenY: event.screenY,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
offsetX: event.offsetX,
|
|
offsetY: event.offsetY,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleWheel = (bookKey: string, event: WheelEvent) => {
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-wheel',
|
|
bookKey,
|
|
deltaMode: event.deltaMode,
|
|
deltaX: event.deltaX,
|
|
deltaY: event.deltaY,
|
|
deltaZ: event.deltaZ,
|
|
screenX: event.screenX,
|
|
screenY: event.screenY,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
offsetX: event.offsetX,
|
|
offsetY: event.offsetY,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleClick = (
|
|
bookKey: string,
|
|
doubleClickDisabled: React.MutableRefObject<boolean>,
|
|
event: MouseEvent,
|
|
) => {
|
|
const now = Date.now();
|
|
|
|
if (!doubleClickDisabled.current && now - lastClickTime < DOUBLE_CLICK_INTERVAL_THRESHOLD_MS) {
|
|
lastClickTime = now;
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-double-click',
|
|
bookKey,
|
|
screenX: event.screenX,
|
|
screenY: event.screenY,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
offsetX: event.offsetX,
|
|
offsetY: event.offsetY,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
return;
|
|
}
|
|
|
|
lastClickTime = now;
|
|
|
|
const postSingleClick = () => {
|
|
const element = event.target as HTMLElement | null;
|
|
if (
|
|
element?.closest('sup, a, audio, video') &&
|
|
!element?.closest('a.duokan-footnote:not([href])')
|
|
) {
|
|
return;
|
|
}
|
|
const footnote = element?.closest('.js_readerFooterNote, .zhangyue-footnote, .duokan-footnote');
|
|
if (footnote) {
|
|
eventDispatcher.dispatch('footnote-popup', {
|
|
bookKey,
|
|
element: footnote,
|
|
footnote:
|
|
footnote.getAttribute('data-wr-footernote') ||
|
|
footnote.getAttribute('zy-footnote') ||
|
|
footnote.getAttribute('alt') ||
|
|
element?.getAttribute('alt') ||
|
|
'',
|
|
});
|
|
return;
|
|
}
|
|
|
|
// if long hold is detected, we don't want to send single click event
|
|
if (!longHoldTimeout) {
|
|
return;
|
|
}
|
|
|
|
window.postMessage(
|
|
{
|
|
type: 'iframe-single-click',
|
|
bookKey,
|
|
screenX: event.screenX,
|
|
screenY: event.screenY,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
offsetX: event.offsetX,
|
|
offsetY: event.offsetY,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
if (!doubleClickDisabled.current) {
|
|
setTimeout(() => {
|
|
if (Date.now() - lastClickTime >= DOUBLE_CLICK_INTERVAL_THRESHOLD_MS) {
|
|
postSingleClick();
|
|
}
|
|
}, DOUBLE_CLICK_INTERVAL_THRESHOLD_MS);
|
|
} else {
|
|
postSingleClick();
|
|
}
|
|
};
|
|
|
|
const handleTouchEv = (bookKey: string, event: TouchEvent, type: string) => {
|
|
const touch = event.targetTouches[0];
|
|
const touches = [];
|
|
if (touch) {
|
|
touches.push({
|
|
clientX: touch.clientX,
|
|
clientY: touch.clientY,
|
|
screenX: touch.screenX,
|
|
screenY: touch.screenY,
|
|
});
|
|
}
|
|
window.postMessage(
|
|
{
|
|
type: type,
|
|
bookKey,
|
|
timeStamp: Date.now(),
|
|
targetTouches: touches,
|
|
...getKeyStatus(event),
|
|
},
|
|
'*',
|
|
);
|
|
};
|
|
|
|
export const handleTouchStart = (bookKey: string, event: TouchEvent) => {
|
|
handleTouchEv(bookKey, event, 'iframe-touchstart');
|
|
};
|
|
|
|
export const handleTouchMove = (bookKey: string, event: TouchEvent) => {
|
|
handleTouchEv(bookKey, event, 'iframe-touchmove');
|
|
};
|
|
|
|
export const handleTouchEnd = (bookKey: string, event: TouchEvent) => {
|
|
handleTouchEv(bookKey, event, 'iframe-touchend');
|
|
};
|