When performing validation (on server) of user submitted form, pass the class(es) along with name and value of element

A javascript component ensures users can only input numbers into numeric input fields. It attaches itself to all inputs with “numeric” class, like below:

<input name="foo" class="numeric" value="22.3">

After form submission, the server’s request blob has an array where key "foo" points to a string "22.3". To ensure the number is within acceptable values, this string must first be converted to float, then it can be validated.

I would like to pass variable type along with value, so I can realiably know which inputs should be validated as numbers, and which should not.

I have considered adding another hiddent input, where all names of inputs with numeric class get written on submission (so on server side the request blob would also have the key "numeric" point to comma separated input names that have the numeric class).

Prepending “numeric_” or appending “_numeric” to input name (foo becomes foo_numeric) is also a possible solution, but ideally there is a way to do this without adding any extra input fields and/or modifying input names.

Ideally the solution is implementable in vanilla JS, but if it can be done only with jQuery or similar library please let me know 🙂

How to list docs in an Automerge repo?

I have instantiated a repo:

const repo = new Repo({
  network: [
    new BroadcastChannelNetworkAdapter(),
    new BrowserWebSocketClientAdapter("ws://localhost:3030"),
  ],
  storage: new IndexedDBStorageAdapter("automerge-repo-demo-todo"),
})

How do I get a list of docs in the repo?

i get 403 forbidden in postman and the console when trying to add or remove products, i tried everything but still cant make it work

function isAdmin(req, res, next) {
    console.log('Session:', req.session); 
    if (req.session.isAdmin) {
        next();
    } else {
        res.status(403).json({ message: 'Forbidden' });
    }
}

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    if (username === 'mishasakvarelidze' && password === '123') {
        req.session.isAdmin = true;
        req.session.save(err => {
            if (err) {
                console.error("Session save error:", err);
                return res.status(500).json({ message: 'Failed to save session' });
            }
            res.json({ message: 'Login successful' });
        });
    } else {
        res.status(401).json({ message: 'Invalid credentials' });
    }
});

https://github.com/LuXClypse/misha_site_anew

this is the code,im not good at js and have no idea what to do now.
i had the database working for a bit too

Calling the toolController.deactiveTool method in a forge viewer custom tool is not triggering on windows while it works on mac

I am trying to create a custom forge viewer tool that only enables the box-selection extension while the shift key is pressed down. This works as expected on Mac devices but on Windows it enables the box-selection but never deactivates the box-selection once the shift key is released.

Both the handleKeyDown and handleKeyUp event handlers are triggered as expected on both platforms. So it seems to me an internal issue with the deactiveTool function. I tried debugging my way through this and I see there is some Mac specific logic in the functions but the root cause of the issue is beyond me.

Here is my custom box-selection tool.

/**
 * Custom forge viewer tool to restrict box-selection to the shift key
 * @param viewer Reference to the forge viewer instance
 * @see {@link https://aps.autodesk.com/blog/custom-tools-forge-viewer | Forge Documentation}
 */
export const getCustomBoxSelectionTool = (viewer) => {
  if (!viewer?.toolController) return null;

  const customTool = {
    active: false,
    getName: () => 'custom-box-selection',
    getNames: () => ['custom-box-selection'],
    getPriority: () => 1, // Set priority in the tool chain
    activate: () => {
      customTool.active = true;
    },
    deactivate: () => {
      customTool.active = false;
    },
    handleKeyDown: (event, keyCode) => {
      // Prevent the default box-selection behaviour
      if (event.key === 'Control' || keyCode === 17) {
        viewer.toolController.deactivateTool('box-selection');
        return true; // Stop propagation
      }

      // Enable new box-selection behaviour
      if (event.key === 'Shift' || keyCode === 16) {
        viewer.toolController.activateTool('box-selection');
      }

      return false; // Allow propagation
    },
    handleKeyUp: (event, keyCode) => {
      // Disable box-selection after shift is released
      if (event.key === 'Shift' || keyCode === 17) {
        viewer.toolController.deactivateTool('box-selection');
      }

      return false; // Allow propagation
    }
  };

  return customTool;
};

Note I added checks for the event.key and keyCode as that was a suggested difference between the two platforms but this has made no difference. Does anyone have any insights to this?

I need to write a promise that returns error when the data is not number

The instruction is to write promise that returns promise reject when data received is not number, returns promise resolved “odd” after 1s if data is odd, and returns promise resolved “even” after 2s when data is even. I wrote this and it works fine when I didn’t put the reject part in, otherwise it would just return “error”.

