typescript getter function in class returns different type than one specified

  export class TimeSeriesDB {
    private static inst: TimeSeriesDB;
    public static db: InfluxDB;
    public static connected: Promise<boolean>;
  
    constructor() {
      const url = Settings.get('INFLUX2_URL');
      const token = Settings.get('INFLUX2_TOKEN');
      TimeSeriesDB.db = new InfluxDB({
        url,
        token,
      });
    }
  
    static async connect() {
      return true;
    }
  
    static disconnect() {
      // delete this.instance.db
    }
  
    static get instance(): TimeSeriesDB {
      if (this.inst === undefined) {
        this.inst = new TimeSeriesDB();
        this.connected = this.connect();
      }
      return this.connected;
    }
  }

export default TimeSeriesDB.instance

instance getter function should return TimeSeriesDB type but it returns Promise<boolean>.
this code works perfectly fine. Can someone tell me what is happening here?

I expected the code above not to be compilable.

How to sort Javascript array of objects based on mapped keys between objects

I need to write a function that sorts this array based on dialog_node key and previous_sibling keys. The previous_sibling of the next object matches the dialog_node value of the previous object in the array.


export function orderDialogNodes(nodes) {
  // Create a mapping of dialog_node to its corresponding index in the array
  const nodeIndexMap = {};

  nodes.forEach((node, index) => {
    nodeIndexMap[node.dialog_node] = index;
  });

  // Sort the array based on the previous_sibling property
  nodes.sort((a, b) => {
    const indexA = nodeIndexMap[a.previous_sibling];
    const indexB = nodeIndexMap[b.previous_sibling];

    return indexA - indexB;
  });

  return nodes;
}

const inputArray = [
  {
    type: "folder",
    name: "Item 2",
    dialog_node: "node_2_1702794723026",
    previous_sibling: "node_9_1702956631016",
  },
  {
    type: "folder",
    name: "Item 3",
    dialog_node: "node_3_1702794877277",
    previous_sibling: "node_2_1702794723026",
  }, 
  {
    type: "folder",
    name: "Item 1",
    dialog_node: "node_9_1702956631016",
    previous_sibling: "node_7_1702794902054",
  },
];

const orderedArray = orderDialogNodes(inputArray);
console.log(orderedArray);

Misaligned Dropdown Menu with Hamburger Icon in a Flexbox Navbar

I’m working on a responsive navigation bar using HTML, CSS, and vanilla JavaScript. The navbar includes a hamburger icon that, when clicked, toggles the visibility of a dropdown menu. However, I’m facing an issue where the dropdown menu items are not aligning correctly right to the hamburger icon.

