How to pass a long user token from React Native to a React web app?

I’m building a React Native app that includes a button to redirect the user to a React-based web app. The goal is to pass the userToken from the mobile app to the web app so the user can remain authenticated. My initial idea was to include the token as a query parameter in the URL, like this:

const handleStatisticsClick = async () => {
   WebBrowser.openBrowserAsync(
     https://www.MY-URL.COM/user-profile/creator-hub?accessToken=${userToken.replace(/./g,'-')}
  ); };

How can I securely and reliably pass a very long userToken from my React Native app to my React web app? Are there better alternatives to using a query parameter for this purpose?
However, the token is quite large (several thousand characters), and I’m concerned about potential issues with URL length limits or other limitations when passing such a large token in the query string. For example, when testing this implementation, the browser occasionally fails to open the URL properly, and some services seem to truncate the query parameters. Additionally, I am not sure how the receiving server handles URLs with very large query strings. If relevant, the server is configured to accept standard-length query strings but may require adjustments for such edge cases.

Furthermore, I’m worried about the security of passing such sensitive information through the URL.

Debugging Attempts

Using URL Parameters: I initially attempted to pass the token in the URL query string, but encountered issues with length limits and concerns about sensitive data exposure.

Alternative Shortened Token: I considered generating a short-lived unique identifier in the mobile app that the web app could exchange for the actual token via an API call, but this adds complexity and might introduce latency.

Using Cookies: I explored the possibility of setting cookies, but I am unsure how to share cookies securely between a mobile app and a web app.

Question

Has anyone encountered this issue before? What solutions have worked for you? I’m looking for best practices to ensure reliability and security.

I tried passing the userToken directly as a query parameter in the URL using the WebBrowser.openBrowserAsync function in React Native. I expected the token to be transferred seamlessly to the web app and allow the user to stay authenticated.

However, I encountered concerns about potential URL length limits and security issues with including such a long token (several thousand characters) in the query string. Additionally, I’m worried about how this might affect different browsers or platforms.

I’m looking for a reliable and secure alternative to pass this token without running into these issues.

structure of AJV standalone code generation code formats

https://ajv.js.org/standalone.html#configuration-and-limitations

In the following given example of the link above:

import myFormats from "./my-formats"
import Ajv, {_} from "ajv"

const ajv = new Ajv({
  formats: myFormats,
  code: {
    source: true,
    formats: _`require("./my-formats")`,
  },
})

If you only use formats from [ajv-formats](https://github.com/ajv-validator/ajv-formats) this option will be set by this package automatically.

There is no further details on how the “./my-formats” file must be structured.

Can someone link to official documentation that explains how the "./my-formats" must be structured?

Best approach for creating an accessible multi-step form with aria-live step indicator?

I’m working on a multi-step form where each step is dynamically displayed using <template> elements. As the user progresses, the previous step is hidden, and the next step is revealed (with JavaScript).

For accessibility, I’ve added aria-live="polite" to the root of the HTML document, and I dynamically update the content based on the selected step using JavaScript.

My question is: which approach is more accessible?

  • Adding aria-live="polite" to the root element and updating it with JavaScript when the step changes.

OR

  • Adding aria-live="polite" to each <template> element, with the step title already included inside the template.

Ffmpeg Wasm Vanilla Won’t Load

I’ve build a simple script to load ffmpegwasm using vanilla JS but even though it’s simple I can’t get it to load. This is the code:

<script src="https://cdn.jsdelivr.net/npm/@ffmpeg/[email protected]/dist/umd/ffmpeg.min.js"></script> 
<p id="message">Press the button to load FFmpeg</p>
<button id="load-ffmpeg">Load FFmpeg</button>

<script>
    // Create FFmpeg instance
    const { FFmpeg } = FFmpeg;
    const ffmpeg = FFmpeg.createFFmpeg({ log: true });

    // Button click to load FFmpeg
    const button = document.getElementById('load-ffmpeg');
    const message = document.getElementById('message');

    button.addEventListener('click', async () => {
        message.textContent = 'Loading FFmpeg...';

        try {
            await ffmpeg.load();
            message.textContent = 'FFmpeg loaded successfully!';
        } catch (error) {
            message.textContent = 'Failed to load FFmpeg.';
        }
    });
</script>

Can you please help me understand where I have gone wrong.

Submitting a text in a HTML form with more than 1 space

Question at the end!

After years of HTML forms submissions, I realized this for the first time.
Suppose you have an input text in a form, and you type and submit:

There are  2 spaces before the number 2

If the form method=get, it will pass

There+are+2+spaces+before+the+number+2

in the URL, while if the form method=post, it will pass

There are 2 spaces before the number 2

meaning with a single space.

Now, I understand I could use encodeURIElement or whatever, I can JSON it, I can invent a totally new encoding style, base97 for the matter.
But this is just insane! If you REMOVE a space, you LOSE information, it’s not a 1:1 transformation and you will never be able to go back.
I discovered it “by chance” because I was using the string to do a MySQL search with LIKE %whatever% and it was always giving me back 0 elements found (and yes, I am using prepared statements).
This is my question: what is the advantage of NOT ENCODING MULTIPLE SPACES?! I do not see ANY advantage, but if the HTML specifications have been written like this, I hope there is a VALID reason. If the form submits values encoded in a way that is not reversible, then you can NEVER trust it.

Unable to share stream between two users with PeerJS

I am trying to create something where one user shares their stream and other watches the shared stream. I have created basic sharer and viewer.html files and used PeerJS for connection and video sharing.

The issue i am facing is i am unable to view the stream.

Here is my code

sharer.html

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Screen - Sharer</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link rel="shortcut icon" href="chat.png" type="image/x-icon">
  <script src="https://unpkg.com/[email protected]/dist/peerjs.min.js"></script>
  <style>
    * {
      font-family: 'Jetbrains Mono';
    }
  </style>
</head>

<body class="h-screen bg-green-400 flex justify-center items-center">
  <div class="flex gap-2 flex-col bg-white border-2 border-black p-2 justify-center items-center">
    <h1 class="text-xl font-bold">Share Screen</h1>
    <div class="flex justify-center items-center gap-3">
      <p class="text-sm">Your ID: <span id="your-id"></span></p>
      <button id="share" class="p-1 px-2 text-sm border-2 border-black bg-yellow-200">Share</button>
    </div>
    <div class="mt-4">
      <video id="video" class="h-[400px] w-[700px]" autoplay controls></video>
    </div>
  </div>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      let stream;
      const btn = document.getElementById('share');
      const video = document.getElementById('video');

      // Initialize PeerJS
      const peer = new Peer('aditya'); // Use a unique ID for this peer

      peer.on('open', id => {
        document.getElementById('your-id').textContent = id;
        console.log('Peer connected with ID:', id);
      });

      btn.addEventListener('click', () => {
        navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })
          .then(mediaStream => {
            stream = mediaStream;
            video.srcObject = stream;

            // Play the video locally
            video.addEventListener('loadedmetadata', () => {
              video.play();
            });

            // Wait for a viewer to connect
            peer.on('call', call => {
              console.log('Viewer is calling...');
              call.answer(stream); // Send the stream to the viewer
            });
          })
          .catch(err => {
            console.error('Error sharing screen:', err);
          });
      });

      peer.on('error', err => {
        console.error('PeerJS Error:', err);
      });
    });
  </script>
