navigate.js
  1// Adds touchscreen gestures (not trackpad or mouse gestures) to navigate back,
  2// forward and reload the page to Firefox.
  3//
  4// Copyright (C) 2024  Francesco Pasa
  5//
  6// This program is free software: you can redistribute it and/or modify
  7// it under the terms of the GNU General Public License as published by
  8// the Free Software Foundation, either version 3 of the License, or
  9// (at your option) any later version.
 10//
 11// This program is distributed in the hope that it will be useful,
 12// but WITHOUT ANY WARRANTY; without even the implied warranty of
 13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14// GNU General Public License for more details.
 15//
 16// You should have received a copy of the GNU General Public License
 17// along with this program.  If not, see <https://www.gnu.org/licenses/>.
 18
 19let dragStartX = 0
 20let dragStartY = 0
 21let scrollStartY = 0
 22
 23const leftIcon = createIcon("assets/arrow-circle-left.svg")
 24const rightIcon = createIcon("assets/arrow-circle-right.svg")
 25const reloadIcon = createIcon("assets/arrow-clockwise.svg")
 26reloadIcon.style.left = "calc(50vw - 32px)"
 27
 28function createIcon(path) {
 29    const leftIcon = document.createElement("img")
 30    leftIcon.src = browser.runtime.getURL(path)
 31    leftIcon.width = 64
 32    leftIcon.height = 64
 33    leftIcon.style.display = "none"
 34    leftIcon.style.position = "fixed"
 35    leftIcon.style.top = "calc(50vh - 32px)"
 36    document.body.appendChild(leftIcon)
 37    return leftIcon
 38}
 39
 40function onTouchStart(event) {
 41    dragStartX = event.changedTouches[0].clientX
 42    dragStartY = event.changedTouches[0].clientY
 43    scrollStartY = window.scrollY
 44}
 45
 46function onTouchMove(event) {
 47    const dragEndX = event.changedTouches[0].clientX
 48    const dragEndY = event.changedTouches[0].clientY
 49    const dragMovementX = dragEndX - dragStartX
 50    const dragMovementY = dragEndY - dragStartY
 51    const dragMovementXPercent = dragMovementX / window.innerWidth
 52    const dragMovementYPercent = dragMovementY / window.innerHeight
 53    const dragRatio = Math.abs(dragMovementX / dragMovementY)
 54
 55    leftIcon.style.display = "none"
 56    rightIcon.style.display = "none"
 57    reloadIcon.style.display = "none"
 58
 59    if (dragRatio > 1 && dragMovementXPercent > 0) {
 60        const left = Math.min(8*Math.sqrt(Math.abs(dragMovementX)) - 96, 48)
 61        const opacity = Math.min(Math.max(Math.abs(dragMovementX) / 300, 0), 1)
 62
 63        leftIcon.style.display = "block"
 64        leftIcon.style.left = `${left}px`
 65        leftIcon.style.opacity = `${opacity}`
 66    } else if (dragRatio > 1 && dragMovementXPercent < 0) {
 67        const right = Math.min(8*Math.sqrt(Math.abs(dragMovementX)) - 96, 48)
 68        const opacity = Math.min(Math.max(Math.abs(dragMovementX) / 300, 0), 1)
 69        
 70        rightIcon.style.display = "block"
 71        rightIcon.style.right = `${right}px`
 72        rightIcon.style.opacity = `${opacity}`
 73    } else if (dragRatio < 1 && dragMovementYPercent > 0 && window.scrollY === 0) {
 74        const y = Math.max(dragMovementY - scrollStartY, 0)
 75        const rot = Math.min(Math.max((dragMovementY - scrollStartY) / 50, 0), 2*Math.PI)
 76        const opacity = Math.min(Math.max((dragMovementY - scrollStartY) / 300, 0), 1)
 77        const top = Math.min(8*Math.sqrt(y) - 96, 48)
 78        
 79        reloadIcon.style.display = "block"
 80        reloadIcon.style.top = `${top}px`
 81        reloadIcon.style.opacity = `${opacity}`
 82        reloadIcon.style.transform = `rotate(${rot}rad)`
 83    }
 84}
 85
 86function onTouchCancel() {
 87    leftIcon.style.display = "none"
 88    rightIcon.style.display = "none"
 89    reloadIcon.style.display = "none"
 90}
 91
 92function onTouchEnd(event) {
 93    const dragEndX = event.changedTouches[0].clientX
 94    const dragEndY = event.changedTouches[0].clientY
 95    const dragMovementX = dragEndX - dragStartX
 96    const dragMovementY = dragEndY - dragStartY
 97    const dragMovementXPercent = dragMovementX / window.innerWidth
 98    const dragMovementYPercent = dragMovementY / window.innerHeight
 99    const dragRatio = Math.abs(dragMovementX / dragMovementY)
100
101    // Tests if the distance is sufficient
102    if (dragRatio > 1 && dragMovementXPercent > 0.05) {
103        console.log("back")
104        history.back()
105    } else if (dragRatio > 1 && dragMovementXPercent < -0.05) {
106        console.log("forward")
107        history.forward();
108    } else if (dragRatio < 1 && dragMovementYPercent > 0.05 && window.scrollY === 0) {
109        location.reload()
110    }
111
112    leftIcon.style.display = "none"
113    rightIcon.style.display = "none"
114    reloadIcon.style.display = "none"
115}
116
117function listen() {
118    document.body.addEventListener('touchstart', onTouchStart)
119    document.body.addEventListener('touchmove', onTouchMove)
120    document.body.addEventListener('touchend', onTouchEnd)
121    document.body.addEventListener('touchcancel', onTouchCancel)
122}
123
124listen()