Here’s the code to have a better idea of the problem:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Taskboard</title>
    <link rel="stylesheet" href="/static/styling/home.css">
    <style>
        body, html {
            height: 100%;
            margin: 0;
            font-family: Arial, sans-serif;
            background-color: #333;
            color: white;
        }
        
        header {
            background-color: #222;
            padding: 10px 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        nav {
            position: relative; /* Ensure nav is positioned for absolute positioning of children */
        }
        
        nav h1 {
            margin: 0;
            display: flex;
            align-items: center;
            background-color: blue; /* Set the background color to blue */
            padding: 10px; /* Adjust padding as needed */
            position: relative; /* Ensure relative positioning for the nav h1 */
        }
        
        nav .hamburger {
            cursor: pointer;
            font-size: 24px;
            margin-left: 10px;
            color: white; /* Change the color of the hamburger icon to white for better visibility */
        }
        
        .menu {
            list-style: none;
            padding: 0;
            position: absolute;
            top: 0; /* Align with the top of nav h1 */
            left: 100%; /* Position to the right of the nav h1 */
            transform: scaleX(0); /* Initially collapsed along the X axis */
            transform-origin: left;
            transition: transform 350ms ease-in-out; /* Animate the transform property */
            background-color: #222;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2); /* Optional: Add a shadow for better visibility */
        }
        
        .showNavMenu {
            transform: scaleX(1); /* Expand to full width */
        }
        
        ul li {
            display: block; /* Change to block for vertical list */
            padding: 5px;
            background-color: blue;
            color: blue;
            margin-top: -13px;
            margin-left: 8px;
        }
        
        ul li:hover {
            background-color: blue;
        }

        .button-blue {
            background: #007bff; /* Assuming a solid blue; replace with gradient if needed */
            color: white;
            border: none;
            padding: 10px 15px;
            font-size: 16px;
            font-weight: bold;
            border-radius: 4px; /* Adjust as needed */
            margin-left: 10px; /* Space between buttons */
            transition: background-color 0.3s ease; /* Smooth transition for hover effect */
            text-transform: uppercase; /* If the text should be uppercase as in the screenshot */
        }

        nav {
            display: flex;
            justify-content: space-between; /* Adjust this to align the nav items as in the screenshot */
            align-items: center;
            padding: 10px; /* Adjust as needed */
        }
        
        /* Style for the hamburger icon */
        nav .hamburger {
            cursor: pointer;
            font-size: 24px;
            color: white; /* Change the color of the hamburger icon to white for better visibility */
        }
        
        /* You might also want to style the <ul> element to have padding and margin 0 */
        nav ul {
            padding: 0;
            margin: 0;
            display: flex; /* To align the buttons horizontally */
        }
        
        /* Style for the <li> elements to remove list styles and margins */
        nav ul li {
            list-style: none;
            margin: 0;
        }

        ul li button.button-blue:hover {
            background-color: blue; /* Keep the background color blue on hover */
        }


        .button-blue:hover {
            background: linear-gradient(to bottom right, #0056b3, #004291); /* Darker shade for hover effect */
            box-shadow: 0 2px #002b5e; /* Smaller shadow for pressed effect */
            transform: translateY(2px); /* Gives the button a 'pressed' effect on hover */
        }

        #new-taskboard-form {
            display: none;
            margin: 20px;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 8px;
            background-color: #f9f9f9;
        }

        #new-taskboard-form input[type="text"],
        #new-taskboard-form input[type="file"] {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            display: inline-block;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }

        #new-taskboard-form button {
            width: 100%;
            background-color: #4CAF50;
            color: white;
            padding: 14px 20px;
            margin: 8px 0;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        #new-taskboard-form button:hover {
            background-color: #45a049;
        }

        #new-taskboard-form input[type="text"]::placeholder {
            color: #888;
        }

        .modal {
            display: none; /* Hidden by default */
            position: fixed; /* Stay in place */
            z-index: 1; /* Sit on top */
            left: 0;
            top: 0;
            width: 100%; /* Full width */
            height: 100%; /* Full height */
            overflow: auto; /* Enable scroll if needed */
            background-color: rgb(0,0,0); /* Fallback color */
            background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
            padding-top: 60px;
        }
        
        .modal-content {
            background-color: #fefefe;
            margin: 5% auto; /* 5% from the top and centered */
            padding: 20px;
            border: 1px solid #888;
            width: 80%; /* Could be more or less, depending on screen size */
            border-radius: 8px; /* Optional: Rounded corners */
        }
        
        .close-button {
            color: #aaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
        }
        
        .close-button:hover,
        .close-button:focus {
            color: black;
            text-decoration: none;
            cursor: pointer;
        }
        
        .taskboard-selector {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            display: inline-block;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }

        header {
            background-color: #222;
            padding: 10px 20px;
            display: flex;
            justify-content: space-between; /* Aligns items on opposite ends */
            align-items: center;
        }
        
        #settings-button {
            margin-right: 20px; /* Add some margin for spacing */
        }
    </style>