</body>

</html>

this is viewer.html

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Screen - Viewer</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link rel="shortcut icon" href="chat.png" type="image/x-icon">
  <script src="https://unpkg.com/[email protected]/dist/peerjs.min.js"></script>
  <style>
    * {
      font-family: 'Jetbrains Mono';
    }
  </style>
</head>

<body class="h-screen bg-green-400 flex justify-center items-center">
  <div class="flex gap-2 flex-col bg-white border-2 border-black p-2 justify-center items-center">
    <h1 class="text-xl font-bold">Screen Viewer</h1>
    <div class="flex justify-center items-center gap-3">
      <p class="text-sm">Your ID: <span id="your-id"></span></p>
      <button id="connect" class="p-1 px-2 text-sm border-2 border-black bg-yellow-200">Connect</button>
    </div>
    <div class="mt-4">
      <video id="video" class="h-[400px] w-[700px]" autoplay controls></video>
    </div>
  </div>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const btn = document.getElementById('connect');
      const video = document.getElementById('video');

      // Initialize PeerJS
      const peer = new Peer('agrima'); // Use a unique ID for this peer

      peer.on('open', id => {
        document.getElementById('your-id').textContent = id;
        console.log('Peer connected with ID:', id);
      });

      btn.addEventListener('click', () => {
        console.log('Attempting to call "aditya"...');

        // Call the sharer (aditya)
        const call = peer.call('aditya');

        if (call) {
          // Receive the stream
          call.on('stream', remoteStream => {
            console.log('Stream received:', remoteStream);
            video.srcObject = remoteStream;
            video.addEventListener('loadedmetadata', () => {
              video.play();
            });
          });

          call.on('error', err => {
            console.error('Call error:', err);
          });
        } else {
          console.error('Unable to connect to the sharing peer.');
        }
      });

      peer.on('error', err => {
        console.error('PeerJS Error:', err);
      });
    });
  </script>
</body>

</html>

any help is appreciated!

Detect page change with 2sxc app in Blazor/Oqtane

Since Blazor and Oqtane are SPA, content is added to the DOM with no actual load to be always detected.

Also, 2sxc still does not seem to work with Blazor / .net on the client browser.

This leaves quite a few usual cases like every “on page load” with no options for easy implementation.

So, resorting only to common razor server side code and plain javascript, how can I trigger a js function every time a page is shown, either for the first time or recurrent accesses with no forced reloads?

Let’s say a simple:

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

and

function showalert(){
    alert("This is page ABC");
}
window.onload = showalert();

Error WebSocket connection to ‘ws://127.0.0.1:1430/__tauri_cli’ failed: WebSocket is closed due to suspension

I am making a Tauri app Github Repo .

So I have two pages clock.html and index.html . There is one event listener attached to an element in index.html . So when I redirect the user to clock.html, javascript complains about the element no longer existing (basically a TypeError) which possibly halts the execution of upcoming javascript code.
To fix that I added an if statement around the addEventListener call to only add the event listener if it was on the correct page . But on doing that I get this weird error (title) which has no explanation whatsoever in the context of a Tauri app.

main.js ( /frontend/src/main.js )

async function addAlarm(targetDate) {
   const alarm = {
      date: targetDate.toString()
   }
   const response = await fetch("http://localhost:6969/alarms/" + userID + '/',
      {
         method: "POST",
         headers: {
            "Content-Type": "application/json"
         },
         body: JSON.stringify(alarm)
      }

   );
   let t = await response.text();
   console.log(t);
   document.getElementById('alarms').insertAdjacentHTML('afterbegin', `<div> ${t}</div>`);
}

