Zoom Meeting SDK – Init invalid parameter !!! Error

I’m trying to develop an HTML and JavaScript website that can join a meeting room using Zoom Meeting SDK. I’m new to Zoom Meeting SDK so I’m quite confused on why I keep getting the error ‘Init invalid parameter !!! ‘ from Zoom. I’m not sure how do I get the detailed error. For now I’m using front-end only. Below is the error:

Error Description

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kiosk Video Call</title>

    <!-- Required Zoom CSS -->
    <link type="text/css" rel="stylesheet" href="https://source.zoom.us/2.15.0/css/bootstrap.css" />
    <link type="text/css" rel="stylesheet" href="https://source.zoom.us/2.15.0/css/react-select.css" />

    <style>
        #zmmtg-root {
            display: none; /* Initially hidden until Zoom starts */
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1000;
        }
        .controls {
            position: relative;
            z-index: 1001;
            text-align: center;
        }
        .btn {
            background-color: #007bff;
            border: none;
            color: white;
            padding: 15px 32px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 20px;
        }
        .input-field {
            margin: 10px auto;
            padding: 8px;
            width: 80%;
            max-width: 400px;
            display: block;
        }
    </style>
</head>
<body>
    <div id="zmmtg-root"></div>

    <div class="controls">
        <h1>Kiosk Video Call</h1>
        <input id="meetingNumber" class="input-field" type="text" placeholder="Enter Meeting Number">
        <input id="passWord" class="input-field" type="text" placeholder="Enter Meeting Password">
        <input id="userName" class="input-field" type="text" placeholder="Enter Your Name" value="Kiosk Client">
        <input id="sdkKey" class="input-field" type="text" placeholder="Enter SDK Key">
        <input id="signature" class="input-field" type="text" placeholder="Enter Signature">
        <input id="userEmail" class="input-field" type="text" placeholder="Enter Your Email (optional)">
        <button id="startSession" class="btn">Start Session</button>
    </div>

    
    
</body>
<!-- Add Lodash before Zoom SDK -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

<!-- Load Zoom SDK Dependencies -->
<script src="https://source.zoom.us/2.15.0/lib/vendor/react.min.js"></script>
<script src="https://source.zoom.us/2.15.0/lib/vendor/react-dom.min.js"></script>
<script src="https://source.zoom.us/2.15.0/lib/vendor/redux.min.js"></script>
<script src="https://source.zoom.us/2.15.0/lib/vendor/redux-thunk.min.js"></script>
<script src="https://source.zoom.us/2.15.0/zoom-meeting-2.15.0.min.js"></script>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        if (typeof ZoomMtg === "undefined") {
            console.error("Zoom SDK failed to load.");
            return;
        }

        console.log("Zoom SDK Loaded");

        ZoomMtg.setZoomJSLib('https://source.zoom.us/2.15.0/lib', '/av');
        ZoomMtg.preLoadWasm();
        ZoomMtg.prepareWebSDK();

        document.getElementById("startSession").addEventListener("click", function () {
            const meetingConfig = {
                meetingNumber: document.getElementById("meetingNumber").value.trim(),
                passWord: document.getElementById("passWord").value.trim(),
                userName: document.getElementById("userName").value.trim(),
                sdkKey: document.getElementById("sdkKey").value.trim(),
                signature: document.getElementById("signature").value.trim(),
                leaveUrl: window.location.origin + window.location.pathname,
                role: 0
            };

            if (!meetingConfig.meetingNumber || !meetingConfig.passWord || 
                !meetingConfig.userName || !meetingConfig.sdkKey || !meetingConfig.signature) {
                alert("Please fill in all required fields.");
                return;
            }

            console.log("Initializing with signature:", meetingConfig.signature);

            ZoomMtg.init({
                leaveUrl: meetingConfig.leaveUrl,
                patchJsMedia: true,
                success: function () {
                    ZoomMtg.join({
                        signature: meetingConfig.signature,
                        sdkKey: meetingConfig.sdkKey,
                        meetingNumber: meetingConfig.meetingNumber,
                        userName: meetingConfig.userName,
                        passWord: meetingConfig.passWord,
                        success: function () {
                            console.log("Joined Meeting Successfully!");
                            document.querySelector('.controls').style.display = 'none';
                            document.getElementById('zmmtg-root').style.display = 'block';
                        },
                        error: function (error) {
                            console.error("Failed to join meeting:", error);
                            alert("Failed to join meeting: " + (error.errorMessage || JSON.stringify(error)));
                        }
                    });
                },
                error: function (error) {
                    console.error("Failed to initialize:", error);
                    alert("Failed to initialize: " + (error.errorMessage || JSON.stringify(error)));
                }
            });
        });
    });
</script>
</html>

How to fix Uncaught ReferenceError: TableauEventType is not defined when embedded Tableau cloud

This is my first time to use Tableau and I want to embed it into Odoo 18 with custom module

This is my code for embed the Tableau on odoo 18 using javascript, but when want to catch the Authorization errors, I got Uncaught ReferenceError: TableauEventType is not defined

