WebSocket using Stomp reverts to v1.1 even though 1.2 defined in header

I am working with WebSockets and attempting to duplicate a WebSocket connection from a website.

The socket connects great, works great, but after a while, the server disconnects the connection, then it throws an error and stops. My code reconnects and it works again. The original site’s connection never terminates for hours, days,… Mine start disconnecting after 10-15 mins then I have to reconnect.

The original site’s Wss messages received clearly shows Stomp 1.2 version accepted by the server, but my code is forced to 1.1 by the server.

Any ideas? If you want to inspect the js files the original site uses to establish and maintain the Wss connection, let me know.

<!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> 
<body> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.6.1/sockjs.min.js"></script> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>

<script>
    document.addEventListener('DOMContentLoaded', () => {
        const baseUrl = 'https://pe-dingdong.nio.com/pe/dingdong/msg';

        let sock = null;
        let stompClient = null;
        let reconnectTimeout = null;
        let connectionAttempts = 0;
        const MAX_RECONNECT_ATTEMPTS = 10; // Prevent infinite reconnect loops

       function connectWebSocket() {
            if (sock && sock.readyState !== SockJS.CLOSED) {
                console.log("WebSocket already connected or connecting...");
                return;
            }

            if (connectionAttempts >= MAX_RECONNECT_ATTEMPTS) {
                console.error("Max reconnect attempts reached. Stopping further reconnects.");
                return;
            }

            console.log("Connecting to WebSocket...");
            connectionAttempts++;

            sock = new SockJS(baseUrl);
            stompClient = Stomp.over(sock);

            // Disable debug logging
            stompClient.debug = console.log;

            const headers = {
                'accept-version': '1.2', // Explicitly request STOMP version 1.2
                'heart-beat': '5000,5000' // Heartbeat every 5 seconds
            };

            stompClient.heartbeat.outgoing = 5000;
            stompClient.heartbeat.incoming = 5000;

            // Override default STOMP connect method to ensure headers are respected
            stompClient._connectHeaders = headers;


            stompClient.connect(headers, onConnect, onError);

            sock.onclose = function() {
                console.warn('WebSocket closed at', new Date().toISOString());
            };
        }

        function onConnect(frame) {
            console.log('Connected to WebSocket with STOMP version:', frame.headers.version);

            if (frame.headers.version !== "1.2") {
                console.warn("Warning: Server connected with STOMP version", frame.headers.version, "instead of 1.2");
            }

            // Reset connection attempts on successful connection
            connectionAttempts = 0;

            // Subscribe
            stompClient.subscribe('/room/h5/charge-map', onMessageReceived);

            // Clear previous reconnect attempts
            if (reconnectTimeout) {
                clearTimeout(reconnectTimeout);
                reconnectTimeout = null;
            }
        }

        function onError(error) {
            console.error('WebSocket error:', error);

            if (error.command === "ERROR") {
                console.warn("Received STOMP ERROR frame:", error.body || "No details provided.");
                return;
            }

            if (stompClient) {
                stompClient.disconnect(() => {
                    console.log('Disconnected. Reconnecting in 5 seconds...');
                    reconnectTimeout = setTimeout(connectWebSocket, 5000);
                });
            } else {
                reconnectTimeout = setTimeout(connectWebSocket, 5000);
            }
        }

        function onMessageReceived(payload) {
            try {
                //const message = JSON.parse(payload.body);
                //console.log(message);
            } catch (err) {
                console.error("Error processing WebSocket message:", err);
            }
        }

        connectWebSocket();
    });
</script>

</body> 
</html>

Unable to type inside contenteditable span

I have a parent contenteditable div. Inside it I have a child contenteditable span element. Now, what I want is when my div is empty and I click inside the div. I want the cursor to be such that the content I type should be placed inside child contenteditable span element. I have tried below code but still the typed content is coming as a text node before the span.

HTML

<div id="input" contenteditable="true">
    <span contenteditable="true" id="first"></span>
</div>

TS

function setCaret() {
    var element = document.getElementById("input");
    var range = document.createRange();  
    var node;   
    node = document.getElementById("first");  
    range.setStartAfter(node);
    var sel = window.getSelection();
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    element.focus();    
}