async function register() {

   const data = {
      name: document.querySelector('[name="usercode"]').value,
      serverpass: document.querySelector('[name="server-password"]').value
   }
   const response = await fetch('http://localhost:6969/users', {
      method: 'POST',
      headers: {
         "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
   });
   userID = await response.text();
   return userID;
}
if (window.location.href === "/") {
   document.getElementById("login").addEventListener("click", async event => {
      event.preventDefault();
      await register();
      window.location.replace("/clock");
   });
}
if (window.location.href === "/clock") {
   document.getElementById('add-alarm').addEventListener('click', async () => {
      await addAlarm(document.querySelector('[type="datetime-local"]').value);
   })
}

How to enable simultaneous independent scrolling for multiple draggable elements using Interact.js?

I’m building a multi-touch application using HTML, CSS, jQuery, and Interact.js. The application has draggable squares that display content (HTML files loaded via iframes). Each square has its own content area, and I want users to scroll the content of different squares independently.

The issue arises when one user scrolls the content of one square while another user tries to scroll another square. In this case, the scrolling in the first square stops, and only the second square scrolls. My goal is to allow simultaneous independent scrolling in all squares without interruption.

Implemented Interact.js for drag, gesture, and touch-based interactions.
Used touchmove and touchend events for scrolling inside the iframe content areas.
Disabled interactions temporarily during scrolling using interact(element).unset() and re-enabled them after the scroll ends.
Expected Behavior:

Users should be able to scroll the content of multiple squares independently and simultaneously.
Actual Behavior:

When one user starts scrolling a square, any new scrolling interaction in another square interrupts or stops the first scroll.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Multi-touch Squares</title>
    <script src="js/jquery.min.js"></script>
    <script src="js/interact.min.js"></script>
    <link rel="stylesheet" href="css/style.css" />
  </head>
  <body>
    
    <!-- Buttons to show hidden squares -->
    <div class="show-buttons">
      <div class="show-btn type1" id="circle1"></div>
      <div class="show-btn type2" id="circle2"></div>
      <div class="show-btn type3" id="circle3"></div>
      <div class="show-btn type4" id="circle4"></div>
    </div>

    <!-- Boundary container for draggable squares -->
    <div id="boundary">
      <!-- Square 1 -->
      <div id="square1" class="square hidden">
        <div class="square-header">
          Name of Item 1
          <div class="close-btn">&times;</div>
        </div>
        <div class="square-content">
          <iframe src="a.html" frameborder="0" style="pointer-events: auto;"></iframe>
        </div>
      </div>
      <!-- Square 2 -->
      <div id="square2" class="square hidden">
        <div class="square-header">
          Name of Item 2
          <div class="close-btn">&times;</div>
        </div>
        <div class="square-content">
          <iframe src="b.html" frameborder="0" style="pointer-events: auto;"></iframe>
        </div>
      </div>
      <!-- Square 3 -->
      <div id="square3" class="square hidden">
        <div class="square-header">
          Name of Item 3
          <div class="close-btn">&times;</div>
        </div>
        <div class="square-content">
          <iframe src="c.html" frameborder="0" style="pointer-events: auto;"></iframe>
        </div>
      </div>
      <!-- Square 4 -->
      <div id="square4" class="square hidden">
        <div class="square-header">
          Name of Item 4
          <div class="close-btn">&times;</div>
        </div>
        <div class="square-content">
          <iframe src="d.html" frameborder="0" style="pointer-events: auto;"></iframe>
        </div>
      </div>
    </div>


    
    <script src="js/script.js"></script>
  </body>
</html>
/* Remove body margins and center content vertically and horizontally */
body, html {
    background-color: #3a3a3a;
    margin: 0;
    overflow: hidden;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

/* Define the boundary container for draggable squares */
#boundary {
    width: 1920px;
    height: 1080px;
    border: 2px solid #333;
    position: relative;
}

/* Style for draggable squares */
.square {
    width: 480px;
    height: 1080px;
    position: absolute;
    user-select: none;
    transform-origin: center;
    background-color: white;
    overflow: hidden;
    z-index: 500;
}

/* Content inside each square (scrollable if necessary) */
.square-content {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
}

/* Make iframes fit square dimensions and remove borders */
.square iframe {
    width: 100%;
    height: 100%;
    border: none;
}

/* Individual styling for each square with unique colors and positions */
#square1 {
    border: 2px solid red;
    left: 50px;
    top: 50px;
    border-top: 30px solid #FF7F7F;
}
#square2 {
    border: 2px solid red;
    left: 300px;
    top: 50px;
    border-top: 30px solid #ADD8E6;
}
#square3 {
    border: 2px solid red;
    left: 50px;
    top: 300px;
    border-top: 30px solid #90EE90;
}
#square4 {
    border: 2px solid blue;
    left: 50px;
    top: 50px;
    border-top: 30px solid #ADD8E6;
}

/* Close button styling */
.close-btn {
    position: absolute;
    top: 5px;
    right: 5px;
    background: white;
    border: 1px solid #333;
    border-radius: 50%;
    width: 20px;
    height: 20px;
    line-height: 18px;
    text-align: center;
    cursor: pointer;
    font-family: Arial, sans-serif;
    font-size: 12px;
    z-index: 500;
    touch-action: none;
}

/* Styling for the show buttons container */
.show-buttons {
    position: absolute;
    top: 0%;             /* Position from the top */
    left: 50%;             /* Center horizontally */
    transform: translateX(-50%); /* Adjust to truly center */
    z-index: 1;
    display: flex;         /* Use flexbox for alignment */
}