</head>
<body>
    <header>
        <nav>
            <h1>My New Taskboard <span class="hamburger">&#9776;</span></h1>
            <ul class="menu">
                <li><button id="update-button" class="button-blue">Update</button></li>
                <li><button id="edit-button" class="button-blue">Edit</button></li>
                <li><button id="open-button" class="button-blue">Open</button></li>
                <li><button id="new-button" class="button-blue">New</button></li>
            </ul>
        </nav>
        <button id="settings-button" class="button-blue">Settings</button> <!-- Moved outside of the nav, into the header -->
    </header>
    <main>

        <section class="taskboard-grid" id="taskboard-grid">
            <!-- Task cards will be dynamically added here -->
        </section>

        <!-- Hidden form for creating a new taskboard -->
        <div id="new-taskboard-form">
            <input type="text" id="new-taskboard-name" placeholder="Enter Taskboard Name">
            <input type="file" id="new-taskboard-file" accept=".xlsx">
            <button id="submit-new-taskboard">Create Taskboard</button>
        </div>        
    </main>

    <div id="taskboard-selection-modal" class="modal">
        <div class="modal-content">
            <span class="close-button">&times;</span>
            <h2>Select a Taskboard</h2>
            <select id="taskboard-selector" class="taskboard-selector">
                <!-- Taskboard options will be dynamically populated here -->
            </select>
            <button id="select-taskboard-button" class="button-blue">Open Taskboard</button>
        </div>
    </div>

    <script>
        // Toggle menu visibility
        const hamburger = document.querySelector('.hamburger');
        const menu = document.querySelector('.menu');
        hamburger.addEventListener('click', () => {
            menu.classList.toggle('showNavMenu');
        });
    
        // Open settings page
        document.getElementById('settings-button').addEventListener('click', function() {
            window.location.href = 'settings';
        });
    
        // Global variable to keep track of the currently selected taskboard
        let currentTaskboard = null;
    
        // Update taskboard functionality
        document.getElementById('update-button').addEventListener('click', function() {
            if (!currentTaskboard) {
                alert('Please select a taskboard first.');
                return;
            }
    
            var fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.accept = '.xlsx';
    
            fileInput.addEventListener('change', function(event) {
                var file = event.target.files[0];
                var formData = new FormData();
                formData.append('file', file);
                formData.append('name', currentTaskboard);
    
                fetch('/upload/', {
                    method: 'POST',
                    body: formData
                })
                .then(response => response.json())
                .then(data => {
                    fetchTaskboardData(currentTaskboard);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
            });
    
            fileInput.click();
        });
    
        // Open taskboard selection modal
        document.getElementById('open-button').addEventListener('click', function() {
            fetch('/list/')
            .then(response => response.json())
            .then(taskboards => {
                populateTaskboardSelector(Object.values(taskboards));
                showModal();
            })
            .catch(error => {
                console.error('Error fetching taskboards:', error);
            });
        });
    
        // Populate taskboard selector in modal
        function populateTaskboardSelector(taskboards) {
            var selector = document.getElementById('taskboard-selector');
            selector.innerHTML = '';
            taskboards.forEach(taskboard => {
                var option = document.createElement('option');
                option.value = taskboard;
                option.textContent = taskboard;
                selector.appendChild(option);
            });
        }
    
        // Show modal
        function showModal() {
            var modal = document.getElementById('taskboard-selection-modal');
            modal.style.display = 'block';
        }
    
        // Close modal functionality
        document.querySelector('.close-button').addEventListener('click', function() {
            var modal = document.getElementById('taskboard-selection-modal');
            modal.style.display = 'none';
        });
    
        window.onclick = function(event) {
            var modal = document.getElementById('taskboard-selection-modal');
            if (event.target == modal) {
                modal.style.display = 'none';
            }
        }
    
        // Handle taskboard selection from modal
        document.getElementById('select-taskboard-button').addEventListener('click', function() {
            var selectedTaskboard = document.getElementById('taskboard-selector').value;
            if (selectedTaskboard) {
                openTaskboard(selectedTaskboard);
                var modal = document.getElementById('taskboard-selection-modal');
                modal.style.display = 'none';
            } else {
                alert('Please select a taskboard.');
            }
        });
    
        // Edit taskboard functionality
        document.getElementById('edit-button').addEventListener('click', function() {
            if (!currentTaskboard) {
                alert('Please select a taskboard first.');
                return;
            }
            window.location.href = `/edit?name=${encodeURIComponent(currentTaskboard)}`;
        });
    
        // New taskboard functionality
        document.getElementById('new-button').addEventListener('click', function() {
            document.getElementById('new-taskboard-form').style.display = 'block';
        });
    
        // Submit new taskboard
        document.getElementById('submit-new-taskboard').addEventListener('click', function() {
            var taskboardName = document.getElementById('new-taskboard-name').value;
            var fileInput = document.getElementById('new-taskboard-file');
            var file = fileInput.files[0];
    
            if (!taskboardName || !file) {
                alert('Please enter a taskboard name and select a file.');
                return;
            }
    
            var formData = new FormData();
            formData.append('name', taskboardName);
            formData.append('file', file);
    
            fetch('/create', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                console.log('Taskboard created:', data);
                document.getElementById('new-taskboard-form').style.display = 'none';
                document.getElementById('new-taskboard-name').value = '';
                fileInput.value = '';
            })
            .catch(error => {
                console.error('Error:', error);
            });
        });
    
        // Function to fetch and display taskboard data
        function fetchTaskboardData(taskboardName) {
            console.log('Fetching data for:', taskboardName); // Debug log
            fetch(`/get/?name=${encodeURIComponent(taskboardName)}`)
            .then(response => response.json())
            .then(data => {
                console.log('Data received:', data); // Debug log
                var taskboardGrid = document.getElementById('taskboard-grid');
                taskboardGrid.innerHTML = ''; // Clear existing content
        
                // Check if data is an object and not an array
                if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
                    Object.values(data).forEach(createCard); // Iterate over object values
                } else {
                    console.error('Unexpected data format:', data);
                }
            })
            .catch(error => {
                console.error('Error fetching taskboard data:', error);
            });
        }
    
        // Function to create a card for each task
        function createCard(row) {
            console.log('Creating card for:', row); // Debug log
            var card = document.createElement('div');
            card.className = 'task-card';
    
            Object.keys(row).forEach(key => {
                if (row[key] !== null && row[key] !== undefined) {
                    var p = document.createElement('p');
                    p.textContent = `${key}: ${row[key]}`;
                    card.appendChild(p);
                }
            });
    
            document.getElementById('taskboard-grid').appendChild(card); // Append card to taskboard grid
        }
    
        // Open taskboard function
        function openTaskboard(taskboardName) {
            currentTaskboard = taskboardName;
            fetchTaskboardData(taskboardName);
        }
    
        // DOMContentLoaded event listener
        document.addEventListener('DOMContentLoaded', function() {
            const urlParams = new URLSearchParams(window.location.search);
            const taskboardName = urlParams.get('name');
            if (taskboardName) {
                openTaskboard(taskboardName);
            }
        });
    </script>
    