<?xml version="1.0" encoding="UTF-8" ?>
<template>
    <t t-name="tsel_tableau.TableauDashboard">
        <t t-set="title">Tableau Dashboard</t>
        <div class="container mt-4">
            <script type="module" src="https://public.tableau.com/javascripts/api/tableau.embedding.3.latest.min.js"></script>
            <script type="module" src="https://embedding.tableauusercontent.com/tableau.embedding.3.1.0.min.js"></script>

            <tableau-viz id="tableauViz"
                src="https://prod-apsoutheast-b.online.tableau.com/t/views/Dashboard1"
                token="MY_TOKEN"
                onVizLoadError="handleLoadError">
            </tableau-viz>

            <script>
                function viz(){
                    console.log(1);
                    const tableauViz = document.getElementById('tableauViz');
                    tableauViz.addEventListener(TableauEventType.VizLoadError, handleLoadError);

                    function handleLoadError(e){
                        console.log(2);
                    }
                }

                viz();
            </script>

        </div>
    </t>
</template>

This is i got the error

enter image description here

I have read Tableau documentation, and follow the instruction but still not work.
This is link doc : https://embedding.tableauusercontent.com/preview/getting-started-v3.html#improved-error-handling-for-connected-apps

How do I add a loading screen to my website?

from flask import Flask, request, render_template
from Game_Review import GameReview
import os

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/review',methods = ['POST'])
def review():
    username = request.form.get('username')
    link = request.form.get('game_link')
    #print(username)
    try:
        review = GameReview(username)
        results = review.gameReview(link)
        #print(results)
    except Exception:
        return render_template('error.html')
    evals = results.pop()
    positions = results.pop()
    return render_template('review.html', results = results, positions = positions, evals = evals)

if __name__ == "__main__":
    #port = int(os.environ.get('PORT', 5000))
    #app.run(host='0.0.0.0', port=port)
    app.run(debug=True)

I want to add I loading screen to my website (Flask python, Javascript, and HTML), but I am not sure how to add one or which file to put the loading screen code into. The webpage takes a really long time to finish loading the results in the try block so I want the loading screen to appear while the try block is being run in the background.

I already have an animated GIF I would like to use but I don’t know which HTML file I am supposed to add the loading screen code to, since index.html is the homepage and redirects the user to the /review page where the try block is run and review.html is returned after the results have already been computed. Additionally, I need help writing the HTML code required to display the GIF as the loading screen.

django app: how to read a bit of html in javascript

I have a django app that has an design issue I’d like to fix: a script wants to inject a piece of html that contain django tags.

What we have now works, but has some unfortunate aspects:

  • Save the script as an html snippet that is nothing but a script: the first line is <script> and the last line closes that script.
  • Pages which use this script import it just like any html snippet.
  • This script sets a local variable to the html bit, as a long string, then finds where the html bit is wanted, and calls insertAdjacentHTML to insert it.

So the snippet is an html file that contains nothing but a script, and that script in turn contains hard-coded html as a string. This is confusing to read and prevents our javascript and html linters from working on this snippet. But the code works, and the script can be used by any page by simply importing the one snippet.

Is there a cleaner solution? My ideal solution would:

  • Store the html bit in an html file.
  • Store the script in a .js file.
  • Allow pages that use the script to just load the script.

I have tried the following, none of which work, and none which meet all of these goals:

1: Make the html snippet into a static javascript file, while retaining the hard-coded html inside. Load this as any static script.

Unfortunately this fails, because the django tags are not replaced. It also still has hard-coded html in a javascript file.

2: Save the html as a file and read it using an fs call. Apparently fs calls aren’t available in our environment and I’m not sure it’s worth adding the necessary extra library.

3: Use the domain model to get at the html bit, as follows:

  • Move the html bit to a new html snippet, putting it in a new hidden div with a unique id.
  • Have the page that loads the script include the html snippet first, at the end of the {% block content %} section and also have it include the html script snippet (if I could get this working, I’d try moving it to a static js file, but one change at a time).
  • Have the script use the domain model to get the html bit’s containing div, get the html as a string using innerHTML, then delete the hidden div (to prevent the hidden html bit from messing up the the application).

This fails in unit tests in a way that shows that the new html bit is not deleted. But I will say that when I run the application locally it seems to work, and the django tags are being handled correctly. Also if I remove the “hidden” I don’t see the html bit when interacting wth the server. So I’m not really sure what’s going on. But I think that the broken tests show this solution is not fully correct.

In any case, this solution has its own issues:

  • It is risky to add unwanted html to a page (as the broken tests show).
  • It requires any page loading the script to also import the relevant html snippet.

I could change the hidden html so that it could not intefere (i.e. pick new names and then edit them in the script) but that seems ugly and error-prone.

Any ideas for a cleaner solution? I tried lots of web searching, but suspect I am not asking the question right.

insert product price into Offers schema meta tag using javascript

I am building an Offers schema in my ecommerce site and want to insert the product price into it. I verified I am getting the price from the database but can’t figure out how I can get the value into a meta itemprop / content format. What I need is the price value in the content element of the price itemprop meta:

HTML:

<span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<meta itemprop="price" content="price_goes_here">
<meta itemprop="priceCurrency" content="USD">

I’m used to php but not javascript. Sorry for my lack of knowledge. Is there a way to do this?

I’ve tried a variety of replacements and the last one I tried is :

/*convert price html to schema version*/
strhtmlprice = "<%=getCurrentAttribute('item','salespriceHTML')%>";
strhtmlprice = strhtmlprice .replace("$", "");
strhtmlprice = strhtmlprice .replace(",", "");
document.getElementById("strhtmlprice").innerHTML = strhtmlprice;