/* Styling for each show button */
.show-btn {
    width: 50px;           /* Set a fixed width */
    height: 50px;          /* Set a fixed height */
    margin: 5px;           /* Space between circles */
    border-radius: 50%;    /* Make it circular */
    cursor: pointer;        /* Pointer cursor on hover */
    touch-action: none;     /* Disable touch actions */
}

/* Specific styles for each show button */
#circle1 { 
    width: 480px;          /* Specific width */
    height: 1080px;        /* Specific height */
    border-radius: 0;      /* Ensure square shape */
    background-color: red; 
}
#circle2 { 
    width: 480px;          /* Specific width */
    height: 1080px;        /* Specific height */
    border-radius: 0;      /* Ensure square shape */
    background-color: blue; 
}
#circle3 {
    width: 480px;          /* Specific width */
    height: 1080px;        /* Specific height */
    border-radius: 0;      /* Ensure square shape */
    background-color: green; 
}
#circle4 { 
    width: 480px;          /* Specific width */
    height: 1080px;        /* Specific height */
    border-radius: 0;      /* Ensure square shape */
    background-color: yellow; 
}

/* Hide buttons by default */
.hidden {
    display: none;
}

/* Styles for squares in scroll mode */
.square.scroll-mode {
    touch-action: pan-y pinch-zoom;
}

/* Styles for squares not in scroll mode */
.square:not(.scroll-mode) {
    touch-action: none;
}

/* Ensure iframe is interactive */
.square iframe {
    pointer-events: auto;
}

/* Disable interactions when not scrolling */
.square:not(.scroll-mode) iframe {
    pointer-events: none;
}

