How to redirect users to a custom URL after authentication?

I am using html and JS with Clerk.js for authentication on my website. I want to redirect users to a specific URL after they successfully log in or sign up. I include the Clerk SDK in my index.html:

 <script
    async
    crossorigin="anonymous"
    data-clerk-publishable-key="myKey"
    src="https://cdn.jsdelivr.net/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"
    type="text/javascript"
  >
</script>

I’m initializing Clerk and checking if the user is logged in using the following JavaScript code:

window.addEventListener("load", async function () {
    try {
        // Initialize Clerk
        await Clerk.load();
        console.log("ClerkJS is loaded");

        // Check if the user is authenticated
        if (Clerk.user) {
            console.log("User is logged in");
            const url = "https://myUrl.com"
            window.location.href = url;

        } else {
            console.log("User is not logged in");
        }
    } catch (error) {
        console.error("Error initializing Clerk:", error);
    }
});

I expected Clerk to provide an easy way to configure redirection after a user logs in or signs up, possibly using something like signInForceRedirectUrl or signUpForceRedirectUrl. However, I’m unsure how to implement this functionality with their JavaScript SDK, and I don’t fully understand the documentation on how to handle this specific use case.

I’ve tried manually checking Clerk.user to determine if a user is authenticated and using window.location.href to redirect the user after login or sign-up:

if (Clerk.user) {
    console.log("User is logged in");
    const url = "https://myUrl.com";
    window.location.href = url;
}

However, I want to replace window.location.href with a Clerk method to handle the redirection, whether the user logs in or creates an account. What’s the best way to achieve this using Clerk’s built-in methods or configurations?

Mocking node:crypto with Vitest

I tried following another related post on this topic with the creator’s same outcome.

Since its post is dead I’d like to try again if someone has an idea on how to mock the node:crypto function or any builtin function.

My base case study is a simple randomUUID generator just to test that the mocking is working so:

import { randomUUID } from "node:crypto";

export function getRandomUUID() :string {
  return randomUUID();
}

and I tried like in the above post the suggested solution:

 vi.mock("node:crypto", async () => {
      const actual =
        await vi.importActual<typeof import("node:crypto")>("node:crypto");
      return {
        ...actual,
        randomUUID: vi.fn(() => "123456789"),
      };
    });

The getRandomUUID return undefined in this case

I also tried a similar jest solution on this post

but vitest fails with an error about the import:

Error: [vitest] No "default" export is defined on the "node:crypto" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:

vi.mock(import("node:crypto"), async (importOriginal) => {
  const actual = await importOriginal()
  return {
    ...actual,
    // your mocked methods
  }
})

so I tried this one as well with my mock method (the mock doesn’t like the return type of randomUUID tho Type 'string' is not assignable to type '${string}-${string}-${string}-${string}-${string}')

    vi.mock(import("node:crypto"), async (importOriginal) => {
      const actual = await importOriginal();
      return {
        actual,
        // your mocked methods
        randomUUID: vi.fn(() => "123456789"),
      };
    });

and doesn’t mock the actual method

‘this’ is Undefined in Instance Methods when Importing Puppeteer Inside a Module

I am running into an issue with Puppeteer where ‘this’ is undefined within the Puppeteer class instance methods when Puppeteer is imported into a module.

When I import Puppeteer directly in my main script, everything works as expected. However, as soon as I try to import and use Puppeteer within a separate module, I get the following error:

TypeError: Cannot read properties of undefined (reading '#defaultContext')
    at newPage (/node_modules/puppeteer-core/lib/esm/puppeteer/cdp/Browser.js:173:27)

Upon closer inspection, it appears that this is undefined inside of the browser.newPage() instance method whenever Puppeteer is imported within a module.

I have tried tweaking the allowSyntheticDefaultImports, esModuleInterop, module and moduleResolution compiler options in my tsconfig.json to no avail. I also tried importing Puppeteer using the import puppeteer from "puppeteer", import * as puppeteer from "puppeteer", and import puppeteer = require("puppeteer") syntaxes and am running into the same problem in all three situations.

While manually binding this when invoking the instance method appears to be a workaround (e.g., browser.newPage.bind(browser)), you seemingly have to do so every time you call any instance methods of Puppeteer’s classes.

I use ytdl-core in node, but have unstable dowload of videos! My internet stable, i have memory, but sometimes i have error

