Hello,

Christian Hahn's easy links for surf is very outdated and doesn't work
anymore, so I wrote another version called keyboard buttons. It
provides several improvements/features:
- It adds the labels to the end of the DOM, to avoid messing with the
website's code
- It not only works for <a> elements, but also <button> elements
- It uses letters instead of numbers 1-5 so it is easier to reach
- modkey+r is a reserved combination for reloading the positions of the labels
- While holding down modkey, pressing escape cancels the current input

Other than that, it works the same as easy links. It also has a few
unavoidable flaws; sometimes the keyboard shortcuts used conflict with
the website's, a good example of that is that on Wikipedia alt+e is
used to start editing the page, but can also appear as a label. As a
workaround you can disable the use of the letter 'e' when generating
the labels, or you can automatically redirect to the mobile site of
Wikipedia which doesn't have keyboard shortcuts.

Regards,
Kai
/* keyboard buttons for surf */
/* Christian hahn <ch radamanthys de> wrote the original code */

function
keyboardButtons()
{
    const modKey      = "Alt"; /* used to initiate keyboardButtons mode */
    const escKey      = "Escape"; /* used to escape keyboardButtons mode (you 
can also release alt) */
    const labelStyle  = `
        box-sizing: border-box;
        position: absolute;
        display: inline;
        width: auto;
        height: auto;
        margin: 0;
        z-index: 99999;

        padding: 2px;
        border: 1px solid black;
        border-radius: 0;

        color: black;
        font-size: 10px;
        font-weight: normal;
        font-family: sans-serif;
        font-decoration: none;
        text-transform: none;
    `;
    const normalColor    = "yellow";
    const highlightColor = "red";
    
    var labels     = {};
    var input      = "";
    
    function
    updateLabelColor()
    {
        for (let id in labels) {
            if (input && id.startsWith(input)) 
labels[id].elem.style.backgroundColor = highlightColor;
            else                               
labels[id].elem.style.backgroundColor = normalColor;
        }
    }
    
    /* by default, this function chooses some sequence of letters, change it to 
what you like */
    function
    numberToLabel(n)
    {
        ++n;
        const alphabet = "abcdefghijklmnopqstuvwxyz";
        /* r is removed as it reloads keyboardButtons */

        var str = "";
        for (;n; n = Math.floor(n/alphabet.length)) {
            str += alphabet[n%alphabet.length];
        }
        return str;
    }
    
    function
    Label(button, text)
    {
        this.button = button;

        this.elem = document.createElement("span");
        this.elem.innerHTML = text;
        this.elem.style = labelStyle;
        this.elem.style.visibility = "hidden";
        const pos = this.button.getBoundingClientRect();
        this.elem.style.left = pos.left + scrollX + "px";
        this.elem.style.top  = pos.top  + scrollY + "px";
        document.body.appendChild(this.elem);
    }
    
    function
    createLabels()
    {
        for (let id in labels) 
labels[id].elem.parentNode.removeChild(labels[id].elem);
        labels = {};

        var buttons = Array.from(document.getElementsByTagName("a"))
              .concat(Array.from(document.getElementsByTagName("button")));
        for (let i = 0; i < buttons.length; i++) {
            const text = numberToLabel(i);
            labels[text] = new Label(buttons[i], text);
        }
        updateLabelColor();
    }

    /* main */
    createLabels();
    /* set key handlers */
    addEventListener("keydown", function (e) {
        if (e.key === modKey) {
            input = "";
            for (let id in labels) labels[id].elem.style.visibility = "visible";
            updateLabelColor();
        } else if (e.getModifierState(modKey)) {
            if (e.key === escKey || e.key === 'r') {
                if (e.key === 'r') createLabels(); /* reload labels */
                input = "";
                for (let id in labels) labels[id].elem.style.visibility = 
"hidden";
            } else if (e.key.length === 1) {
                input += e.key;
            }
            updateLabelColor();
        }
    });
    addEventListener("keyup", function (e) {
        if (e.key === modKey) {
            if (labels[input] !== undefined) {
                labels[input].button.click();
            }
            input = "";
            for (let id in labels) labels[id].elem.style.visibility = "hidden";
        }
    });
}

if (document.readyState === "complete") keyboardButtons();
else document.addEventListener("readystatechange", function (e) {
    if (e.target.readyState === "complete") keyboardButtons();
});

Reply via email to