/* Allow interaction when in scroll mode */
.square.scroll-mode iframe {
    pointer-events: auto;
}
$(document).ready(function () {
    // Object to store transformation states (position, scale, rotation) for each square
    const transformStates = {};

    // Boundary element where squares are confined
    const boundary = document.getElementById('boundary');

    // Variables for touch-based scrolling in square content
    let touchStartY = 0;
    let scrolling = false;

    // Variables for tracking click vs drag
    let isDragging = false;
    let dragStartTime = 0;
    const DRAG_THRESHOLD = 10; // Pixel movement threshold
    const DRAG_TIME_THRESHOLD = 200; // Milliseconds

    // Track the highest z-index dynamically
    let highestZIndex = 1000;

    // Prevent default behavior for touch events on buttons to avoid accidental gestures
    $('.show-btn, .close-btn').on('touchstart touchmove touchend', function (e) {
        e.preventDefault(); // Prevent default touch behavior
    });

    // Disable dragging for the show-buttons (circles)
    interact('.show-btn').unset();

    // Explicitly ensure show-buttons cannot be moved
    $('.show-btn').on('mousedown touchstart', function (e) {
        e.stopPropagation(); // Stop event propagation to prevent dragging
    });

    // Function to set up interactions for draggable and gesturable elements
    function setupInteractions(element) {
        interact(element)
            .draggable({
                inertia: true, // Enable inertia for smooth dragging
                modifiers: [
                    interact.modifiers.restrict({
                        restriction: boundary, // Restrict dragging within boundary
                        elementRect: { top: 0, left: 0, bottom: 1, right: 1 },
                    }),
                ],
                listeners: {
                    start(event) {
                        bringToFrontIfNeeded(event.target); // Bring square to the front if needed
                    },
                    move: dragMoveListener, // Handle drag movements
                    end(event) {
                        // Retain z-index on release
                        const state = transformStates[event.target.id];
                        state.scale = state.currentScale;
                    },
                },
            })
            .gesturable({
                listeners: {
                    start(event) {
                        // Initialize the starting angle and scale for gestures
                        const state = transformStates[event.target.id];
                        state.startAngle = state.angle - event.angle;
                        state.startScale = state.currentScale;
                    },
                    move(event) {
                        // Update the scale and angle based on gesture movements
                        const state = transformStates[event.target.id];
                        const newScale = state.startScale * event.scale;
                        state.currentScale = Math.max(0.5, Math.min(1.5, newScale));

                        const interpolationFactor = 0.1;
                        state.scale = state.scale + (state.currentScale - state.scale) * interpolationFactor;
                        state.angle = state.startAngle + event.angle;

                        updateElementTransform(event.target); // Apply transformations
                    },
                    end(event) {
                        // Finalize the scale after gesture ends
                        const state = transformStates[event.target.id];
                        state.scale = state.currentScale;
                    },
                },
            });

        // Add touch/click listener to bring tapped squares to the front
        $(element).on('mousedown touchstart', function (e) {
            if (!isDragging) {
                bringToFrontIfNeeded(element); // Bring to front if not dragging
            }
        });

        const $content = $(element).find('.square-content');

        // Handle touch-based scrolling within square content
        $content.on('touchmove', function (e) {
            const touchY = e.originalEvent.touches[0].clientY;
            const deltaY = touchStartY - touchY;
            if (!scrolling && Math.abs(deltaY) > 10) {
                scrolling = true;
                $(element).addClass('scroll-mode'); // Enable scroll mode
                interact(element).unset(); // Disable interactions during scroll
            }

            if (scrolling) {
                e.stopPropagation(); // Stop event propagation
                this.scrollTop += deltaY; // Scroll content
                touchStartY = touchY; // Update touch start position
            }
        });

        // Reset scrolling state on touch end
        $content.on('touchend', function () {
            scrolling = false;
            $(element).removeClass('scroll-mode'); // Disable scroll mode
            setTimeout(() => setupInteractions(element), 100); // Re-enable interactions
        });
    }

    // Function to set up handlers for each circle button
    function setupCircleHandlers(circleId, squareIds) {
        $(`#${circleId}`).on("mousedown touchstart", function (e) {
            isDragging = false;
            dragStartTime = Date.now(); // Record start time

            const startX = e.type === "mousedown" ? e.pageX : e.originalEvent.touches[0].pageX;
            const startY = e.type === "mousedown" ? e.pageY : e.originalEvent.touches[0].pageY;

            $(document).on("mousemove touchmove", function (moveEvent) {
                const currentX = moveEvent.type === "mousemove" ? moveEvent.pageX : moveEvent.originalEvent.touches[0].pageX;
                const currentY = moveEvent.type === "mousemove" ? moveEvent.pageY : moveEvent.originalEvent.touches[0].pageY;

                if (Math.abs(currentX - startX) > DRAG_THRESHOLD || Math.abs(currentY - startY) > DRAG_THRESHOLD) {
                    isDragging = true; // Mark as dragging if movement exceeds threshold
                }
            });

            $(document).on("mouseup touchend", function () {
                $(document).off("mousemove touchmove");
                $(document).off("mouseup touchend");

                if (!isDragging && Date.now() - dragStartTime < DRAG_TIME_THRESHOLD) {
                    positionSquare(circleId, squareIds[0]); // Position square if not dragging
                }
            });
        });
    }

    // Function to position a square based on the corresponding circle button
    function positionSquare(circleId, squareId) {
        const circlePosition = $(`#${circleId}`).offset();
        const top = circlePosition.top;
        const left = circlePosition.left;

        const $square = $(`#${squareId}`);
        $square.css({
            top: top + 'px',
            left: left + 'px',
            transform: 'translate(0px, 0px) scale(1) rotate(0deg)'
        }).removeClass('hidden').show(); // Show and position square

        // Reset transform state when repositioned
        transformStates[squareId] = {
            x: 0,
            y: 0,
            scale: 1,
            angle: 0,
            currentScale: 1,
        };

        // Reinitialize interactions to ensure proper dragging
        setupInteractions($square[0]);

        // Reset iframe content to default
        const iframe = $square.find('iframe');
        if (iframe.length > 0) {
            const defaultSrc = iframe.attr('data-default-src');
            if (defaultSrc) {
                iframe.attr('src', defaultSrc);
            }
        }

        // Bring the square to the front
        bringToFrontIfNeeded($square[0]);
    }

    // Function to bring an element to the front by updating its z-index
    function bringToFrontIfNeeded(element) {
        const currentZIndex = parseInt($(element).css('z-index'), 10);
        if (currentZIndex < highestZIndex) {
            highestZIndex += 1; // Increment the highest z-index
            $(element).css('z-index', highestZIndex); // Assign the new z-index
        }
    }

    // Listener for drag movements
    function dragMoveListener(event) {
        const target = event.target;
        const state = transformStates[target.id];

        state.x += event.dx; // Update x position
        state.y += event.dy; // Update y position

        updateElementTransform(target); // Apply transformations
    }

    // Function to update the transform property of an element
    function updateElementTransform(element) {
        const state = transformStates[element.id];
        element.style.transform = `translate(${state.x}px, ${state.y}px) scale(${state.scale}) rotate(${state.angle}deg)`; // Apply CSS transform
    }

    // Set up handlers for each circle button
    setupCircleHandlers("circle1", ["square1"]);
    setupCircleHandlers("circle2", ["square2"]);
    setupCircleHandlers("circle3", ["square3"]);
    setupCircleHandlers("circle4", ["square4"]);

    // Initialize interactions for each square
    $(".square").each(function () {
        const element = this;
        const $square = $(element);

        // Initialize transformation state for this square
        transformStates[element.id] = {
            x: 0,
            y: 0,
            scale: 1,
            angle: 0,
            currentScale: 1,
        };

        // Store default iframe source
        const iframe = $square.find('iframe');
        if (iframe.length > 0) {
            iframe.attr('data-default-src', iframe.attr('src'));
        }

        setupInteractions(element); // Set up interactions for the square
    });

    // Close button handler to hide the square
    $(".close-btn").on("click touchend", function (e) {
        e.stopPropagation();
        e.preventDefault();
        const square = $(this).closest(".square");
        const squareId = square[0].id;
        
        // Force reset any ongoing interaction
        interact(square[0]).unset();
        
        // Reset transform state
        transformStates[squareId] = {
            x: 0,
            y: 0,
            scale: 1,
            angle: 0,
            currentScale: 1,
        };
        
        // Reset the square's transform
        square.css('transform', 'translate(0px, 0px) scale(1) rotate(0deg)');
        
        // Hide the square
        square.addClass("hidden").hide();
        
        // Simulate touchend/mouseup to reset any ongoing drag
        $(document).trigger('touchend');
        $(document).trigger('mouseup');
        
        // Re-initialize interactions after a brief delay
        setTimeout(() => {
            setupInteractions(square[0]);
        }, 100);
    });
});

Enable web browser’s microphone access automatically

  const checkMicrophoneAccess = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      stream.getTracks().forEach((track) => track.stop());
      localStorage.setItem("isMicAllowed", true);
    } catch (error) {
     console.log(error)
    }
  };

while trying to turn on automatically, getting error of “NotAllowedError: Permission denied”

How can I enable microphone access in a browser automatically from a React.js frontend application without showing the browser’s default pop-up? For example, the Google Meet web application uses its own custom pop-up to manage microphone access across all browsers like Chrome, Safari etc. I need a similar flow, ideally a silent enabling process.