Sometimes the video downloads successfully, and sometimes not, despite the fact that the code does not change in any way, the Internet is stable. It gives this error when an attempt fails:

C:UsersGlebDesktopворк[email protected]:180
            throw new Error(`No such format found: ${QUALITY}`);
                  ^
Error: No such format found: highestaudio
    at FormatUtils.chooseFormat (C:UsersGlebDesktopворк[email protected]:180:19)
    at C:UsersGlebDesktopворк1.0.2node_modules@ybd-projectytdl-corepackagecoreDownloadDownload.js:92:51
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5).

Don’t mind that I use not original ytdl-core. I tried using “try” and “catch” to run the program in a circle until a successful attempt, but this error is not caught. How i can fix it?

my code:

import fs from 'fs';
import { YtdlCore, toPipeableStream } from '@ybd-project/ytdl-core';

const ytdl = new YtdlCore({});

let vidId = '';

ytdl
  .download(`https://www.youtube.com/watch?v=${vidId}`, {
    quality: 'highestaudio',
  })
  .then((stream) =>
    toPipeableStream(stream).pipe(fs.createWriteStream(`${vidId}_audio.m4a`)),
  );

ytdl
  .download(`https://www.youtube.com/watch?v=${vidId}`, { quality: 'highest' })
  .then((stream) =>
    toPipeableStream(stream).pipe(fs.createWriteStream(`${vidId}.mp4`)),
  );

Shaders lookAt does’t function right WebGl



    var projection = glMatrix.mat4.create()

    var modelView = glMatrix.mat4.create();

    var modelViewProjection = glMatrix.mat4.create();

    

    glMatrix.mat4.perspective(projection, { 0: 120 }, 4 / 3, .1, 10000.);

    

    glMatrix.mat4.lookAt(modelView, [ x, y, z ], [ x + cf(lx), y + cf(ly), z + sf(lx) ], [0, 1, 0]);

    glMatrix.mat4.translate(modelView, modelView, [0, 0, 0]);

    

    var k = glMatrix.mat4.multiply(modelViewProjection, modelView, projection);

    

    var loc_modelView = gl.getUniformLocation(shaderProgram, "modelView");

    var loc_projection = gl.getUniformLocation(shaderProgram, "projection");

    

    gl.uniformMatrix4fv(loc_modelView, false, modelView);

    gl.uniformMatrix4fv(loc_projection, false, projection);

    

    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);

    

    

Shaders are linked correct and o tested using others codes i’m here only for no repeat the process, probabily something like identity of lookAt or not fitting right the sequence of matrix

I tested the thing i Said before!

Is there a way to accept multiple images as one attachment option within a Discord Slash Command?

I’m currently building a discord JS bot to host an art server and I’m working on a command to allow users to upload their own reference photos to a designated #references channel. Users cannot type in this channel, only the bot. I was able to build this functionality where users can call the /submit function and upload one image at a time, but is there a way to accept n different images within one slash command?

My SlashCommandBuilder looks like this (with some additional for things like a description or who to credit for the image):