Now let’s say I click inside div and type “Kartik”. The result that I am looking for is:

<div id="input" (focus)="setCaret()" contenteditable="true">
    <span contenteditable="true" id="first">Kartik</span>
</div>

But I am getting:

<div id="input" (focus)="setCaret()" contenteditable="true">
    Kartik
    <span contenteditable="true" id="first"></span>
</div>

JS Fiddle

Can you help me with this?

PS: I know I can tinker with document object by removing the text node and then putting that inside span element, but I am not looking for that solution because in Angular I am facing some issues when I am manually changing the DOM instead of relying on property binding.

Context: I will be property binding with elements array in component. I will be having multiple spans of different types. If element.type=='text', the span will be content editable. If the element.type=='expression' it will look somewhat different and will not be content editable. That’s why I need span inside div. So, initially when I have only one element by default in array of type text, I want the content I type to be put inside the span not outside it.

Pagination with Datatable.js and aspnet mvc

I have a code that I am trying to understand, from another developer. He is using datatable.js, something like:

function initservicesDataTable(){

    DataTable.datetime('DD/MM/YYYY');

    let config = {

        order: [[3, 'asc']],
        columns: [
            { orderable: false, width: '10%' }
            // more columns
        ],

        fixedColumns: {
            left: 1,
        },
        fixedHeader: {
            header: false,
            footer: true
        },
        initComplete: function () {
            // add embedded filters
            this.api()
                .columns()
                .every(function () {
                    let column = this;
                    let title = column.footer().textContent;

                    // More code

                });
        }

    };
    
    return new DataTable('.dt-services', $.extend({}, config, GetDatatableConfig()));
}

He then has an action (Index) inside the Home Controller where he fills up the model and use to create the table:

public IActionResult Index()
{ // call all the data from the context, as of View(Model). No pagination applied }

The View is as below:

<div class="datatable table-responsive shadow-sm mt-3 mb-3">
    <table class="table table-striped table-hover dt-services">
        <thead>
            <tr>
                <th scope="col" class="text-center">Name</th>
            </tr>
        </thead>
        <tbody>
                        
            @foreach (Service service in Model)
            {
                <tr>
                    <td class="dt-left">
                        @service.Name
                    </td>
                </tr>
            }
        </tbody>
        <tfoot>
            <tr>
                <th>Name</th>
            </tr>
        </tfoot>
    </table>
</div>

I have been trying to apply pagination, but I could not, in the index nothing is paginated, all the data is returned, and I cannot paginate, could anyone shed a light on how I could applomplish this? I tried multiple exampled and none worked.

How do I add a Bearer token to @stomp/stompjs or even just a regular JS WebSocket?

I have a simple Spring Boot app, using spring-security to control access to web socket topics. This is done using OAuth to authenticate users with a bearer token. Everything seems to work except I can’t get the UI to interact with the web socket properly. I am using the @stomp/stompjs library to try to connect like…


import { Client } from '@stomp/stompjs';

const WebSocketClient = () => {
...
        const stompClient = new Client({
            brokerURL,
            onConnect: () => {
                setConnected(true);
                setError('');
                setCanRetryGreetings(true);

                // Subscribe to public status topic
                stompClient.subscribe('/topic/greeting', (message) => {
                    setMessages(prev => [...prev, `Status: ${message.body}`]);
                });

                // Announce presence
                stompClient.publish({
                    destination: '/app/hello',
                    body: 'Client connected'
                });
            },
            onDisconnect: () => {
                setConnected(false);
                setCanRetryGreetings(true);
            },
            onError: (err) => {
                setError(`Connection error: ${err.message}`);
            }
        });

The problem is that I am unsure how to set the authorization header. Here is what the working Postman request looks like…

enter image description here

This works perfect and fails if I remove the header. But it looks like you can’t do custom Headers for the handshake in STOMP. So how do I get it to work?

The full project is available here

So how do I get the Stomp client to work the same as the Postman one? Do I need to use a Raw Web socket instead of Stomp? Is there a better way to use OAuth with stomp?

How to change the implementation of on method of a global mocked class on jest?

I come again asking for help with jest. The scenario is a bit complicated, I think:

  • I have a an API class (BaseApi) whose contructor return a Proxy to wrap a set of Rest calls
    • This class has a manual mock
  • I have another file, where this class is initialized on the file level
    • This API proxy-ed methods (get and post mostly) are called in functions on this file
  • I want to change the return of the proxied method on at least one of my tests
// auth/base.js
class BaseApi {
    constructor() {
        return new Proxy(this, {
            get(api, calledProp, receiver) {
                const reflectedProp = Reflect.get(api, calledProp, receiver);
                const fetchCalls = ['post', 'delete', 'get', 'patch'];

                if (fetchCalls.indexOf(calledProp) >= 0) {
                    return (...args) => api.apiCall(calledProp.toUpperCase(), ...args);
                }

                return reflectedProp;
            },
        });
    }
    async apiCall(method, endpoint, headers, queryParams, body = null) {
        //implementation ...
    }
}

module.exports = { BaseApi };
// auth/__mocks__/base.js
class BaseApi {
    apiCall = jest.fn().mockImplementation(method, endpoint, headers, queryParams, body = null)

    get = jest.fn().mockResolvedValue({
        ok: false,
    });
}

module.exports = { BaseApi };
// main/index.js
const { BaseApi } = require('../auth/base');
const baseApi = new BaseApi();

// ... more stuff
const processRequest = async function() {
    const processCall = await baseApi.get(route, headers);

    if (processCall.ok) {
        return 'Success'
    } else {
        return null
    }
}

module.exports = { processRequest };
// main/index.test.js
jest.mock('../auth/base');
const { BaseApi } = require('../auth/base');
const { processRequest } = require('./index');

describe('process', () => {
    it('correct based on the global mock', async () => { // this is working as expected
        const result = await processRequest();
        expect(result).toBe(null);
    });

    it('this test is erro-ing', async () => {
        /*
          This fails with the following message:     Property `get` does not exist in the provided object
         */
        jest.spyOn(BaseApi, 'get').mockResolvedValueOnce({ ok: true });
        const result = await processRequest();

        expect(result).toBe('Success');
    });
});

So, my question is, how can I change the return of a method of a manual mocked class on a specific test?

Cannot find module ‘rpc-websockets’ from ‘node_modules/@solana/web3.js/lib/index.cjs.js’

Is there no better way to solve this other than downgrading to [email protected]?

I am trying to add a Jest unit test, and getting the error, Cannot find module 'rpc-websockets'.

Stack:

  • expo
  • react native
  • solana/web3.js
  • jest

Error:

npx jest
 FAIL  helpers/formatters.test.ts
  ● Test suite failed to run

    Cannot find module 'rpc-websockets' from 'node_modules/@solana/web3.js/lib/index.cjs.js'

    Require stack:
      node_modules/@solana/web3.js/lib/index.cjs.js
      helpers/formatters.ts
      helpers/formatters.test.ts

      1 | import { SOL_DECIMALS } from '@/constants/solana';
      2 | import { DAY, HOUR, MINUTE, MONTH, YEAR } from '@/constants/time';
    > 3 | import { PublicKey } from '@solana/web3.js';
        | ^
      4 | import BN from 'bignumber.js';
      5 | import { DateTime } from 'luxon';
      6 | import { isAddress as isEvmAddress } from 'viem';

      at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:427:11)
      at Object.<anonymous> (node_modules/@solana/web3.js/lib/index.cjs.js:17:21)
      at Object.require (helpers/formatters.ts:3:1)
      at Object.require (helpers/formatters.test.ts:1:1)

Also, can someone explain why this is exactly happening?

According to rpc-websockets, this issue should have been resolved by 9+ upgrade, but it’s still happening!

When I reduce screen width, my Div’s height increases – how do i stop this? [closed]

The title says it all.

When i reduce the screen width i have all the children scaling and translating nicely, but for some reason this forces the height of the parent to increase, progressively pushing the footer further away the more i decrease the screen width.

When i turn off the children’s transforms this fixes the problem, but i need the transforms switched on for the effect I’m trying to achieve.

Is there a way to force the height of the parent to remain static no matter what? I’ve already tried specifying the height and max-height, but the problem persists.

I’m still learning so I won’t post my code as I’d like to learn from fundamental principles rather than having my code fixed.

Adding aria-label to dynamically generated menu option elements

The following JavaScript function to dynamically create dropdown menu for Start and End day. The drop down shows short name of each day. I want to add aria-label for each days that will have full name of each day. For example, aria-label=”Monday” and so forth, for each option elements in the start and end day menu. Can you suggest a way to do that? I had tried creating a case approach like Mon=Monday and then try updating label for each option element but for some reason it’s not working.

function WorkHours() {
    const startDayLabel = createLabel('startDayLabel', 'Start Day:', 'whlabel');
    const startDayMenu = createDropdown('startDayMenu', ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], '', 'Mon');
    const endDayLabel = createLabel('endDayLabel', 'End Day:', 'whlabel');
    const endDayMenu = createDropdown('endDayMenu', ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], '', 'Fri');    
}