GSAP: Make hero banner stick until animation is complete

I am trying to achieve this type of animation on this website

https://the-goonies.webflow.io

gsap.registerPlugin(ScrollTrigger);

    let timeline = gsap.timeline({
      scrollTrigger: {
        trigger: '#hero-container',
        start: 'top top',
        end: '+=600px',
        scrub: true, // Smooth scrolling effect
      },
    });

    // Step 1: Logo zooms first
    timeline.to('#scrolllogo', {
      scale: 1.15,
      opacity: 1,
      duration: 1,
    });

    // Step 2: Overlay starts zooming & fades the logo
    timeline.to(
      '#scrolloverlay',
      {
        scale: 1.3,
        duration: 1,
      },
      '-=0.5',
    ); // Overlapping animation for smooth transition

    timeline.to(
      '#scrolllogo',
      {
        opacity: 0,
        duration: 0.5,
      },
      '-=0.5',
    );

    // Step 3: Background zooms last
    timeline.to('#scrollbackground', {
      scale: 1.6,
      duration: 1,
    });

    // Step 4: Fade out overlay & background after animation
    timeline.to(
      ['#scrolloverlay', '#scrollbackground'],
      {
        opacity: 0,
        duration: 1,
      },
      '-=0.5',
    );
#hero-container {
      height: 90vh;
      overflow: hidden;
          background: linear-gradient(0deg, #0f5812, #f50505);
    }

    #content {
      position: relative;
      z-index: 10;
      background-color: white;
    }

    #hero-section {
      position: relative;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }

    .hero-image,
    .hero-overlay {
      position: absolute;
      width: 100%;
      height: 100%;
      background-size: cover;
      background-position: center;
      transition: transform 0.3s ease-out, opacity 0.3s ease-out;
    }

    .hero-overlay {
      mix-blend-mode: multiply;
    }

    .hero-logo {
      position: absolute;
      z-index: 2;
      transition: transform 0.3s ease-out, opacity 0.3s ease-out;
    }

    .hero-logo img {
      display: block;
      width: 100%;
      height: auto;
      width: 150px;
    }
    .justclass{
      min-height: 1000px;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
  <div
    id='hero-container'
    class='container container--hero'
  >
    <div id='hero-section'>
      <div
        class='hero-image'
        id='scrollbackground'
        style="background-image: url('https://cdn.shopify.com/s/files/1/0489/9962/8949/files/INTRO_BENNER_Mobile_3_IMAGE_result.webp?v=1740380000"
      ></div>
      <div
        class='hero-image hero-overlay'
        id='scrolloverlay'
        style="background-image: url('https://cdn.shopify.com/s/files/1/0489/9962/8949/files/INTRO_BENNER_Desktop_2_FOLIAGE_result.webp?v=1740379992"
      ></div>
      <div class='hero-logo' id='scrolllogo'>
          <img src='https://cdn.shopify.com/s/files/1/0489/9962/8949/files/O_RIGINALS_LOGO_B_1.png?v=1739809208'
            alt='Logo'
          >
      </div>
    </div>
  </div>
  
<div class="justclass">
cxcx
</div>

I have been able to do the animation, but I want to be able to make the page static until the animation of the hero is complete then continue normal scroll. I have tried to make it work, but I end up making my page freeze completely.

Trying to create a testimonial slider where continously plays slides on aloop fromright to left

Hope some can help as its driving me nuts.

I want the test to scroll from right to left and once the thirds slides through the first slide follows behind it.

At the moment it slides all three back to the right in one go and just looks awful.

<-slider one-> <-slider 2-> <-slider 3-> then again.

Anyhelp would be handy and thanks in advance.

<style>
        .text-carousel {
            position: relative;
            width: 100%; /* Full width */
            max-width: 600px; /* Maximum width */
            margin: auto; /* Center the carousel */
            overflow: hidden; /* Hides overflow content */
            background-color: transparent; /* Transparent background */
        }

        .carousel-slides {
            display: flex; /* Use flex for sliding effect */
            transition: transform 0.5s ease-in-out; /* Smooth transition */
            width: 300%; /* Set width to accommodate three slides */
        }

        .carousel-slide {
            width: 33.33%; /* Adjust to fit one slide at a time */
            box-sizing: border-box; /* Include padding in width */
            padding: 20px; /* Space inside slides */
            text-align: center; /* Center text */
            font-size: 1.2rem; /* Font size */
            font-weight: bold; /* Bold text */
            color: #20A2D3; /* Text color */
        }
    </style>
</head>
<body>

    <div class="text-carousel">
        <div class="carousel-slides">
            <div class="carousel-slide">
                <p>“A unique recruitment, operations and finance solution that expertly supports aspiring businesses bridge their current capabilities and future potential.”</p>
            </div>
            <div class="carousel-slide">
                <p>“Genuine people that collaborate by offering a simplistic and excellent approach.”</p>
            </div>
            <div class="carousel-slide">
                <p>“Recruitment, Finance, and Operation experts on demand.”</p>
            </div>
        </div>
    </div>

    <script>
        let currentIndex = 0;
        const slides = document.querySelectorAll('.carousel-slide');
        const slidesContainer = document.querySelector('.carousel-slides');
        const totalSlides = slides.length;

        // Function to display the current slide
        function showSlide(index) {
            const newPosition = -index * (100 / totalSlides); // Move slides left based on index
            slidesContainer.style.transition = "transform 0.5s ease-in-out"; // Set transition
            slidesContainer.style.transform = `translateX(${newPosition}%)`; // Move slides left
        }

        // Function to handle the auto-scrolling
        function autoScroll() {
            currentIndex++;

            // If currentIndex goes beyond the last slide
            if (currentIndex >= totalSlides) {
                currentIndex = 0; // Reset to first slide
                slidesContainer.style.transition = "none"; // Disable transition for an instant change
                showSlide(currentIndex); // Show the first slide instantly
                
                // Allow a brief pause before transitioning again
                setTimeout(() => {
                    slidesContainer.style.transition = "transform 0.5s ease-in-out"; // Re-enable transition
                    showSlide(currentIndex); // Show the first slide
                }, 50); // Short timeout to feel the transition
            } else {
                showSlide(currentIndex);
            }
        }

        // Start auto-scrolling every 3 seconds
        setInterval(autoScroll, 3000);
        showSlide(currentIndex); // Initial display
    </script>

Image alt captions showing as ‘undefined’ on modal image

I have mutiple art galleries on my website containing lots of images, I use an onclick event to pull up a full res, uncropped version of the image and it works perfectly fine. However, I want a unique caption to pop up with each image, using the image’s alt as the caption; I’ve got the caption popping up onclick, but it shows as ‘undefined’.

I’ve tried everything I can think of and looked through countless questions, answers and comments and I just can’t seem to figure it out at all.

Here is the code:

function showFullRes(imageSrc) {
  const overlay = document.getElementById("full-res-overlay");
  const fullResImage = document.getElementById("full-res-image");
  const captionText = document.getElementById("caption");

  fullResImage.src = imageSrc;
  overlay.style.display = "block";
  captionText.innerHTML = this.alt;
}

function closeFullRes() {
  const overlay = document.getElementById("full-res-overlay");
  overlay.style.display = "none";
}
.full-res-overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.9);
  z-index: 10;
  text-align: center;
}