</body>
</html>

Here’s an image showing the problem:

enter image description here

And here is an image how it should look like:

enter image description here

Could someone help me understand why the dropdown menu is not aligning correctly right the hamburger icon and how to fix it?

Bundling an emscripten web assembly module in a CloudFlare worker

I’m trying to use this WASM compiled library on CloudFlare workers, which in theory support Web Assembly. The library is only meant for Node.js or browsers and it’s proven tricky to get working on CloudFlare workers.

There’s a .wasm file as well as some glue code generated by emscripten to load it. I have confirmed that my file is uploaded as part of my deployment static public files, and accessible. The glue code which can be seen here in xmllint-browser.mjs does a fetch and a call to WebAssembly.Instantiate.

When running on CloudFlare Workers, the fetch does work but then the following error occurs:

failed to asynchronously prepare wasm: CompileError: WebAssembly.instantiate(): Wasm code generation disallowed by embedder

A short conversation on Discord with someone from CF has revealed bits of info:

The module can’t be inlined as a buffer, it needs to be uploaded as a separate .wasm file

After clarifying that I was I was in fact fetching a separate .wasm file:

Fetch as in from a URL? That’s basically the same thing as loading it as a Buffer.

It needs to be bundled

With that information however I am still confused as to how that should work. I am using Astro (and thus Vite) to build my app. How can I manage to satisfy the requirements of the Cloudflare workers runtime for .wasm with this library?

New to JavaScript! PDF sales form that calculates tax [closed]

I created a basic sales order form with a checkbox that runs a simple script that calculates sales tax based on a subtotal. My default tax amount is 6% however, I need to make this able to calculate the taxes based on the percentage listed in the tax_rate box. The problem I’m having is I don’t know how to make my script work with the percentage placed in the tax_rate box, and not just the .06.