module.exports = {
    data: new SlashCommandBuilder()
            .setName('submitref')
            .setDescription('Submit a reference photo to the server.')
            .addAttachmentOption(option =>
                option
                    .setName('ref')
                    .setDescription('Reference Photo')
                    .setRequired(true)
                )

I tried using the existing implementation and ‘ctrl+click’ing multiple images from my local to see if the parameter would accept multiple files, but it seems to only accept 1 image at a time.

I could instead modify the command and take (e.g.) 4 additional optional images as separate parameters, but the ideal solution from a user experience perspective here would be to be able to ‘ctrl click’ multiple files and upload in one go.

How to close window in motd file?

I have a problem with closing the window. I’m writing a motd file for cs1.6, the task is to launch the gif in the second window, which opens after clicking on the link. Everything opens, the gif plays, the window can be closed, but it does not close automatically after a timeout. In the classic browser it closes and everything works correctly, one CS does not want to skip my script. Help please

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        function closeWindow(gifWindow){
            if (gifWindow) {
                gifWindow.close();
            }
        }
        function openGifWindow() {
            var gifWindow = window.open("video.gif", "GifWindow", "width=400,height=400");
            setTimeout(function() { closeWindow(gifWindow); }, 1000);
        }
    </script>
</head>
<body>
    <a href="#" onclick="openGifWindow(); return false;">Открыть гифку</a>
</body>
</html>

How to display span tag based on language related to selected drop down?

I work on jQuery I face issue I need to display span tag based on language

when language Arabic then it will be اختر منفذ

when language English then it will be Select an Option

I get Language based on MYLang.ReturnLang() == "ar-KW" ? "اختر منفذ" : "Select an Option";

jQuery I need to modify

for (var i in Result.d.P_DISPATCH_DATA) {
  var row =
    "<tr align='center' id='tr" +
    Result.d.P_DISPATCH_DATA[i].DISPATCH_RECID +
    "' class='record'> " +
    "<td>" +
    //(rowNo + 1) + "</td><td>" +
    Result.d.P_DISPATCH_DATA[i].CUSTOMER_NAME +
    "</td> <td style='width:150px'>";

  row +=
    "<select id='ddl_Port" +
    Result.d.P_DISPATCH_DATA[i].DISPATCH_RECID +
    "' class='form-control chosen-select chosen-rtl' tabindex='1'><option value=''></option>";
  var countriesPorts = Result.d.P_PORTS.filter(
    (d) => d.COUNTRY_ID == Result.d.P_DISPATCH_DATA[i].PICKUP_COUNTRY_ID,
  );

  if (countriesPorts != null) {
    if (countriesPorts.length > 0) {
      for (var j in countriesPorts) {
        row +=
          " <option value='" +
          countriesPorts[j].PORT_ID +
          "'>" +
          countriesPorts[j].PORT_NAME +
          "</option> ";
      }
      row += " </select></td>";
    }
  }
}

on browser jQuery code above display html as below

<td style="width: 150px">
  <select
    id="ddl_Port127"
    class="form-control chosen-select chosen-rtl"
    tabindex="-1"
    style="display: none"
  >
    <option value=""></option>
    <option value="1">السالمي</option>
    <option value="3">العبدلي</option>
    <option value="2">النويصيب</option>
  </select>
  <div
    class="chosen-container chosen-container-single chosen-rtl chosen-container-active"
    title=""
    id="ddl_Port127_chosen"
    style="width: 100%"
  >
    <a class="chosen-single chosen-default">
      <span>Select an Option</span>
      <div><b></b></div>
    </a>
    <div class="chosen-drop">
      <div class="chosen-search">
        <input
          class="chosen-search-input"
          type="text"
          autocomplete="off"
          tabindex="1"
        />
      </div>
      <ul class="chosen-results">
        <li class="active-result" data-option-array-index="1">السالمي</li>
        <li class="active-result" data-option-array-index="2">العبدلي</li>
        <li class="active-result" data-option-array-index="3">النويصيب</li>
      </ul>
    </div>
  </div>
</td>

Audio tracks not stopping after stopping the audio inputs – Chrome extension

I have a screen recorder in the extension where I use the microphone user, everything is working fine with the screen recorder but I have a small thing, after clicking on the Stop Recording button in the extension or the Stop Share chrome button it says that the microphone is still in use, and I can’t seem to debug it myself. I checked different ways here in stackoverflow but none seems to work so I decided to post a question.

Here’s the logic and code (this is all in the contentScript.js).

First when we open the extension it lists different audio types:

async function listAudioInputSources () {
    try {
        currentAudioStream = await navigator.mediaDevices.getUserMedia({audio: true});
        window.streamReference = currentAudioStream;
        const devices = await navigator.mediaDevices.enumerateDevices();
        audioInputs = devices.filter(device => device.kind === 'audioinput').map(device => ({
            deviceId: device.deviceId,
            label: device.label,
            volumeLevels: Array(10).fill(false)
        }));
        
        if(audioInputs.length > 0) {
            audioInputs.forEach(input => setupAudioContext(input.deviceId));
        }

        audioError = false

        chrome.runtime.sendMessage({
            action: 'audio-sources-updated',
            audios: audioInputs,
            micMsg: audioError
        });
    } catch (error) {
        console.error('Error accessing audio devices:', error);
        audioError = true
        chrome.runtime.sendMessage({ action: "audio-sources-updated", micMsg: audioError });
    }
}

After we click in one of the audios we call this function which sets that audio:

async function switchAudioInput(deviceId) {
    if (selectedAudioInput === deviceId) return;

    selectedAudioInput = deviceId;

    Object.values(audioStreams).forEach(stream => {
        stream.getTracks().forEach(track => track.stop());
    });

    Object.values(audioContexts).forEach(context => context.close());

    audioContexts = {};
    analyzerNodes = {};
    audioStreams = {};

    setupAudioContext(deviceId)
}
async function setupAudioContext (deviceId) {
    try {
        currentAudioStream = await navigator.mediaDevices.getUserMedia({
            audio: {
                deviceId: { exact: deviceId },
                noiseSuppression: false,
                echoCancellation: false,
            }
        });
        initializeAudioContext(deviceId, currentAudioStream);
    } catch (error) {
        console.log(error)
    }
}

And then we start recording (in the below function we also have the onstop event where are the steps that we do when the screensharing stops):

async function startScreenRecording() {
    try {
        const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: {frameRate: { max: 10 }, width: { max: 1280 }, height: { max: 720 }}});

        const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });

        mediaStream = new MediaStream([
            ...screenStream.getTracks(),
            ...audioStream.getTracks()
        ]);

        startTimer()

        mediaRecorder = new MediaRecorder(mediaStream, { mimeType: "video/mp4" });
        mediaRecorder.ondataavailable = (event) => {
            if (event.data.size > 0) {
                recordedChunks.push(event.data);
            }
        };

        let ended = false

        const videoStream = screenStream.getVideoTracks()[0];
        videoStream.addEventListener('ended', () => {
            ended = true
            mediaRecorder.stop();
            chrome.storage.local.set({ state: 'prerecording' })
        });

        mediaRecorder.onstop = () => {
            document.title = title
            clearInterval(timer)
            recordTime = 0

            const blob = new Blob(recordedChunks, { type: "video/mp4" });
            if (!isReset) {
                if (!ended) chrome.runtime.sendMessage({ action: "opening-video" });

                setTimeout(async () => {
                    const openVideoBackr = window.open(`${baseURL}/record?ext`, '_blank')

                    window.addEventListener('message', function handleMessage(event) {
                        if (event.source === openVideoBackr && event.data === 'ready') {
                            openVideoBackr.postMessage({ blob }, baseURL);
                            window.removeEventListener('message', handleMessage);
                        }
                    });
        
                    const checkWindowReady = setInterval(() => {
                        if (openVideoBackr) {
                            openVideoBackr.postMessage({ blob }, baseURL);
                        }
                    }, 100);
                    
                    setTimeout(() => {
                        clearInterval(checkWindowReady);
                    }, 5000);
                }, !ended ? 700 : 100)
            }
            recordedChunks = [];
            isReset = false
            ended = false
        };

        mediaRecorder.start();
    } catch (error) {
        console.error("Error starting screen recording:", error);
    }
}