function createLabel(forId, text, className) {
    const label = document.createElement('label');
    label.htmlFor = forId;
    label.textContent = text;
    label.className = className;
    return label;
}

function createDropdown(id, options, className, defaultValue) {
    const select = document.createElement('select');
    select.id = id;
    select.className = className;
    options.forEach(option => {
        const opt = document.createElement('option');
        opt.value = option;
        opt.textContent = option;
        if (option === defaultValue) {
            opt.selected = true;
        }
        select.appendChild(opt);
    });
    return select;
}

still getting “require is not defined” error in server side js script

First, I am new to JS but not to programming. I know 6 other languages proficiently. I need to read and write files in a server side JS script on Windows 11. The simple code is shown below. I am getting “require is not defined” when I call the function from html. I have searched this site and followed all the instructions to fix this such as making sure there is no “type”: “module” line in my package.json file – there isn’t. I am also sure that my script ends in .js and not .mjs or anything else. I have confirmed that node.js is installed (v22.13.1) and used “npm init” to create my package.json file. I can’t proceed with Javascript any further until I resolve this problem.

Here is the test code I’m trying to execute:

function Process()
{
const fs = require('fs');

fs.readFile('Drivers.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});
}

navigator.mediaDevices.getUserMedia() portrait mode in mobile device – video track is not cropped and resized

In a small web project I am trying to get a video element that displays a video track “cropped” in portrait mode.
In my HTML I have something like:

<video id="video" style="width:300px; height:600px"></video>

where the dimensions I have written are just to show I want to render the element with a “portrait” aspect ratio.

Following what specified here:

https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia

in a script, I have something like this:

const constraints = {
  audio: false,
  video: { 
    width: 300, 
    height: 600 
  },
};

navigator.mediaDevices
  .getUserMedia(constraints)
    .then((mediaStream) => {
      const video = document.querySelector("video");
      video.srcObject = mediaStream;
      video.onloadedmetadata = () => {
        video.play();
      };
   })
   .catch((err) => {
     // always check for errors at the end.
     console.error(`${err.name}: ${err.message}`);
   });

When I open the webpage on a mobile device in portrait mode, I get that:

  • the video element has the requested size;
  • the video track displayed inside the video element spans at the required width, but its height is much less than that required and, in particular is exactly equal to the required width times the aspect ratio obtained by the required constraints. So in the case of the example I have provided the height of the video track is 300 * 0.5 = 150(px).

Instead, if I open the webpage on a desktop computer or on a mobile device kept in landscape mode, I can get a video track with those exact width and height, and in turn that fits its video element.

I have tried also to set width and height ranges, and ideal values, as well exact aspect ratio value through the following constraints definition:

constraints : {
                audio: false,
                video : {            
                          width: {
                                   min: 200,
                                   max: 700,
                                   ideal: 300
                                 },
                          height: {
                                    min: 400,
                                    max: 1400,
                                    ideal: 600
                                  },                    
                          aspectRatio : {exact:0.5},
                          resizeMode: "crop-and-scale",
                        }
               }

but I get always the same result as that described above.
This behaviour seems to be due to the mobile device that, when in portrait mode, doesn’t “crop and resize” the video track in order to fill the video element. It looks like the mobile device doesn’t expand the width of the video track over the available width of the video element but forces it to have a maximum width equal to that of the video element.

I could only get an ephemeral result by using the following constraints:

constraints = {
                audio: false,
                video: { 
                         width: 300, 
                         aspectRatio: 0.5 
                },
              };

i.e. by requesting the width of the video track equal to that of the video element, leaving the height unconstrained at all and requesting the specific aspect ratio.
With this setup, the video track, at page loading (or when the first frame from the mediastream object is served), is cropped and resized as I request (the image fills the video element) but immediately after the video track is set again with its height equal to its width times the aspect ratio (300*0.5 = 150).

Is there a known way to keep the video track cropped and resized?

usage of SCP/nonce on electron app with node.js

I’m making a desktop application with electro and node.js, in which I’m trying to implement SCP/nonce.

So far I have developed a script that should generate the content security policy:

const crypto = require('crypto');

class SecurityPolicies {
    static NONCE_LENGTH = 32;

    static CSP_DIRECTIVES = {
        'default-src': ["'self'"],
        'script-src': ["'self'", "'unsafe-inline'"],
        'style-src': ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
        'img-src': ["'self'", "data:", "https:", "blob:"],
        'font-src': ["'self'", "data:","https://fonts.gstatic.com"],
        'object-src': ["'none'"],
        'base-uri': ["'self'"],
        'form-action': ["'self'"],
        'frame-ancestors': ["'none'"],
        'connect-src': ["'self'","ws:", "wss:"],
        'media-src': ["'self'", "blob:"],
        'worker-src': ["'self'", "blob:"],
        'manifest-src': ["'self'"]
    };

    constructor() {
        this.nonce = this.generateNonce();
        this.contentSecurityPolicy = this.buildCSP();
    }

    generateNonce() {
        return crypto.randomBytes(SecurityPolicies.NONCE_LENGTH).toString('base64');
    }

    buildCSP() {
        const directives = {...SecurityPolicies.CSP_DIRECTIVES};

        directives['script-src'] = [
            ...directives['script-src'],
            `'unsafe-inline'`,
            `'nonce-${this.nonce}'`
        ];

        directives['style-src'] = [
            ...directives['style-src'],
            `'unsafe-inline'`,
            `'nonce-${this.nonce}'`
        ];

        return Object.entries(directives)
            .map(([key, values]) => `${key} ${values.join(' ')}`)
            .join('; ');
    }

    applySecurityPolicy(window) {

        window.webContents.session.webRequest.onHeadersReceived((details, callback) => {
            callback({
                responseHeaders: {
                    ...details.responseHeaders,
                    'Content-Security-Policy': [this.contentSecurityPolicy],
                    'X-Content-Type-Options': ['nosniff'],
                    'X-Frame-Options': ['DENY'],
                    'X-XSS-Protection': ['1; mode=block'],
                    'Referrer-Policy': ['strict-origin-when-cross-origin'],
                    'Permissions-Policy': ['camera=(), microphone=(), geolocation=()']
                }
            });
        });

        // Disable navigation
        window.webContents.on('will-navigate', (event, url) => {
            const parsedUrl = new URL(url);
            if (parsedUrl.origin !== 'file://') {
                event.preventDefault();
            }
        });

        // Block new window creation
        window.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));

        // Disable all permission requests
        window.webContents.session.setPermissionRequestHandler((_, __, callback) => {
            callback(false);
        });

        // Disable remote content
        window.webContents.session.setPreloads([]);
    }

    static initSecurity(window) {
        const securityPolicies = new SecurityPolicies();
        securityPolicies.applySecurityPolicy(window);
        return securityPolicies;
    }

    getNonce() {
        return this.nonce;
    }

    getCSP() {
        return this.contentSecurityPolicy;
    }
}

module.exports = SecurityPolicies;

The main idea is to apply nonce to the elements which are then incorporated my index.html looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-<%nonce%>'; style-src 'self' 'nonce-<%nonce%>';">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebStack Deployer for Docker</title>
    <link rel="stylesheet" href="assets/css/bootstrap.css" nonce="<%nonce%>">
    <link rel="stylesheet" href="assets/css/styles.css" nonce="<%nonce%>">
</head>
<body class="d-flex vh-100 vw-100">
<div class="title-bar top">
    <div class="dropdown">
        <button class="btn btn-dark dropbtn" id="maintenanceBtn" data-bs-toggle="dropdown" aria-expanded="false">
            Maintenance
        </button>
        <div class="dropdown-content dropdown-menu" aria-labelledby="maintenanceBtn">
            <a class="dropdown-item" href="#" id="option1">Check for Updates</a>
            <a class="dropdown-item" href="#" id="option2">Backup Settings</a>
            <a class="dropdown-item" href="#" id="option3">Clear Cache</a>
            <a class="dropdown-item" href="#" id="option4">Reset Preferences</a>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" href="#" id="option5">Exit</a>
        </div>
    </div>
    <div class="top-right-controls">
        <button class="btn btn-dark control-button" id="settingsBtn">Settings</button>
        <button class="btn btn-dark control-button" id="helpBtn">Help</button>
        <button class="btn btn-dark control-button" id="aboutBtn">About</button>
        <button class="btn window-control minimize" id="minimizeBtn">─</button>
        <button class="btn window-control maximize" id="maximizeBtn">□</button>
        <button class="btn window-control close" id="closeBtn">×</button>
    </div>
</div>

<div class="content container-fluid">
    <h1 class="display-4">WebStack Deployer for Docker</h1>
</div>
<script src="assets/js/bootstrap.bundle.min.js" nonce="<%nonce%>"></script>
<script src="assets/js/renderer.js" nonce="<%nonce%>"></script>
</body>
</html>

As you can see, I have implemented this token <%nonce%> which should be replaced by the generated nonce string nonce.

I assumed I could use a content loader:

async loadContent() {
        const isDev = process.env.NODE_ENV === 'development';
        const nonce = this.securityPolicies.getNonce();

        if (isDev) {
            await this.mainWindow.loadURL('http://localhost:3000');
        } else {
            const htmlPath = path.join(__dirname, '../index.html');
            let htmlContent = readFileSync(htmlPath, 'utf8');
            htmlContent = htmlContent.replace(/<%nonce%>/g, nonce);

            await this.mainWindow.loadFile(htmlPath, {
                query: {nonce: nonce}
            });
        }
    }

Here is where the problem is, if I already saw that I generate htmlContent and I don’t use it… this is because in order to use it I must generate a uri but I always get an error about the malformed uri, what other way can I implement it?


windows manager class:

const { BrowserWindow } = require('electron');
const path = require('node:path');
const SecurityPolicies = require('../security/SecurityPolicies');
const {readFileSync} = require("node:fs");


class WindowManager {
    constructor() {
        this.mainWindow = null;
        this.securityPolicies = null;
    }

    async createMainWindow() {
        this.mainWindow = new BrowserWindow({
                width: 1200,
                height: 800,
                frame: false,
                webPreferences: {
                    nodeIntegration: false,
                    contextIsolation: true,
                    webSecurity: true,
                    preload: path.join(__dirname, '../preload.js'),
                    sandbox: true
                }
            });

        this.securityPolicies = SecurityPolicies.initSecurity(this.mainWindow);
        this.mainWindow.maximize();
        await this.loadContent();

        return new Promise((resolve) => {
            this.mainWindow.once('ready-to-show', () => {
                resolve(this.mainWindow);
            });

            this.mainWindow.on('closed', () => {
                this.mainWindow = null;
                this.securityPolicies = null;
            });
        });
    }

    async loadContent() {
        const isDev = process.env.NODE_ENV === 'development';
        const nonce = this.securityPolicies.getNonce();

        if (isDev) {
            await this.mainWindow.loadURL('http://localhost:3000');
        } else {
            const htmlPath = path.join(__dirname, '../index.html');
            let htmlContent = readFileSync(htmlPath, 'utf8');
            htmlContent = htmlContent.replace(/<%nonce%>/g, nonce);

            await this.mainWindow.loadFile(htmlPath, {
                query: {nonce: nonce}
            });
        }
    }

    getMainWindow() {
        return this.mainWindow;
    }

    getSecurityPolicies() {
        return this.securityPolicies;
    }
}

module.exports = WindowManager;

main.js class:

const {app} = require('electron');
const AppConfig = require('./models/AppConfig');
const BackendController = require('./controllers/BackendController');
const WindowManager = require('./views/WindowManager');
const IpcController = require('./controllers/IpcController');
const ApplicationSetup = require('./core/ApplicationSetup');
const DevToolsManager = require('./core/DevToolsManager');
const AppLifecycle = require('./core/AppLifecycle');

