molstar
Version:
A comprehensive macromolecular library.
593 lines • 24.4 kB
JavaScript
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputObserver = exports.ButtonsType = exports.ModifiersKeys = exports.DefaultInputObserverProps = exports.getModifiers = exports.getButton = exports.getButtons = void 0;
var tslib_1 = require("tslib");
var rxjs_1 = require("rxjs");
var linear_algebra_1 = require("../../mol-math/linear-algebra");
var mol_util_1 = require("../../mol-util");
function getButtons(event) {
if (typeof event === 'object') {
if ('buttons' in event) {
return event.buttons;
}
else if ('which' in event) {
var b = event.which; // 'any' to support older browsers
if (b === 2) {
return 4;
}
else if (b === 3) {
return 2;
}
else if (b > 0) {
return 1 << (b - 1);
}
}
}
return 0;
}
exports.getButtons = getButtons;
function getButton(event) {
if (typeof event === 'object') {
if ('button' in event) {
var b = event.button;
if (b === 1) {
return 4;
}
else if (b === 2) {
return 2;
}
else if (b >= 0) {
return 1 << b;
}
}
}
return 0;
}
exports.getButton = getButton;
function getModifiers(event) {
return {
alt: 'altKey' in event ? event.altKey : false,
shift: 'shiftKey' in event ? event.shiftKey : false,
control: 'ctrlKey' in event ? event.ctrlKey : false,
meta: 'metaKey' in event ? event.metaKey : false
};
}
exports.getModifiers = getModifiers;
exports.DefaultInputObserverProps = {
noScroll: true,
noMiddleClickScroll: true,
noContextMenu: true,
noPinchZoom: true,
noTextSelect: true,
preventGestures: false,
mask: function (x, y) { return true; },
pixelScale: 1
};
var ModifiersKeys;
(function (ModifiersKeys) {
ModifiersKeys.None = create();
function areEqual(a, b) {
return a.shift === b.shift && a.alt === b.alt && a.control === b.control && a.meta === b.meta;
}
ModifiersKeys.areEqual = areEqual;
function size(a) {
if (!a)
return 0;
var ret = 0;
if (!!a.shift)
ret++;
if (!!a.alt)
ret++;
if (!!a.control)
ret++;
if (!!a.meta)
ret++;
return ret;
}
ModifiersKeys.size = size;
function create(modifierKeys) {
if (modifierKeys === void 0) { modifierKeys = {}; }
return {
shift: !!modifierKeys.shift,
alt: !!modifierKeys.alt,
control: !!modifierKeys.control,
meta: !!modifierKeys.meta
};
}
ModifiersKeys.create = create;
})(ModifiersKeys = exports.ModifiersKeys || (exports.ModifiersKeys = {}));
var ButtonsType;
(function (ButtonsType) {
ButtonsType.has = mol_util_1.BitFlags.has;
ButtonsType.create = mol_util_1.BitFlags.create;
})(ButtonsType = exports.ButtonsType || (exports.ButtonsType = {}));
function createEvents() {
return {
drag: new rxjs_1.Subject(),
interactionEnd: new rxjs_1.Subject(),
click: new rxjs_1.Subject(),
move: new rxjs_1.Subject(),
wheel: new rxjs_1.Subject(),
pinch: new rxjs_1.Subject(),
gesture: new rxjs_1.Subject(),
resize: new rxjs_1.Subject(),
leave: new rxjs_1.Subject(),
enter: new rxjs_1.Subject(),
modifiers: new rxjs_1.Subject(),
key: new rxjs_1.Subject(),
};
}
var AllowedNonPrintableKeys = ['Backspace', 'Delete'];
var InputObserver;
(function (InputObserver) {
function create(props) {
if (props === void 0) { props = {}; }
var _a = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, exports.DefaultInputObserverProps), props), noScroll = _a.noScroll, noContextMenu = _a.noContextMenu;
return (0, tslib_1.__assign)((0, tslib_1.__assign)({ noScroll: noScroll, noContextMenu: noContextMenu, width: 0, height: 0, pixelRatio: 1 }, createEvents()), { dispose: mol_util_1.noop });
}
InputObserver.create = create;
function fromElement(element, props) {
if (props === void 0) { props = {}; }
var _a = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, exports.DefaultInputObserverProps), props), noScroll = _a.noScroll, noMiddleClickScroll = _a.noMiddleClickScroll, noContextMenu = _a.noContextMenu, noPinchZoom = _a.noPinchZoom, noTextSelect = _a.noTextSelect, mask = _a.mask, pixelScale = _a.pixelScale, preventGestures = _a.preventGestures;
var width = element.clientWidth * pixelRatio();
var height = element.clientHeight * pixelRatio();
var lastTouchDistance = 0, lastTouchFraction = 0;
var pointerDown = (0, linear_algebra_1.Vec2)();
var pointerStart = (0, linear_algebra_1.Vec2)();
var pointerEnd = (0, linear_algebra_1.Vec2)();
var pointerDelta = (0, linear_algebra_1.Vec2)();
var rectSize = (0, linear_algebra_1.Vec2)();
var modifierKeys = {
shift: false,
alt: false,
control: false,
meta: false
};
function pixelRatio() {
return window.devicePixelRatio * pixelScale;
}
function getModifierKeys() {
return (0, tslib_1.__assign)({}, modifierKeys);
}
var dragging = 0 /* Stopped */;
var disposed = false;
var buttons = ButtonsType.create(0 /* None */);
var button = 0 /* None */;
var isInside = false;
var events = createEvents();
var drag = events.drag, interactionEnd = events.interactionEnd, wheel = events.wheel, pinch = events.pinch, gesture = events.gesture, click = events.click, move = events.move, leave = events.leave, enter = events.enter, resize = events.resize, modifiers = events.modifiers, key = events.key;
attach();
return (0, tslib_1.__assign)((0, tslib_1.__assign)({ get noScroll() { return noScroll; },
set noScroll(value) { noScroll = value; },
get noContextMenu() { return noContextMenu; },
set noContextMenu(value) { noContextMenu = value; },
get width() { return width; },
get height() { return height; },
get pixelRatio() { return pixelRatio(); } }, events), { dispose: dispose });
function attach() {
element.addEventListener('contextmenu', onContextMenu, false);
element.addEventListener('wheel', onMouseWheel, false);
element.addEventListener('mousedown', onMouseDown, false);
// for dragging to work outside canvas bounds,
// mouse move/up events have to be added to a parent, i.e. window
window.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('mouseup', onMouseUp, false);
element.addEventListener('mouseenter', onMouseEnter, false);
element.addEventListener('mouseleave', onMouseLeave, false);
element.addEventListener('touchstart', onTouchStart, false);
element.addEventListener('touchmove', onTouchMove, false);
element.addEventListener('touchend', onTouchEnd, false);
element.addEventListener('gesturechange', onGestureChange, false);
element.addEventListener('gesturestart', onGestureStart, false);
element.addEventListener('gestureend', onGestureEnd, false);
// reset buttons and modifier keys state when browser window looses focus
window.addEventListener('blur', handleBlur);
window.addEventListener('keyup', handleKeyUp, false);
window.addEventListener('keydown', handleKeyDown, false);
window.addEventListener('keypress', handleKeyPress, false);
window.addEventListener('resize', onResize, false);
}
function dispose() {
if (disposed)
return;
disposed = true;
element.removeEventListener('contextmenu', onContextMenu, false);
element.removeEventListener('wheel', onMouseWheel, false);
element.removeEventListener('mousedown', onMouseDown, false);
window.removeEventListener('mousemove', onMouseMove, false);
window.removeEventListener('mouseup', onMouseUp, false);
element.removeEventListener('mouseenter', onMouseEnter, false);
element.removeEventListener('mouseleave', onMouseLeave, false);
element.removeEventListener('touchstart', onTouchStart, false);
element.removeEventListener('touchmove', onTouchMove, false);
element.removeEventListener('touchend', onTouchEnd, false);
element.removeEventListener('gesturechange', onGestureChange, false);
element.removeEventListener('gesturestart', onGestureStart, false);
element.removeEventListener('gestureend', onGestureEnd, false);
window.removeEventListener('blur', handleBlur);
window.removeEventListener('keyup', handleKeyUp, false);
window.removeEventListener('keydown', handleKeyDown, false);
window.removeEventListener('keypress', handleKeyPress, false);
window.removeEventListener('resize', onResize, false);
}
function onContextMenu(event) {
if (!mask(event.clientX, event.clientY))
return;
if (noContextMenu) {
event.preventDefault();
}
}
function updateModifierKeys(event) {
modifierKeys.alt = event.altKey;
modifierKeys.shift = event.shiftKey;
modifierKeys.control = event.ctrlKey;
modifierKeys.meta = event.metaKey;
}
function handleBlur() {
if (buttons || modifierKeys.shift || modifierKeys.alt || modifierKeys.meta || modifierKeys.control) {
buttons = 0;
modifierKeys.shift = modifierKeys.alt = modifierKeys.control = modifierKeys.meta = false;
}
}
function handleKeyDown(event) {
var changed = false;
if (!modifierKeys.alt && event.altKey) {
changed = true;
modifierKeys.alt = true;
}
if (!modifierKeys.shift && event.shiftKey) {
changed = true;
modifierKeys.shift = true;
}
if (!modifierKeys.control && event.ctrlKey) {
changed = true;
modifierKeys.control = true;
}
if (!modifierKeys.meta && event.metaKey) {
changed = true;
modifierKeys.meta = true;
}
if (changed && isInside)
modifiers.next(getModifierKeys());
}
function handleKeyUp(event) {
var changed = false;
if (modifierKeys.alt && !event.altKey) {
changed = true;
modifierKeys.alt = false;
}
if (modifierKeys.shift && !event.shiftKey) {
changed = true;
modifierKeys.shift = false;
}
if (modifierKeys.control && !event.ctrlKey) {
changed = true;
modifierKeys.control = false;
}
if (modifierKeys.meta && !event.metaKey) {
changed = true;
modifierKeys.meta = false;
}
if (changed && isInside)
modifiers.next(getModifierKeys());
if (AllowedNonPrintableKeys.includes(event.key))
handleKeyPress(event);
}
function handleKeyPress(event) {
key.next({
key: event.key,
modifiers: getModifierKeys()
});
}
function getCenterTouch(ev) {
var t0 = ev.touches[0];
var t1 = ev.touches[1];
return {
clientX: (t0.clientX + t1.clientX) / 2,
clientY: (t0.clientY + t1.clientY) / 2,
pageX: (t0.pageX + t1.pageX) / 2,
pageY: (t0.pageY + t1.pageY) / 2
};
}
function getTouchDistance(ev) {
var dx = ev.touches[0].pageX - ev.touches[1].pageX;
var dy = ev.touches[0].pageY - ev.touches[1].pageY;
return Math.sqrt(dx * dx + dy * dy);
}
function onTouchStart(ev) {
ev.preventDefault();
if (ev.touches.length === 1) {
buttons = button = 1 /* Primary */;
onPointerDown(ev.touches[0]);
}
else if (ev.touches.length === 2) {
buttons = 2 /* Secondary */ & 4 /* Auxilary */;
button = 2 /* Secondary */;
onPointerDown(getCenterTouch(ev));
var touchDistance = getTouchDistance(ev);
lastTouchDistance = touchDistance;
pinch.next({
distance: touchDistance,
fraction: 1,
fractionDelta: 0,
delta: 0,
isStart: true,
buttons: buttons,
button: button,
modifiers: getModifierKeys()
});
}
else if (ev.touches.length === 3) {
buttons = button = 8 /* Forth */;
onPointerDown(getCenterTouch(ev));
}
}
function onTouchEnd(ev) {
endDrag();
}
function onTouchMove(ev) {
button = 0 /* None */;
if (noPinchZoom) {
ev.preventDefault();
ev.stopPropagation();
if (ev.originalEvent) {
ev.originalEvent.preventDefault();
ev.originalEvent.stopPropagation();
}
}
if (ev.touches.length === 1) {
buttons = 1 /* Primary */;
onPointerMove(ev.touches[0]);
}
else if (ev.touches.length === 2) {
var touchDistance = getTouchDistance(ev);
var touchDelta = lastTouchDistance - touchDistance;
if (Math.abs(touchDelta) < 4) {
buttons = 2 /* Secondary */;
onPointerMove(getCenterTouch(ev));
}
else {
buttons = 4 /* Auxilary */;
updateModifierKeys(ev);
var fraction = lastTouchDistance / touchDistance;
pinch.next({
delta: touchDelta,
fraction: fraction,
fractionDelta: lastTouchFraction - fraction,
distance: touchDistance,
isStart: false,
buttons: buttons,
button: button,
modifiers: getModifierKeys()
});
lastTouchFraction = fraction;
}
lastTouchDistance = touchDistance;
}
else if (ev.touches.length === 3) {
buttons = 8 /* Forth */;
onPointerMove(getCenterTouch(ev));
}
}
function onMouseDown(ev) {
updateModifierKeys(ev);
buttons = getButtons(ev);
button = getButton(ev);
if (noMiddleClickScroll && buttons === 4 /* Auxilary */) {
ev.preventDefault;
}
onPointerDown(ev);
}
function onMouseMove(ev) {
updateModifierKeys(ev);
buttons = getButtons(ev);
button = 0 /* None */;
onPointerMove(ev);
}
function onMouseUp(ev) {
updateModifierKeys(ev);
buttons = getButtons(ev);
button = getButton(ev);
onPointerUp(ev);
endDrag();
}
function endDrag() {
interactionEnd.next(void 0);
}
function onPointerDown(ev) {
if (!mask(ev.clientX, ev.clientY))
return;
eventOffset(pointerStart, ev);
linear_algebra_1.Vec2.copy(pointerDown, pointerStart);
if (insideBounds(pointerStart)) {
dragging = 1 /* Started */;
}
}
function onPointerUp(ev) {
dragging = 0 /* Stopped */;
if (!mask(ev.clientX, ev.clientY))
return;
eventOffset(pointerEnd, ev);
if (linear_algebra_1.Vec2.distance(pointerEnd, pointerDown) < 4) {
var pageX = ev.pageX, pageY = ev.pageY;
var x = pointerEnd[0], y = pointerEnd[1];
click.next({ x: x, y: y, pageX: pageX, pageY: pageY, buttons: buttons, button: button, modifiers: getModifierKeys() });
}
}
function onPointerMove(ev) {
var _a;
eventOffset(pointerEnd, ev);
var pageX = ev.pageX, pageY = ev.pageY;
var x = pointerEnd[0], y = pointerEnd[1];
var inside = insideBounds(pointerEnd);
move.next({ x: x, y: y, pageX: pageX, pageY: pageY, buttons: buttons, button: button, modifiers: getModifierKeys(), inside: inside });
if (dragging === 0 /* Stopped */)
return;
if (noTextSelect) {
(_a = ev.preventDefault) === null || _a === void 0 ? void 0 : _a.call(ev);
}
linear_algebra_1.Vec2.div(pointerDelta, linear_algebra_1.Vec2.sub(pointerDelta, pointerEnd, pointerStart), getClientSize(rectSize));
if (linear_algebra_1.Vec2.magnitude(pointerDelta) < linear_algebra_1.EPSILON)
return;
var isStart = dragging === 1 /* Started */;
if (isStart && !mask(ev.clientX, ev.clientY))
return;
var dx = pointerDelta[0], dy = pointerDelta[1];
drag.next({ x: x, y: y, dx: dx, dy: dy, pageX: pageX, pageY: pageY, buttons: buttons, button: button, modifiers: getModifierKeys(), isStart: isStart });
linear_algebra_1.Vec2.copy(pointerStart, pointerEnd);
dragging = 2 /* Moving */;
}
function onMouseWheel(ev) {
if (!mask(ev.clientX, ev.clientY))
return;
eventOffset(pointerEnd, ev);
var pageX = ev.pageX, pageY = ev.pageY;
var x = pointerEnd[0], y = pointerEnd[1];
if (noScroll) {
ev.preventDefault();
}
var normalized = normalizeWheel(ev);
buttons = button = 4 /* Auxilary */;
if (normalized.dx || normalized.dy || normalized.dz) {
wheel.next((0, tslib_1.__assign)((0, tslib_1.__assign)({ x: x, y: y, pageX: pageX, pageY: pageY }, normalized), { buttons: buttons, button: button, modifiers: getModifierKeys() }));
}
}
function tryPreventGesture(ev) {
var _a, _b;
// console.log(ev, preventGestures);
if (!preventGestures)
return;
ev.preventDefault();
(_a = ev.stopImmediatePropagation) === null || _a === void 0 ? void 0 : _a.call(ev);
(_b = ev.stopPropagation) === null || _b === void 0 ? void 0 : _b.call(ev);
}
var prevGestureScale = 0, prevGestureRotation = 0;
function onGestureStart(ev) {
tryPreventGesture(ev);
prevGestureScale = ev.scale;
prevGestureRotation = ev.rotation;
gesture.next({ scale: ev.scale, rotation: ev.rotation, deltaRotation: 0, deltaScale: 0, isStart: true });
}
function gestureDelta(ev, isEnd) {
gesture.next({
scale: ev.scale,
rotation: ev.rotation,
deltaRotation: prevGestureRotation - ev.rotation,
deltaScale: prevGestureScale - ev.scale,
isEnd: isEnd
});
prevGestureRotation = ev.rotation;
prevGestureScale = ev.scale;
}
function onGestureChange(ev) {
tryPreventGesture(ev);
gestureDelta(ev);
}
function onGestureEnd(ev) {
tryPreventGesture(ev);
gestureDelta(ev, true);
}
function onMouseEnter(ev) {
isInside = true;
enter.next(void 0);
}
function onMouseLeave(ev) {
isInside = false;
leave.next(void 0);
}
function onResize(ev) {
resize.next({});
}
function insideBounds(pos) {
if (element instanceof Window || element instanceof Document || element === document.body) {
return true;
}
else {
var rect = element.getBoundingClientRect();
return pos[0] >= 0 && pos[1] >= 0 && pos[0] < rect.width && pos[1] < rect.height;
}
}
function getClientSize(out) {
out[0] = element.clientWidth;
out[1] = element.clientHeight;
return out;
}
function eventOffset(out, ev) {
width = element.clientWidth * pixelRatio();
height = element.clientHeight * pixelRatio();
var cx = ev.clientX || 0;
var cy = ev.clientY || 0;
var rect = element.getBoundingClientRect();
out[0] = cx - rect.left;
out[1] = cy - rect.top;
return out;
}
}
InputObserver.fromElement = fromElement;
})(InputObserver || (InputObserver = {}));
exports.InputObserver = InputObserver;
// Adapted from https://stackoverflow.com/a/30134826
// License: https://creativecommons.org/licenses/by-sa/3.0/
function normalizeWheel(event) {
// Reasonable defaults
var PIXEL_STEP = 10;
var LINE_HEIGHT = 40;
var PAGE_HEIGHT = 800;
var spinX = 0, spinY = 0, dx = 0, dy = 0, dz = 0; // pixelX, pixelY, pixelZ
// Legacy
if ('detail' in event) {
spinY = event.detail;
}
if ('wheelDelta' in event) {
spinY = -event.wheelDelta / 120;
}
if ('wheelDeltaY' in event) {
spinY = -event.wheelDeltaY / 120;
}
if ('wheelDeltaX' in event) {
spinX = -event.wheelDeltaX / 120;
}
// side scrolling on FF with DOMMouseScroll
if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
spinX = spinY;
spinY = 0;
}
dx = spinX * PIXEL_STEP;
dy = spinY * PIXEL_STEP;
if ('deltaY' in event) {
dy = event.deltaY;
}
if ('deltaX' in event) {
dx = event.deltaX;
}
if ('deltaZ' in event) {
dz = event.deltaZ;
}
if ((dx || dy || dz) && event.deltaMode) {
if (event.deltaMode === 1) { // delta in LINE units
dx *= LINE_HEIGHT;
dy *= LINE_HEIGHT;
dz *= LINE_HEIGHT;
}
else { // delta in PAGE units
dx *= PAGE_HEIGHT;
dy *= PAGE_HEIGHT;
dz *= PAGE_HEIGHT;
}
}
// Fall-back if spin cannot be determined
if (dx && !spinX) {
spinX = (dx < 1) ? -1 : 1;
}
if (dy && !spinY) {
spinY = (dy < 1) ? -1 : 1;
}
return { spinX: spinX, spinY: spinY, dx: dx, dy: dy, dz: dz };
}
//# sourceMappingURL=input-observer.js.map
;