Prevent form submission if password !== “” &&

I am trying to prevent form submission if the password !== &&.

I have an expression that works already if the passwords don’t match or are not up to standard:

  const password = form.watch("password");
  const confirmPassword = form.watch("confirmPassword");

  const handleConfirmPasswordBlur = () => {
    setConfirmPasswordTouched(true);
  };

Which works with this:

        <InputWithLabel
          id="password"
          label="Password:"
          placeholder={authFormConstants.placeholderPassword}
          type="password"
          register={form.register("password")}
          horizontal
        />
        {password !== "" && (
          <div className="col-start-2 col-span-3">
            <ValidationList rules={passwordRules} value={password} />
          </div>
        )}

However, I want to have those same errors show up if the password does not equal an empty string as well as if the form is submitted without any password inputs.

In plain JS I would usually handle it with something like this:

        document.getElementById("myForm").addEventListener("submit", function(event) {
            var password = document.getElementById("password").value;

            // Prevent form submission if password is not empty
            if (password !== "") {
                event.preventDefault();
                alert("Password must be empty to submit the form.");
            }
        });

However, this does not work in my case.

React CodeMirror restore scrollbar state

I was developing a text editor app using react and codemirror. CodeMirror is accepting a prop (initialState) to initialize the editor with initial props (selection, etc..). This works perfectly. The thing is, it is not restoring scrollbar position (scroll amount of the editor). So, on every render (like tab change), whole editor is re-rendering, causing it to scroll to top.

What I Tried
I tried getting dom element that scrolls the editor (.cm-scroller) and manually scroll it (with values i am getting BEFORE the tab change event and storing it in my tab’s state) but it is not worked.

My Code

Tab Manager Component

