bipp-sneltoetsen: Zoeken, Reset, Nieuw, Opslaan, Indienen, Terug (1.1.0)
Ctrl+Z/R/N/S/I/T next to Ctrl+B, one table of label, letter, key. Hints for all buttons while Ctrl is held. BIPP's own title kept, shortcut on a line below. AGENTS.md: Claude installs the dev-stub itself, only the Install click is left, pick the UA Chrome profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6d9ae45a2f
commit
3b2528d72a
@@ -1,8 +1,8 @@
|
||||
// ==UserScript==
|
||||
// @name BIPP: sneltoets Ctrl+B voor Bevestigen
|
||||
// @name BIPP: sneltoetsen voor knoppen
|
||||
// @namespace https://github.com/glgoose/ua-userscripts
|
||||
// @version 1.0.0
|
||||
// @description Ctrl+B drukt op Bevestigen. De B in de knop is onderstreept en bij ingedrukte Ctrl verschijnt Ctrl+B boven de knop.
|
||||
// @version 1.1.0
|
||||
// @description Ctrl+letter drukt op Bevestigen (B), Zoeken (Z), Reset (R), Nieuw (N), Opslaan (S), Indienen (I) en Terug (T). De letter is onderstreept en bij ingedrukte Ctrl verschijnt de sneltoets boven de knop.
|
||||
// @author glgoose
|
||||
// @license GPL-3.0-or-later
|
||||
// @homepageURL https://github.com/glgoose/ua-userscripts/tree/main/scripts/bipp-sneltoetsen
|
||||
@@ -16,32 +16,43 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var LABEL = 'bevestigen';
|
||||
var HINT = 'Ctrl+B';
|
||||
// Knoplabel (kleine letters), de letter die onderstreept wordt, en de toets.
|
||||
var KEYS = [
|
||||
{ label: 'bevestigen', letter: 'b', code: 'KeyB' },
|
||||
{ label: 'zoeken', letter: 'z', code: 'KeyZ' },
|
||||
{ label: 'reset', letter: 'r', code: 'KeyR' },
|
||||
{ label: 'nieuw', letter: 'n', code: 'KeyN' },
|
||||
{ label: 'opslaan', letter: 's', code: 'KeyS' },
|
||||
{ label: 'indienen', letter: 'i', code: 'KeyI' },
|
||||
{ label: 'terug', letter: 't', code: 'KeyT' }
|
||||
];
|
||||
var MARK = 'data-bipp-sneltoets';
|
||||
|
||||
function hint(k) { return 'Ctrl+' + k.letter.toUpperCase(); }
|
||||
|
||||
function norm(s) { return (s || '').replace(/\s+/g, ' ').trim().toLowerCase(); }
|
||||
|
||||
function visible(el) { return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); }
|
||||
|
||||
// Eerste zichtbare, actieve knop met dit label. Een postback-pagina kan er verborgen kopieën van hebben.
|
||||
function findButton() {
|
||||
function findButton(k) {
|
||||
var btns = document.querySelectorAll('input[type="submit"], input[type="button"], button');
|
||||
for (var i = 0; i < btns.length; i++) {
|
||||
var b = btns[i];
|
||||
if (norm(b.value || b.textContent) === LABEL && !b.disabled && visible(b)) return b;
|
||||
if (norm(b.value || b.textContent) === k.label && !b.disabled && visible(b)) return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// <button>: de B in een <u>. <input> kan geen HTML bevatten, dus daar komt een lijntje over de letter.
|
||||
function underline(btn) {
|
||||
// <button>: de letter in een <u>. <input> kan geen HTML bevatten, dus daar komt een lijntje onder de letter.
|
||||
function underline(btn, k) {
|
||||
if (btn.getAttribute(MARK)) return;
|
||||
btn.setAttribute(MARK, '1');
|
||||
if (!btn.title) btn.title = HINT;
|
||||
btn.setAttribute(MARK, k.letter);
|
||||
// BIPP zet soms zelf een title (Bevestigen): dan komt de sneltoets op een eigen regel eronder.
|
||||
btn.title = btn.title ? btn.title + '\n' + hint(k) : hint(k);
|
||||
if (btn.tagName === 'BUTTON' && btn.children.length === 0) {
|
||||
var t = btn.textContent;
|
||||
var i = t.toLowerCase().indexOf('b');
|
||||
var i = t.toLowerCase().indexOf(k.letter);
|
||||
btn.textContent = '';
|
||||
btn.appendChild(document.createTextNode(t.slice(0, i)));
|
||||
var u = document.createElement('u');
|
||||
@@ -50,18 +61,18 @@
|
||||
btn.appendChild(document.createTextNode(t.slice(i + 1)));
|
||||
return;
|
||||
}
|
||||
drawLine(btn);
|
||||
window.addEventListener('resize', function () { drawLine(btn); });
|
||||
drawLine(btn, k);
|
||||
window.addEventListener('resize', function () { drawLine(btn, k); });
|
||||
}
|
||||
|
||||
function drawLine(btn) {
|
||||
function drawLine(btn, k) {
|
||||
var old = btn.nextSibling && btn.nextSibling.getAttribute && btn.nextSibling.getAttribute(MARK + '-line');
|
||||
if (old) btn.parentNode.removeChild(btn.nextSibling);
|
||||
var cs = getComputedStyle(btn);
|
||||
var ctx = document.createElement('canvas').getContext('2d');
|
||||
ctx.font = cs.fontStyle + ' ' + cs.fontWeight + ' ' + cs.fontSize + ' ' + cs.fontFamily;
|
||||
var text = btn.value || btn.textContent;
|
||||
var i = text.toLowerCase().indexOf('b');
|
||||
var i = text.toLowerCase().indexOf(k.letter);
|
||||
var before = ctx.measureText(text.slice(0, i)).width;
|
||||
var letter = ctx.measureText(text.charAt(i)).width;
|
||||
var total = ctx.measureText(text).width;
|
||||
@@ -77,39 +88,51 @@
|
||||
btn.parentNode.insertBefore(line, btn.nextSibling);
|
||||
}
|
||||
|
||||
var tip = null;
|
||||
function showTip() {
|
||||
var btn = findButton();
|
||||
if (!btn) return;
|
||||
hideTip();
|
||||
var r = btn.getBoundingClientRect();
|
||||
tip = document.createElement('span');
|
||||
tip.textContent = HINT;
|
||||
tip.style.cssText = 'position:absolute;z-index:99999;pointer-events:none;white-space:nowrap;' +
|
||||
'font:11px ui-monospace,Menlo,Consolas,monospace;background:#1f2937;color:#fff;border-radius:4px;padding:1px 6px;' +
|
||||
'left:' + (r.left + window.scrollX + r.width / 2) + 'px;top:' + (r.top + window.scrollY - 22) + 'px;transform:translateX(-50%)';
|
||||
document.body.appendChild(tip);
|
||||
var tips = [];
|
||||
function showTips() {
|
||||
hideTips();
|
||||
KEYS.forEach(function (k) {
|
||||
var btn = findButton(k);
|
||||
if (!btn) return;
|
||||
var r = btn.getBoundingClientRect();
|
||||
var tip = document.createElement('span');
|
||||
tip.textContent = hint(k);
|
||||
tip.style.cssText = 'position:absolute;z-index:99999;pointer-events:none;white-space:nowrap;' +
|
||||
'font:11px ui-monospace,Menlo,Consolas,monospace;background:#1f2937;color:#fff;border-radius:4px;padding:1px 6px;' +
|
||||
'left:' + (r.left + window.scrollX + r.width / 2) + 'px;top:' + (r.top + window.scrollY - 22) + 'px;transform:translateX(-50%)';
|
||||
document.body.appendChild(tip);
|
||||
tips.push(tip);
|
||||
});
|
||||
}
|
||||
function hideTips() {
|
||||
tips.forEach(function (t) { if (t.parentNode) t.parentNode.removeChild(t); });
|
||||
tips = [];
|
||||
}
|
||||
function hideTip() { if (tip && tip.parentNode) tip.parentNode.removeChild(tip); tip = null; }
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Control') { if (!e.repeat) showTip(); return; }
|
||||
if (e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey && e.code === 'KeyB') {
|
||||
var btn = findButton();
|
||||
if (e.key === 'Control') { if (!e.repeat) showTips(); return; }
|
||||
if (!e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;
|
||||
for (var i = 0; i < KEYS.length; i++) {
|
||||
if (e.code !== KEYS[i].code) continue;
|
||||
var btn = findButton(KEYS[i]);
|
||||
if (!btn) return;
|
||||
e.preventDefault();
|
||||
hideTip();
|
||||
hideTips();
|
||||
btn.click();
|
||||
return;
|
||||
}
|
||||
}, true);
|
||||
document.addEventListener('keyup', function (e) { if (e.key === 'Control') hideTip(); }, true);
|
||||
window.addEventListener('blur', hideTip);
|
||||
document.addEventListener('keyup', function (e) { if (e.key === 'Control') hideTips(); }, true);
|
||||
window.addEventListener('blur', hideTips);
|
||||
|
||||
// De pagina bouwt de knoppen soms pas na een postback op: probeer het een paar keer.
|
||||
var tries = 0;
|
||||
(function init() {
|
||||
var btn = findButton();
|
||||
if (btn) { underline(btn); return; }
|
||||
if (++tries < 30) setTimeout(init, 500);
|
||||
var left = 0;
|
||||
KEYS.forEach(function (k) {
|
||||
var btn = findButton(k);
|
||||
if (btn) underline(btn, k); else left++;
|
||||
});
|
||||
if (left && ++tries < 30) setTimeout(init, 500);
|
||||
})();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user