ps. I’m still learning on basic promise with no async/await yet

const getData = (data) => {
  return new Promise((resolve, reject) => {
    if (data % 2 === 1) {
      setTimeout(() => {
        resolve("odd");
      }, 1000);
    } else if (data % 2 === 2) {
      setTimeout(() => {
        resolve("even");
      }, 2000);
    }

    if (data !== Number) {
      reject("error");
    }
  });
};

getData(12)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });

How to apply the minimal theme in sweetalert2?

I am trying to apply minimal theme of sweetalert2. But it is not working. This I put in the head –

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/sweetalert2.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-minimal/minimal.css">

and this I put before the end of body tag –

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/sweetalert2.all.min.js"></script>

I used this code –

if (document.getElementById("fname").value == "") {
            Swal.fire({
            title: "Error!",
            text: "Please enter your name!",
            icon: "error",
            theme: "minimal"
            });
            return false;
}

Supabase Authentication Email and Password Error saying email_not_confirmed

I am signing up with email and password function as below:-



   async function SignUp(email: string, password: string, name: string) {
        try {
            const { data, error } = await supabase.auth.signUp({
                email, password,
                options: {
                    data: {
                        name
                    }
                }
            });
            if (error) {
                throw error;
            }

            return {
                success: true,
                message: "Signup successful. Please check your email for verification.",
                user: data.user
            }
        }
        catch (error) {
            return {
                success: false,
                message: error instanceof Error ? error.message : "An error occured"
            }
        }
    }

    async function SignIn(email: string, password: string) {
        try {
            const { data, error } = await supabase.auth.signInWithPassword({
                email, password
            })
            if (error) throw error;
            return {
                success: true,
                message: "Successfully Signed In",
                user: data.user,
                session: data.session
            }
        } catch (err) {
            return {
                success: false,
                message: err instanceof Error ? err.message : "An error occured !"
            }
        }
    }



Now the issue is that signing in with same credentials what I used for signing up, is throwing error saying
“email_not_confirmed”. What to do? Is there something I am missing something.

BullMQ Job Not Removed from Queue After Calling job.remove()

I’m prototyping a project using BullMQ and Redis, and so far, everything seems to be working fine. Jobs are being added to the queue successfully. However, when I process the jobs using a worker and call job.remove() after completing the job, the job is not removed from the queue.

The queue remains filled with the job, and I have to manually run the FLUSHDB command in redis-cli to reset the contents of the queue.

Here is the code I’m using for the prototype:

import { Queue, Worker } from 'bullmq';
import { Redis } from 'ioredis';

const redis_connection = new Redis({
    host: 'localhost',
    port: 6379,
    maxRetriesPerRequest: null 
})


const myQueue = new Queue('my-queue',{
    connection:redis_connection,
});

// Add jobs to the queue
for (let i = 0; i < 50; i++) {
    myQueue.add('myJob', { foo: 'bar' , jobNumber: i });
}

const myWorker = new Worker('my-queue', async (job) => {
    // Process the job data
    console.log('Processing job:', job.data);
// even adding job.remove() here doesnt remove the job from the queue
  }, {
    connection: redis_connection,
  });

  myWorker.on('completed', async (job) => {
    console.log(`Job with ID ${job.id} has been completed.`);
    await job.remove(); // Should removes the job from the queue
});

myWorker.on('failed', async (job, err) => {
    console.error(`Job with ID ${job.id} has failed with error: ${err.message}`);
    await job.remove(); // Should remove the job from the queue
});

Loop inside setInterval does not iterate over the second interation

I have a useTypewriter hook that seems to skip the second iteration since the i never logs as 1. Why is the code not working? The output should be howdy, not hwdy.

Here is the full code:

import React, { useState, useEffect } from 'react';

const useTypewriter = (text, speed = 50) => {
    const [displayText, setDisplayText] = useState('');

    useEffect(() => {
        let i = 0;
        const typingInterval = setInterval(() => {
            console.log('text.length: ', text.length);
            if (i < text.length) {
                // problem: i is not 1 on the second iteration
                setDisplayText((prevText) => {
                    console.log('prevText: ', prevText);
                    console.log(i, 'text.charAt(i): ', text.charAt(i));

                    return prevText + text.charAt(i);
                });
                i++;
            } else {
                clearInterval(typingInterval);
            }
        }, speed);

        return () => {
            clearInterval(typingInterval);
        };
    }, [text, speed]);

    return displayText;
};

export function App(props) {
    const displayText = useTypewriter('howdy', 2000);

  return (
    <div className='App'>
      <h1>{displayText}</h1>
    </div>
  );
}

Show hidden content in Chrome with javascript [closed]

I was wondering if there is any possible way to show hidden content/values in chrome and how I can show it?

<input type="hidden" id="ct_bot_detector_event_token_5020" name="ct_bot_detector_event_token" value="6f187995510502d1381ed304c4ec3a628bf3df8f9aa23507f065facdf398a3ab">
function disableRepeatOrder(elem, form) {
    jQuery(form).submit();
    var isvalid = jQuery('.jyaml-form-validate').data('validator').valid();
    if (isvalid) {
        jQuery(elem).attr("disabled", "disabled");
        jQuery(elem).css({"opacity": 0.3});
        jQuery('<p class="submitOrderMessage">Order in process. Please wait</p>').insertAfter(elem);
    }
    return false;
}

not tried yet but need help for full tutorial

How we can import the new changes in json file made by previous test in Cypress?

I have a spec file where I have to loop through the it block to create orders, and storing them in a Json object of array, then In same test I have to use the new created data in previous step and loop through it for verification purpose. I have to use same spec file as in parallel execution if I used different spec file for verification data it may execute before execution(because i’m using lambdatest and unfortunately this is lambdatest behaviour for Parallel exectuion.

Below is my pseudo code



import DuplicateExistingNewOrderStyle from "../../fixtures/DuplicateExistingNewOrderStyle.json"
import newOrders from '../../POcsv/dataform1.json'

describe("Create brands Orders", () => {
    context("Create all orders", () => {
        let PO_Array = []
        let object1 = {}

        DuplicateExistingNewOrderStyle.forEach((DuplicateStyleData, index) => {

            it("Script to create order", () => {
                createOrder.getPONumber(DuplicateStyleData.brand).then((PO) => {
                    object1 = {
                        brand: DuplicateStyleData.brand,
                        purchaseOrder: `${PO}`
                    }
                    PO_Array.push(object1)

                })
            })

        })

        after("save data", () => {
            cy.writeFile(`cypress/orders/dataform.json`, PO_Array)

        })
    })

    context("verify all order", () => {
        newOrders.forEach((object) => {
            it(`${object.brand} duplicate`, () => { })
            cy.log(object.purchaseOrder)
        })
    })
})
})

In above code in second context I’m trying to read the data from previous context it block but it doesn’t come on context 2 block and continue to execute context 1 block again and again.

How I can resolve this issue, So I could save data in previous test and loop through it in next text?

Javascipt to get the android browser inner dimensions width and height excluding browser title bar, toolbar and tabs

I’m trying to get Javascipt to get the android browser inner dimensions width and height excluding browser title bar, toolbar and tabs.

I have already used the current answer from 2 questions here and none of them work.

I have included a simple HTML page with jQuery to use all the available configurations available but none of them dimension the div containers within the actual content area of the browser.

When I rotate my tablet none of these redimension the div within the content area.

$(document).ready(function() {
  $("#winDiv").width($(window).width());
  $("#winDiv").height($(window).height());
  $("#docDiv").width($(document).width());
  $("#docDiv").height($(document).height());
  $("#scrDiv").width(screen.availWidth);
  $("#scrDiv").height(screen.availHeight);
  $("#clnDiv").width(document.documentElement.clientWidth);
  $("#clnDiv").height(document.documentElement.clientHeight);
  $("#innDiv").height(window.innerWidth);
  $("#innDiv").height(window.innerHeight);
});
window.addEventListener("orientationchange", function() {
  if (window.orientation == 0 || window.orientation == 180) {
    // PORTRAIT
    //alert("PORTRAIT - winWidth:" + $(window).width() + " - docWidth:" + $(document).width() + " - winHeight:" + $(window).height() + " - docHeight:" + $(document).height());
    $("#winDiv").width($(window).width());
    $("#winDiv").height($(window).height());
    $("#docDiv").width($(document).width());
    $("#docDiv").height($(document).height());
    $("#scrDiv").width(screen.availWidth);
    $("#scrDiv").height(screen.availHeight);
    $("#clnDiv").width(document.documentElement.clientWidth);
    $("#clnDiv").height(document.documentElement.clientHeight);
    $("#innDiv").height(window.innerWidth);
    $("#innDiv").height(window.innerHeight);
  } else {
    // LANDSCAPE
    //alert("LANDSCAPE - winWidth:" + $(window).width() + " - docWidth:" + $(document).width() + " - winHeight:" + $(window).height() + " - docHeight:" + $(document).height());
    $("#winDiv").width($(window).width());
    $("#winDiv").height($(window).height());
    $("#docDiv").width($(document).width());
    $("#docDiv").height($(document).height());
    $("#scrDiv").width(screen.availWidth);
    $("#scrDiv").height(screen.availHeight);
    $("#clnDiv").width(document.documentElement.clientWidth);
    $("#clnDiv").height(document.documentElement.clientHeight);
    $("#innDiv").height(window.innerWidth);
    $("#innDiv").height(window.innerHeight);
  }
}, false);
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<div id="winDiv" style="color:red;font-size:30px;border:4px solid red;margin:0px;position:absolute;top:1px;right:1;padding:10px;">winDiv</div>
<div id="docDiv" style="color:green;font-size:30px;border:4px solid green;margin:0px;position:absolute;top:1px;right:1;padding:40px;">docDiv</div>
<div id="scrDiv" style="color:blue;font-size:30px;border:4px solid blue;margin:0px;position:absolute;top:1px;right:1;padding:70px;">scrDiv</div>
<div id="innDiv" style="color:purple;font-size:30px;border:4px solid purple;margin:0px;position:absolute;top:1px;right:1;padding:100px;">innDiv</div>

Unable to render the image on canvas

I am trying to create a simple game using JavaScript. I do JavaScript as a hobby, and I did not major in computer. I used ChatGPT’s help to write my code, so it might be unnecessarily large and messy—sorry about that. Also, since English is not my native language, I used ChatGPT to translate this question.

I will briefly explain my current code.
I am trying to make a simple simulation game.
On the first screen, when the user clicks “Start,” the following effects are implemented: blinking text, fade-out, click lock, and hiding the mouse cursor. After about 4 seconds, the necessary code for the next screen is loaded.

I manage this main screen with script.js, and once the main screen transitions, I plan to manage the game with another file called demo.js.
To put it simply, script.js works like the game engine, while demo.js contains the game data.

The problem I encountered is that when I try to load the background image in demo.js, the console log correctly prints a message indicating that the image has loaded, but the background does not actually render.

However, when I load the background inside script.js, it renders correctly. So, I don’t think it’s an issue with the folder path.

I’ve been stuck on this problem for several days, and now it’s been about two weeks since I made any progress. Please help me.

script.js

// script.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const testModeButton = document.getElementById('TestMode');

// Canvas setup
canvas.width = 600;
canvas.height = 600;
canvas.style.display = 'block';
canvas.style.margin = '0 auto';
canvas.style.background = 'rgba(0, 0, 0, 0.25)';

// Change mouse cursor
canvas.addEventListener('mouseenter', () => {
    canvas.style.cursor = 'url(file/mouse.cur), auto';
});
canvas.addEventListener('mouseleave', () => {
    canvas.style.cursor = 'auto';
});

// Test mode border on/off
let testModeBorder = false;
ctx.save();
ctx.globalAlpha = 0;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.strokeStyle = 'rgba(255, 99, 71, 0.8)';
ctx.lineWidth = 3;

// Test mode click event
testModeButton.addEventListener('click', function() {
    console.log('Test Mode clicked');
    testModeBorder = !testModeBorder;  // Toggle border drawing
    console.log('Draw border:', testModeBorder);
});

// Background
const titleBackgroundVideo = document.createElement('video');
titleBackgroundVideo.src = 'file/space.mp4';
titleBackgroundVideo.autoplay = true;
titleBackgroundVideo.loop = true;
titleBackgroundVideo.muted = true;

// Title logo
const titleImage = new Image();
titleImage.src = 'file/imp.png';
let titleImageWidth = 400, titleImageHeight = 250;
let titleImageX = (canvas.width - titleImageWidth) / 2, titleImageY = 60;

// Title menu text
let startText = 'Start';
let endText = 'Exit';
let showText = true;

// Function to draw text on the title menu
function drawText(titleText, positionY, color = 'white') {
    if (!showText) return;
    ctx.save();
    ctx.font = '30px Arial';
    ctx.fillStyle = color;
    
    const textWidth = ctx.measureText(titleText).width;
    const titleTextX = (canvas.width - textWidth) / 2;
    ctx.fillText(titleText, titleTextX, positionY);
    ctx.restore();
}

// Blinking text effect
let startBlinkInterval = null;
let endBlinkInterval = null;

function startBlinkingText(target, interval = 100) {
    let textBlinkInterval = setInterval(() => {
        if (target === 'start') {
            startText = startText === 'Start' ? '' : 'Start';
        } else if (target === 'end') {
            endText = endText === 'Exit' ? '' : 'Exit';
        }
    }, interval);
    return textBlinkInterval;
}

// Clickable area rectangle
const clickAreaWidth = 50;
const clickAreaHeight = 50;
const clickAreaX = (canvas.width - clickAreaWidth) / 2;
const clickAreaY = (canvas.height - clickAreaHeight) / 2;

// Draw clickable area
function drawClickArea(x, y, width, height) {
    ctx.save();
    ctx.fillStyle = 'rgba(255, 173, 173, 0)';
    ctx.fillRect(x, y, width, height);
    ctx.restore();
}

// Screen filter (used for fade effects)
let filterOn = false;
let alphaStart = 0;
let alphaEnd = 1;
let startTime = Date.now();
let frameCount = 0;
let filterSettings = { speed: 0.004 };

// Function to animate screen filter
function animateScreenFilter(x, y, width, height, start, end, callback) {
    filterOn = true;
    alphaStart = start;
    alphaEnd = end;

    function animate() {
        if (filterOn) {
            drawScreenFilter(x, y, width, height);
            requestAnimationFrame(animate);
        } else if (callback) {
            callback();
        }
    }

    animate();
}

// Rendering function
function renderTitleScreen() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(titleBackgroundVideo, 0, 0, canvas.width, canvas.height);

    if (testModeBorder) {
        ctx.strokeRect(0, 0, canvas.width, canvas.height);
    }
    
    ctx.drawImage(titleImage, titleImageX, titleImageY, titleImageWidth, titleImageHeight);
    drawText(startText, canvas.height / 2 + 90, 'white');
    drawClickArea(clickAreaX - 38, clickAreaY + 80, clickAreaWidth + 78, clickAreaHeight - 3);
    drawText(endText, canvas.height / 2 + 140, 'white');
    drawClickArea(clickAreaX - 38, clickAreaY + 130, clickAreaWidth + 78, clickAreaHeight - 3);
    requestAnimationFrame(renderTitleScreen);
}

// Mouse click lock function
function enableClickAfterDelay(canvas, delayInSeconds, cursorStyle = 'url(file/mouse.cur), auto') {
    setTimeout(() => {
        isClickDisabled = false;
        console.log('Click is enabled again.');
        canvas.style.cursor = cursorStyle;
        const event = new MouseEvent('mouseenter', { bubbles: true });
        canvas.dispatchEvent(event);
    }, delayInSeconds * 1000);
}

// Menu click event
canvas.addEventListener('click', function(event) {
    if (isClickDisabled) return;
    
    const rect = canvas.getBoundingClientRect();
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    if (
        mouseX >= clickAreaX - 38 &&
        mouseX <= clickAreaX - 38 + clickAreaWidth + 78 &&
        mouseY >= clickAreaY + 80 &&
        mouseY <= clickAreaY + 80 + clickAreaHeight - 3
    ) {
        console.log('Start Click Area clicked!');
        isClickDisabled = true;
        clearInterval(startBlinkInterval);
        startBlinkInterval = startBlinkingText('start', 100);
        canvas.style.cursor = 'none';
        enableClickAfterDelay(canvas, 3, 'pointer');
        filterSettings.speed = 0.003;
        animateScreenFilter(0, 0, canvas.width, canvas.height, 0, 1);
        titleBackgroundVideo.pause();
    }

    if (
        mouseX >= clickAreaX - 38 &&
        mouseX <= clickAreaX - 38 + clickAreaWidth + 78 &&
        mouseY >= clickAreaY + 130 &&
        mouseY <= clickAreaY + 130 + clickAreaHeight - 3
    ) {
        console.log('Exit Click Area clicked!');
        isClickDisabled = true;
        clearInterval(endBlinkInterval);
        endBlinkInterval = startBlinkingText('end', 100);
        canvas.style.cursor = 'none';
        filterOn = true;
        animateScreenFilter(0, 0, canvas.width, canvas.height, 0, 1);
        titleBackgroundVideo.pause();
        setTimeout(() => {
            window.close();
        }, 1850);
    }
});

requestAnimationFrame(renderTitleScreen);

demo.js

const img = new Image();

function mainScreen(){
    img.src = 'file/demo_back01.jpg';
    img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        console.log('The image has been loaded.');
    };
};

function showMainScreen(){
            mainScreen();
    };

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="TestMod" style="color: gray; position: fixed;">MainTestMod</div>
    <canvas id="myCanvas"></canvas>
    <script src="startBar.js"></script>
    <script src="script.js"></script>
</body>
<style>
    body {
        background-color: rgb(63, 23, 126);
        margin: 0;
    }
</style>
</html>

style.css

html, body {
    height: 100%;
    overflow: hidden; /* Prevent scrolling */
}

body {
    background-color: black;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;  /* Set parent element to relative to overlap the canvas and menu bar */
}

canvas {
    width: 1080px;
    height: 100%;
    display: block;
    z-index: 1;  /* Ensure the canvas is below the menu bar */
}

#menu-bar {
    position: fixed;  /* Fixed at the top */
    top: 0;           /* Position at the top of the browser */
    left: 0;
    width: 100%;
    height: 6vh;      /* Occupy 6% of the top area */
    background-color: #c0c0c0;
    z-index: 2;
    opacity: 0;       /* Initially hidden */
    display: flex;    /* Arrange menu items horizontally */
    justify-content: flex-start; /* Align to the left */
    align-items: center; /* Vertically center */
    padding-left: 0; /* Add left padding */
    border: 2px outset white;
}

#menu-bar:hover {
    opacity: 1;       /* Make menu visible when hovered */
}

.menu-item {
    list-style: none;
    font-size: 16px;
    cursor: pointer;
    position: relative;
    margin-right: 30px; /* Space between menu items */
    padding: 0 0 6px 0;
    display: inline-block; /* Arrange items horizontally */
}

.submenu {
    visibility: hidden;     /* Hide submenu by default */
    opacity: 0;
    position: absolute;
    top: 100%;              /* Position submenu below parent menu item */
    left: 0;
    background-color: #c0c0c0;
    padding: 0;
    border: 2px outset white;
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    z-index: 3;
}

.submenu.open {
    visibility: visible;
    opacity: 1;
}

.menu-item:hover .submenu {
    visibility: visible;
    opacity: 1;
}

/* Hide menu items */
.menu-item.hidden {
    display: none !important; /* Completely hide menu item */
}

.submenu li {
    padding: 10px;
    cursor: pointer;
    white-space: nowrap;  /* Prevent line breaks */
    list-style-type: none;
}

.submenu li:hover {
    background-color: #000082;
    color: white;
}

/* Default popup style */
.popup {
    display: none; /* Hidden by default */
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    z-index: 10; /* Display above the top menu */
}

.popup-content {
    display: flex;
    background-color: #c0c0c0;
    border: 2px outset white;
    padding: 20px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Popup shadow effect */
    width: 50%;
    max-width: 800px;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

.popup-left {
    flex: 1;
    padding-right: 20px;
}

.popup-left img {
    width: 100%;
    height: auto;
    border-radius: 5px;
}

.popup-right {
    flex: 2;
}

.popup-right p {
    font-size: 16px;
    line-height: 1.5;
}

demo.js


function mainScreen(){
img.src = 'file/demo_back01.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
console.log('Image loaded.');
};
};

function showMainScreen(){
mainScreen();
};

When I tried this code in script.js, it rendered properly. So I don’t think it’s a problem with the folder path.

Load external resources only on button click

I would like to have a “Load More” button that displays all the content of a div tag when clicked. It’s important that all external resources within this content (such as images, JavaScripts, iframes, etc.) are fetched by the browser not immediately on each page load, but only after the user clicks the button.

I believe this can be achieved by first removing the content div from the DOM and then adding it back on button click, but I am not sure how to do that. Can someone please help me? Thank you!

I’d like to mention that the content div can be quite large (several kilobytes), so it needs to be located in the HTML code (as shown in the code below), rather than in the JS code. It will also be dynamic, constantly changing.

Alex