when we click in the stop recording button in the extension we first call this func:

function stopScreenRecording() {
    if (mediaRecorder && mediaRecorder.state !== "inactive") {
        mediaRecorder.stop();
        if (mediaStream) {
            mediaStream.getTracks().forEach(track => track.stop());
            mediaStream = null;
        }
    }
}

I tried different ways of stopping the audio tracks but didnt seem to work.
I thought one thing might be that we are opening a new tab and redirecting the user to a new tab at the same time we stop recording or something. I tried to mess around with that but couldn’t figure it out.

That’s besically more or less all the main steps but if you need more information I’d be glad to provide it.

Thank you for your help!

div display status not changed by javascript

A short time ago, I posted a question similar to this which had a typo causing confusion. I apologize for that, it did not serve any of us well, and has since been deleted. I have done my best to ensure that this code is clean and typo-free. This code also shows the described problem.

This question better represents the problem that I have: that a div is not being shown when its display property is set to ‘block’. The reduced code is as follows:

function selectVidId(youtubeId, tableRowNbr, tableRowId, videoWidth, videoHeight) {
  const embedElement = document.getElementById('embedDiv');
  const videoElement = document.getElementById('videoDiv');

  let response = "<div id='muteYouTubeVideoPlayer'></div> 
    <script> 
      function onYouTubeIframeAPIReady() 
        {
        var player = new YT.Player('muteYouTubeVideoPlayer', 
            { videoId : 'f5JVDUI81nk' // YouTube Video ID    
            , width   : 480          // Player width (in px) 
            , height  : 360         // Player height (in px) 
            , playerVars: 
              { autoplay       : 0  // Auto-play the video on load       
              , controls       : 1  // Show pause/play buttons in player 
              , showinfo       : 1  // Show the video title              
              , modestbranding : 1  // Hide the Youtube Logo             
              , loop           : 0  // Don't run the video in a loop     
              , fs             : 0  // Don't hide the full screen button 
              , cc_load_policy : 0  // Hide closed captions              
              , iv_load_policy : 3  // Hide the Video Annotations        
              , autohide       : 0  // Hide video controls when playing  
              } // playerVars 
            , events: {} // events 
            } 
          ) ; // new .Player 
        } // function 
      // Written by @labnol 
    </script>";

  videoElement.style.display = 'none';
  embedElement.innerHTML = response;
  embedElement.style.display = 'block';
}
<!DOCTYPE html>
<html lang='en'>

