Ecosia Google button

Adds a Google button to Ecosia search page.

// ==UserScript==
// @name         Ecosia Google button
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Adds a Google button to Ecosia search page.
// @author       reagent
// @match        https://www.ecosia.org/search?*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    const icon = document.createElement("img");
    icon.src = "https://www.google.com/s2/favicons?domain=www.google.com";

    const link = document.createElement("a");
    link.setAttribute("class", "navbar-link");
    link.appendChild(icon);
    link.appendChild(document.createTextNode("Google"));

    const updateLink = search => link.setAttribute("href", "https://google.com/search?q=" + encodeURIComponent(search));

    // Class for knowing when element was added to DOM, for hooking events and such
    // Assumes that once element is added, it wont be removed again.
    class DomWatch{
        constructor(){
            this.awaiting = [];
            this.watching = false;
            this.obs = new MutationObserver(this.onMutations.bind(this));
        }
        watch(){
            this.watching = true;
            this.obs.observe(document, {subtree:true, childList: true});
        }
        on(selector, ...fns){
            const found = document.querySelector(selector);
            if(found){
                fns.forEach(fn => fn(found));
                return this;
            }

            const i = this.awaiting.findIndex(item => item.selector === selector);
            if(i === -1){
                this.awaiting.push({selector, fns});
            }else{
                this.awaiting[i].fns = this.awaiting[i].fns.concat(fns);
            }
            if(!this.watching){
                this.watch();
            }

            return this;

        }
        off(){
            this.watching = false;
            this.obs.disconnect();
        }
        onMutations(muts) {
            for(const mut of muts){
                const item = this.awaiting.findIndex(({selector}) => mut.target.matches(selector));
                if(item !== -1){
                    this.awaiting[item].fns.forEach(fn => fn(mut.target));
                    this.awaiting.splice(item, 1);
                }else{
                    let queried = null;
                    const item = this.awaiting.findIndex(({selector}) => queried = mut.target.querySelector(selector));

                    if(item !== -1){
                        this.awaiting[item].fns.forEach(fn => fn(queried));
                        this.awaiting.splice(item, 1);
                    }
                }
                if(this.watching && !this.awaiting.length){
                    return this.off();
                }
            }
        }
    }

    const watch = new DomWatch();

    watch.on("nav.navbar-row", row => {
        const webButton = row.querySelector("div li");
        const search = document.querySelector("input.search-form-input");

        webButton.parentNode.insertBefore((() => {
            let el = document.createElement("li");
            el.setAttribute("class", "navbar-item navbar-item-vertical");
            el.appendChild(link);
            return el;
        })(), webButton)


        if(!search) return console.error("No search found");
        search.addEventListener("keydown", e => updateLink(search.value))

        updateLink(search.value);
    });
})();