.full-res-image {
  max-width: 80%;
  max-height: 80%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.caption {
  font-family: 'MATRIX';
  font-size: 2vw;
  margin: auto;
  display: block;
  width: 80%;
  max-width: 700px;
  text-align: center;
  padding: 10px 0;
  height: 150px;
}

.artwork {
  cursor: pointer;
  width: 18vw;
  height: 25vw;
  max-width: 100%;
  position: relative;
}

.artwork:hover {
  opacity: 0.8;
  transform: scale(95%);
  transition: opacity 0.3s ease;
  transition: transform 0.3s ease;
}

.artwork img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.artwork-container {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
}

.gallery-container {
  width: 89%;
  max-width: 89%;
  padding: 20px;
  background-color: transparent;
  border-radius: 10px;
  text-align: center;
}
<div class="gallery-container expanded" style="width: 89%;">
  <div class="artwork-container">

    <div class="artwork" style="width: 35vw; height: 44vw;" onclick="showFullRes('https://sinniister.neocities.org/ARTWORK/VASCHA/REDVOH2.JPG')" alt="Original sketch">
      <img
              src="https://sinniister.neocities.org/ARTWORK/VASCHA/REDVOH2TN.jpeg" alt="Original Sketch"
             />
    </div>

    <div class="artwork" style="width: 35vw; height: 44vw;" onclick="showFullRes('https://sinniister.neocities.org/ARTWORK/VASCHA/VOH21.JPG')" alt="Final version">
      <img
              src="https://sinniister.neocities.org/ARTWORK/VASCHA/VOH21S.JPG" alt="Final Version"
             />
    </div>
  </div>
</div>

<div class="full-res-overlay" id="full-res-overlay" onclick="closeFullRes()">
  <img
        src=""
        alt="Full Resolution Artwork"
        class="full-res-image"
        id="full-res-image"
      />
  <div id="caption" style="color: white;"></div>
</div>

How do I hide/show a element within giving a sibling elements width?

Using react, I’m displaying the following

<g>
  <rect x={x} y={y} width={width} height={height} />
  <text x={x + width / 2} y={y + height / 2 + 7} textAnchor="middle">
    {someText}
  </text>
</g>

But given the value of width, I want to show or hide my <text> element.
Or, I want my text element to behave with overflow: ellipsis if the text element is wider than width.

Ie, if width = 100 and the width of my <text> element = 200, then display 50% of <text> and append ... to someText.

Browsing through mozilla documentation and some svg documentation I’m not finding obvious.

What would be the most clever and efficient way to go about doing this?

Why I am getting htmx:targetError when calling form.requestSubmit?

I am using htmx to load some content when openning an accordion in my django template. I initially include the cover card partial template and when I close the accordion I remove the whole partial template. Then whenever the accordion opens I dispatch a custom event to trigger the htmx swap and return the same partial template in the response. The code looks like this initially:

...
    <details open id="cover-card-section" hx-get="{% url 'update-cover-card' object.id %}" hx-swap="outerHTML" hx-target="#temp-cover-card-form" hx-trigger="open-cover-card-section">
            <summary  >
                Cover Card
            </summary>
            {% include "path/to/cover_card.html" %}

            {% comment %} Element to be replaced by actual cover card form {% endcomment %}
            <div id="temp-cover-card-form" style="display: none; align-items: center; justify-content: center; width: 100%; height: 200px;">
                <svg width="40" height="40" stroke="rgba(0, 0, 0, 0.5)" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_V8m1{transform-origin:center;animation:spinner_zKoa 2s linear infinite}.spinner_V8m1 circle{stroke-linecap:round;animation:spinner_YpZS 1.5s ease-in-out infinite}@keyframes spinner_zKoa{100%{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%{stroke-dasharray:42 150;stroke-dashoffset:-16}95%,100%{stroke-dasharray:42 150;stroke-dashoffset:-59}}</style><g class="spinner_V8m1"><circle cx="12" cy="12" r="9.5" fill="none" stroke-width="3"></circle></g></svg>
            </div>
    </details>
...

The cover card partial template looks like this:

<div class="container" id="cover-card-form-container" style="max-width:700px;" object-settings>
    <style>
        ...
    </style>

    <div>
        <form id='image-upload-form' hx-swap="none" hx-encoding='multipart/form-data' hx-post='/en/home/upload-card-image/'>
            {% csrf_token %}
            {{ form.image.errors }}
            {{ form.image.label_tag }}
            {{ form.image }}
        
            <div style="display: none; width: 100%; align-items: center; gap: 8px; margin-bottom: var(--pico-spacing)" id="progress-container">
                <progress style="width: 50%; margin-bottom: 0" id="progress-bar" value="0" max="100"></progress>
                <span id="progress-text">0%</span>
            </div>
            <div id="image-errors" style="display: none;"></div>
        </form>
        <hr> <!-- Divider -->
            
        <form id="cover-card-form" class="container" hx-post="{% url 'update-cover-card' object.id %}" hx-encoding='multipart/form-data' hx-swap="outerHTML" hx-target="#cover-card-form-container">
           ...
        </form>
            
        <footer>
            <button type="submit" id="submit-button" class="primary" form="cover-card-form">Submit</button>
        </footer>
    </div>
<div>
<script defer src="{% static 'path/to/modals_script.js' %}"></script>

At the end is the script where I am loading the js that is giving me the error after the first htmx swap. Everything works fine when the page first loads. The modals_script.js looks like this:

setupEventsListeners()

function setupEventsListeners() {
  let uploadRequest = null;
  let fileId = null;

  console.log("Setting up event listeners")
   // Handle the image upload
   htmx.on('#image-upload-form', 'change', function(evt) {
    if (evt.target.files.length > 0) {

      if (uploadRequest) {
        uploadRequest.abort()
      }
  
      evt.currentTarget.requestSubmit()
      htmx.find('#submit-button').disabled = true;
      htmx.find('#progress-bar').setAttribute('value', 0);
      htmx.find('#progress-container').style.display = 'flex';
    }
  })

  ...
}

The error I am getting is at the line where I am calling evt.currentTarget.requestSubmit(). I have verified if evt.currentTarget is not null and is always the desired element. I have also tried getting the element directly using document.getElementById and still getting the same error. I have also tried event delegation without any luck. There has to be something I am missing out but I don’t seem to find what it is.

I tried providing everything that I thought could help but if there is something missing or unclear let me know I will be happy to edit the question.

How can I simulate a javascript forward using Panther within Symfony?

Is there a way to simulate a javascript forward in Panther within Symfony.

The 2nd click line is crashing with the following error. The site is using a lot of JScript for weird redirects and button clicks.

ERROR: Reached error page: about:neterror?e=nssFailure&u=domainname.com
HTML link looks like this:
Add A Car

Code:

$link = $client->findElement(WebDriverBy::linkText(“Add A Car”));
$link->click();

Thanks everyone!

How to select collapsible header text

Collapsible code sample in https://www.w3schools.com/howto/howto_js_collapsible.asp

does not allow to select text “Open collapsible”. If mouse is clicked and moved to select it. If mouse button is released, collapsible is opened. How to allow select text is collapsible snipped title?

Tried to add capture: true to clikc event handler

coll[i].addEventListener("click", function() {
  this.classList.toggle("active")
  var content = this.nextElementSibling
  if (content.style.display === "block")
    content.style.display = "none"
   else
    content.style.display = "block"
}, { capture: true } ) ;

but problem persists.

Lorem ipsum… text can selected but OPen collapsible text cannot.

w3schools sample : https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_collapsible

var coll = document.getElementsByClassName("collapsible");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
.collapsible {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  border: none;
  text-align: left;
  outline: none;
  font-size: 15px;
}

/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .collapsible:hover {
  background-color: #ccc;
}

/* Style the collapsible content. Note: hidden by default */
.content {
  padding: 0 18px;
  display: none;
  overflow: hidden;
  background-color: #f1f1f1;
}
<button type="button" class="collapsible">Open Collapsible</button>
<div class="content">
  <p>Lorem ipsum...</p>
</div>

ThreeJs 3d game camera controls different for every direction

I have made this game and when I look one direction, the camera controls are normal, but if I look behind me then they inverse and to the side it makes my camera roll. I suspect this is because the camera in only relative to looking the initial direction, but I cant fix it. Can somebody help? For some reason it displays an error on stack overflow, but it works normal in another editor. Here is my code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Person Game with Four Cameras</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
        #minimap {
            position: absolute;
            top: 10px;
            left: 10px;
            width: 200px;
            height: 200px;
            border: 1px solid #000;
            background-color: rgba(255, 255, 255, 0.5);
            pointer-events: none;
        }
        #controls {
            position: absolute;
            top: 10px;
            right: 10px;
            background: rgba(255, 255, 255, 0.8);
            padding: 10px;
            border-radius: 5px;
        }
        #controls label {
            display: block;
            margin: 10px 0;
        }
        input[type=range] {
            width: 100%;
            height: 10px;
            -webkit-appearance: none;
            appearance: none;
            background: #ddd;
            border-radius: 5px;
        }
        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 20px;
            height: 20px;
            background: #4CAF50;
            border-radius: 50%;
            cursor: pointer;
        }
        input[type=range]::-moz-range-thumb {
            width: 20px;
            height: 20px;
            background: #4CAF50;
            border: none;
            border-radius: 50%;
            cursor: pointer;
        }
    </style>
