Aeronautica.fi edit note button

When booking more than ~10 slots, you can't see all bookings under "My bookings" which

// ==UserScript==
// @name        Aeronautica.fi edit note button
// @namespace   Violentmonkey Scripts
// @match       https://book.aeronautica.fi/customer-bookings*
// @grant       none
// @version     1.0
// @author      buq2 / Matti Jukola
// @description When booking more than ~10 slots, you can't see all bookings under "My bookings" which
//              is only place with the edit button for notes. This script adds edit button under
//              "Customer bookings" page which can display all your bookings.
// @source      https://github.com/buq2/UserScripts/aeronautica.fi-add-missing-note-edit-button.user.js
// @license     MIT
// @run-at      document-idle
// ==/UserScript==


/**
 * A utility function for userscripts that detects and handles AJAXed content.
 *
 * @example
 * waitForKeyElements("div.comments", (element) => {
 *   element.innerHTML = "This text inserted by waitForKeyElements().";
 * });
 *
 * waitForKeyElements(() => {
 *   const iframe = document.querySelector('iframe');
 *   if (iframe) {
 *     const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
 *     return iframeDoc.querySelectorAll("div.comments");
 *   }
 *   return null;
 * }, callbackFunc);
 *
 * @param {(string|function)} selectorOrFunction - The selector string or function.
 * @param {function}          callback           - The callback function; takes a single DOM element as parameter.
 *                                                 If returns true, element will be processed again on subsequent iterations.
 * @param {boolean}           [waitOnce=true]    - Whether to stop after the first elements are found.
 * @param {number}            [interval=300]     - The time (ms) to wait between iterations.
 * @param {number}            [maxIntervals=-1]  - The max number of intervals to run (negative number for unlimited).
 */
function waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals) {
    if (typeof waitOnce === "undefined") {
        waitOnce = true;
    }
    if (typeof interval === "undefined") {
        interval = 300;
    }
    if (typeof maxIntervals === "undefined") {
        maxIntervals = -1;
    }
    if (typeof waitForKeyElements.namespace === "undefined") {
        waitForKeyElements.namespace = Date.now().toString();
    }
    var targetNodes = (typeof selectorOrFunction === "function")
        ? selectorOrFunction()
        : document.querySelectorAll(selectorOrFunction);

    var targetsFound = targetNodes && targetNodes.length > 0;
    if (targetsFound) {
        targetNodes.forEach(function (targetNode) {
            var attrAlreadyFound = `data-userscript-${waitForKeyElements.namespace}-alreadyFound`;
            var alreadyFound = targetNode.getAttribute(attrAlreadyFound) || false;
            if (!alreadyFound) {
                var cancelFound = callback(targetNode);
                if (cancelFound) {
                    targetsFound = false;
                }
                else {
                    targetNode.setAttribute(attrAlreadyFound, true);
                }
            }
        });
    }

    if (maxIntervals !== 0 && !(targetsFound && waitOnce)) {
        maxIntervals -= 1;
        setTimeout(function () {
            waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals);
        }, interval);
    }
}

/**
 * Extracts the booking ID from the 'unbook' button's href attribute.
 * 
 * @param {HTMLElement} button - The 'unbook' button element.
 * @returns {string} - The booking ID extracted from the button's href.
 */
function getBookingIdFromButton(button) {
    const unbookLink = button.getAttribute('href');
    const match = /booking_id=(\d+)/.exec(unbookLink);
    return match ? match[1] : null;  // Return null if booking ID is not found.
}

/**
 * Retrieves all 'Unbook' buttons on the page.
 * 
 * @returns {HTMLElement[]} - Array of 'Unbook' button elements.
 */
function getUnbookButtons() {
    return Array.from(document.querySelectorAll('.btn')).filter(el => el.textContent.trim() === 'Unbook');
}

/**
 * Creates an "Edit Note" button next to the provided 'Unbook' button.
 * 
 * @param {HTMLElement} unbookButton - The 'Unbook' button to base the new button on.
 */
function createEditNoteButton(unbookButton) {
    const bookingId = getBookingIdFromButton(unbookButton);
    if (!bookingId) {
        console.error('Booking ID not found.');
        return;  // Prevent creating button if booking ID is missing.
    }

    const parent = unbookButton.parentElement;
    const editNoteButton = unbookButton.cloneNode(true);
    editNoteButton.setAttribute('onClick', `popupBookingNote(${bookingId})`);
    editNoteButton.removeAttribute('href'); // Prevent redirect on click.
    editNoteButton.textContent = "Note"; // Change text to "Note".
    parent.appendChild(editNoteButton); // Append the new button.
}

// Call `waitForKeyElements` to wait for the unbook buttons and create "Edit Note" buttons for them.
waitForKeyElements(getUnbookButtons, createEditNoteButton);