class Application {
    constructor() {
        this.appConfig = new AppConfig();
        this.backendController = new BackendController();
        this.windowManager = null;
        this.mainWindow = null;
        this.ipcController = null;
    }

    async initialize() {
        ApplicationSetup.configureCommandLineFlags();
        app.setPath('userData', this.appConfig.getUserDataPath());

        // Enable sandbox for all renderers
        app.enableSandbox();

        // Disable navigation outside the app
        app.on('web-contents-created', (_, contents) => {
            contents.on('will-navigate', (event) => {
                event.preventDefault();
            });
            contents.setWindowOpenHandler(() => ({ action: 'deny' }));
        });

        try {
            await this.backendController.initializeFetch();
            await this.setupAppEvents();
            return this.ipcController;
        } catch (error) {
            console.error('Initialization error:', error);
            throw error;
        }
    }

    async setupAppEvents() {
        await app.whenReady();

        try {
            await this.backendController.startGoBackend();

            this.windowManager = new WindowManager();
            this.mainWindow = await this.windowManager.createMainWindow();

            if (this.mainWindow) {
                this.ipcController = new IpcController(
                    this.windowManager,
                    this.backendController,
                    this.appConfig
                );

                // Only enable DevTools in development
                //if (process.env.NODE_ENV === 'development') {
                    DevToolsManager.setupDevToolsHandlers(this.mainWindow);
                //}
            }

            AppLifecycle.setupAppEventHandlers(this.windowManager, this.backendController);
            ApplicationSetup.setupProcessEventHandlers(this.backendController);

        } catch (error) {
            console.error('Setup error:', error);
            throw error;
        }
    }
}

const application = new Application();
application.initialize().catch(err => {
    console.error('Application initialization failed:', err);
    app.quit();
});

module.exports = application;

HTML5 Canvas image data is slanted

I am trying to create a array of points from canvas imagedata method. All the points are collected fine but when I redraw the points using the collected x y values the image is slanted. How can I get the correct pixel values?

const canvas = document.getElementById('skills');
const ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 300;
const linesNumber = 2;
const text = ['FRANK', 'ASTIN'];

ctx.font = '150px lorimer';
ctx.fillStyle = 'red';

ctx.fillText(text[0], 0, 110);
ctx.fillText(text[1], 0, 260);

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const particles = [];
const pixels = imageData.data;

let x = 0;
let y = 0;

for (let p = 0; p < pixels.length; p += 4) {

  if (pixels[p + 3] > 0) {

    particles.push({
      x: x,
      y: y
    });


  }

  x += 1;

  if (x > canvas.width) {
    x = 0;
    y++;
  }
}


ctx.clearRect(0, 0, canvas.width, canvas.height);

particles.forEach((particle) => {

  ctx.fillRect(particle.x, particle.y, 3, 3)


})
<canvas id="skills">

I wrote the code based on another project I saw online but got a slanted image

ESP32 Webpage not finding uploaded JavaScript file

I am working to create a slider on a webpage that can return the value to the ESP32. Everything seems right but for some reason the webpage doesn’t recognize the JavaScript file (see image)

enter image description here

and no file called script2.js is added (naming it script.js gave me the same issue).

I am using LittleFS on the ESP32 to upload the file and it is showing me that it has been uploaded successfully.

enter image description here

Not sure why this is happening, I have my html, js and .ino code below.

<!DOCTYPE html>
<html>

<head>
  <title>DASHBOARD</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="styles.css">
  <script src="node_moduleschart.jsdistchart.umd.js"></script>
</head>

<body>
  <div class="topnav">
    <h1 id="header"> Pressure Waveform and Control Data</h1>
  </div>

  <div class="container">
    <canvas id="pressuregraph" width="800" height="400"></canvas>
  </div>
  <div class="slider-group">
    <div class="slider-container">
    <label for="pressureSlider">Pressure (cmH20)</label>
    <span id="pressliderValue">%SLIDERVALUE%</span>
      <input type="range" onchange="updateslider(this)" id="pressureSlider" min="0" max="50" value="%SLIDERVALUE%"
        step="1" class="slider">
    </div>
  </div>
  <button class="download-button">Download Data</button>
  <button class="user-manual">User Manual</button>

  <script src="script2.js"></script>