How to solve scrollTrigger update problem with code used from gsap

There is a problem in the following code.
When the user refreshes in the middle or bottom of the page, transform: translate(0px, 600px); has the hero class, but when the user scrolls from the bottom to the top, transform: translate(0px, 600px);
It will not update and will transform: translate(0px, 0px); does not convert In fact, the hero class cannot return to its initial values.
This mode only occurs when the user refreshes in the middle or bottom of the page.
It’s like gsap can’t tell if the user is at the beginning or the end of the page.
To implement it, I got help from the following site (https://www.greatwalstead.co.uk/). But this site has exactly the same problem.
Is there a way to solve it?

Initial values:

translate: none;
rotate: none;
scale: none;
left: 0px;
top: 0.001px;
margin: 0px;
max-width: 654px;
width: 654px;
max-height: 545px;
height: 545px;
padding: 0px;
box-sizing: border-box;
position: fixed;
transform: translate(0px, 0px);

Secondary values ​​after running gsap:

translate: none;
rotate: none;
scale: none;
inset: 0px auto auto 0px;
margin: 0px;
max-width: 654px;
width: 654px;
max-height: 545px;
height: 545px;
padding: 0px;
transform: translate(0px, 600.001px);

document.addEventListener("DOMContentLoaded", function (e) {
  gsap.registerPlugin(ScrollTrigger);
  var t,
    a = gsap.matchMedia();
  function n(e) {
    var t = arguments.length > 1 && void 0 !== arguments[1] && arguments[1],
      a = "power3";
    e.querySelectorAll(".gs-animate").forEach(function (e) {
      var n, r,i, o, l,s,c, d,g, u,
        m = null !== (n = e.dataset.animX) && void 0 !== n && n,
        p = null !== (r = e.dataset.animY) && void 0 !== r && r,
        v = null !== (i = e.dataset.animRotate) && void 0 !== i ? i : 0,
        y =
          (e.dataset.animOpacity,
          null !== (o = e.dataset.animScale) && void 0 !== o ? o : 1),
        f = e.dataset.animOpacity ? document.querySelector(f) : e,
        h = null !== (l = e.dataset.animScrub) && void 0 !== l && l,
        S = null !== (s = e.dataset.animStart) && void 0 !== s ? s : "top 75%",
        A = null !== (c = e.dataset.animEnd) && void 0 !== c ? c : "top 25%",
        T = null !== (d = e.dataset.animDuration) && void 0 !== d ? d : 1.5,
        j = null !== (g = e.dataset.animDelay) && void 0 !== g ? g : 0,
        q = null !== (u = e.dataset.animType) && void 0 !== u ? u : "from",
        x = {
          xPercent: m,
          yPercent: p,
          rotate: v,
          scale: y,
          ease: a,
          scrollTrigger: {
            trigger: f,
            start: S,
            end: A,
            scrub: h,
            containerAnimation: t,
          },
          delay: j,
          duration: T,
        };
      "to" == q ? gsap.to(e, x) : gsap.from(e, x);
    });
  }
  (t = document.querySelector(".gs-section-intro")),
    setTimeout(function () {
      gsap.to(".js-intro-line-wrap", { scaleX: 1, delay: 1.5 });
    }, 500),
    gsap.set(".js-intro-line-scroll", { autoAlpha: 0, scaleY: 0 }),
    gsap
      .timeline({
        scrollTrigger: {
          trigger: t,
          pin: !0,
          start: "top top",
          end: "+=600",
          scrub: 1,
        },
      })
      .to(".js-intro-line", { scaleX: 4, immediateRender: !1 })
      .set(".js-intro-line-scroll", { autoAlpha: 1, immediateRender: !1 })
      .set(".js-intro-line", { autoAlpha: 0, immediateRender: !1 })
      .to(".js-intro-line-scroll", { scaleY: 1 }),
    gsap.utils.toArray(".gs-section").forEach(function (e) {
      n(e);
    });
});
.hero {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--primary-color);
    background-image: url(../img/bg-green.jpg);
    background-repeat: no-repeat;
    background-size: cover;
    color: var(--baseLight-color);
    position: relative;
    overflow: hidden;
    margin-block-end: 0 !important;
    transform: translate(0px, 0px);
}

.hero--inner {
    width: 100%;
    position: relative;
    margin-block-end: 3rem;
}

.hero--content {
    width: 50%;
    margin-inline: auto;
}

.hero--content>picture>img {
    margin-inline: auto;
    display: block;
    width: 140px;
    height: 180px;
}


/* for animate line */
.hero--line__wrap {
    width: 100vw;
    height: 50vh;
    z-index: 10;
    position: absolute;
    left: 0;
    right: 0;
    top: 110%;
    overflow: hidden;
    transform-origin: center;
}

.hero--line {
    position: absolute;
    margin-left: auto;
    margin-right: auto;
    top: 0;
    left: 0;
    right: 0;
    display: block;
    height: 9px;
    width: 40%;
    overflow: hidden;
    transform-origin: center;
}

.hero--line:after {
    animation: moveline 3s linear infinite;
    background-image: url(../img/intro-line-sm.svg);
    background-repeat: repeat-x;
    background-size: 50% 100%;
    content: "";
    display: block;
    height: 100%;
    width: 200%;
}

.hero--line__bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
    transform-origin: top;
    background-image: url(../img/intro-line.svg);
    background-position: top;
    background-size: 100% 100%;
    opacity: 0;
    visibility: hidden;
}

/* hero scroll */
.hero--scroll {
    position: absolute;
    left: 50%;
    bottom: 1rem;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
}