export function TabManager() {
    const [tabs, setTabs] = useState(currentTabs);

    const selectedTabRef = useRef(tabs[0]);
    const tabsRef = useRef(currentTabs);

    useEffect(() => {
        currentTabs = tabs;
        tabsRef.current = tabs;
    }, [tabs]);

    useEffect(() => {
        addTab({isTemp: true});
    }, []);

    useEffect(() => {
        document.getElementById(selectedTabRef.current?.id)?.scrollIntoView(); // if a newly created tab is not in view area, scroll it!
    }, [selectedTabRef.current]);

    const [editorData, setEditorData] = useState({
        length: 0,
        lineCount: 1,
        selection: {
            selectionLength: 0
        }
    });

    const selectTab = (tab) => {
        if (tab?.id === selectedTabRef.current?.id) return;

        if (tabsRef.current.indexOf(tab) >= 0) {
            selectedTabRef.current = tab;
        }

        setTabs([...tabsRef.current]);
    };

    const addTab = ({isTemp, name, file, content}) => {
        if (file && tabsRef.current.some(t => t.file === file)) {
            return selectTab(tabsRef.current.find(t => t.file === file));
        }

        const tabId = crypto.randomUUID();

        const tab = {
            id: tabId,
            name: generateUniqueName(name || DEFAULT_TAB_NAME),
            file: file,
            content: content,
            isTemp: isTemp,
        };

        tab.displayName = tab.name.length > 20 ? tab.name.substring(0, 20) + "..." : tab.name;

        const newTabs = [...tabsRef.current, tab];

        setTabs(newTabs);

        selectedTabRef.current = tab;
    }

    const removeTab = (event, data) => {
        const newTabs = currentTabs.filter(t => t !== data.tab);
        
        if(newTabs.length > 0) selectedTabRef.current = newTabs[newTabs.length-1]; // select the last tab after remove
        else selectedTabRef.current = null;
            
        setTabs(newTabs);

        if(event && event.stopPropagation) event.stopPropagation();
    }

    const handleChange = (content, viewUpdate) => {
        selectedTabRef.current.content = content;
    }

    const onUpdate = (viewUpdate) => {
        selectedTabRef.current.state = {...selectedTabRef.current.state, ...viewUpdate.state.toJSON()};
    }

    const handleStatistics = (data) => {
        const newMetadata = {
            length: selectedTabRef.current?.content?.length,
            lineCount: data.lineCount,
            selection: {
                selectionLength: data.selectedText ? data.selections.map(sel => sel.length).reduce((previous, current) => previous + current, 0) : 0
            },
        };

        if (JSON.stringify(editorData) !== JSON.stringify(newMetadata)) setEditorData(newMetadata); // sometimes this causing an infinite loop
    }

    return (
        <div id={"tabManager"}>
            <div id={"tabListWrapper"}>
                <div id={"tabList"}>
                    {tabs && tabs.length > 0 &&
                        tabs.map(tab => (
                            <div id={tab.id} key={tab.id} className={"editorTabHeader" + (tab.id === selectedTabRef?.current?.id ? " selected" : "")} onClick={() => selectTab(tab)}>
                                <div>
                                    <FontAwesomeIcon icon={faFileText}/>
                                    <span className={"tabHeaderName"} style={{marginLeft: "5px"}} title={tab.fileName || tab.name}>{tab.displayName}</span>
                                </div>
                                <div>
                                    <FontAwesomeIcon className={"closeTabButton"} icon={faRectangleTimes} onClick={(e) => removeTab(e, {tab})}/>
                                </div>
                            </div>
                        ))
                    }
                </div>
                <div id={"newTabPanel"}>
                    <FontAwesomeIcon icon={faPlusSquare} className={"iconButton"} onClick={() => addTab({isTemp: true})}/>
                </div>
            </div>

            <div id={"tabContent"}>
                {selectedTabRef.current &&
                    <Editor key={selectedTabRef.current.id}
                            language={selectedTabRef.current.language || SupportedLanguages.findByFileName(selectedTabRef.current.file)}
                            content={selectedTabRef.current.content}
                            changeListener={(val, viewUpdate) => handleChange(val, viewUpdate)}
                            updateListener={onUpdate}
                            statisticListener={handleStatistics}
                            initialState={selectedTabRef.current.state}
                    />
                }
            </div>

            <div id={"footer"}>
                <div id={"footerLeft"}>
                    {selectedTabRef.current?.file}
                </div>
                {editorData &&
                    <div id={"footerRight"}>
                        <label className={"editorDataLabel"}>
                            length: <span className={"editorDataContent"}>{editorData.length}</span>
                        </label>
                        <label className={"editorDataLabel"}>
                            lines: <span className={"editorDataContent"}>{editorData.lineCount}</span>
                        </label>
                        <label className={"editorDataLabel"}>
                            selection: <span className={"editorDataContent"}>{editorData.selection?.selectionLength}</span>
                        </label>
                    </div>
                }
            </div>
        </div>
    );
}

Editor

export default function Editor({language, content, changeListener, updateListener, statisticListener, initialState}) {
    if (!content) content = "";
    if (typeof content !== "string") throw new Error("Document content must be a string");

    const editorRef = useRef();

    let contentLang;

    if (language) {
        contentLang = getLanguagePack(language);
    }

    const extensions = [
        search({top: true}),
        syntaxHighlighting(classHighlighter),
        foldGutter({}),
        indentUnit.of("    "),
    ];

    if (contentLang) extensions.push(contentLang);

    return <ReactCodeMirror theme={"none"}
                            basicSetup={true}
                            value={content}
                            indentWithTab={true}
                            placeholder={"Empty document.."}
                            onChange={changeListener}
                            onUpdate={updateListener}
                            onStatistics={statisticListener}
                            extensions={extensions}
                            ref={editorRef}
                            initialState={initialState?.doc && {json: initialState}}
    />
}

*Note: I’m also open for any other suggestions about my code. I am new in react and i might be mis-used some code. Or if there is any way to improve my code (like if it is possible to prevent it from re-rendering on every tab change, we would not even need to store it’s initial state), some help would be great !

Hi all, I’m building a very simple website on cargo.site. I’m using cargo 3. Why isn’t this code working when published?

