diff --git a/src/viewer.js b/src/viewer.js index 442f8b7..e21504e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -25,7 +25,75 @@ function redirectIfNotDisplayedInFrame() { window.location.href = '/' } -redirectIfNotDisplayedInFrame(); +redirectIfNotDisplayedInFrame() + +/** + * The editor pans on the non-standard mousewheel event, ignoring it while Shift + * is held, and zooms on it with Ctrl or Cmd once it reports more than 100. + * Re-dispatch wheel events as mousewheel where needed, with the deltas Chrome + * reports for a mouse notch of three lines (120): + * - in Firefox, which has no mousewheel at all, + * - with Shift, so that it scrolls horizontally, + * - with Ctrl or Cmd, at least 120, so that touchpad pinches and small wheel + * steps zoom too; the editor zooms once per gesture, after 100 ms of quiet. + */ +function normalizeMouseWheel() { + const nativeMouseWheel = 'onmousewheel' in window + if (!nativeMouseWheel) { + // kity only listens for an event an element has an on property for + Object.defineProperty(Element.prototype, 'onmousewheel', { value: null, writable: true, configurable: true }) + } + const redispatched = new WeakSet() + window.addEventListener(nativeMouseWheel ? 'mousewheel' : 'wheel', (event) => { + if (redispatched.has(event)) { + return + } + const zoom = event.ctrlKey || event.metaKey + const horizontal = event.shiftKey && !zoom + let deltaX = event.wheelDeltaX + let deltaY = event.wheelDeltaY + if (!nativeMouseWheel) { + // Read deltaMode first: reading a delta first makes Firefox report pixels + const pixelsPerUnit = [1.2, 40, 800][event.deltaMode] + deltaX = -event.deltaX * pixelsPerUnit + deltaY = -event.deltaY * pixelsPerUnit + } else if (!horizontal && !(zoom && Math.abs(deltaY || deltaX) < 120)) { + return + } + if (horizontal) { + deltaX = deltaX || deltaY + deltaY = 0 + } + let delta = deltaY || deltaX + if (zoom) { + delta = Math.sign(delta) * Math.max(120, Math.abs(delta)) + } + const mouseWheel = new WheelEvent('mousewheel', { + bubbles: true, + cancelable: true, + view: window, + screenX: event.screenX, + screenY: event.screenY, + clientX: event.clientX, + clientY: event.clientY, + ctrlKey: event.ctrlKey, + metaKey: event.metaKey, + altKey: event.altKey, + shiftKey: event.shiftKey && !horizontal, + }) + Object.defineProperties(mouseWheel, { + wheelDelta: { value: delta }, + wheelDeltaX: { value: deltaX }, + wheelDeltaY: { value: deltaY }, + }) + redispatched.add(mouseWheel) + event.stopImmediatePropagation() + if (!event.target.dispatchEvent(mouseWheel)) { + event.preventDefault() + } + }, { capture: true, passive: false }) +} +normalizeMouseWheel(); (function() { const t = function(msg) {