Skip to content

Instantly share code, notes, and snippets.

@mjpieters
Last active May 22, 2022 15:16
Show Gist options
  • Save mjpieters/f4d3a250091a6626792e317d35c2d89d to your computer and use it in GitHub Desktop.
Save mjpieters/f4d3a250091a6626792e317d35c2d89d to your computer and use it in GitHub Desktop.
Touch support for h5ai previews
/*
* Touch support for h5ai previews
*
* Maps left and right swiping gestures on the preview to next and previous controls, double-top to toggling full screen
* Place in the `public/ext` directory, then add `"touch.js"` to the `resources.scripts` list in your `private/conf/options.json` file.
*
* * Copyright: 2022 Martijn Pieters
* * License: The MIT License (MIT)
*/
(function () {
const DISTANCE = 40
const MAXTIME = 250
const OVERLAY_ID = 'pv-overlay'
const CONTAINER_ID = 'pv-container'
const KEYS = {
left: { keyCode: 37 },
right: { keyCode: 39 },
f: { keyCode: 70 }
}
const doc = document
const waitForPrevOverlay = function () {
const ready = new Promise(resolve => (/^(i|c|loade)/).test(doc.readyState) ? resolve() : doc.addEventListener('DOMContentLoaded', () => resolve()))
const wait = () => {
return new Promise(resolve => {
let ov = doc.getElementById(OVERLAY_ID)
if (ov) return resolve(ov)
const obs = new MutationObserver(mut => {
ov = doc.getElementById(OVERLAY_ID)
if (ov) {
resolve(ov)
obs.disconnect()
}
})
obs.observe(doc.body, { childList: true })
})
}
return ready.then(wait)
}
const previewTouchControls = function (overlay) {
const prevCont = doc.getElementById(CONTAINER_ID)
const state = {}
const onTouchStart = function (e) {
if (overlay.classList.contains('hidden')) return
const now = Date.now()
const deltaT = now - (state.last || now)
const touch = e.changedTouches
state.x = touch[0].pageX
state.y = touch[0].pageY
state.last = now
state.deltaX = 0
state.deltaY = 0
state.isHorizontal = null
state.isDoubleTap = touch.length === 1 && deltaT > 0 && deltaT <= MAXTIME
doc.addEventListener('touchmove', onTouchMove)
doc.addEventListener('touchend', onTouchEnd)
}
const onTouchMove = function (e) {
if (overlay.classList.contains('hidden')) {
doc.removeEventListener('touchmove', onTouchMove)
doc.removeEventListener('touchend', onTouchEnd)
return
}
const touch = e.changedTouches
if ((touch && touch.length > 1) || (e.scale && e.scale !== 1)) return
state.deltaX = touch[0].pageX - state.x
state.deltaY = touch[0].pageY - state.y
if (state.isHorizontal === null) state.isHorizontal = Math.abs(state.deltaX) >= Math.abs(state.deltaY)
e.preventDefault()
e.stopPropagation()
}
const onTouchEnd = function (e) {
if (!overlay.classList.contains('hidden')) {
const dX = Math.abs(state.deltaX)
const dY = Math.abs(state.deltaY)
if (state.isDoubleTap && dX <= DISTANCE && dY <= DISTANCE) {
e.preventDefault()
e.stopPropagation()
// map double-tap gestures to keyDown events that the preview code maps to fullscreen toggling
overlay.dispatchEvent(new KeyboardEvent('keydown', KEYS.f))
} else if (state.isHorizontal && dX > DISTANCE) {
if ((Date.now() - state.last) < MAXTIME) {
e.preventDefault()
e.stopPropagation()
// map left or right sliding gestures to keyDown events that the preview code maps to next / prev
const opts = state.deltaX > 0 ? KEYS.left : KEYS.right
overlay.dispatchEvent(new KeyboardEvent('keydown', opts))
}
}
}
doc.removeEventListener('touchmove', onTouchMove)
doc.removeEventListener('touchend', onTouchEnd)
}
prevCont.addEventListener('touchstart', onTouchStart)
}
// Wait for the preview div to be added to the DOM
waitForPrevOverlay().then((overlay) => previewTouchControls(overlay))
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment