CSS design not affecting the input boxes with div and class

I am trying to make a google web application with input and select elements. I want to align both [input] and [select] elements with same margin and padding. I tried with below written code on my google web application but can’t happen. but if i go with [form input] in CSS then it happens but not with [div] and [class].

<style>
  .heading {
    font-family: "Times New Roman", Times, serif;
    text-align:center;
    font-weight: bold;
    color: black;
    font-size: 180%;
    padding: 20px 20px 15px 20px;
    background-color: #76ff03;
  }

  label {
    font-size: 30px;
  }

  select {
    height: 45px;
    width: 340px;
    font-size: 20px;
    font-family: Arial;
    border-radius: 15px;
  }
  /* input[type=number] {
    height: 40px;
    width: 150px;
    border: none;
    background: transparent;
    border-bottom: 1px solid black;
    outline: none;
    margin-left: 200px;
    padding: 30px;
    font-family: "Times New Roman", Times, serif;
    font-size: 20px;
   } */

   
  .inputfield {
    height: 40px;
    width: 150px;
    border: none;
    background: transparent;
    border-bottom: 1px solid black;
    outline: none;
    margin-left: 200px;
    padding: 30px;
    font-family: "Times New Roman", Times, serif;
    font-size: 20px;
  }
  
  .Row {
    margin-left: 200px;
    padding: 30px;
  }

  ::placeholder {
    color: Black;
  }
    
</style>
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <base target="_top">
  </head>
  <body>
    <iframe name="dummyframe" id="dummyframe" style="display: none;"></iframe>
    <form action="submitscript.php" target="dummyframe">

      <div class="heading">
        <label>Sample Monitoring Sheet</label>
      </div>
  
      <div class="Row">
        <select id="Category">
          <option>Hardware</option> 
          <option>Software</option>
          <option>Accessories</option>   
        </select>
      

        <select id="subcategory">
          <option>Processor</option> 
          <option>RAM</option>
          <option>ROM</option>   
        </select>
        

        <select id="shift">
          <option>Hard disk</option> 
          <option>Key board</option>
          <option>Mouse</option>
        </select>
      </div>

    

   
      <div class="row">
        <input id="plan" type="number" class="validate" placeholder="MPlan">
      </div>
         

      
      <div class="row inputfield">
        <input id="per" type="number" class="validate" placeholder="Permanent">
        <input id="tra" type="number" class="validate" placeholder="Trainee">
        <input id="adi" type="number" class="validate" placeholder="Adiraj">
      </div>
     
    

   
      <div class="row inputfield">
        <input id="cad" type="number" class="validate" placeholder="Cadmaxx">  
        <input id="lay" type="number" class="validate" placeholder="Layam">
        <input id="mau" type="number" class="validate" placeholder="Mauli">
      </div>
    

   
      <div class="row">
        <input type="submit" id="btn" value="Submit">
      </div>
    
    </form>  
      
  </body>
</html>
       

My page just keeps refreshing when i import function and set type module to js file [duplicate]

so this is my code ,i am a beginner at js and i am trying to learn it by making some thing , this is the code where i am trying to validate user by password by checking with server side admin password ,
when i try importing export.js function and set the type to module it just refreshes , i didnt face this problem with other pages , please help me


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="../css/styles.css">
</head>
<body>
    <div class="container">
        <form name="loginForm" method="post" action="./admin.html">
            <input type="text" size="25" name="userid" placeholder="Enter username" required>
            <input type="password" size="25" name="pwd" placeholder="Enter password" required>
            <button type="reset">Reset</button>
            <button type="submit" onclick="return check(this.form)">Login</button>
        </form>
    </div>
</body>
<!-- this works -->
<script src="../js/admin_login.js"></script> 
<!-- this doesnot work -->
<!-- <script src="../js/admin_login.js" type="module"></script> -->
</html>

admin_login.js

// REFERENCE OF THIS FILE: ttps://stackoverflow.com/questions/74308397/javascript-login-username-password
//this works
async function check(form) {
    const username = form.userid.value;
    const password = form.pwd.value;

    if (username === "test" && password === "test") {
        sessionStorage.setItem("authenticated", "true"); 
        window.location.href = "admin.html"; 
        return false;
    } else {
        alert("Invalid username or password. Please try again.");
        return false;
    }
}



// Doesnot work

// import { get } from "../server/credentials/export.js";


// //second answer
// function check(form) {
//     const username = form.userid.value;
//     const password = form.pwd.value;
//     validate(password)
// }

// async function validate(password){
//     var address = await get();
//     var res = await fetch(`${address}/login/${password}`, {
//         method: "GET",
//         headers: {
//             "Content-Type": "application/json",
//         }
//     });
//     if (res.status == 200){
//         sessionStorage.setItem("authenticated", "true"); 
//         window.location.href = "admin.html"; 
//         return false;
//     }
//     else{
//         alert("Invalid username or password. Please try again.");
//         return false;
//     }
// }

export.js

async function get(){
    var response = await fetch('../server/credentials/get.txt')
    var data  = await response.text()
    return data;
}
async function post(){
    var response = await fetch('../server/credentials/post.txt')
    var data  = await response.text()
    return data;
}

export {get,post}

Alternative to setTimeout for Managing Animation State in React

I’m working on a React component that handles animations for a login modal. Currently, I’m using setTimeout in combination with useEffect to delay the activation of animation classes when the modal opens.

The setTimeout introduces a delay to ensure animations trigger correctly when the modal is displayed. However, I feel this approach is not ideal, and I’m wondering if there’s a better or more React-idiomatic way to achieve the same result without relying on setTimeout.

{loginOpen && (
  <>
    <div
      className={`nav-background-fade ${fadeAnimation ? "nav-background-fade-animation" : ""}`}
      onClick={(e) => handleCloseLoginBox(e)}
    ></div>
    <div className={`right-side-page ${fadeAnimation ? "right-side-page-animation" : ""}`}>
      <h1>Login</h1>
    </div>
  </>
)}

useEffect(() => {
  if (loginOpen) {
    const timer = setTimeout(() => {
      setfadeAnimation(true);
    }, 100);
    return () => clearTimeout(timer);
  } else {
    setfadeAnimation(false);
  }
}, [loginOpen]);


.nav-background-fade {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 998;
  transition: opacity 1s ease;
  opacity: 0;
}

.nav-background-fade-animation {
  opacity: 1;
}

.right-side-page {
  position: fixed;
  top: 0;
  right: -20vw;
  width: 20vw;
  height: 100vh;
  background-color: var(--main-white-color);
  z-index: 999;
  transition: right 0.3s ease;
}

.right-side-page-animation {
  right: 0;
}




JavaScript Failing to Retrieve JSON Field Data from HTML and Execute SQL Query

I’m having trouble with my JavaScript code not properly retrieving and using data sent as JSON from an HTML form. Here’s the process:

A user enters a single or multiple addresses in an HTML form.
The addresses are parsed and sent as JSON to the server.
The JavaScript is supposed to extract the “streetName” from the JSON, use it in an SQL query to match the “NAME” field, and return values like FULL_ADD, LATITUDE, and LONGITUDE to the HTML page.

The problem is that the JavaScript is not picking up the fields from the JSON, even though I can see the data is being sent in the console. The SQL query is not returning any results because the data isn’t being passed correctly. I’ve also tried hardcoding values in the SQL query, but that didn’t work either.

Here’s a link to my code:
https://jsfiddle.net/6owvz91y

HTML Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Address Geocoding and CSV Export</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        textarea {
            width: 100%;
            height: 150px;
            margin-bottom: 20px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        table, th, td {
            border: 1px solid black;
        }
        th, td {
            padding: 8px;
            text-align: left;
        }
        .invalid {
            background-color: lightcoral;
        }
        button {
            margin-top: 10px;
            padding: 10px 15px;
            font-size: 16px;
            cursor: pointer;
        }
        #progress-container {
            margin-top: 20px;
            width: 100%;
            background-color: #f3f3f3;
            border: 1px solid #ccc;
            border-radius: 5px;
            overflow: hidden;
            height: 25px;
        }
        #progress-bar {
            height: 100%;
            width: 0%;
            background-color: #4caf50;
            text-align: center;
            line-height: 25px;
            color: white;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h1>Address Geocoding and CSV Export</h1>
    <p>Enter addresses (one per line):</p>
    <textarea id="addressInput" placeholder="Enter addresses here..."></textarea>
    <button onclick="geocodeAddresses()">Geocode Addresses</button>
    <button onclick="downloadCSV()">Download as CSV</button>

    <div id="progress-container">
        <div id="progress-bar">0%</div>
    </div>

    <h2>Geocoded Results:</h2>
    <table id="resultTable">
        <thead>
            <tr>
                <th>Input Address</th>
                <th>Street Number</th>
                <th>Street Name</th>
                <th>Street Type</th>
                <th>Post Direction</th>
                <th>Unit</th>
                <th>Municipality</th>
                <th>Province</th>
                <th>Postal Code</th>
                <th>Matched Address</th>    
                <th>Latitude</th>
                <th>Longitude</th>          
                
            </tr>
        </thead>
        <tbody></tbody>
    </table>

    <script>
        const streetTypes = ["AV", "AVE", "AVENUE", "BLVD", "BOULEVARD", "BV", "CE", "CENTRE", "CIR", "CIRCLE", "CL", "CLOSE", "CM", "CMN", "COMMON", "COURT", "CR", "CRES", "CRESCENT", "CROSSING", "CRT", "CS", "CT", "CTR", "DR", "DRIVE", "GARDEN", "GARDENS", "GATE", "GDN", "GDNS", "GR", "GREEN", "GROVE", "GT", "GV", "HEIGHTS", "HIGHWAY", "HILL", "HL", "HOLLOW", "HT", "HW", "HWY", "LANDING", "LANE", "LG", "LI", "LINE", "LN", "LNDG", "MANOR", "MR", "PARK", "PARKWAY", "PASS", "PATH", "PH", "PK", "PKWY", "PL", "PLACE", "POINT", "PS", "PT", "PY", "RD", "RN", "ROAD", "RUN", "SIDEROAD", "SQ", "SQUARE", "SR", "ST", "STREET", "TER", "TERRACE", "TL", "TLIN", "TOWNLINE", "TR", "TRAIL", "VIEW", "VW", "WALK", "WAY", "WK", "WO", "WOOD", "WY"];
        const directions = ["E", "EAST", "N", "NORTH", "S", "SOUTH", "W", "WEST"];
        const exceptions = [
            { regex: /bHWYs+(?:[1-9]|[1-9]d|4d{2})b/, words: 2 },
            { regex: /bHIGHWAYs+(?:[1-9]|[1-9]d|4d{2})b/, words: 2 },
            { regex: /b(?:[1-9]|[1-9]d|4d{2})s+HWYb/, words: 2 },
            { regex: /b(?:[1-9]|[1-9]d|4d{2})s+HIGHWAYb/, words: 2 }
        ];

        function isValidStreetType(word) {
            return streetTypes.includes(word.toUpperCase());
        }

        function processAddress(input) {
            return input
                .replace(/,/g, ' ')
                .replace(/[/\]/g, ' ')
                .replace(/[^a-zA-Z0-9s-]/g, '')
                .replace(/(d+)s+(d+)/g, '$1-$2')
                .replace(/s-s/g, '-')
                .toUpperCase()
                .trim();
        }

        async function geocodeAddresses() {
            const addressInput = document.getElementById("addressInput").value.trim();
            const addresses = addressInput.split('n');
            const resultsTable = document.getElementById("resultTable").getElementsByTagName("tbody")[0];
            const progressBar = document.getElementById("progress-bar");
            resultsTable.innerHTML = '';

            const totalAddresses = addresses.length;
            let currentIndex = 0;

            function updateProgress() {
                const progress = Math.round((currentIndex / totalAddresses) * 100);
                progressBar.style.width = `${progress}%`;
                progressBar.textContent = `${progress}%`;
            }

            for (const address of addresses) {
                try {
                    let processedAddress = processAddress(address);
                    let unit = '', streetNumber = '', streetName = '', streetType = '', postDirection = '', province = '', postalCode = '', municipality = '';

                // Step 1: Handle exceptions, assign address and unit number
                let exceptionHandled = false;
                if (/^d/.test(processedAddress)) {
                for (const exception of exceptions) {
                    const match = processedAddress.match(exception.regex);
                    if (match) {
                        const matchedWords = match[0];
                        streetName = matchedWords;
                        streetType = "";
                        processedAddress = processedAddress.replace(matchedWords, '').trim();
                        exceptionHandled = true;
                        break;
                    }
                }       
 
                } else {
                    // If the address does not start with a number, apply new logic
                    const parts = processedAddress.split(' ');
                    let firstStreetTypeIndex = -1;
                    for (let i = 0; i < parts.length; i++) {
                        if (isValidStreetType(parts[i])) {
                            firstStreetTypeIndex = i;
                            break;
                        }
                    }
                    //More Parsing logic
                    
                    // Send JSON POST request for each field
                    const response = await fetch('http://localhost:3000/geocode', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            parsedData: {
                            streetNumber,
                            streetName,
                            streetType,
                            postDirection,
                            unit,
                            municipality,
                            }
                        }),
                    });
                    
                const responseData = await response.json();

                // Extract data from the response
                const matchedAddress = responseData.FULL_ADD || 'No match found';
                const latitude = responseData.latitude || 'N/A';
                const longitude = responseData.longitude || 'N/A';

                // Add a new row to the results table
                const row = resultsTable.insertRow();
                row.insertCell(0).textContent = address;
                row.insertCell(1).textContent = streetNumber;
                row.insertCell(2).textContent = streetName;
                row.insertCell(3).textContent = streetType;
                row.insertCell(4).textContent = postDirection;
                row.insertCell(5).textContent = unit;
                row.insertCell(6).textContent = municipality;
                row.insertCell(7).textContent = province;
                row.insertCell(8).textContent = postalCode;
                row.insertCell(9).textContent = matchedAddress;
                row.insertCell(10).textContent = latitude;
                row.insertCell(11).textContent = longitude;

            } catch (error) {
                console.error('Error geocoding address:', error);

                // Add a row with an error message
                const row = resultsTable.insertRow();
                row.insertCell(0).textContent = address;
                row.insertCell(1).colSpan = 11;
                row.insertCell(1).textContent = 'Error processing this address';
                row.classList.add('invalid');
            }

            currentIndex++;
            updateProgress();
        }
    }   

function downloadCSV() {
    const table = document.getElementById("resultTable");
    const rows = table.querySelectorAll("tr");
    const csv = [];

    // Include the header row from the table
    const headers = table.querySelectorAll("th");
    const headerRow = Array.from(headers).map(header => header.innerText.trim());
    csv.push(headerRow.join(','));  // Adding header row to CSV

    // Include each data row from the table
    rows.forEach((row, index) => {
        // Skip the header row, we already added it
        if (index > 0) {
            const data = Array.from(row.querySelectorAll("td"))
                .map(cell => {
                    let cellValue = cell.innerText.trim();
                    return cellValue === "" ? "" : `"${cellValue}"`; // Wrap non-empty values in quotes, keeping empty values as they are
                });
            csv.push(data.join(','));  // Adding the data row to CSV
        }
    });

    // Convert the array to a CSV string and trigger a download
    const csvString = csv.join('n');
    const blob = new Blob([csvString], { type: 'text/csv' });
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = 'geocoded_addresses.csv';
    link.click();
}
    </script>
</body>
</html>

JavaScript Code:

const express = require('express');
const cors = require('cors');
const { Connection, Request, TYPES } = require('tedious'); // Ensure TYPES is imported
const path = require('path'); // Import path module to resolve file paths

// Initialize Express app
const app = express();
app.use(cors());
app.use(express.json()); // To parse JSON request bodies

// Server configuration
const PORT = 3000;

// Database configuration using NTLM (Windows Authentication)
const config = {
    server: 'REXT21',
    options: {
        database: 'SPATIAL',
        port: 1433,   
        encrypt: false, 
        trustServerCertificate: true,  
    },
    authentication: {
        type: 'ntlm',
        options: {
            domain: 'TEST-NET',     
            userName: 'MILO',     
            password: 'Setter4', 
        },
    },
};

// Create the connection
const connection = new Connection(config);

let isConnected = false;

// Ensure the connection is fully established before starting any requests
connection.on('connect', (err) => {
  if (err) {
    console.error('Connection Failed:', err);
  } else {
    console.log('Connected to SQL Server');
    isConnected = true;
  }
});

// Connect to the database
connection.connect();

// Endpoint to handle geocoding requests
app.post('/geocode', async (req, res) => {
  const { addresses } = req.body;

  if (!addresses || !Array.isArray(addresses) || address.length === 0) {
    return res.status(400).json({ error: 'At least one address is required' });
  }

  if (!isConnected) {
    return res.status(500).json({ error: 'Database not connected' });
  }

  let results = [];
  let processedCount = 0;

  // Process each address asynchronously
  for (const streetName of addresses) {

    // SQL query with additional logic to handle addresses without commas
    const sqlQuery = `
      SELECT TOP 1 LATITUDE, LONGITUDE, FULL_ADD, NAME
      FROM Addresses_p
      WHERE @streetName = NAME
    `;

    const request = new Request(sqlQuery, (err) => {
      if (err) {
        console.error('Error executing query:', err);
        results.push({ streetName, error: 'Error executing query' });
        processedCount++;
      }
    });

    request.addParameter('streetName', TYPES.NVarChar, streetName);

    request.on('row', (columns) => {
      const result = {
        streetName,
        latitude: columns[0].value,
        longitude: columns[1].value,
        FULL_ADD: columns[3].value,
      };

      // Log the extracted fields to the console
      console.log('Extracted fields for address:', streetName);
      console.log(result);

      results.push(result);
    });

    request.on('requestCompleted', () => {
      processedCount++;

      // Once the request is completed, send the response
      if (processedCount === addresses.length) {
        res.json(results);
      }
    });

    connection.execSql(request);
  }
});

// Serve the index.html file
app.use(express.static(__dirname));
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'index.html'));
});

// Start the Express server
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Why does Day.js return ‘Invalid Date’ for API date strings with timezone offsets in iOS 15 (mPaaS)?

I’m working on a project using the Mini Program mPaaS framework by Alibaba Cloud, and I’m encountering an issue with date parsing in JavaScript. Specifically, on iOS 15, my date strings (e.g., “2025-01-10 00:00:00.000+0700”) return “Invalid Date”. I suspect this is due to stricter parsing rules in the WebKit engine used by the mini-program environment. Here’s my code…

import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";

dayjs.extend(utc);
dayjs.extend(timezone);

function dateFormat(datetimeValue) {
  let date = dayjs(datetimeValue);

  if (!date.isValid() && typeof datetimeValue === "string") {
    datetimeValue = datetimeValue.replace(" ", "T"); // Replace space with 'T'
    date = dayjs(datetimeValue);
  }

  return date.format("YYYY-MM-DD HH:mm:ss.SSSZ");
}

I’m processing date strings from an API, for example: “2025-01-10 00:00:00.000+0700”. This format works in most environments but returns “Invalid Date” in certain cases, particularly on iOS 15.

My questions are:

  1. Why does Day.js fail to parse this date string in some environments?
  2. Is replacing the space with a T sufficient to fix this issue?
  3. Should I normalize the timezone offset (e.g., +0700 to +07:00)? If yes, how can I implement this effectively?
  4. What’s the best way to handle invalid date strings in Day.js, especially when working with API data?
    Invalid Date

I’m using Day.js to format date strings returned by an API. The API provides date strings in this format: “2025-01-10 00:00:00.000+0700”. I noticed that these strings cause Day.js to return Invalid Date, particularly on iOS 15
To fix this, I tried replacing the space between the date and time with a

datetimeValue = datetimeValue.replace(" ", "T");

But it still returned invalid date

I expected Day.js to correctly parse the date strings and allow me to format them into readable formats like “1 Nov 2023”.
T:expected result

I’m looking for a dynamic rounded border with transparency gradient

I’m wondering if the background-image property can be edited in real time via JavaScript, what I’m trying to do has a similar concept to the following question:
How to change the color of the background based on mouse position (specific colors)

but instead of editing the background I’m editing the border, giving it 100% opacity only in the corner where the cursor is.

The CSS method I tried to get the gradient rounded border is as follows:
https://gist.github.com/stereokai/36dc0095b9d24ce93b045e2ddc60d7a0

On JavaScript, the method I did was as follows (using jQuery 3.7.1 to detect the mouse position within the element):

if(x <= middle_x && y <= middle_y)
{
    content.style.backgroundImage = "linear-gradient(#303030, #303030), radial-gradient(circle at top left, #8B686B, #00000000, #00000000);";
}
else if(x >= middle_x && y <= middle_y)
{
    content.style.backgroundImage = "linear-gradient(#303030, #303030), radial-gradient(circle at top right, #8B686B, #00000000, #00000000);";
}
else if(x <= middle_x && y >= middle_y)
{
    content.style.backgroundImage = "linear-gradient(#303030, #303030), radial-gradient(circle at bottom left, #8B686B, #00000000, #00000000);";
}
else if(x >= middle_x && y >= middle_y)
{
    content.style.backgroundImage = "linear-gradient(#303030, #303030), radial-gradient(circle at bottom right, #8B686B, #00000000, #00000000);";
}

(content is the div element that contains the border).

But after testing, it seems that the algorithm had no effect, as the element’s border remained static, even though the algorithm detected the position and corner corresponding to the cursor position.

So after this, I thought that background-image cannot be edited in real time.

Should I try another CSS method? Is this last thought wrong?

Thank you in advantage 🙂

How can I “finish” the gulp serve when I didn’t write the gulp?

I’m new to working with javascript and I’ve been requested to add my project to a SharePoint.
After making the changes, I save all the files in VSCode and then use Ctrl+C in the terminal to stop the previous gulp serve

I receive this error from the default webpart tutorial (i.e, stopping gulp serve with no changes made to the files):

The following tasks did not complete: serve
Did you forget to signal async completion?

I read through this solution but don’t know what to make of it, since I didn’t write the serve command (it’s built into the yeoman template), so I’m not sure where or how I need to insert done(). I imagine this is also why there’s a significant delay in showing updates on the workbench when I make changes in Visual Studio Code.

The gulpfile.js for reference:

'use strict';
 
const build = require('@microsoft/sp-build-web');
 
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
 
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
  var result = getTasks.call(build.rig);
 
  result.set('serve', result.get('serve-deprecated'));
 
  return result;
};
 
build.initialize(require('gulp'));

I tried rebuilding the webpart from scratch 4 separate times, I also tried inserting done() in random locations. It’s still not live updating and is giving the async error.

Why does my repeater in Wix Code only load the first related video instead of all related videos?

I am working with Wix Code to display related videos in a repeater. I am using the following code to configure the repeater elements with the data obtained from my backend:

$w('#repeatervideos').onItemReady(($item, itemData) => {            
    const videoLinks = [
        itemData.relatedvideo1,
        itemData.relatedvideo2,
        itemData.relatedvideo3
    ].filter(link => link);
    console.log("videos encontrados", videoLinks);

    if (videoLinks.length > 0) {
        $item('#videoPlayer1').src = videoLinks[0];
        $item('#videoPlayer1').show();
    } else {
        $item('#videoPlayer1').hide();
    }
});
$w('#repeatervideos').data = res.items;

I want the repeater to show related videos (relatedvideo1, relatedvideo2, relatedvideo3) in different elements if available.
Currently, it only loads the first video (relatedvideo1) and does not show the other videos in the repeater. Even when more videos are available in itemData, they are not reflected in the repeater elements. I have used an array (videoLinks) to filter out undefined or empty values. But only the first video in the list seems to load in the repeater.

How can I make the repeater show all available videos relatedvideo1, relatedvideo2, etc.?

Is there anything I need to adjust in my logic or repeater settings to get the other videos to display correctly?

I am sure that relatedvideo2 and relatedvideo3 data exists in some cases because I see it in console.log(videoLinks).
The repeater is correctly linked and the data is set with res.items.

How to handle GitHub updating correctly?

I am relatively new to github and have been trying to program a system for my discord.js bot which would update the bot’s code with my private repositories. For that system I have 3 repositories. The first one authCodeRepoUrl is the repository I made for the general code of the bot (commands, events etc.), so it should always download the newest code from that Repository. The second one is authDataRepoUrl which is for the Data my bot stores in .json files, it should check which file is newer and depending on that change either local or remote Data files. The third one authBackupRepoUrl is so I backup the entire folder from any device into the repository, so I can always check the folder.

Now I have been trying to do this and when i had issues i asked a AI to help me fix the issues, but I’m not sure if that really helped, so I want to ask here for help (sorry in advance).

My code looks like this:

const { exec } = require('child_process');
const path = require('path');
const fs = require('fs');
require('dotenv').config();
const os = require('os');
const codeRepoUrl = process.env.CODE_REPO_URL;
const dataRepoUrl = process.env.DATA_REPO_URL;
const backupRepoUrl = process.env.BACKUP_REPO_URL;
const token = process.env.GITHUB_PAT;
const allowedHost = 'ncpi';
const currentHost = os.hostname();

if (!codeRepoUrl || !dataRepoUrl || !backupRepoUrl || !token) {
    console.error('[ERROR] CODE_REPO_URL, DATA_REPO_URL, BACKUP_REPO_URL, or GITHUB_PAT is not set in .env');
    process.exit(1);
}

const codeRepoPath = path.join(__dirname, './');
const dataRepoPath = path.join(__dirname, 'Data');
const logFilePath = path.join(dataRepoPath, 'sync_log.txt');

const authCodeRepoUrl = codeRepoUrl.replace('https://', `https://${token}@`);
const authDataRepoUrl = dataRepoUrl.replace('https://', `https://${token}@`);
const authBackupRepoUrl = backupRepoUrl.replace('https://', `https://${token}@`);

// Hilfsfunktionen für Git-Operationen
function execPromise(command) {
    return new Promise((resolve, reject) => {
        exec(command, (error, stdout, stderr) => {
            if (error) {
                reject(stderr || stdout);
            } else {
                resolve(stdout);
            }
        });
    });
}

async function getDefaultBranch(repoPath) {
    try {
        const branchName = await execPromise(`git -C ${repoPath} symbolic-ref --short HEAD`);
        return branchName.trim();
    } catch {
        return 'main';
    }
}

async function syncDataRepo() {
    try {
        console.log('[INFO] Syncing data repository...');
        if (!fs.existsSync(dataRepoPath)) {
            console.log('[INFO] Cloning data repository...');
            await execPromise(`git clone ${authDataRepoUrl} ${dataRepoPath}`);
        } else {
            console.log('[INFO] Pulling latest changes for the data repository...');
            await execPromise(`git -C ${dataRepoPath} pull`);
        }

        await execPromise(`git -C ${dataRepoPath} branch --set-upstream-to=origin/main main`);

        const localChanges = await execPromise(`git -C ${dataRepoPath} status --porcelain`);
        if (localChanges.trim().length > 0) {
            console.log('[INFO] Local changes detected. Pushing to remote repository...');
            await execPromise(
                `git -C ${dataRepoPath} add . && git -C ${dataRepoPath} commit -m "Auto-sync: ${new Date().toISOString()}" && git -C ${dataRepoPath} push`
            );
        }

        console.log('[INFO] Data repository synchronized.');
        logSyncSuccess();
    } catch (error) {
        console.error('[ERROR] Failed to synchronize data repository:', error);
    }
}


function logSyncSuccess() {
    const timestamp = `[SUCCESS] Data repository synchronized at: ${new Date().toISOString()}nn`;
    try {
        fs.appendFileSync(logFilePath, timestamp, 'utf8');
        console.log('[INFO] Sync timestamp logged.');
    } catch (err) {
        console.error('[ERROR] Failed to write sync log:', err);
    }
}

async function syncCodeRepo() {
    if (currentHost !== allowedHost) {
        console.log('[INFO] Code synchronization skipped: Not running on the allowed device.');
        return;
    }

    try {
        console.log('[INFO] Syncing code repository...');
        const branchName = await getDefaultBranch(codeRepoPath);

        if (!fs.existsSync(path.join(codeRepoPath, '.git'))) {
            console.log('[INFO] Cloning code repository...');
            await execPromise(`git clone ${authCodeRepoUrl} ${codeRepoPath}`);
        } else {
            console.log('[INFO] Pulling latest changes for the code repository...');
            try {
                await execPromise(`git -C ${codeRepoPath} remote add origin ${authCodeRepoUrl}`);
            } catch (error) {
                if (!error.includes('remote origin already exists')) {
                    throw error;
                }
            }
            await execPromise(`git -C ${codeRepoPath} pull origin ${branchName}`);
        }

        await execPromise(`git -C ${codeRepoPath} branch --set-upstream-to=origin/${branchName} ${branchName}`);

        console.log('[INFO] Code repository synchronized.');
    } catch (error) {
        console.error('[ERROR] Failed to synchronize code repository:', error);
    }
}


async function backupToRemoteRepo() {
    if (currentHost !== allowedHost) {
        console.log('[INFO] Backup skipped: Not running on the allowed device.');
        return;
    }

    try {
        console.log('[INFO] Backing up directly to remote repository...');
        const branchName = 'main';

        if (!fs.existsSync(path.join(codeRepoPath, '.git'))) {
            console.log('[INFO] Initializing repository for backup...');
            await execPromise(`git -C ${codeRepoPath} init`);
            await execPromise(`git -C ${codeRepoPath} checkout -b ${branchName}`);
            await execPromise(`git -C ${codeRepoPath} remote add origin ${authBackupRepoUrl}`);
        }

        await execPromise(`git -C ${codeRepoPath} config user.email "email censored"`);
        await execPromise(`git -C ${codeRepoPath} config user.name "github user censored"`);

        await execPromise(`git -C ${codeRepoPath} add .`);
        await execPromise(`git -C ${codeRepoPath} commit -m "Backup: ${new Date().toISOString()}"`);
        await execPromise(`git -C ${codeRepoPath} push origin ${branchName} --force`);

        console.log('[INFO] Backup to remote repository completed.');
    } catch (error) {
        console.error('[ERROR] Failed to backup to remote repository:', error);
    }
}


async function checkAndUpdate() {
    console.log('[INFO] Starting update process...');
    await syncCodeRepo();
    await syncDataRepo();
    await backupToRemoteRepo();
    console.log('[INFO] Update process completed.');
    scheduleNextSync();
}

function scheduleNextSync() {
    const now = new Date();
    const minutes = now.getMinutes();
    const nextInterval = 10 - (minutes % 10);
    const delay = nextInterval * 60 * 1000;

    console.log(`[INFO] Next sync scheduled in ${nextInterval} minutes (${new Date(now.getTime() + delay).toLocaleTimeString()}).`);

    setTimeout(() => {
        checkAndUpdate().then(scheduleRecurringSync);
    }, delay);
}

function scheduleRecurringSync() {
    console.log('[INFO] Scheduling recurring sync every 10 minutes.');
    setInterval(checkAndUpdate, 10 * 60 * 1000);
}

module.exports = { checkAndUpdate };

This is the log data, which comes when i try the code:

0| [ERROR] Failed to synchronize data repository: There is no tracking information for the current branch.
0| Please specify which branch you want to merge with.
0| See git-pull(1) for details.
0|     git pull <remote> <branch>
0| If you wish to set tracking information for this branch you can do so with:
0|     git branch --set-upstream-to=<remote>/<branch> master
0| [INFO] Backing up directly to remote repository...
0| [ERROR] Failed to backup to remote repository: error: src refspec main does not match any
0| error: failed to push some refs to 'https://github.com/censored/censored'
0| [INFO] Update process completed.
0| [INFO] Next sync scheduled in 10 minutes (04:10:37).
0| [INFO] Bot and data repositories are up-to-date. Starting bot...
0| [INFO] Starting update process...
0| [INFO] Syncing code repository...
0| [INFO] Pulling latest changes for the code repository...
0| [ERROR] Failed to synchronize code repository: fatal: couldn't find remote ref master
0| [INFO] Syncing data repository...
0| [INFO] Pulling latest changes for the data repository...
0| [ERROR] Failed to synchronize data repository: There is no tracking information for the current branch.
0| Please specify which branch you want to merge with.
0| See git-pull(1) for details.
0|     git pull <remote> <branch>
0| If you wish to set tracking information for this branch you can do so with:
0|     git branch --set-upstream-to=<remote>/<branch> master
0| [INFO] Backing up directly to remote repository...
0| [ERROR] Failed to backup to remote repository: On branch master
0| nothing to commit, working tree clean
0| [INFO] Update process completed.
0| [INFO] Next sync scheduled in 10 minutes (04:20:39).
0| [INFO] Scheduling recurring sync every 10 minutes.

I appreciate every help, i really just want this to work.
Thanks in advance.

Log only clicked button in console | Chrome extension content.js

I want only the clicked button to be logged in the console, regardless of whether the button is newly injected or already present in the page.

I want the click event to be attached to all the buttons that are present, and only the button that was actually clicked should be logged in the console.

However on clicking the said button, all instances(all injected buutons) present in the page gets logged on console.

Below code is a part of content.js of a chrome extension under development—