<head>
  <title>Livestreams</title>
  <style>
  </style>
</head>
<script src="dbg.js">
</script>
</head>

<body>
  <div id='fullPageDiv' style='width: 100%; overflow:hidden;'>
    <div id='livestreamTable' style='width: 60%;float:left;'>
      <table border='1'>
        <thead>
          <tr>
            <th>Started</th>
            <th>Channel</th>
            <th>Songs noted</th>
            <th style='text-align:right;'>Duration</th>
            <th style='text-align:right;'>Dimensions</th>
            <th>Livestream Title</th>
          </tr>
        </thead>
        <tbody>
          <tr id='livestream_row_0'>
            <td>2021-12-04 07:15:08</td>
            <td style='text-align:left;'>Primary</td>
            <td style='text-align:right;'></td>
            <td style='text-align:right;'>1:04:54</td>
            <td style='text-align:right;' id='videoDimensions0'>1280x720*</td>
            <td><span onclick='selectVidId("f5JVDUI81nk", 0, "livestream_row_0", "1280", "720");'>Click-this-element</span></td>
          </tr>
        </tbody>
      </table>
    </div><!-- Livestream table -->
    <div id='videoDiv' style='display: none;'>
      <video id='idVideo' width="320" height="240" controls>
                <!-- <source src='replaceme.mp4' type='video/mp4'> -->
                Need top insert some video content here.
        </video>
    </div><!--videoDiv-->
    <div id='embedDiv' style='display: none;'>
      <p>Why, hello there!</p>
    </div><!-- embedDiv-->
  </div><!-- This is the "page" division (the whole page after the form) -->

</html>

The unfulfilled purpose of this page, is to present a list of items, and when one of those items is clicked, an associated YouTube video will be displayed in the top right. When I run this through the Chrome debugger, each line of the javascript code is executed. At the end, embedElement.style.display value is indeed set to ‘block’ but nothing from the embedDiv shows. (I inserted the silly “Why hello there” text solely because it should appear even if other things are problematic.)

Again I ask, what fundamental thing am I missing that causes this div to (apparently) not display?

What would be the correct approach to replacing the content of embedDiv in such a way that the necessary code actually is executed?

tsx (typescript-execute) imports index.js instead of index.d.ts

I’m trying to set up tsx (typescript-execute) in my project, my package.json is as follows:

{
  "name": "bb-tcg-backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node index.js",
    "dev": "npx tsx watch ./index.js",
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "devDependencies": {
    "nodemon": "^3.1.7",
    "prisma": "^6.2.1"
  },
  "dependencies": {
    "@prisma/client": "^6.2.1",
    "@types/express": "^5.0.0",
    "@types/node": "^22.10.7",
    "express": "^4.21.2",
    "express-openapi": "^12.1.3",
    "openapi-request-validator": "^12.1.3",
    "tsx": "^4.19.2",
    "typescript": "^5.7.3"
  }
}

and my tsconfig.json:

{
    "compilerOptions": {
        "moduleDetection": "force",
        "module": "ES2022",
        "moduleResolution": "node",
        "target": "ES2022",
        "resolveJsonModule": true,
        "allowJs": true,
        "esModuleInterop": true,
        "isolatedModules": true
    }
}

When I do npm run dev tsx fails to import the openapi-request-validator package. It imports its index.js instead of the correct index.d.ts which is set up differently and as a result my code breaks. I cant figure out why it cant find the correct file. What could be the problem?

EDIT: tsc can find it without issue

