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()