const observer = new MutationObserver(function (mutationsList) {
    mutationsList.forEach(mutation => {
        if (mutation.type === "childList") {
            const toolBarDivs = document.querySelectorAll('div[aria-label*="replies"]')

            toolBarDivs.forEach(toolBarDiv => {
                if (!toolBarDiv.dataset.siblingInjected) {
                    try {
                        const newDiv = document.createElement("div")
                        newDiv.classList.add("css-175oi2r", "r-18u37iz", "r-1h0z5md", "r-13awgt0", "copy-paste-button")
                        newDiv.innerHTML = `
                          <button aria-label="Copypaste" role="button" class="css-175oi2r r-1777fci r-bt1l66 r-bztko3 r-lrvibr r-1loqt21 r-1ny4l3l" data-testid="copypaste" type="button" data-copy-paste-button-added="true">
                              <svg fill="#000000" height="18px" width="18px" viewBox="0 0 502 502">
                                  <path d="M81.5,174H247c5.523,0,10-4.477,10-10s-4.477-10-10-10H81.5c-5.523,0-10,4.477-10,10S75.977,174,81.5,174z"></path>
                                  <path d="M306.5,220h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,220,306.5,220z"></path>
                                  <path d="M306.5,286h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,286,306.5,286z"></path>
                                  <path d="M306.5,352h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,352,306.5,352z"></path>
                                  <path d="M306.5,154h-22c-5.523,0-10,4.477-10,10s4.477,10,10,10h22c5.523,0,10-4.477,10-10S312.023,154,306.5,154z"></path>
                                  <path d="M481,379.417h-47.713c-2.364,0-4.287-1.923-4.287-4.287v-9.98c0-9.858-5.885-18.666-14.993-22.438c-9.108-3.772-19.497-1.706-26.468,5.265L377,358.516V103c0-2.652-1.054-5.196-2.929-7.071l-93-93C279.196,1.054,276.652,0,274,0H21c-5.523,0-10,4.477-10,10v482c0,5.523,4.477,10,10,10h346c5.523,0,10-4.477,10-10v-7.681l10.539,10.539c4.658,4.657,10.841,7.125,17.149,7.125c3.133,0,6.297-0.609,9.319-1.861c9.107-3.772,14.993-12.58,14.993-22.438v-9.98c0-2.364,1.923-4.287,4.287-4.287H481c5.523,0,10-4.477,10-10v-64C491,383.894,486.523,379.417,481,379.417z M284,34.142L342.858,93H284V34.142z M357,482H31V20h233v83c0,5.523,4.477,10,10,10h83v265.516l-35.831,35.83c-1.406,1.407-2.351,3.189-2.735,5.111c-0.128,0.641-0.194,1.297-0.194,1.96c0,2.652,1.054,5.196,2.929,7.071L357,464.319V482z M471,443.417h-37.713c-13.392,0-24.287,10.895-24.287,24.287v9.98c0,2.493-1.658,3.551-2.646,3.961c-0.99,0.409-2.911,0.833-4.672-0.93l-59.299-59.298l59.298-59.298c1.763-1.762,3.684-1.338,4.672-0.929c0.988,0.409,2.646,1.468,2.646,3.96v9.98c0,13.392,10.895,24.287,24.287,24.287H471V443.417z"></path>
                              </svg>
                          </button>
                      `
                        toolBarDiv.insertBefore(newDiv, toolBarDiv.children[3] || null)
                        toolBarDiv.dataset.siblingInjected = "true"
                    } catch (error) {
                        console.error("Error injecting button:", error)
                    }
                }
            })
        }
    })

    const allButtons = document.querySelectorAll('button[data-testid="copypaste"]')

    allButtons.forEach(button => {
        button.addEventListener("click", event => {
            console.log("Button clicked:", event.target)
        })
    })
})

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

Later,

I tried this(outside mutation observer logic) also didn’t works—

document.body.addEventListener('click', function(event) {
  if (event.target.tagName === 'BUTTON' && event.target.hasAttribute('data-testid') && event.target.getAttribute('data-testid') === 'copypaste') {
    console.log('Button clicked:', event.target);
  }
});

JavaScript code is not working properly on Android

I have this code below, which calculates the vehicle’s ISV and IUC.

It works perfectly on desktop and android. However, on IOS, it does not calculate the ISV, only the IUC, returning this:

Total ISV: NaN€
Total IUC: 148.22€