My script:

this.getField("Total_Tax").value=this.getField("Subtotal").value *.06;

I tried the code below as well:

this.getField("Total_Tax").value=this.getField("Subtotal").value * this.getField(Tax_Rate).value;

Boolean not updating from button click in Next.js dashboard

I am creating a dashboard with a lot of ‘control’ buttons that update a mongoDB database. Most of my buttons function properly, but I am having trouble with a simple toggle button called ‘Standby’, pressed once standby is on, pressed again standby is off and the database is updated.

Here is the html for the button, the id and the current standby state is passed into the standByButton function.

<form action={standByButton}>
   <input type="hidden" name="id" value={staff.id} />
   <input type="hidden" name="isStandBy" value={staff.standBy} />
   <StandByButton type={"submit"}/>
</form>

Here is the standByButton function. My issue comes from changing the state of the boolean. Here I expect it to reverse every time the button is pressed, but the state does not change with const standBy = !isStandBy;. In fact it works elsewhere in my script, but not here. I have confirmed that the database is updated with each call, but standBy does not change state.

export const standByButton = async (formData) => {
    const { id, isStandBy } = Object.fromEntries(formData);
 
    const standBy = !isStandBy;


    try {
        connectToDB();

        const updateFields = {
            standBy
        }

        console.log(updateFields);

        await Daily.findByIdAndUpdate(id, updateFields);

    } catch (err) {
        console.log(err);
        throw new Error("Failed to update standby");
    }

    revalidatePath("/dashboard/board");
}

I have tried two methods currently to change the boolean. I have tried const standBy = !isStandBy; which did not work. I also tried changing the state after updatedFields has been created.

export const standByButton = async (formData) => {
    const { id, isStandBy } = Object.fromEntries(formData);
 
    const standBy = !isStandBy;

    try {
        connectToDB();
        const updateFields = {
            standBy
        }

        Object.keys(updateFields).forEach(
            (key) =>
               (parseInt(updateFields[key]) == true) ? (updateFields[key] = false) :
               (parseInt(updateFields[key]) == false) && (updateFields[key] = true)
        )

        console.log(updateFields);
        await Daily.findByIdAndUpdate(id, updateFields);

    } catch (err) {
        console.log(err);
        throw new Error("Failed to update standby");
    }

    revalidatePath("/dashboard/board");
}

This method did not work either. I have checked that the form is being passed correctly, and the database is being updated correctly, so the error must be with the boolean state change but I can’t pin point exactly what is causing it. I have also tried changing the the type of variable by using let standBy = !isStandBy; but it still does not change the outcome.

How to trigger the first build with esbuild.context

I sometimes just want to build instead of watching how to trigger the build without ctx.watch(). In this code I trigger the build by ctx.watch(), if the watch variable is false then run ctx.dispose() to close esbuild so it just builds without watching. There should be a good and proff way of doing it but idk how.

const ctx = await esbuild.context({
    entryPoints: ["./index.js"],
    outdir: outDir,
    write: false,
    plugins: [
        {
            name: "on-end",
            setup(build) {
                build.onEnd(async (result) => {
                    await processCode(result)
                })
            },
        },
    ],
})
await ctx.watch()
if (!watch) ctx.dispose()

ES6 classes extends a class with returning constructor

In the following code I have an unexpected behaviour for me. I was waited to see the method A2::method() to be called, but it is A0::method() instead.
More tests showed me that if A1 returns something, it will keep public prop from A2 but forget all methods from A2. Maybe I’m doing something wrong. Is there a workaround

class A0 {
  method() {
    return "from A0 " + this.prop
  }
}

class A1 {
  constructor() {
    return new A0;
  }
}


class A2 extends A1 {
  constructor(v) {
    super()
    console.log('here')
    this.prop = v;
  }
  prop = 42;

  method() {
    return "from A2 " + this.prop
  }
}
const a = new A2(42);
console.log(a.prop, a.method())

Result

[LOG]: "here" 
[LOG]: 42,  "from A0 42" 

Expected