I’m building a very simple website on cargo.site. I’m using cargo 3. I coded this code with no problem and everything works while in edit mode. When I publish the site, everything seems to work except for the javascript that is supposed to open up the buttons revealing hidden information.

This is the HTML, the javascript had to be put in tags at the end because there is nowhere to inport js.

<column-set gutter="2" mobile-gutter="2rem" mobile-stack="false"><column-unit slot="0"><h2 style="--font-scale: 0.7;">File Format Multimedia Services</h2></column-unit><column-unit slot="1"><span class="links" style="--font-scale: 0.63;">Excellence in every Byte.</span></column-unit></column-set><hr>


<gallery-slideshow pause-on-hover="true"><media-item class="zoomable" hash="U1999828862461535591177623191262" limit-by="width" rotation="0" scale="100" slot="slot-0"><figcaption class="caption" slot="caption"><i>Shot by Gabriele Saraceno</i><hr></figcaption></media-item><media-item class="zoomable" hash="I1999831740799247132447509593822" limit-by="width" rotation="0" scale="100" slot="slot-1"><figcaption class="caption" slot="caption"><i>Shot by Andres Ramirez<br>
</i><hr></figcaption></media-item><media-item class="zoomable" hash="K1999828862479982335251332742878" limit-by="width" rotation="0" scale="100" slot="slot-2"><figcaption class="caption" slot="caption"><i>Shot by Gabriele Saraceno</i><hr></figcaption></media-item><media-item class="zoomable" hash="D1999831541150136022689032453854" limit-by="width" rotation="0" scale="100" slot="slot-3"><figcaption class="caption" slot="caption"><i>Shot by Andres Ramirez</i><hr></figcaption></media-item><media-item class="zoomable" hash="Z1999831218498135429435265138398" limit-by="width" rotation="0" scale="100" slot="slot-4"><figcaption class="caption" slot="caption"><i>Shot by Jonathan Noda</i><hr></figcaption></media-item></gallery-slideshow><ul class="expandable-list"><li><button class="expand-btn">Film Processing</button>
        <div class="expanded-content" style="display: none;">
<p>All Film Processing Orders Include:<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Developing, Scans Emailed, and Negatives Returned<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - BLACK AND WHITE $10<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - COLOR C41 $11<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - PUSHING AND PULLING IS AN ADDITIONAL $2 FEE<br>
<br>
<a href="https://forms.fillout.com/t/wUGeXHhtHUus" target="_blank">Click here to fill out a form and get started!</a></p>
    </div></li><li><button class="expand-btn">Media Scanning</button>
        <div class="expanded-content" style="display: none;">
            <p>CHART HERE</p>
        </div>
    </li>
<li>
        <button class="expand-btn">Prints</button>
<div class="expanded-content" style="display: none;">
          <p>CHART HERE</p>
        </div>
</li>
<li>
        <button class="expand-btn">Stickers</button>
        <div class="expanded-content" style="display: none;">
            <p>CHART HERE</p>
        </div>
    </li>
<li>
        <button class="expand-btn">Photos</button>
        <div class="expanded-content" style="display: none;">
            <p>CHART HERE</p>
        </div>
    </li>
<li>
        <button class="expand-btn">Flyers</button>
        <div class="expanded-content" style="display: none;">
            <p>CHART HERE</p>
        </div>
    </li>
<li>
        <button class="expand-btn">Posters</button>
        <div class="expanded-content" style="display: none;">
            <p>CHART HERE</p>
        </div>
    </li>

<script>
    document.querySelectorAll('.expand-btn').forEach(button => {
        button.addEventListener('click', function() {
            const content = this.nextElementSibling;
            if (content.style.display === "none" || content.style.display === "") {
                content.style.display = "block";
            } else {
                content.style.display = "none";
            }
        });
    });
</script></ul>

This is the CSS:

[id="Y1912964742"] .backdrop {
}

.mobile [id="Y1912964742"] .backdrop {
}

.mobile [id="Y1912964742"] .page-layout {
}

[id="Y1912964742"] .page-content {
    width: 100%;
}

.expandable-list {
    list-style-type: none;
    padding: 1;
}

