/
wheel-on-input-number.user.js
148 lines (138 loc) · 4.08 KB
/
wheel-on-input-number.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// ==UserScript==
// @name wheel on input number
// @namespace http://gholk.github.io
// @match https://gholk.github.io/pdf-js-tool/pdf-js-tool.html
// @grant GM.getValue
// @version 1.6
// @author gholk
// @description make mouse wheel increment or decrement number in text field or textarea.
// ==/UserScript==
GM.getValue('url-map').then(list => {
if (!list) list = []
const config = list.find(c => new RegExp(c.regexp).test(window.location.href))
window.addEventListener('wheel', event => {
console.debug('get wheel event')
const input = event.target
if (config && !input.matches(config.selector)) return
let labele = input
if (input.nodeName == 'LABEL' && input.control) {
labele = input.control
}
if (labele.type == 'radio') {
return wheelOnRadioHandler(labele, event)
}
if (labele.nodeName == 'SELECT') {
return moveStepOnSelect(labele, event)
}
if (allowRangeSet(input)) {
return moveStepOnNode(input, event)
}
if (!isTextBox(input)) return
const content = Number(input.value)
if (Number.isNaN(content)) return
nop(event)
input.value = padTo((event.deltaY > 0) ? content-1 : content+1, input.value)
eventSendAuto(input)
}, {passive: false})
})
function nop(e) {
e.preventDefault()
}
function isTextBox(e) {
const n = e.nodeName
// if scrollable
if (e.scrollHeight > e.offsetHeight) return false
if (n == 'TEXTAREA') return true
if (n != 'INPUT') return false
switch (e.type) {
case 'search':
case 'tel':
case 'search':
case 'text':
case 'email':
return true
}
return false
}
function wheelOnRadioHandler(e, event) {
const step = event.deltaY > 0 ? 1 : -1
const name = e.name
if (e.disabled || e.hidden) return
nop(event)
let l = Array.from(document.getElementsByName(name))
l = l.filter(e => !e.disabled && !e.hidden)
let i = l.findIndex(e => e.checked)
if (i == -1) i = step > 0 ? 0 : -1
else i += step
l.at(i % l.length).click()
}
// todo: single digit move 9-0, prevent negative
function allowRangeSet(e) {
return e.type != 'number' && // number has built-in wheel event support
e.selectionStart != null && // a text field element
// user does not need scrolling
e.scrollHeight <= e.offsetHeight &&
// detect number on cursor only when focus
(e == document.activeElement ||
// always detect number if any text selected
e.selectionStart != e.selectionEnd)
}
function eventSend(e, ...arg) {
return e.dispatchEvent(new Event(...arg))
}
function isFocus(e) {
return e == document.activeElement
}
function eventSendAuto(e) {
eventSend(e, 'input')
if (!isFocus(e)) eventSend(e, 'change')
}
function moveStepOnNode(e, event) {
const step = event.deltaY>0 ? -1 : 1
const [start, end] = [e.selectionStart, e.selectionEnd]
if (start != end) {
const scan = e.value.substring(start, end).match(/\d+/)
if (!scan) return
nop(event)
e.setRangeText(moveStep2s(scan[0], step), start + scan.index, start + scan.index + scan[0].length)
eventSendAuto(e)
return
}
for (const scan of e.value.matchAll(/\d+/g)) {
if (scan.index > end) break
if (scan.index <= end && end <= scan.index + scan[0].length) {
nop(event)
e.setRangeText(moveStep2s(scan[0],step), scan.index, scan.index+scan[0].length, 'end')
eventSendAuto(e)
break
}
}
}
function moveStep2s(s, step) {
const n = Number(s)
let n2 = n + step
if (n2 < 0) n2 = n
return padTo(n2, s)
}
function moveStepOnSelect(e, event) {
nop(event)
const n = event.deltaY > 0 ? 1 : -1
const index = e.selectedIndex + n
if (index < 0 || e.length <= index) return null
e.selectedIndex = index
eventSend(e, 'input')
eventSend(e, 'change')
return index
}
function padTo(x, t) {
let s = String(x)
let negative = false
if (t.charAt(0) == '-') t = t.slice(1)
if (s.charAt(0) == '-') {
s = s.slice(1)
negative = true
}
while (s.length < t.length) s = '0' + s
if (negative) s = '-' + s
return s
}