[LOG]: "here" 
[LOG]: 42,  "from A2 42" 

A Code Formatter in VSCode for both C# and JavaScript

I’m coding C# – ASP.NET Core as Backend and JavaScript – React as Frontend at the same time in VSCode in one ‘VSCode window’ as two workspaces, if I need to format my JS codes I use ‘Prettier’ extention and for formatting C# I use ‘CSharpier’, so I have to change ‘fomatter setting’ in VSCode between CSharpier and Prettier all the time, is there any extention or anyway to format both languages codes without changing ‘formatter setting’ in VSCode? or any better idea for coding in both languages at the same time?

VSCode version: 1.85

enter image description here

RegEx to replace full decimal number with a single pound (#)

My users will be entering decimal numbers (positive & negative).
I’m already saving that decimal number.
What I need is to replace that full decimal number with a single # sign.

For example:
The user might enter /m-125.45Enter
What I need for further processing is m/#Enter

In JavaScript, I could walk the user’s string and

  1. remove all -.0123456789
  2. add a single #

Is there a JS regEx or string manipulation to do the conversion more directly?

How to stop awaiting and exit exceution of a function while api calls?

Here i have a fuction that update the data

async function update() {
      try {
        const res = await axios.post('update', { data: '123' })

       //This response may be delayed
        navigate(previousScreen)

      } catch (error) {
        //handle error
      }
    }

Everything works as expected, but the problem is while updating, if i click to other page it goes to the other screen. Then once the update api promise is resolved, then again navigate() is called which is below await.

Sometime while updating if i click logout, it logout and again it navigate.

Is there is any way to stop all awaiting queues?

Set button text when appending to form

I have buttons that are created when reading info from a database. When one of the buttons is clicked, a form is created on the fly. The form creates fine, but the problem I am having is setting the text of the button for the form. I have the following to create the form and set the text:

This is the code that sets the button:

<button class="replybtn" id="replybtn<?php echo $commid; ?>" value="<?php echo $commid; ?>">Reply</button>

And this is the code that creates the form when the button is clicked:

$('.replybtn').click(function (e) {
    e.preventDefault();
    var btnnum = $(this).val();
    
    var form = $("<form>", {
        action: 'http://intranet/generalinfo/communitybb/wp-admin/admin-ajax.php',
        method: 'POST',
        id: replyform,
        class: 'replyform'
    });
    
    form.append( $("<textarea>", { 
        name: 'reply', 
        class: 'commentbox',
        rows: '5',
        cols: '150'
    }));
    
    form.append($("<button>", {
        class: 'replybtn',
        id: replybtn
    }));
    
    $("#" + replybtn).html = "Reply";
});

Javascript created Input box not showing typed text

I am dynamically created HTML text entry boxes, via JavaScript.
They do show up on the webpage, and their border does highlight when I attempt to
type into them. However, nothing show up in the Input box as I type.
Below is the code. The Input Boxes are positioned absolutely over a Canvas.
Does anyone have any ideas? Thanks so much!

for (i = 0; i < 25; i++) {
    line(40, 50 + i * 42, 1875, 50 + i * 42);

    var inputBox = document.createElement('input');
    inputBox.setAttribute('type', 'text');
    inputBox.style.position = 'absolute';
    inputBox.style.top = 142 + i * 42 + 'px';
    inputBox.style.left = '500px';
    inputBox.style.width = '30px';
    inputBox.style.height = '23px';
    inputBox.style.fontSize = '10px';
    inputBox.style.display = 'block';
    // inputBox.innerHTML = 'J';
    inputBox.id.attr = 'attr' + i;

    var parentItem = document.getElementById('canvasForHTML');
    parentItem.appendChild(inputBox);

   

}

Javascript’s equivalent to seaborn diverging_palette(x, y, as_cmap=True)

For an AG Grid data table I would like to display the cell background and font using the same principle as Seaborn’s diverging_palette where I pass two anchor hues for negative and positive extents of the map and I get back a diverging palette. I can then map a range of values to the corresponding color. I would also like to it to switch the font color between white and black depending on the background darkness.

Is there such Javascript functionality?

An example of what I’m after is:
enter image description here