UNPKG

molstar

Version:

A comprehensive macromolecular library.

593 lines 24.4 kB
"use strict"; /** * 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