.hero--scroll__border {
    display: block;
    padding: .5rem;
    width: 2.5rem;
    height: 4rem;
    border: 2px solid #f6be56;
    border-radius: var(--border-radius30);
}


.hero--scroll__circle {
    display: block;
    margin-inline: auto;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background-color: #5f9893;
    animation: mouse 1000ms linear infinite;
    transition-timing-function: cubic-bezier(.33, 1, .68, 1);
}

.introduction {
    position: relative;
    padding-bottom: 10rem;
    background-color: var(--secondary-color);
}

.introduction::before {
    position: absolute;
    content: "";
    left: 20px;
    top: -100px;
    width: 250px;
    height: 182px;
    border: 1px solid green;
    background-image: url(/assets/img/vector-leaf.svg);
    background-repeat: no-repeat;
}

/* introduction--item */
ul li.introduction--item {
    overflow: hidden;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-block: 6rem;
}

ul li.introduction--item:nth-child(odd) {
    flex-direction: row;
}

ul li.introduction--item:nth-child(even) {
    flex-direction: row-reverse;
}

/* introduction--text */
.introduction--text {
    color: var(--text-color);
    position: relative;
    width: 45%;
    padding-inline: 5rem 0;
    position: relative;
}

.introduction--text .introduction--title {
    display: flex;
    align-items: center;
}

.introduction--text .introduction--title h3 {
    overflow: hidden;
    display: inline-block;
    overflow: hidden;
    line-height: var(--line-height140);
}

.introduction--text .introduction--title__line{
    width:85%;
    position: absolute;
    top: 20%;
    overflow: hidden;
}

.introduction--text .introduction--title__line::after {
    animation: moveline 3s linear infinite;
    background-image: url(../img/intro-line-sm-white.svg);
    background-repeat: repeat-x;
    background-size: 50% 100%;
    content: "";
    display: block;
    height: 9px;
    width: 200%;
}


.introduction--text>div {
    margin-block: 2.5rem 2rem;
    text-align: justify;
    font-weight: 500;
    line-height: var(--line-height140);
}

/* .introduction--video */
.introduction--video {
    width: 55%;
    position: relative;
}

ul li.introduction--item:nth-child(odd) .introduction--video {
    left: -10%;
}

ul li.introduction--item:nth-child(even) .introduction--video {
    right: -10%;
}

.introduction--video>.introduction--video__inner {
    position: relative;
    padding: 1.5rem;
}

ul li.introduction--item:nth-child(odd) .introduction--video>.introduction--video__inner {
    transform: rotate(-13deg);
}

ul li.introduction--item:nth-child(even) .introduction--video>.introduction--video__inner {
    transform: rotate(13deg);
}

ul li.introduction--item .introduction--video>.introduction--video__inner::after {
    position: absolute;
    content: "";
    width: 100%;
    height: 100%;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    background-image: url(/assets/img/border-video-white.svg);
    background-repeat: no-repeat;
    background-size: contain;
    z-index: -1;
}


.introduction--video>.introduction--video__inner video {
    border-radius: 50px;
    width: 100%;
    height: 100%;
}

.introduction--video>.introduction--video__inner .play--video {
    position: absolute;
    top: 82%;
    left: 50%;
    cursor: pointer;
    transform: translateX(-50%) rotate(10deg);
}

/* introduction--gif */
ul li.introduction--item .introduction--gif {
    display: block;
}

ul li.introduction--item:nth-child(odd) .introduction--gif {
    margin-inline-start: auto;
    margin-inline-end: 2rem;
}

ul li.introduction--item:nth-child(even) .introduction--gif {
    margin-inline-start: auto;
    margin-inline-end: 5rem;
}

.introduction--arrow {
    position: absolute;
    right: 45%;
    width: 10rem;
    transform-origin: right;
    bottom: 2rem;
}

.introduction--arrow>img {
    width: 100%;
}

/* vectors */
ul li.introduction--item:nth-child(1) .introduction--text::before {
    position: absolute;
    content: "";
    bottom: -50px;
    left: 50%;
    width: 90px;
    height: 93px;
    background-repeat: no-repeat;
    background-image: url(../img/vector-diamond.svg);
}

ul li.introduction--item:nth-child(1) .introduction--video::before {
    position: absolute;
    content: "";
    bottom: -20px;
    left: 75%;
    width: 214px;
    height: 100px;
    background-repeat: no-repeat;
    background-image: url(../img/vector-line.svg);
}

ul li.introduction--item:nth-child(2)::before {
    position: absolute;
    content: "";
    top: 0;
    right: -5px;
    width: 84px;
    height: 85px;
    background-repeat: no-repeat;
    background-image: url(../img/vector-star.svg);
}

ul li.introduction--item:nth-child(3)::before {
    position: absolute;
    content: "";
    top: 10px;
    left: 30px;
    width: 110px;
    height: 99px;
    background-repeat: no-repeat;
    background-image: url(../img/vector-circle-untidy.svg);
}
      <section class="gs-section-intro hero">
        <div class="hero--inner">
          <!-- hero content -->
          <div class="hero--content">
            <picture>
              <img src="/assets/img/logo-hero.svg" alt="" />
            </picture>
          </div>

          <!-- for animate line -->
          <div class="hero--line__wrap js-intro-line-wrap">
            <span class="hero--line js-intro-line"></span>
            <span class="hero--line__bg js-intro-line-scroll"></span>
          </div>
        </div>

        <!-- scroll down -->
        <div class="hero--scroll">
          <span class="hero--scroll__border">
            <span class="hero--scroll__circle"></span>
          </span>
        </div>

      </section>
      
      <section class="introduction gs-section" data-gs-type="items">
        <ul>
          <li class="introduction--item">
            <div
              class="introduction--text gs-animate"
              data-anim-x="150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--title">
                <img src="/assets/img/vector-title-white.svg" alt="" />
                </div>
              <span class="introduction--title__line"></span>

              <div>