.expandable-list li {
    margin: 30px 0;
}

.expand-btn {
    background: none;
    border: none;
    color: #000000;
    text-align: inherit;
    font-size: 30px;
    cursor: pointer;
    padding: 10px;
    width: 100%;
    font-family:  "IBM Plex Mono";
    font-weight: 700;
    font-style: normal;
}

.expand-btn:hover {
    text-decoration: underline;
}

.expanded-content {
    display: none;
    padding: 10px;
    margin-top: 5px;
    background-color: #f9f9f9;
    border: 1px solid #ddd;
}

.expanded-content p {
    margin: 0;
}

Everything works, except for the javascript I’m pretty sure.
This is the link to the site where it doesn’t work at the moment: fileformat.media

I don’t know why it doesn’t work once it is published.

I have tried to ask my friend which he said to add some “aria-expanded” and some event listeners in the JS to listen for the click or to check if it is expanded or not.
Not sure.

NextJS 15 with Stripe Error /.well-known/vercel/microfrontend-routing 404 (Not Found)

I dont know how to fix this error message again (i did once) and need help from somebody who worked with stripe unlike me or knows why vercel is doing this. I was finished but rolled back from github… long story cant switch back.

https://github.com/onurz24/valet-booking/
It tells me already on https://valet-booking.vercel.app/checkout in the console visiting that there is a error with microfrontend something.
I set all enviroment variables now and also a webhook key from stripe with Endpoint-URLhttps://valet-booking.vercel.app/api/webhooks

Can anyone tell me why the stripe checkout doesnt popup again ?
I already did it and tried to fix some other error after the payment with the test credit card (I just forgot to set enviroment variable for my site url and didnt see it send me to localhost:3000/sucess instead of from https://valet-booking.vercel.app/checkout to https://valet-booking.vercel.app/?session_id={CHECKOUT_SESSION_ID}

I have no clue what microfrontend routing is and the docs just tell me

// Partial of ./pages/api/webhooks/index.ts


import Cors from 'micro-cors';


const cors = Cors({


  allowMethods: ['POST', 'HEAD'],


});


// ...

Asking ChatGPT didnt help at all, he gave me the /app/api/webhook/route.ts (or do i have to use index.ts ? i thought its only because the docs is using older nextjs version) :

export default cors(webhookHandler as any);

import { NextResponse } from 'next/server';
import { buffer } from 'micro';
import Stripe from 'stripe';
import Cors from 'micro-cors';

// Stripe initialisieren
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-10-28.acacia', // API-Version
});

// Webhook-Signatur und Konfiguration
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;

// Diese Zeilen verhindern, dass Next.js den Body automatisch parst (Stripe benötigt den Roh-Body)
export const config = {
  api: {
    bodyParser: false, // Verhindert, dass Next.js den Body automatisch parst
  },
};

// CORS Middleware für die Stripe Webhook-Verarbeitung
const cors = Cors({
  allowMethods: ['POST', 'HEAD'], // Nur POST und HEAD erlauben
  allowHeaders: ['stripe-signature'], // Nur stripe-signature-Header erlauben
});

// POST-Methode für den Webhook-Handler
export async function POST(req: Request) {
  // CORS-Verarbeitung sicherstellen
  await cors(async (req: any, res: any) => {
    if (req.method === 'POST') {
      // Buffer für den raw body erstellen (Stripe erfordert den raw body)
      const buf = await buffer(req);
      const sig = req.headers.get('stripe-signature')!;

      let event: Stripe.Event;

      try {
        // Event mit der Signatur und dem Raw-Body verifizieren
        event = stripe.webhooks.constructEvent(buf.toString(), sig, webhookSecret);
      } catch (err: any) {
        console.error(`❌ Fehler beim Verarbeiten des Webhooks: ${err.message}`);
        return new NextResponse(`Webhook Error: ${err.message}`, { status: 400 });
      }

      // Event erfolgreich verarbeitet, logge das Event
      console.log('✅ Webhook Event erfolgreich verarbeitet:', event.id);

      // Event Handling, hier kannst du auf verschiedene Event-Typen reagieren
      switch (event.type) {
        case 'checkout.session.completed':
          const session = event.data.object as Stripe.Checkout.Session;
          console.log('Zahlung erfolgreich abgeschlossen, Session ID:', session.id);
          break;
        // Weitere Event-Typen hier hinzufügen
        default:
          console.log('Unbehandeltes Event:', event.type);
      }

      // Erfolgreiche Antwort zurückgeben
      return new NextResponse(JSON.stringify({ received: true }), { status: 200 });
    } else {
      return new NextResponse('Method Not Allowed', { status: 405 });
    }
  })(req as any, {} as any);
}

Jest unit tests are failing after jest upgrade to 29.7.0 from 27.0.6

I am running some unit tests after a node version upgrade and jest version upgrade in package.json. It is erroring out with

TypeError: Cannot read properties of undefined (reading ‘_setAdapter’)

The unit test attempts to mock a knex config.

import { mock as mockKnex, getTracker } from "mock-knex";
import { postgresConnectionPool } from "../../src/database/PostgresConnectionPool";

mockKnex(postgresConnectionPool); // This line is erroring out

describe("Test Suites", () => {
  // test cases
})

However this is erroring out because a new object of mockKnex is not created. The error is TypeError: Cannot read properties of undefined (reading '_setAdapter')

// This code is from mock-knex public library
value: function mock(db) {
  this._setAdapter(db); // It is failing on this line because the this object is undefined

  return this._adapter.mock(db);
}

One of my questions is why is this happening ? Is it a jest issue or node issue ?

My Jest Config

module.exports = {
  moduleFileExtensions: ["ts", "js"],
  transform: {
    ".(ts)": "ts-jest",
  },
  testMatch: ["**/test/**/*.test.(ts|js)"],
  testEnvironment: "node",
  setupFiles: [
    "<rootDir>/test/jestSetup.js", // mock required environment variables
  ],
};

Playwright test not running in parallel

I have a single Playwright test setup to run multiple times with different parameters.

const chars = [
  {name: "John"},
  {name: "Jane"},
];


chars.forEach(async char => {
  test(`Test for ${char.name}`, async ({ page }) => {
    await runTest(page, char.name);
  });
});

I have a list of characters, which a test is run for each. However, these tests happen in sequence and not in parallel. They are all in one file.

In the playwright.config.js fullyParallel: true, on each project such as chromium and it is also set to true in defineConfig.

If you need more information, please ask. How can I run these in parallel?

Gradient polyline in cesium. js

Is there any way to display gradient polyline in cesium.js? Color will matter from speed like on garmin or strava. First get the color for first point, count percents of speed beetwen min speed and max speed and set for the start color then set the right color between blue and red with yellow on the middle, then for the second point set as end color and then display gradient for that petacular segment interpolated from start and end color. Also with 3d tileset please:) .

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.122/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.122/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <style>
    html, body, #cesiumContainer {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'MY_CESIUM.ION_ACCESS_TOKEN';
    const viewer = new Cesium.Viewer("cesiumContainer", {
        requestRenderMode: true,
        maximumRenderTimeChange: Infinity
    });
    positions = {
    // lat, lon, speed(m/s)
        -75, 35,30
        -100,35,56
        -125, 35,76
        -150,35,75
    }
    viewer.entities.add{
        //The magic here
    }
  </script>
</body>
</html>

effect should look like this image from garmin

Thank you very much in advance!

How can i overlay the images and make the text in the back keep moving and appearing on the opposite direction?

enter image description hereI am trying to create an effect where two images are overlaid on top of each other while a background text moves in the opposite direction

The images should stay on top of each other, but the text behind them should move horizontally or vertically in the opposite direction to create the illusion of movement. I would like to achieve this using React.

I tried using position: fixed and absolute but it didnt work. And i cant figure out how i would start doing the text animation

Get Year and Month of Next Month Javscript

I have this code below that gets the the current month and year:

var d = new Date();
var currentMonth = d.getMonth() + 1;
var currentYear = d.getFullYear();

I have a while loop that I want to loop through every month starting at 01/2024 and including this month:

var year = 2024
var month = 1

while(month != currentMonth || year != currentYear){  
    //do stuff
}

My issue with this is this will not include the current month of 11/2024. What I really want is the year and month of next month. What is the best way to do this?

Why does IEEE 754 define 1 ^ NaN as 1, and why do Java and Javascript violate this?

IEEE 754 defines 1 ^ n as 1, regardless of n. (I’m not paying $106 to confirm this for myself, but this paper cites page 44 from the 2008 standard for this claim.) Most programming languages seem to follow this prescription: Python, C, C#, PHP, Go, and Rust all return 1 for 1 ^ NaN. However, Java and Javascript both return NaN.

  1. Why does IEEE 754 create this exception to the general rule of NaN propagation?
  2. Why do Java and Javascript not follow the standard?

Regarding programing languages [closed]

How can I efficiently convert data between different programming languages (e.g., Python to JavaScript) while maintaining data integrity and minimizing performance overhead

I’ve tried using various libraries and tools like json in Python for serialization and JSON.parse() in JavaScript, but I’m encountering issues with maintaining the correct data types and performance when converting large datasets. I expected these methods to work seamlessly across languages, but I’m facing problems with complex data structures (like nested objects or custom classes).

I’m hoping to find an efficient solution or best practices for handling these cross-language conversions while avoiding performance bottlenecks and data loss.

water fall effect in javascript

I need help to apply the water effect which is fading like the below image. In the image the white particles are fading to blue blurry effect. This is what I am trying to achieve.

I believe with fast blur or simplex-noise we should be able to achieve this, but not sure how to proceed.

(click to enlarge)

Water fall

Achieved the water fall with splinters in HTML5 canvas.

CodePen Source

const canvas = document.getElementById('fountainCanvas');
const ctx = canvas.getContext('2d');

// Set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const particles = [];
const splinters = [];
const gravity = 0.1; // Gravity constant
const fountainX = canvas.width / 2;
const fountainY = canvas.height / 2;

// Mouse position
let mouseX = fountainX;
let mouseY = fountainY;

// Particle class
class Particle {
  constructor(x, y, angle, isSplinter = false) {
    this.x = x;
    this.y = y;
    const speed = Math.random() * 3 + 2; // Random speed
    const spread = Math.random() * 0.4 - 0.2; // Randomize direction slightly
    this.vx = isSplinter ?
      (Math.random() * 2 - 1) * 3 :
      Math.cos(angle + spread) * speed;
    this.vy = isSplinter ?
      Math.random() * -3 :
      Math.sin(angle + spread) * speed;
    this.alpha = isSplinter ? 1 : 1; // Opacity
    this.radius = isSplinter ? Math.random() : Math.random() + 1; // Size
    this.isSplinter = isSplinter;
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.vy += gravity; // Apply gravity
    this.alpha -= this.isSplinter ? 0.02 : 0.005; // Fade out

    // Check if main particles reach the bottom of the canvas
    if (!this.isSplinter && this.y >= canvas.height) {
      this.createSplinters(); // Create splinters on impact
      this.alpha = 0; // Make particle invisible
    }
  }

  createSplinters() {
    for (let i = 0; i < 10; i++) {
      splinters.push(new Particle(this.x, canvas.height, 0, true));
    }
  }

  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(199, 100%, 67%, ${this.alpha})`; // Blue shade with opacity
    ctx.fill();
  }

  isAlive() {
    return this.alpha > 0; // Check if particle is still visible
  }
}

// Create particles over time
function emitParticles() {
  const angle = Math.atan2(mouseY - fountainY, mouseX - fountainX);
  for (let i = 0; i < 5; i++) { // Emit a few particles per frame
    particles.push(new Particle(fountainX, fountainY, angle));
  }
}

// Animation loop
function animate() {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; // Trail effect
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  emitParticles(); // Emit new particles continuously

  // Update and draw particles
  particles.forEach((particle, index) => {
    particle.update();
    if (!particle.isAlive()) {
      particles.splice(index, 1); // Remove dead particles
    } else {
      particle.draw();
    }
  });

  // Update and draw splinters
  splinters.forEach((splinter, index) => {
    splinter.update();
    if (!splinter.isAlive()) {
      splinters.splice(index, 1); // Remove dead splinters
    } else {
      splinter.draw();
    }
  });

  requestAnimationFrame(animate);
}

// Update mouse position on move
canvas.addEventListener('mousemove', (event) => {
  mouseX = event.clientX;
  mouseY = event.clientY;
});

// Initialize animation
animate();
canvas {
  display: block;
  margin: 0 auto;
  background: #000;
  /* Black background */
}
<canvas id="fountainCanvas"></canvas>

Sort two arrays using one of the arrays with duplicate values [closed]

I have two arrays. One of the arrays (ids) contains IDs and the other (counts) contains counts for each ID. Note there could be duplicate count values in array1 (e.g. 663 occurs at 0 & 4). Also there will be many counts of 0 in array1. How can I sort both arrays based on counts in descending order?

counts=[663,99,0,0,663,0,265,0,0,0,0,3,0,0,210,0,298,815,14730,0,1132,7199,0,0,0,0,5269,1259,16680,233,0,0,0,0,4788,0,0,0,0,0,0,0,22,1158,0,0,125,127,0,1669,0]

ids=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51]

edit… Here’s what I’ve tried so far and yields incorrect results, I think because of duplicate values.

counts.sort((a, b) => b - a); 
ids.sort((a, b) => {
  const indexA = counts.indexOf(counts.indexOf(a));
  const indexB = counts.indexOf(counts.indexOf(b));
  return indexA - indexB;
});

Issue with Javascript Audio Output | On Mobile Devices Considered as Call Volume

async function startListening() {
                stream = await navigator.mediaDevices.getUserMedia({
                    audio: {
                        channelCount: 1,
                        echoCancellation: true,
                        autoGainControl: true,
                        noiseSuppression: true,
                    },

                });
                mediaRecorder = new MediaRecorder(stream);
                micStream = audioMotion.audioCtx.createMediaStreamSource(stream);
                audioMotion.connectInput(micStream);
                mediaRecorder.ondataavailable = async (event) => {
                    const base64Data = await blobToBase64(event.data);
                    socket.send(JSON.stringify({ type: 'audioIn', data: base64Data }));
                    // console.log('Send voice data:', base64Data);
                };

                async function blobToBase64(blob) {
                    const reader = new FileReader();
                    reader.readAsDataURL(blob);
                    return new Promise((resolve) => {
                        reader.onloadend = () => resolve(reader.result.split(',')[1]);
                    });
                }
                mediaRecorder.start(1000);
            }

If I am using the echoCancellation: true it is working fine on PC but when using it on mobile devices the when volume buttons are clicked the call volume is displayed and the volume cannot be controlled unless we manually adjust the media volume. If I turn it off, the playback from the device is taken as input.

here is my Audio Playback method.

async function playAudioResponse(audioBase64) {
                try {
                    if (!audioBase64 || audioBase64.trim() === '') {
                        // console.error('Empty or invalid audio data received.');
                        return;
                    }
                    const binaryString = atob(audioBase64);
                    const len = binaryString.length;
                    const buffer = new Float32Array(len); // 8-bit

                    for (let i = 0; i < len; i++) {
                        buffer[i] = decodeSample(binaryString.charCodeAt(i)) / 32768;
                    }

                    // console.log('buffer', buffer)
                    audioWriter(buffer).then((data) => {
                        audioMotion.connectInput(micStream);
                        audioMotion.volume = 1;
                    }).catch((error) => {
                        // console.error('Error writing audio data:', error);
                    });


                } catch (error) {
                    console.error('Error playing audio:', error);
                }
            }

And in the above play audio response, i am using a WebAudioWrite which uses AudioWorkletNode

Thanks in advance.