function calculateIsv(co2Emissions, engineSize, isHybrid, firstRegistration, fuelType) {
   if (!co2Emissions || !engineSize || !firstRegistration || !fuelType) {
        console.error("Error: Some inputs are invalid or empty.");
        document.querySelector('.resultado-isv').innerHTML = "Error: Invalid data";
        return;
    }
   co2Emissions = parseInt(co2Emissions.replace(",", ".").match(/d+/)?.[0] || 0);
    engineSize = parseInt(engineSize.replace(",", ".").match(/d+/)?.[0] || 0);

    if (isNaN(co2Emissions) || isNaN(engineSize)) {
        console.error("Error: Could not convert emissions or engine capacity to number.");
        document.querySelector('.resultado-isv').innerHTML = "Error: Invalid data";
        return;
    }
    taxaB = 0
    vehicle_type = document.getElementById("vehicle-type")
    if (vehicle_type.value == "mixed-van") {
      if (engineSize <= 1.250) {
        taxaB = ((engineSize * 5.30) - 3331.68)
      } else {
        taxaB = ((engineSize * 12.58) - 12138.47)
      }
      taxaB *= (1 - 0.85).toFixed(2);
    }
   const year = parseInt(firstRegistration.split('/').pop());
   const registrationDate = new Date(firstRegistration.split('/').reverse().join('/'));
   const age = new Date().getFullYear() - registrationDate.getFullYear();
   const reductions = [0.1, 0.2, 0.28, 0.35, 0.43, 0.52, 0.6, 0.65, 0.7, 0.75, 0.8];
   const reductionPercentage = reductions[Math.min(age, 10)];
   
   let ccRate, ccDeduction;
   if (engineSize <= 1000) {
   ccRate = 1.09;
   ccDeduction = 849.03;
   } else if (engineSize <= 1250) {
   ccRate = 1.18;
   ccDeduction = 850.69;
   } else {
   ccRate = 5.61;
   ccDeduction = 6194.88;
   }
   
   const ccTax = Math.max(0, (engineSize * ccRate - ccDeduction).toFixed(2));
   
   const co2Tables = {
   'gasoline': {
      'NEDC': [
          [99, 4.62, 427], [115, 8.09, 750.99], [145, 52.56, 5903.94],
          [175, 61.24, 7140.17], [195, 155.97, 23627.27], [Infinity, 205.65, 33390.12]
      ],
      'WLTP': [
          [110, 0.44, 43.02], [115, 1.1, 115.8], [120, 1.38, 147.79],
          [130, 5.27, 619.17], [145, 6.38, 762.73], [175, 41.54, 5819.56],
          [195, 51.38, 7247.39], [235, 193.01, 34190.52], [Infinity, 233.81, 41910.96]
      ]
   },
   'diesel': {
      'NEDC': [
          [79, 5.78, 439.04], [95, 23.45, 1848.58], [120, 79.22, 7195.63],
          [140, 175.73, 18924.92], [160, 195.43, 21720.92], [Infinity, 268.42, 33447.90]
      ],
      'WLTP': [
          [110, 1.72, 11.50], [120, 18.96, 1906.19], [140, 65.04, 7360.85],
          [150, 127.40, 16080.57], [160, 160.81, 21176.06], [170, 221.69, 29227.38],
          [190, 274.08, 36987.98], [Infinity, 282.35, 38271.32]
      ]
   }
   };
   
   const standard = document.getElementById("wltp").checked ? 'WLTP' : 'NEDC';
   const co2Table = co2Tables[fuelType][standard];
   let co2Tax = 0;
   
   for (let i = 0; i < co2Table.length; i++) {
   const [limit, rate, deduction] = co2Table[i];
   if (co2Emissions <= limit) {
      co2Tax = (co2Emissions * rate - deduction).toFixed(2);
      break;
   }
   }
   
   let totalIsv = (parseFloat(ccTax) + parseFloat(co2Tax));
   
   totalIsv = (totalIsv * (1 - reductionPercentage)).toFixed(2);
   
   plug_in_hybrid = document.getElementById("plug-in-hybrid")
   if (isHybrid) {
   totalIsv *= (1 - 0.40);
   } else if (plug_in_hybrid.checked) {
   totalIsv *= (1 - 0.75);
   }
   if (taxaB > 0) {
        taxaB = parseFloat(taxaB);
   }
   totalIsv = parseFloat(totalIsv) + taxaB;
   totalIsv = totalIsv.toFixed(2);
   document.querySelector('.resultado').style.display = "initial"
   document.querySelector('.resultado-isv').innerHTML = totalIsv
   }
   function calculateIuc(engineSize, co2Emissions, firstRegistration, fuelType) {
    engineSize = parseInt(engineSize.match(/d+/)[0]);
    co2Emissions = parseInt(co2Emissions.match(/d+/)[0]);
    year = parseInt(firstRegistration.split('/').pop());
    wltp_element = document.getElementById("wltp")
    standard = wltp_element.checked ? 'WLTP' : 'NEDC';
   
    let baseTax = 0;
    if (year >= 2007) {
        if (engineSize <= 1250) {
            baseTax = 31.77;
        } else if (engineSize <= 1750) {
            baseTax = 63.74;
        } else if (engineSize <= 2500) {
            baseTax = 127.35;
        } else {
            baseTax = 435.84;
        }
    } else {
        if (fuelType === 'gasoline') {
            if (engineSize <= 1000) {
                baseTax = year >= 1996 ? 19.90 : year >= 1990 ? 12.20 : 8.80;
            } else if (engineSize <= 1300) {
                baseTax = year >= 1996 ? 39.95 : year >= 1990 ? 22.45 : 12.55;
            } else if (engineSize <= 1750) {
                baseTax = year >= 1996 ? 62.40 : year >= 1990 ? 34.87 : 17.49;
            } else if (engineSize <= 2600) {
                baseTax = year >= 1996 ? 158.31 : year >= 1990 ? 83.49 : 36.09;
            } else if (engineSize <= 3500) {
                baseTax = year >= 1996 ? 287.49 : year >= 1990 ? 156.54 : 79.72;
            } else {
                baseTax = year >= 1996 ? 512.23 : year >= 1990 ? 263.11 : 120.90;
            }
        } else if (fuelType === 'diesel') {
            if (engineSize <= 1500) {
                baseTax = year >= 1996 ? 22.48 : year >= 1990 ? 14.18 : 10.19;
            } else if (engineSize <= 2000) {
                baseTax = year >= 1996 ? 45.13 : year >= 1990 ? 25.37 : 14.18;
            } else if (engineSize <= 3000) {
                baseTax = year >= 1996 ? 70.50 : year >= 1990 ? 39.40 : 19.76;
            } else {
                baseTax = year >= 1996 ? 178.86 : year >= 1990 ? 94.33 : 40.77;
            }
        }
    }
   
    let co2Tax = 0;
    const co2Rates = standard === 'WLTP' ? [140, 205, 260, Infinity] : [120, 180, 250, Infinity];
    const co2Taxes = standard === 'WLTP' ? [65.15, 97.63, 212.04, 363.25] : [65.15, 97.63, 212.04, 363.25];
    const co2Additional = standard === 'WLTP' ? [0, 0, 31.77, 63.74] : [0, 0, 31.77, 63.74];
   
    for (let i = 0; i < co2Rates.length; i++) {
        if (co2Emissions <= co2Rates[i]) {
            co2Tax = co2Taxes[i] + (co2Emissions > co2Rates[i - 1] ? co2Additional[i] : 0);
            break;
        }
    }
   
    let coefficient = 1;
    if (year >= 2008) coefficient = 1.05;
    if (year >= 2009) coefficient = 1.10;
    if (year >= 2010) coefficient = 1.15;
   
    let dieselAdditionalTax = 0;
    if (fuelType === 'diesel') {
        if (engineSize <= 1250) {
            dieselAdditionalTax = 5.02;
        } else if (engineSize <= 1750) {
            dieselAdditionalTax = 10.07;
        } else if (engineSize <= 2500) {
            dieselAdditionalTax = 20.12;
        } else {
            dieselAdditionalTax = 68.85;
        }
    }
    let totalIuc = ((baseTax + co2Tax) * coefficient + dieselAdditionalTax).toFixed(2);
    document.querySelector('.resultado-iuc').innerHTML = totalIuc;
    document.querySelector('.wpp-proposta').style.display = "block"
   }

All fields are being passed correctly in both functions

I have already tried several different code syntaxes, but with no success.

I also tested it on several browsers, it worked perfectly on all of them on Android and Windows. Only on IOS it doesn’t work.

I tested Safari and Google Chrome on IOS, both didn’t work.

Disable “keyboard” option in Bootstrap modal instance and then enable it back on Photoswipe events

I need to disable the “Esc” button in the Bootstrap modal instance when the Photoswipe instance is open to prevent closing the modal with that key when closing the lightbox.

I get “Maximum call stack size exceeded” when trying to disable “keyboard” option in the modal instance and then enable it back on the lightbox events:

error in console

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lightbox in modal</title>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.4/photoswipe.min.css" rel="stylesheet">
</head>