...
              </div>
              <img
                src="/assets/img/GWS_hockey-girl.png"
                alt=""
                class="introduction--gif"
              />
            </div>

            <div
              class="introduction--video gs-animate"
              data-anim-x="-150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--video__inner">
               ...
              </div>              
            </div>
          </li>

          <li class="introduction--item">
            <div
              class="introduction--text gs-animate"
              data-anim-x="-150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--title">
                <img src="/assets/img/vector-title-white.svg" alt="" />
              </div>
              <span class="introduction--title__line"></span>

              <div>...</div>

              <a href="" class="btn btn--fill">درباره درخت دانش</a>

              <img
                src="/assets/img/GWS_football-boy.png"
                alt=""
                class="introduction--gif"
              />
            </div>

            <div
              class="introduction--video gs-animate"
              data-anim-x="150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--video__inner">...</div>
            </div>

            <div
              class="introduction--arrow gs-animate"
              data-anim-scale="0"
              data-anim-start="top bottom"
              data-anim-scrub="1"
            >
              <img
                src="/assets/img/icons/arrow-white-left.svg"
                alt="Arrow"
                loading="lazy"
              />
            </div>
          </li>

          <li class="introduction--item">
            <div
              class="introduction--text gs-animate"
              data-anim-x="150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--title">
                <img src="/assets/img/vector-title-white.svg" alt="" />
              </div>

              <span class="introduction--title__line"></span>

              <div> ...</div>

              <img
                src="/assets/img/kindergarten.png"
                alt=""
                class="introduction--gif"
              />
            </div>

            <div
              class="introduction--video gs-animate"
              data-anim-x="-150"
              data-anim-scrub="1"
              data-anim-start="top bottom"
            >
              <div class="introduction--video__inner"> ...</div>
            </div>
          </li>
        </ul>
      </section>

How to improve EventEmitter event notification function?

I wrote the following EventEmitter class, and it works great so far. But today I came across a scenario where if event listeners are created and destroyed frequently, and the off function is not used to remove the listener, there will be potential performance issues. How can I optimize this problem? Can anyone give me some ideas?

class EventEmitter {

    private listeners: Record<string, [Set<Function>, Set<Function>]> = {};

    existsEvent(eventName: string | number) {
        return !!this.listeners[eventName];
    }

    on(eventName: string | number, listener: Function) {
        if (!this.listeners[eventName]) {
            this.listeners[eventName] = [new Set(), new Set()];
        }
        this.listeners[eventName][0].add(listener);
    }

    once(eventName: string | number, listener: Function) {
        if (!this.listeners[eventName]) {
            this.listeners[eventName] = [new Set(), new Set()];
        }
        this.listeners[eventName][1].add(listener);
    }

    emit(eventName: string | number, ...args: any[]) {
        if (!this.listeners[eventName]) {
            return
        }

        this.listeners[eventName][0].forEach((listener: Function) => {
            listener(...args);
        });
        
        this.listeners[eventName][1].forEach((listener: Function) => {
            listener(...args);
        });
        this.listeners[eventName][1].clear();
    }

    off(eventName: string | number, listener: Function) {
        if (!this.listeners[eventName]) {
            return;
        }
        if (typeof listener === 'function') {
            this.listeners[eventName][0].delete(listener);
            this.listeners[eventName][1].delete(listener);
        } else {
            this.listeners[eventName][0].clear();
            this.listeners[eventName][1].clear();
        }
    }
}

export const eventEmitter = new EventEmitter();

xterm.js generates full row of characters before writeln output

While experimenting with xterm.js I made the follwing fiddle.

const divTerm = document.querySelector("#term");

const xterm = new Terminal({
cols: 100,
rows: 80,
cursorBlink: true
});

xterm.open(divTerm);
xterm.writeln("Hello World");

jsfiddle output

As you can see, I am only writing “Hello World” to my terminal.
However, the “Hello World” String is only displayed on the second row of the terminal. The row above is filled with ‘d’s, and I don’t understand where they are coming from.

The weird thing is, when I did the same thing on my test site instead on jsfiddle, the behaviour was the same, except the first row was filled with ‘s’ characters.

What’s going on here?

How can we change a pie-doughnut chart into an arrow ring chart?

Currently I’ve below design in echarts apache

enter image description here

and i want as below pie doughnut chart with arrows inside

enter image description here

Here is my current code:

var data = [
  { name: 'Label 1 n', value: 16.67, },
  { name: 'Label 2 n', value: 16.67, },
  { name: 'Label 3 n', value: 16.67, },
  { name: 'Label 4 n', value: 16.67, },
  { name: 'Label 5 n', value: 16.67, },
  { name: 'Label 6 n', value: 16.67, },
];

option = {
  series: {
    type: "pie",
    radius: ['50%', '70%'],
    left:"center",
    data: data,
  }
};

visibilitychange not firing on app switch on macOS

visibilitychange event is not fired when switching between apps when the window is not maximized.

I have pasted this code block in the console. While this is working on tab change, it is not triggering on app switch ( not maximised )

window.addEventListener('visibilitychange', () => console.log('triggered'), {capture:true});