</body>

</html> 
window.addEventListener('load', getReadings);
 

function updateslider(element) {
    var sliderValue = document.getElementById("pressureSlider").value;
    document.getElementById("pressliderValue").innerHTML = sliderValue;
    console.log(sliderValue);
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/slider?value="+sliderValue, true);
    xhr.send();
} 
window.updateslider = updateslider;



const pressureData = [];

function updateChart(newValue) {
    pressureData.push(parseFloat(newValue)); // Ensure numerical values

    if (pressureData.length > 20) {
        pressureData.shift();
        pressureChart.data.labels.shift(); // Remove oldest label
    }

    // Update labels dynamically to match data points
    pressureChart.data.labels = pressureData.map((_, index) => index + 1);

    // Explicitly update the dataset reference
    pressureChart.data.datasets[0].data = [...pressureData];

    // Ensure Chart.js properly detects the update
    pressureChart.update('none');

}

const ctx = document.getElementById('pressuregraph').getContext('2d');
const pressureChart = new Chart(ctx, {
    type: 'line',
    data: {
        labels: [],
        datasets: [{
            label: 'Pressure (cmH20)',
            data: [],
            borderColor: 'blue',
            borderWidth: 2,
            fill: false,
            tension: 0.2
        }]
    },
    options: {
        responsive: false,
        scales: {
            x: { title: { display: true, text: 'Data Points' } },
            y: { title: { display: true, text: 'Pressure (cmH20)' } }
        }
    }
});


function getReadings() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
        }
    };
    xhr.open("GET", "/readings", true);
    xhr.send();
}

if (!!window.EventSource) {
    var source = new EventSource('/events');

    source.addEventListener('open', function (e) {
        console.log("Events Connected");
    }, false);

    source.addEventListener('error', function (e) {
        if (e.target.readyState != EventSource.OPEN) {
            console.log("Events Disconnected");
        }
    }, false);

    source.addEventListener('pressure', function (e) {

        updateChart(e.data);
    }, false);
} 
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
#include <Arduino_JSON.h>

const char* ssid = "";
const char* password = "";

AsyncWebServer server(80);
AsyncEventSource events("/events");

String sliderValue = "0";
const char* PARAM_INPUT = "value";
unsigned long lastTime = 0;
unsigned long timerDelay = 750;

String processor(const String& var){
  //Serial.println(var);
  if (var == "SLIDERVALUE"){
    return sliderValue;
  }
  return String();
}

void initLittleFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFS mounted successfully");
  }
}

void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi ..");
  //Serial.println(cmH20)
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  Serial.println();
  initLittleFS();

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html");
  });

  server.on("/pressureslider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
    if (request->hasParam(PARAM_INPUT)) {
      inputMessage = request->getParam(PARAM_INPUT)->value();
      sliderValue = inputMessage;
    }
    else {
      inputMessage = "No message sent";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
    String json = "sample";
    request->send(200, "application/json", json);
    json = String();
  });
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %un", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);

  // Start server
  server.begin();

}

void loop() {
  String pressuredata = "sample";
  if ((millis() - lastTime) > timerDelay) {
    // Send Events to the client with the Sensor Readings Every 10 seconds
    events.send("ping",NULL,millis());
    events.send(pressuredata.c_str(),"pressure",millis());
    lastTime = millis();
  }
  delay(100);
} 

When running the HTML file directly (locally), the script is recognized. So it can’t be a naming issue. Ienter image description here

I have run out of ideas that I could implement.

How was the central circle effect made on this website?

good afternoon.

I would like to know how the circle in the center of this website was made: https://www.itsoffbrand.com/. I understand that it’s a ripple animation, but I don’t fully understand how it was achieved.

I want to create a ripple effect when moving the cursor over it, and then have it return to its original shape. I’m somewhat new to Three.js, and I would love to achieve this effect.

I would really appreciate it if you could help me or provide code examples to guide me in the right direction.

Thank you very much!
P.S.: Sorry if my English is not perfect.

I tried using dat.GUI, jQuery.ripples, and creating a sphere and animating it with shaders, but nothing worked.