<body>
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modal">
    Open Image Gallery
  </button>

  <div class="modal fade" id="modal" tabindex="-1" aria-labelledby="modal-title" aria-hidden="true">
    <div class="modal-dialog modal-lg">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="modal-title">Image Gallery</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          <div id="gallery" class="carousel slide" data-bs-ride="carousel">
            <div class="carousel-inner">
              <div class="carousel-item active">
                <figure>
                  <a href="https://picsum.photos/id/12/800/400" data-pswp-width="800" data-pswp-height="400">
                    <img src="https://picsum.photos/id/12/800/400" alt="Image 1">
                  </a>
                </figure>
              </div>
              <div class="carousel-item">
                <figure>
                  <a href="https://picsum.photos/id/28/800/400" data-pswp-width="800" data-pswp-height="400">
                    <img src="https://picsum.photos/id/28/800/400" alt="Image 2">
                  </a>
                </figure>
              </div>
              <div class="carousel-item">
              <figure>
                <a href="https://picsum.photos/id/10/800/400" data-pswp-width="800" data-pswp-height="400">
                  <img src="https://picsum.photos/id/10/800/400" alt="Image 3">
                </a>
              </figure>
              </div>
            </div>
            <button class="carousel-control-prev" type="button" data-bs-target="#gallery" data-bs-slide="prev">
              <span class="carousel-control-prev-icon" aria-hidden="true"></span>
              <span class="visually-hidden">Previous</span>
            </button>
            <button class="carousel-control-next" type="button" data-bs-target="#gallery" data-bs-slide="next">
              <span class="carousel-control-next-icon" aria-hidden="true"></span>
              <span class="visually-hidden">Next</span>
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.4/umd/photoswipe-lightbox.umd.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/5.4.4/umd/photoswipe.umd.min.js"></script>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const modalElement = document.getElementById('modal');
      modalElement.addEventListener('shown.bs.modal', function () {
        let modalInstance = bootstrap.Modal.getOrCreateInstance(modalElement);
        const lightbox = new PhotoSwipeLightbox({
          gallery: '#gallery',
          children: 'a',
          pswpModule: PhotoSwipe
        });
        lightbox.init();
        lightbox.on('beforeOpen', () => {
          modalInstance._config.keyboard = false;
        });
        lightbox.on('close', () => {
          modalInstance._config.keyboard = true;
        });
      });
    });
  </script>
</body>

</html>

How to customize CKEditor 5 color palette in Strapi 4 with @ckeditor/strapi-plugin-ckeditor 0.0.10

I’m trying to customize the default color palette in CKEditor 5 for my Strapi 4 application. I want to replace the default colors with our brand colors from our design system.

Environment:

  • Strapi version: 4.25.5
  • CKEditor plugin: @ckeditor/[email protected]
  • Node.js version: 22.11.0

Current package.json dependencies:

{
  "dependencies": {
    "@ckeditor/strapi-plugin-ckeditor": "1.1.0",
    "@strapi/strapi": "4.25.5",
    // ... other relevant dependencies
  }
}

1. Added CKEditor configuration in config/plugins.js:

module.exports = () => ({
  ckeditor: {
    enabled: true,
    config: {
      // ... my configuration attempt
    }
  }
});

2. Cleared cache and rebuilt:
rm -rf build
yarn build
yarn develop
I have no .cache folder. 
However, the default color palette remains unchanged.

**Expected behavior:
  *Custom brand colors should appear in the color picker dropdown 
  *Each color should have a custom label (e.g., "Pin Grün Dark", "PIN Blau Dark")
**Current behavior:
  *Default CKEditor color palette is still showing
  *Custom colors are not appearing in the dropdown
**Question:
How can I properly configure the CKEditor plugin to use my custom color palette? What's the correct configuration structure for version 1.1.0 of the plugin?

AR.js low performance on xiaomi 14

Very low performance on my xiaomi 14.
I made a simple application to detect markers,
everything is fine when I start it, but once I find one or two markers, the fps starts to drop sharply, I think to 5 or even 1.

I also tried to do it from the example, there is not even a script, only one marker and image output, the result is the same (

I can’t find a detailed description of this solution, maybe someone can suggest an alternative?
I need to develop a web AR application with the ability to determine markers and maybe in the future also GPS functionality.
Please help.

<!doctype html>
<html>
<head>
    <link rel="icon" href="/resources/favicon.ico" type="image/x-icon">
    <!-- A-Frame 1.6.0 ar-3d -->
    <script src="/script/aframe.min.js"></script>
    <!-- AR.js Marker based and Location based AR -->
    <script src="/script/aframe-ar.js"></script>
    <script src="/script/gesture-detector.js"></script>
    <script src="/script/gesture-handler.js"></script>
    <script>
        AFRAME.registerComponent('videohandler', {
            init: function () {
                var marker = this.el;
                this.vid = document.querySelector("#vid-" + marker.id);

                marker.addEventListener('markerFound', function () {
                    console.log(marker.id + " found!");
                    this.toggle = true;
                    if (this.vid) {
                        this.vid.play();
                        this.vid.muted = false;
                    }
                }.bind(this));

                marker.addEventListener('markerLost', function () {
                    console.log(marker.id + " lost!");
                    this.toggle = false;
                    if (this.vid) {
                        this.vid.pause();
                    }
                }.bind(this));
            },
        });
        function openLink() {
            window.open('https://yandex.ru/maps/-/CHUKY-2N', '_blank');
        }
    </script>
</head>

<body style="margin: 0; overflow: hidden;">
    <a-scene
        vr-mode-ui="enabled: false"
        loading-screen="enabled: true;"
        arjs='sourceType: webcam; debugUIEnabled: false;'
        id="scene"
        embedded
        gesture-detector
    >
        <a-assets>
            <video
                id="vid-marker1"
                src="/resources/pattern-0001.mp4"
                preload="none"
                loop
                crossorigin
                webkit-playsinline
                muted
            ></video>
            <video
                id="vid-marker2"
                src="/resources/pattern-0002.mp4"
                preload="none"
                loop
                crossorigin
                webkit-playsinline
                muted
            ></video>
        </a-assets>

        <a-marker
            type="pattern"
            preset="custom"
            url="/resources/marker/pattern-0001.patt"
            videohandler
            id="marker1"
            cursor="fuse: false; rayOrigin: mouse;"
        >
            <a-video
                src="#vid-marker1"
                scale="1 1.5 1"
                position="0 0.1 0"
                rotation="-90 0 0"
                gesture-handler
            ></a-video>
        </a-marker>

        <a-marker
            type="pattern"
            preset="custom"
            url="/resources/marker/pattern-0002.patt"
            videohandler
            id="marker2"
            cursor="fuse: false; rayOrigin: mouse;"
        >
            <a-video
                src="#vid-marker2"
                scale="1 1.5 1"
                position="0 0.1 0"
                rotation="-90 0 0"
                gesture-handler
            ></a-video>
        </a-marker>

        <a-marker
            type="pattern"
            preset="custom"
            url="/resources/marker/pattern-0003.patt"
            id="marker3"
        >
            <a-entity
                geometry="primitive: plane; width: 1.5; height: 1;"
                material="color: gray; opacity: 0.8;"
                position="0 0.1 0"
                rotation="-90 0 0"
                text="value: Open MAPS!; color: black; align: center; width: 4; size: 72;"
                event-set__enter="_event: mouseenter; color: #0056b3"
                event-set__leave="_event: mouseleave; color: gray"
                onclick="openLink()"
            ></a-entity>
        </a-marker>

        <a-entity camera></a-entity>
    </a-scene>
</body>
</html>