function load_more() {
  document.getElementById('content_div').style.display = 'block';
}
<html>
  <body>
    Content here...<br><br>
    <div style='display: inline-block; border: 1px solid black; padding: 10px; cursor: pointer' onclick='load_more()'>Load More</div><br><br>
    <div id='content_div' style='display: none'>
      More dynamic content here, including external images, scripts, and even iframes. These should NOT be automatically retrieved by the browser immediately on each page load, as they currently are with this code alone. Instead, they should only be fetched after the button above is pressed. So, if the user loads the page but does NOT click the button, no external resources will be retrieved by the browser.
    </div>
  </body>
</html>

Is it possible to customize the date picker in Ninja Forms so that one field is dependent on another field?

I have two date fields in Ninja Forms. I’ve already blocked past dates and set it so that the user can only choose a date that is at least three working days in the future. Now, I want to make one datepicker field dependent on the other. For example: I have a field called ‘OrderDate’ and another called ‘PickUpDate.’ The ‘PickUpDate’ field should depend on the ‘OrderDate’ field. If the ‘OrderDate’ is set to January 29, the ‘PickUpDate’ should also be January 29 and “higher”.

I basically know nothing about javascripts and made this code by googling and with the help of chatgpt. I cant find a way to achieve what i want to achieve. Thanks for the help.
Here is my Code that blocks past dates and gets the date after 3 days:

<script>
jQuery(document).ready(function ($) {
  function initCustomDatePicker() {
    var customDatePicker = Marionette.Object.extend({
      initialize: function () {
        this.listenTo(Backbone.Radio.channel("flatpickr"), "init", this.modifyDatepicker);
      },
      modifyDatepicker: function (dateObject, fieldModel) {
        var currentDate = new Date();
        currentDate.setDate(currentDate.getDate() + 3); // Allow only future dates

        dateObject.set("minDate", currentDate);
      },
    });

    new customDatePicker();
  }

  var formIds = ["#nf-form-37-cont", "#nf-form-47-cont", "#nf-form-48-cont", "#nf-form-51-cont",
  "#nf-form-52-cont", "#nf-form-55-cont"]; 

  function isAnyFormPresent() {
    return formIds.some(id => $(id).length > 0);
  }

  if (isAnyFormPresent()) {
    initCustomDatePicker();
  } else {
    // Falls das Formular erst später geladen wird
    var observer = new MutationObserver(function (mutations, observerInstance) {
      if (isAnyFormPresent()) {
        initCustomDatePicker();
        observerInstance.disconnect();
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  }
});
</script>

i tried different codes with an if function, i referenced the field keys of the 2 fields in the code, but nothing seemed to work.
Here is the Code i tried:

jQuery(document).ready(function ($) {

  function initCustomDatePicker() {
    var customDatePicker = Marionette.Object.extend({
      initialize: function () {
        this.listenTo(Backbone.Radio.channel("flatpickr"), "init", this.modifyDatepicker);
      },
      modifyDatepicker: function (dateObject, fieldModel) {
        var currentDate = new Date();
        currentDate.setDate(currentDate.getDate() + 3 ); // Allow only future dates
        dateObject.set("minDate", currentDate);
      },
    });

    new customDatePicker();
  }

  var formIds = ["#nf-form-37-cont", "#nf-form-47-cont", "#nf-form-48-cont", "#nf-form-51-cont",
  "#nf-form-52-cont", "#nf-form-55-cont"]; 

  function isAnyFormPresent() {
    return formIds.some(id => $(id).length > 0);
  }

  if (isAnyFormPresent()) {
    initCustomDatePicker();
  } else {
    // Falls das Formular erst später geladen wird
    var observer = new MutationObserver(function (mutations, observerInstance) {
      if (isAnyFormPresent()) {
        initCustomDatePicker();
        observerInstance.disconnect();
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  }

  var stelldatumSelector = '#nf-field-1266'; 
  var abholdatumSelector = '#nf-field-2186';

  var stelldatumFlatpickr = $(stelldatumSelector).flatpickr({
    dateFormat: "Y-m-d", // Datum Format
    onChange: function(selectedDates, dateStr, instance) {

      var stelldatum = new Date(dateStr); 
      if (!isNaN(stelldatum.getTime())) {
        var abholdatumFlatpickr = $(abholdatumSelector).flatpickr();
        abholdatumFlatpickr.set("minDate", stelldatum); 
      }
    }
  });

  var abholdatumFlatpickr = $(abholdatumSelector).flatpickr({
    dateFormat: "Y-m-d", 
    minDate: "today" 
  });

});