======== Module name 'openapi-request-validator' was successfully resolved to '/home/martinkupa/Documents/coding/intro-camejo/tp2/back/node_modules/openapi-request-validator/dist/index.d.ts' with Package ID 'openapi-request-validator/dist/[email protected]'. ========

Bug using with Microsoft Edge

I’ve found a strange issue with the <datalist> tag and Microsoft Edge.

In the code below, the first <input> tag displays the items from the <datalist> but the second one does not. If I change the name attribute of the second <input> do any other value, then the <datalist> items are displayed!

It seemed to work fine in Chrome. Can anyone help me figure out what is going on here?

$(function () {
  var dataitems = [
    { text: 'Text1', value: 'Value1' },
    { text: 'Text2', value: 'Value2' },
    { text: 'Text3', value: 'Value3' },
    { text: 'Text4', value: 'Value4' },
    { text: 'Text5', value: 'Value5' },
    { text: 'Text6', value: 'Value6' },
    { text: 'Text7', value: 'Value7' },
    { text: 'Text8', value: 'Value8' },
    { text: 'Text9', value: 'Value9' },
    { text: 'Text10', value: 'Value10' },
  ];

  var $list = $('#test-datalist');
  $list.empty();
  $.each(dataitems, function () {
    var $option = $('<option>', {
      value: this.value,
      label: this.text
    });
    $option.attr('data-id', '123');
    $list.append($option);
  });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.slim.min.js"></script>
<p>This input shows the data items:</p>
<p>
  <input id="test-input1" type="text" list="test-datalist" />
</p>

<p>This input does not show the data items in Edge:</p>
<p>
  <input id="test-input2" type="text" list="test-datalist" name="RailcarName">
</p>

<datalist id="test-datalist" />

I also created a CodePen.

How to create a bar chart with two sub bars instead of one bar

I saw here about using charts in JavaScript, and I am thankful with the information I received. I need to create a bar chart, and creating a bar chart is kind of like this:

var xValues = ["Italy", "France", "Spain", "USA", "Argentina"];
var yValues = [55, 49, 44, 24, 15];
var barColors = ["red", "green","blue","orange","brown"];

new Chart("myChart", {
  type: "bar",
  data: {
    labels: xValues,
    datasets: [{
      backgroundColor: barColors,
      data: yValues
    }]
  },
  options: {
    legend: {display: false},
    title: {
      display: true,
      text: "World Wine Production 2018"
    }
  }
});

However, I would like to know of a way to actually display two small bars in the position of one big bar. My initial thought was to make the x-values a 2d array, so that there would be a grouping of two bars in the same location that one bar would otherwise be placed at. However, it just prints both the names at once, and the bars do not divide. Here is my code right now:

var xValues = [["ItalyTequila", "ItalyBeer"], ["FranceChampagne","FranceRum"],["SpainWhiskey", "SpainLiquor"]];
var yValues = [55, 49, 44, 24, 15, 85];
var barColors = ["red", "orange","yellow", "green", "blue","black"];

new Chart("myChart", {
  type: "bar",
  data: {
    labels: xValues,
    datasets: [{
      backgroundColor: barColors,
      data: yValues
    }]
  },
  options: {
    legend: {display: false},
    title: {
      display: true,
      text: "World Wine Production 2018"
    }
  }
});

Could there be a way to split the bars such that there are two sub bars instead. How can that be done? Any help would be appreciated

jQuery plugin renders textbox instead of dropdown in editable table

I am developing a jQuery plugin for editable HTML tables. The table should allow inline editing, and certain columns (e.g., column 1 for “Position”) should render a dropdown for editing. However, instead of a dropdown, a textbox is being rendered.

What I Have Done:
I have implemented logic to check if a column is configured for a dropdown. If yes, it should render a element with predefined options.
If the column is not configured for a dropdown, it renders a textbox for editing.
I have also verified the column configuration and added console.log() statements to debug the issue.
Expected Behavior:
When I double-click a cell in column 1, I expect a dropdown with the following options to appear:

Developer
Designer
Manager
Analyst
Actual Behavior:
When I double-click the cell in column 1, a textbox is rendered instead of a dropdown. For other columns, the correct behavI am developing a jQuery plugin for editable HTML tables. The table should allow inline editing, and certain columns (e.g., column 1 for “Position”) should render a dropdown for editing. However, instead of a dropdown, a textbox is being rendered.

What I Have Done:
I have implemented logic to check if a column is configured for a dropdown. If yes, it should render a element with predefined options.
If the column is not configured for a dropdown, it renders a textbox for editing.
I have also verified the column configuration and added console.log() statements to debug the issue.
Expected Behavior:
When I double-click a cell in column 1, I expect a dropdown with the following options to appear:

Developer
Designer
Manager
Analyst

Actual Behavior:
When I double-click the cell in column 1, a textbox is rendered instead of a dropdown. For other columns, the correct behaviour (textbox) works fine.

Debugging Output:
I added console.log() statements to check the column index and the dropdown configuration. Here is the output:ior (textbox) works fine.

Debugging Output:
I added console.log() statements to check the column index and the dropdown configuration. Here is the output:

Selected column index: 1
Dropdown columns value: undefined

This suggests that the plugin is not correctly detecting the dropdown configuration for column 1.

My Code:
Plugin Code

if (settings.enableEditing) {
  $table.on('dblclick', 'td', function() {
    const $cell = $(this);
    const colIndex = $cell.index(); // Get column index

    // 4.1 Skip if the column is not editable
    if (settings.editableColumns.length > 0 && !settings.editableColumns.includes(colIndex)) {
      return; // Skip if column is not editable
    }

    // 4.2 Prevent multiple edit fields
    if ($cell.find('input, select').length > 0) return;

    // 4.3 Save original value
    const originalValue = $cell.text().trim();

    // Debugging: Check what colIndex is and what dropdownColumns[colIndex] contains
    console.log("Selected column index: " + colIndex);
    console.log("Dropdown columns value: ", settings.dropdownColumns[colIndex]);

    // 4.4 Check if the column has a dropdown
    if (settings.dropdownColumns && settings.dropdownColumns[colIndex] && settings.dropdownColumns[colIndex].length > 0) {
      // 4.4.1 Create dropdown
      const $dropdown = $('<select>').addClass('editable-dropdown');
      settings.dropdownColumns[colIndex].forEach((option) => {
        const $option = $('<option>')
          .val(option)
          .text(option)
          .prop('selected', option === originalValue); // Set original value as selected
        $dropdown.append($option);
      });

      // 4.4.2 Add Save and Cancel icons
      const $saveIcon = $('<span>')
        .addClass('editable-save-icon')
        .html('&#10004;') // Checkmark icon (✔)
        .on('click', function() {
          const newValue = $dropdown.val();
          $cell.empty().text(newValue); // Update cell value
          if (settings.onEditComplete) {
            settings.onEditComplete($cell, originalValue, newValue); // Trigger callback
          }
        });

      const $cancelIcon = $('<span>')
        .addClass('editable-cancel-icon')
        .html('&#10006;') // Cross icon (✖)
        .on('click', function() {
          $cell.empty().text(originalValue); // Revert to original value
        });

      // 4.4.3 Append dropdown and icons to the cell
      $cell.empty().append($dropdown).append($saveIcon).append($cancelIcon);
    } else {
      // 4.5 Default text input for non-dropdown columns
      const $input = $('<input>')
        .attr('type', 'text')
        .val(originalValue)
        .addClass('editable-input');

      // 4.5.1 Add Save and Cancel icons
      const $saveIcon = $('<span>')
        .addClass('editable-save-icon')
        .html('&#10004;') // Checkmark icon (✔)
        .on('click', function() {
          const newValue = $input.val().trim();
          $cell.empty().text(newValue);
          if (settings.onEditComplete) {
            settings.onEditComplete($cell, originalValue, newValue);
          }
        });

      const $cancelIcon = $('<span>')
        .addClass('editable-cancel-icon')
        .html('&#10006;') // Cross icon (✖)
        .on('click', function() {
          $cell.empty().text(originalValue);
        });

      // 4.5.2 Append input and icons to the cell
      $cell.empty().append($input).append($saveIcon).append($cancelIcon);

      // 4.5.3 Focus input field
      $input.focus();
    }
  });
}

$(document).ready(function () {
  $('#example').editableTable({
    enableEditing: true,
    editableColumns: [1, 2],
    dropdownColumns: {
      1: ['Developer', 'Designer', 'Manager', 'Analyst'],
    },
    onEditComplete: function ($cell, originalValue, newValue) {
      console.log(`Updated from "${originalValue}" to "${newValue}"`);
    },
  });
});