</head>
<body>
<div id="minimap"></div>
<div id="controls">
    <label for="fov">Field of View: <input type="range" id="fov" min="30" max="100" value="75"></label>
    <label for="brightness">Brightness: <input type="range" id="brightness" min="0" max="2" step="0.1" value="1"></label>
    <button id="pause">Pause</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
    let scene, renderer;
    let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
    let velocity = new THREE.Vector3();
    let direction = new THREE.Vector3();
    let canJump = false;
    let prevTime = performance.now();
    let cubes = [], cubeVelocities = [];
    let heldCube = null;
    let aiBlock;

    const speed = 150.0;
    const jumpVelocity = 30.0;
    const gravity = 9.8 * 5.0;
    const pickUpDistance = 2.0;
    const originalCubeScale = 1;
    const heldCubeScale = 0.5;
    const friction = 0.05;
    let pitch = 0, yaw = 0;

    // Day/Night cycle variables
    let dayTime = 0;
    const dayDuration = 10000;
    let isPaused = false;

    // Define four cameras for North, South, East, West
    const cameras = {
        north: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
        south: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
        east: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
        west: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
    };

    // Initial camera positions and rotations
    cameras.north.position.set(0, 1.6, -5);
    cameras.south.position.set(0, 1.6, 5);
    cameras.east.position.set(5, 1.6, 0);
    cameras.west.position.set(-5, 1.6, 0);

    cameras.north.rotation.set(0, 0, 0);
    cameras.south.rotation.set(0, Math.PI, 0);
    cameras.east.rotation.set(0, Math.PI / 2, 0);
    cameras.west.rotation.set(0, -Math.PI / 2, 0);

    // Starting camera direction
    let currentCamera = cameras.north;

    init();
    animate();

    function init() {
        scene = new THREE.Scene();
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const groundGeometry = new THREE.PlaneGeometry(50, 50);
        const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        scene.add(ground);

        const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
        const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });
        for (let i = 0; i < 3; i++) {
            const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
            cube.position.set(Math.random() * 30 - 15, 1.6, Math.random() * 30 - 15);
            cubes.push(cube);
            cubeVelocities.push(new THREE.Vector3());
            scene.add(cube);
        }

        const aiMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
        aiBlock = new THREE.Mesh(cubeGeometry, aiMaterial);
        aiBlock.position.set(Math.random() * 30 - 15, 1.6, Math.random() * 30 - 15);
        scene.add(aiBlock);

        document.addEventListener('keydown', onKeyDown);
        document.addEventListener('keyup', onKeyUp);
        document.getElementById('pause').addEventListener('click', togglePause);

        document.getElementById('fov').addEventListener('input', (event) => {
            for (let cam in cameras) {
                cameras[cam].fov = event.target.value;
                cameras[cam].updateProjectionMatrix();
            }
        });
        document.getElementById('brightness').addEventListener('input', (event) => {
            scene.background = new THREE.Color(`hsl(0, 0%, ${event.target.value * 100}%)`);
        });

        document.body.addEventListener('click', () => {
            document.body.requestPointerLock();
        });
        document.addEventListener('mousemove', onMouseMove);

        window.addEventListener('resize', onWindowResize);
    }

    function togglePause() {
        isPaused = !isPaused;
        const controls = document.getElementById('controls');
        if (isPaused) {
            controls.classList.add('paused');
        } else {
            controls.classList.remove('paused');
        }
    }

    function onWindowResize() {
        for (let cam in cameras) {
            cameras[cam].aspect = window.innerWidth / window.innerHeight;
            cameras[cam].updateProjectionMatrix();
        }
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function onKeyDown(event) {
        switch (event.code) {
            case 'KeyW': moveForward = true; break;
            case 'KeyS': moveBackward = true; break;
            case 'KeyA': moveLeft = true; break; 
            case 'KeyD': moveRight = true; break;
            case 'Space':
                if (canJump) {
                    velocity.y = jumpVelocity;
                    canJump = false;
                }
                break;
            case 'KeyF':
                pickOrThrowCube();
                break;
            case 'KeyP':
                togglePause();
                break;
            case 'Digit1':
                currentCamera = cameras.north; // Switch to North camera
                break;
            case 'Digit2':
                currentCamera = cameras.south; // Switch to South camera
                break;
            case 'Digit3':
                currentCamera = cameras.east; // Switch to East camera
                break;
            case 'Digit4':
                currentCamera = cameras.west; // Switch to West camera
                break;
        }
    }

    function onKeyUp(event) {
        switch (event.code) {
            case 'KeyW': moveForward = false; break;
            case 'KeyS': moveBackward = false; break;
            case 'KeyA': moveLeft = false; break; 
            case 'KeyD': moveRight = false; break;
        }
    }

    function onMouseMove(event) {
        if (document.pointerLockElement) {
            const sensitivity = 0.002;

            yaw -= event.movementX * sensitivity;
            pitch -= event.movementY * sensitivity;
            pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch));

            // Apply rotation to the current camera
            currentCamera.rotation.y = yaw;
            currentCamera.rotation.x = pitch;
            currentCamera.rotation.z = 0;
        }
    }

    function pickOrThrowCube() {
        if (heldCube) {
            const throwVelocity = new THREE.Vector3();
            currentCamera.getWorldDirection(throwVelocity);
            throwVelocity.multiplyScalar(300);
            heldCube.position.add(throwVelocity.multiplyScalar(0.04));
            cubeVelocities[cubes.indexOf(heldCube)].copy(throwVelocity);
            heldCube.scale.set(originalCubeScale, originalCubeScale, originalCubeScale);
            heldCube = null;
        } else {
            const raycaster = new THREE.Raycaster();
            raycaster.setFromCamera(new THREE.Vector2(0, 0), currentCamera);
            const intersects = raycaster.intersectObjects(cubes.concat(aiBlock));

            if (intersects.length > 0) {
                const intersect = intersects[0];
                if (intersect.distance < pickUpDistance) {
                    heldCube = intersect.object;
                    cubeVelocities[cubes.indexOf(heldCube)] = new THREE.Vector3();
                    heldCube.scale.set(heldCubeScale, heldCubeScale, heldCubeScale);
                    heldCube.position.copy(currentCamera.position).add(currentCamera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
                }
            }
        }
    }

    function animate() {
        if (!isPaused) {
            requestAnimationFrame(animate);

            const time = performance.now();
            const delta = (time - prevTime) / 1000;

            dayTime += delta;
            if (dayTime > dayDuration) dayTime = 0;

            const brightness = (Math.sin((dayTime / dayDuration) * Math.PI) + 1) / 2;
            scene.background = new THREE.Color(`hsl(200, 100%, ${brightness * 100}%)`);

            direction.z = Number(moveForward) - Number(moveBackward);
            direction.x = Number(moveRight) - Number(moveLeft);
            direction.normalize();

            velocity.x -= velocity.x * 10.0 * delta;
            velocity.z -= velocity.z * 10.0 * delta;
            velocity.y -= gravity * delta;

            if (moveForward || moveBackward || moveLeft || moveRight) {
                const frontDirection = new THREE.Vector3();
                currentCamera.getWorldDirection(frontDirection);
                frontDirection.y = 0;
                frontDirection.normalize();

                const rightDirection = new THREE.Vector3();
                rightDirection.crossVectors(currentCamera.up, frontDirection).normalize();

                frontDirection.multiplyScalar(direction.z * speed * delta);
                rightDirection.multiplyScalar(direction.x * speed * delta);

                velocity.add(frontDirection).add(rightDirection);
            }

            currentCamera.position.addScaledVector(velocity, delta);

            currentCamera.position.x = Math.max(-24, Math.min(24, currentCamera.position.x));
            currentCamera.position.z = Math.max(-24, Math.min(24, currentCamera.position.z));

            if (currentCamera.position.y < 1.6) {
                velocity.y = 0;
                currentCamera.position.y = 1.6;
                canJump = true;
            }

            if (heldCube) {
                heldCube.position.copy(currentCamera.position).add(currentCamera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
            }

            for (let i = 0; i < cubes.length; i++) {
                if (cubes[i] !== heldCube) {
                    cubeVelocities[i].x *= (1 - friction);
                    cubeVelocities[i].z *= (1 - friction);
                    cubeVelocities[i].y -= gravity * delta;
                    cubes[i].position.addScaledVector(cubeVelocities[i], delta);

                    if (cubes[i].position.y < 0.5) {
                        cubes[i].position.y = 0.5;
                        cubeVelocities[i].y = Math.abs(cubeVelocities[i].y) * 0.2;
                    }

                    for (let j = 0; j < cubes.length; j++) {
                        if (i !== j) {
                            const distance = cubes[i].position.distanceTo(cubes[j].position);
                            if (distance < 1.5) {
                                const collisionDirection = new THREE.Vector3().subVectors(cubes[i].position, cubes[j].position).normalize();
                                const relativeVelocity = new THREE.Vector3().subVectors(cubeVelocities[i], cubeVelocities[j]);
                                if (relativeVelocity.dot(collisionDirection) < 0) {
                                    const pushAmount = 0.1;
                                    cubeVelocities[i].add(collisionDirection.clone().multiplyScalar(pushAmount));
                                    cubeVelocities[j].sub(collisionDirection.clone().multiplyScalar(pushAmount));
                                }
                            }
                        }
                    }
                }
            }

            aiBlock.position.y -= gravity * delta;
            aiBlock.position.x += (Math.random() - 0.5) * 0.05;
            aiBlock.position.z += (Math.random() - 0.5) * 0.05;

            aiBlock.position.x = Math.max(-24, Math.min(24, aiBlock.position.x));
            aiBlock.position.z = Math.max(-24, Math.min(24, aiBlock.position.z));
            if (aiBlock.position.y < 1.6) {
                aiBlock.position.y = 1.6;
            }

            const playerBox = new THREE.Box3().setFromCenterAndSize(currentCamera.position, new THREE.Vector3(1, 1, 1));
            const aiBox = new THREE.Box3().setFromCenterAndSize(aiBlock.position, new THREE.Vector3(1, 1, 1));
            if (playerBox.intersectsBox(aiBox)) {
                const collisionDirection = new THREE.Vector3().subVectors(currentCamera.position, aiBlock.position).normalize();
                velocity.add(collisionDirection.multiplyScalar(10));
                aiBlock.position.add(collisionDirection.multiplyScalar(7));
            }

            renderer.render(scene, currentCamera);
            prevTime = time;

            updateMiniMap();
        }
    }

    function updateMiniMap() {
        const minimap = document.getElementById('minimap');
        const ctx = minimap.getContext('2d');
        ctx.clearRect(0, 0, minimap.width, minimap.height);

        ctx.fillStyle = 'lightgreen';
        ctx.fillRect(0, 0, minimap.width, minimap.height);

        ctx.fillStyle = 'blue';
        const playerX = (currentCamera.position.x + 25) * 4;
        const playerZ = (currentCamera.position.z + 25) * 4;
        ctx.fillRect(playerX, playerZ, 5, 5);

        ctx.fillStyle = 'blue';
        cubes.forEach(cube => {
            const cubeX = (cube.position.x + 25) * 4;
            const cubeZ = (cube.position.z + 25) * 4;
            ctx.fillRect(cubeX, cubeZ, 5, 5);
        });

        ctx.fillStyle = 'red';
        const aiX = (aiBlock.position.x + 25) * 4;
        const aiZ = (aiBlock.position.z + 25) * 4;
        ctx.fillRect(aiX, aiZ, 5, 5);
    }
</script>
</body>
</html>

Capturing Meeting Transcripts with Speaker Attribution from Google Meet & Zoom

I’m developing a solution to automatically obtain meeting transcripts from Google Meet and Zoom, ensuring that each segment is attributed to the correct speaker—just like the live captions shown during meetings.

My primary goal is to capture the audio or recording data directly from these platforms. I attempted using third-party speech-to-text services (e.g., Deepgram), but they haven’t met the requirement for speaker separation. I suspect there might be browser extensions or techniques (possibly intercepting WebRTC streams or the caption data itself) that can capture the exact transcripts as displayed in the meeting interfaces.

I’m looking for guidance on:

  • Methods to capture the live caption data or audio streams.
  • Approaches to extract and process this data to achieve accurate
    transcription with speaker diarization.
  • Any existing projects or resources that have implemented similar
    functionality.

Any pointers on where to start, useful libraries, or relevant APIs would be greatly appreciated. Thanks!

Note – Not looking for third party API

I attempted to capture the audio from Google Meet and Zoom using third-party speech-to-text platforms like Deepgram, but they didn’t meet my requirements, particularly in terms of speaker separation. The transcription quality wasn’t as accurate as the captions displayed natively on these platforms.

What I am looking for is a way to obtain transcriptions exactly as they appear in the live captions of Google Meet and Zoom, with proper speaker attribution. I suspect this could be done by:

  1. Capturing the audio stream from the meeting.
  2. Intercepting the captions generated by the platform (possibly via WebRTC or browser API).

I noticed there is at least one extension that seems to achieve this, possibly by capturing audio from Zoom or Google Meet captions directly. However, I’m unsure about how it works—whether it’s intercepting WebRTC streams, reading live captions, or using some other approach.

I’d appreciate guidance on where to start, whether there are APIs or browser capabilities to tap into these captions, or if there’s an alternative way to extract them with speaker identification.

MediaRecorder Not Capturing Updated Konva Canvas in React

I’m building a simple video creator using react-konva, where I manipulate a Konva canvas and record it using MediaRecorder. One feature I want to implement is simple subtitles—an array of strings displayed sequentially with a 1.5s gap between each (i.e., start recording → switch between all subtitles → stop recording).

I’ve implemented this, and it almost works. However, I keep running into this issue:

  • The recorded video only contains a still background and the first subtitle, resulting in a short (~1s) video.
  • If I manually drag the text around with my mouse during recording, everything is recorded properly.

Reproducing the Issue

I created a CodeSandbox example:

  1. Click Start Recording, wait for the subtitles to change, then download the video.
  2. Try again, but this time move the text with your mouse while recording—the video records correctly!

What I’ve Tried

To force canvas updates to propagate to the MediaStream, I’ve tried:

  • clearCache(), draw(), batchDraw()
  • Firing dragstart, dragmove, dragend events
  • Forcing a slight position change (node.x(node.x() + 1)) after each subtitle update
  • Saving the canvas as an image using toDataURL() after each subtitle update (the correct subtitles are shown on the saved images, but the recording still doesn’t have the updated subtitles)

Nothing has worked. I’m not sure why manually dragging the text works but updating it programmatically doesn’t.

Any insights on why this happens or how to ensure all subtitle changes get recorded correctly?