How to sort locations by distance with constraint that pickup location must go before delivery location

I was wondering if there is solution to sort Locations by distance from first reference location but respecting constraint that pickup location should be placed before delivery location.

I was thinking if it is even possible as pickup location of one order can be delivery of other order and if we have two orders where pickup and delivery locations are reversed (A order’s pickup location equals B order’s delivery location and A order’s delivery location equals B order’s pickup location) then it will never meet constraint.

Below is code for sorting locations by distance, but not respecting constraint of pickup before delivery. It is rough one as probably graphs should be used for better result.

Am I right that it’s probably impossible to solve with these constraints?

interface Order {
    id: number;
    pickup: Location;
    delivery: Location;
}

interface Location {
    id: number;
    latitude: number;
    longitude: number;
}

// Function to calculate distance using the Haversine formula
function getDistance(loc1: Location, loc2: Location): number {
/** Implementation not important here **/
}

function getSortedUniqueLocations(orderArray: Order[]): Location[] {
    if (orderArray.length === 0) return [];

    const uniqueLocations = new Map<string, Location>();

    for (let i = 0; i < orderArray.length; i++) {
        const order = orderArray[i];
        const pickup = order.pickup;
        const delivery = order.delivery;

        const pickupKey = `${pickup.latitude},${pickup.longitude}`;
        const deliveryKey = `${delivery.latitude},${delivery.longitude}`;

        if (!uniqueLocations.has(pickupKey)) {
            uniqueLocations.set(pickupKey, pickup);
        }
        if (!uniqueLocations.has(deliveryKey)) {
            uniqueLocations.set(deliveryKey, delivery);
        }
    }

    const locations = Array.from(uniqueLocations.values());

    const firstLocation = orderArray[0].pickup;
    locations.sort((a, b) => getDistance(firstLocation, a) - getDistance(firstLocation, b));

    return locations;
}

How to achieve typing and cursor effect across multiple lines?

I was trying to split a title in html into 3 separate lines and apply a typing effect to them. Each line would be invisible (i.e. black because that’s the background color) until the cursor moves past it, turning it white. After applying CSS+JS, several problems occured:

  • The cursor disappeared;
  • The last char in each line stays black.

I tried asking Github Copilot and ChatGPT but they weren’t super helpful… How can I fix these issues?

document.addEventListener("DOMContentLoaded", function() {
  const lines = [{
      element: document.getElementById("line1"),
      text: "Lorem ipsum dolor"
    },
    {
      element: document.getElementById("line2"),
      text: "sit"
    },
    {
      element: document.getElementById("line3"),
      text: "amet, consectetur adipiscing elit."
    }
  ];

  let currentLine = 0;
  let currentChar = 0;

  function type() {
    if (currentLine < lines.length) {
      const line = lines[currentLine];
      const text = line.text;
      const cursor = document.createElement("span");
      cursor.className = "cursor";
      line.element.appendChild(cursor);

      function typeChar() {
        if (currentChar < text.length) {
          line.element.childNodes[currentChar].style.color = "white";
          currentChar++;
          cursor.style.left = `${currentChar * 0.6}em`;
          setTimeout(typeChar, 40); // Typing speed
        } else {
          line.element.childNodes[currentChar - 1].style.color = "white";
          // Ensure last character is visible/white

          line.element.removeChild(cursor);
          currentLine++;
          currentChar = 0;
          setTimeout(type, 20); // Delay before starting the next line
        }
      }

      for (let i = 0; i < text.length; i++) {
        const span = document.createElement("span");
        span.textContent = text[i];
        span.style.color = "black"; // Initially black
        line.element.appendChild(span);
      }

      line.element.removeChild(line.element.firstChild);
      typeChar();
    } else {
      // Add blinking cursor to the end of the last line
      const lastLine = lines[lines.length - 1].element;
      const cursor = document.createElement("span");
      cursor.className = "cursor";
      lastLine.appendChild(cursor);
      cursor.style.left = `${lines[lines.length - 1].text.length * 0.6}em`;
    }
  }

  type();
});
.top-container h1 {
  font-size: 5rem;
  font-weight: 950;
  line-height: 1.2;
  word-wrap: break-word;
  white-space: nowrap;
  overflow: hidden;
  background-color:black;
}

.top-container h1 .text {
  display: inline-block;
  color: black;
  /* Initially invisible */
  position: relative;
}

.cursor {
  display: inline-block;
  width: 0.15em;
  background-color: white;
  animation: blink-caret 0.75s step-end infinite;
  position: absolute;
}

@keyframes blink-caret {
  50% {
    background-color: transparent;
  }
}

/* Button Container */
.button-container {
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  width: 100%;
  margin-top: 5%;
  margin-top: 200px;
}
<div class="top-container">
  <h1>
    <span class="text" id="line1">Lorem ipsum dolor</span><br>
    <span class="text" id="line2">sit</span><br>
    <span class="text" id="line3">amet, consectetur adipiscing elit.</span>
  </h1>
</div>

Selectable row in Tanstack Table in React

I have a table I built with Tanstack table in React. I am trying to do a selectable row functionality but I am currently running into an issue where whenever I click on any row, it always selects the first row and when I click on any row again, it unselects, so it feels all the rows on the table acts as a toggle for the first row. I have tried several solutions like letting each item have a unique ID but it still doesn’t work. I currently handle the select with a checkbox. How can I fix the issue such that whenever I click on a row, it selects that particular, and whenever I click for the second time it unselects (a toggle for each row).

My code:

// columns definition

const columns = [
    {
        id: "select",
        header: ({ table }) => (
            <div className="transform translate-y-[5px]">
                <CheckBoxButton 
                                   checked={table.getIsAllPageRowsSelected()}
                   className="check-minus"
                   name="select"
                   onChange={table.getToggleAllRowsSelectedHandler()}
                />
            </div>
        ),
        cell: ({ row }) => {
            return (
                <div className="transform translate-y-[5px]">
                  <CheckBoxButton
                    checked={row.getIsSelected()}
                    name=”item”
                    onChange {row.getToggleSelectedHandler()}
                    />
                </div>
            );
        },
    },
…
]

const [rowSelection, setRowSelection] = useState({});
const cols = columns;
const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    state: {
          rowSelection,
    },
    onRowSelectionChange: setRowSelection,
});

// my table component
Table
     allowSelection
     page={2}
     pageCount={20}
     table={table}
/>

Extract user data using regex from react mention

Hi please help me to extract to get Name and username from this content below:

Hello @[Walter White](walter) with @[中本聪](satoshi1) are you @[Gustavo "Gus" Fring](gus) ok?

I don’t really understand how regex works, so i try this formula:

@[[A-Za-z0-9 ]+]

only show @[name], i want to make the mention change to Link.

I’m using React JS, thank you

How to contain javascript math.random animation within a div/container?

I’m trying to contain animated divs within another div container while using the Math.random javascript command on a mouseover. This is for a squarespace site.

See the code below for what I have, and you can find a live version here. https://www.shayla-designs.com/home-1

I need the white bubbles with black text to stay in the middle of the page, right now they go off to the right side and off the visible page.

jQuery(function($) {
  $('#description1').mouseover(function() {
    //var dWidth = $(document).width() - 100, // 100 = image width
    //dHeight = $(document).height() - 100, // 100 = image height
    var nextX = Math.floor(Math.random() * ($(this).parent().width() - $(this).width() - 1) + 1),
      nextY = Math.floor(Math.random() * ($(this).parent().height() - $(this).height() + 1) - 1);
    $(this).animate({
      left: nextX + 'px',
      top: nextY + 'px'
    });
  });
});

jQuery(function($) {
  $('#description2').mouseover(function() {
    //var dWidth = $(document).width() - 100, // 100 = image width
    //dHeight = $(document).height() - 100, // 100 = image height
    var nextX = Math.floor(Math.random() * ($(this).parent().width() - $(this).width() - 1) + 1),
      nextY = Math.floor(Math.random() * ($(this).parent().height() - $(this).height() + 1) - 1);
    $(this).animate({
      left: nextX + 'px',
      top: nextY + 'px'
    });
  });
});

jQuery(function($) {
  $('#description3').mouseover(function() {
    //var dWidth = $(document).width() - 100, // 100 = image width
    //dHeight = $(document).height() - 100, // 100 = image height
    var nextX = Math.floor(Math.random() * ($(this).parent().width() - $(this).width() - 1) + 1),
      nextY = Math.floor(Math.random() * ($(this).parent().height() - $(this).height() + 1) - 1);
    $(this).animate({
      left: nextX + 'px',
      top: nextY + 'px'
    });
  });
});

jQuery(function($) {
  $('#description4').mouseover(function() {
    //var dWidth = $(document).width() - 100, // 100 = image width
    //dHeight = $(document).height() - 100, // 100 = image height
    var nextX = Math.floor(Math.random() * ($(this).parent().width() - $(this).width() - 1) + 1),
      nextY = Math.floor(Math.random() * ($(this).parent().height() - $(this).height() + 1) - 1);
    $(this).animate({
      left: nextX + 'px',
      top: nextY + 'px'
    });
  });
});
body {
  margin: 0;
  padding: 0;
  align: center;
  background-color: teal;
}

#container {
  width: auto;
  height: 100px;
  margin-left: auto;
  margin-right: auto;
  padding: 0px;
  position: relative;
  align-content: center;
}

#description1 {
  position: relative;
}

#description2 {
  position: relative;
}

#description3 {
  position: relative;
}

#description4 {
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div id="container">
  <div id="description1" style="width: fit-content;  display:inline; height: 26px; background-color:rgba(255, 255, 255, 0.75); color: #000;  border-radius: 999px; font-weight: bold; text-align: center; margin:8px; padding: 8px 16px 8px 16px;">Lorem Ipsum</div>

  <div id="description2" style="width: fit-content;  display:inline; height: 26px; background-color:rgba(255, 255, 255, 0.75); color: #000;  border-radius: 999px; font-weight: bold; text-align: center; margin:8px; padding: 8px 16px 8px 16px;">Lorem Ipsum</div>

  <div id="description3" style="width: fit-content;  display:inline; height: 26px; background-color:rgba(255, 255, 255, 0.75); color: #000;  border-radius: 999px; font-weight: bold; text-align: center; margin:8px; padding: 8px 16px 8px 16px;">Lorem Ipsum</div>

  <div id="description4" style="width: fit-content;  display:inline; height: 26px; background-color:rgba(255, 255, 255, 0.75); color: #000;  border-radius: 999px; font-weight: bold; text-align: center; margin:8px; padding: 8px 16px 8px 16px;">Lorem Ipsum</div>
</div>

Name of video still “video.mp4” after downloading using yt-dlp

I’m using yt-dlp in a Node.js Express application to download videos from YouTube. I’m extracting the video title and using it to name the downloaded file, but all downloaded files are still named video.mp4 instead of the extracted title.

Here’s the relevant code from my Express route:

app.get('/download', async (req, res) => {
    const videoURL = req.query.url;
    const format = req.query.format || 'mp4';

    if (!videoURL) {
        return res.status(400).send({ error: 'No URL provided' });
    }

    const cookiesFile = path.resolve(__dirname, 'cookies.txt');

    // Validate cookies file exists
    if (!fs.existsSync(cookiesFile)) {
        console.error('Missing cookies.txt');
        return res.status(500).send({ error: 'Authentication failed' });
    }

    // Set timeout to handle long downloads
    req.setTimeout(600000); // 10 minutes

    // Get video metadata to extract the title
    exec(`yt-dlp --cookies "${cookiesFile}" --get-title ${videoURL}`, (error, stdout, stderr) => {
        if (error) {
            console.error('Failed to get video title:', stderr);
            return res.status(500).send({ error: 'Failed to get video title' });
        }

        const videoTitle = stdout.trim().replace(/[<>:"/\|?*]+/g, ''); // Sanitize title
        const outputFile = path.resolve(os.tmpdir(), `${videoTitle}.${format}`);

        // Configure yt-dlp command
        const command = format === 'mp3' 
            ? `yt-dlp --cookies "${cookiesFile}" --extract-audio --audio-format mp3 -o "${outputFile}" ${videoURL}`
            : `yt-dlp --cookies "${cookiesFile}" -o "${outputFile}" --merge-output-format mp4 ${videoURL}`;

        exec(command, async (error, stdout, stderr) => {
            try {
                if (error) {
                    throw new Error(`yt-dlp error: ${stderr}`);
                }

                // Verify output file exists
                if (!fs.existsSync(outputFile)) {
                    throw new Error('Output file not created');
                }

                // Validate file size
                const stats = fs.statSync(outputFile);
                const maxSize = 1024 * 1024 * 500; // 500MB
                if (stats.size > maxSize) {
                    fs.unlinkSync(outputFile);
                    return res.status(413).send({ error: 'File exceeds maximum size limit' });
                }

                // Set proper content headers
                res.setHeader('Content-Type', format === 'mp3' ? 'audio/mpeg' : 'video/mp4');
                res.setHeader('Content-Length', stats.size);
                res.setHeader('Content-Disposition', `attachment; filename="${path.basename(outputFile)}"`);

                // Stream the file
                const fileStream = fs.createReadStream(outputFile);
                fileStream.pipe(res);

                // Cleanup after stream finishes
                fileStream.on('end', () => {
                    fs.unlink(outputFile, (err) => {
                        if (err) console.error('Cleanup error:', err);
                    });
                });

            } catch (err) {
                console.error('Download failed:', err);
                
                // Cleanup failed download
                if (fs.existsSync(outputFile)) {
                    fs.unlinkSync(outputFile);
                }

                res.status(500).send({ 
                    error: 'Download failed',
                    details: err.message
                });
            }
        });
    });
});

The issue is that despite extracting the video title and using it in the outputFile path, the downloaded file is consistently named video.mp4. I’ve verified that the videoTitle variable contains the correct title after sanitization, and the outputFile path seems to be constructed correctly.

What I’ve Tried:

I’ve checked the output of the yt-dlp –get-title command and confirmed that it’s returning the expected title.
I’ve added console.log(outputFile) to verify that the file path is being generated with the correct title.
I’ve checked that the format variable is correctly set to either mp4 or mp3 based on the query parameter.
I have tried to change the output file path to a static path, and the file is still named video.mp4.

How to limit google maps auto complete to only one country?

I have tried everything so my search input only gives autocomplete options from only Dominican Republic but I have not been able to achieve it, anyone here knows how to do this?


<input 
                                    type="text" 
                                    id="searchInput" 
                                    class="form-control" 
                                    placeholder="Search for a location..."
                                >

let autocomplete;

const input = document.getElementById('searchInput');
    autocomplete = new google.maps.places.Autocomplete(input, {
        types: ['(cities)', 'geocode'], // Allow both city and address searches
        componentRestrictions: { country: 'do' }
    });

    // Handle place selection
    autocomplete.addListener('place_changed', function() {
        const place = autocomplete.getPlace();
        
        if (!place.geometry) {
            return;
        }

Email OTP Generation Apps Script

I asked Gemini how to do what the title of this question says, using an apps script. (I am discouraged to use plugins as they usually come with a limited free trial and a cost so no such solutions are accepted unless a trusted free plugin is suggested here). It gave me an Apps-script solution (to be deployed in a google form) that it claims, does the following:

It accepts the respondent’s email ID and lets the respondent submit once with an empty OTP field (NOT set as required) and the submission then triggers generation and sending of OTP to the email id filled in the field. The respondent is then required to edit response (the email id will be prefilled anyway) and fill the OTP and submit again and the script verifies keeps the respondent trying until it is keyed in correctly.

However the script and the triggers together were not able to do the above even after I tried repeated modifications with Gemini’s assistance and followed its instructions to the tee.

I might have made minor errors though and maybe also my fundamental understanding of triggers might be lacking. I could use an extra pair of eyes to check and find the errors and also point fundamental flaws and fix this issue.

Below are some essential details. Kindly ask for more information as needed and I will see if I can furnish.

Here is the Link to Fill the Form

Below is the script deployed:

function onSubmit(e) {
  Logger.log("onSubmit triggered");

  var itemResponses = e.response.getItemResponses();
  var emailItem = itemResponses.find(item => item.getItem().getTitle() == "Your Email 
Address"); // Replace with your email question title
 
  if (!emailItem) {
    Logger.log("ERROR: Email question NOT found!");
    itemResponses.forEach(itemResponse => Logger.log("Item Title: " + 
itemResponse.getItem().getTitle()));
    return;
  }

  var email = emailItem.getResponse();
  Logger.log("Email: " + email);

  var emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;

  if (!emailRegex.test(email)) {
    Logger.log("ERROR: Invalid email address: " + email);
    e.response.withItemResponses().setConfirmationMessage("Invalid email address. 
Please enter a valid email.");
    return;
  }

  // Get the edit URL
  var editUrl = e.response.getEditResponseUrl();
  Logger.log("Edit URL: " + editUrl);

  var cache = CacheService.getScriptCache();
  cache.put("pending_email", email, 300); // Store email for 5 minutes
  cache.put("edit_url", editUrl, 300); // Store edit URL

  // Generate and send OTP
  var otp = generateOTP();
  cache.put(email + "_otp", otp, 300); // Store OTP for 5 minutes
  sendEmail(email, otp, editUrl); // Send email with OTP and edit URL

  // Instead of setEditResponse, use a confirmation message to prompt for OTP entry
  e.response.withItemResponses().setConfirmationMessage("Thank you for submitting your 
email. Please check your inbox (and spam folder) for the OTP. Click the link in the 
email to complete your submission.");

  return;
}

function onFormSubmit(e) {
  Logger.log("onFormSubmit triggered");

  var itemResponses = e.response.getItemResponses();
  var emailItem = itemResponses.find(item => item.getItem().getTitle() == "Your Email 
 Address"); // Replace with your email question title
  var email = emailItem.getResponse();
  var otpQuestion = itemResponses.find(item => item.getItem().getTitle() == "OTP"); // 
Replace with your OTP question title
  var submittedOtp = otpQuestion ? otpQuestion.getResponse() : null;

  var cache = CacheService.getScriptCache();
  var cachedOtp = cache.get(email + "_otp");

  if (submittedOtp && cachedOtp && submittedOtp == cachedOtp) {
    cache.put(email + "_verified", true, 3600);
    e.response.withItemResponses().setConfirmationMessage("Your email has been 
verified. Thank you for your submission!");
  } else if (submittedOtp) {
    e.response.withItemResponses().setConfirmationMessage("Incorrect OTP. Please try 
again.");
  } else {
    e.response.withItemResponses().setConfirmationMessage("Please enter the OTP.");
  }
}

function generateOTP() {
  var otp = Math.floor(100000 + Math.random() * 900000); // 6-digit OTP
  return otp.toString();
}

function sendEmail(email, otp, editUrl) { // Add editUrl parameter
  var subject = "Your OTP for Google Form Verification";
  var body = "Your OTP is: " + otp + "nnPlease click the following link to complete 
your submission: " + editUrl; // Include editUrl

  try {
    MailApp.sendEmail(email, subject, body);
    Logger.log("OTP email sent successfully to: " + email);
  } catch (error) {
    Logger.log("ERROR sending OTP email: " + error);
  }
}

Below is a screenshot of the two triggers set.

Triggers Set for Form

…Finally, below are the error logs upon the execution of the triggers:

Cloud logs
    Feb 24, 2025, 3:22:24 PM    Info    onSubmit triggered
    Feb 24, 2025, 3:22:24 PM    Info    Email: [email protected]
    Feb 24, 2025, 3:22:25 PM    Info    Edit URL: https://docs.google.com/forms/d/e/1FAIpQLSdtWPCzDm92Tah34gnj4UZHiZgQ22L6nQPxC5LrnwDCoUphsA/viewform?edit2=2_ABaOnud9q2OpMIXEeE6nvxwkIxPCr6JbP-5y9OQJoLoNKEN2va_vWQLR-nQZ8QauOy8iPHw
    Feb 24, 2025, 3:22:25 PM    Info    OTP email sent successfully to: [email protected]
    Feb 24, 2025, 3:22:25 PM    Error   TypeError: e.response.withItemResponses is not a function
                                            at onSubmit(Code:39:14)

Cloud logs
    Feb 24, 2025, 3:22:22 PM    Info    onFormSubmit triggered
    Feb 24, 2025, 3:22:23 PM    Error   TypeError: e.response.withItemResponses is not a function
                                            at onFormSubmit(Code:62:16)

I made a website for my conlang, and am having issues with multiple translations being displayed on pages

I created a conlang with my friend and we decided to make an online dictionary for it. All of the pages are dynamically generated using node.js, express, and handlebars. The data for the pages are stored in .json files to allow for easy editing.

An issue arises when I give a word multiple english translations, it all shows up in a string ‘at, on, in’ on the words page. This was bound to happen cause I never implemented a system to account for this.

What I need is to have these translations show up separately on the words page under the correct letter heading but still link to the same word page. I do not want to restructure my data majorly, but if it’s required I will. The result should look like this (This is an old static site)

I apologise for the lack of comments, I will be adding more in a little bit.
I also apologise if I have forgotten and important piece of information.

I have tried automatically formatting english translations into arrays and the looping through it, but I couldn’t find a working solution that was neat, didn’t require ridiculous amounts of changes, and still auto sorted the words into groups. The results of that attempt, just broke every page on the site. Adding multiple word entries in the .json file also doesn’t work as it link to the one highest in the file and it makes data more clunky.

I could not find any similar questions anywhere else on the internet and AI did not help in anyway.
Is there also anyway to make the URL https://grifeng.org/word/d%C3%BCf.html look like https://grifeng.org/word/düf.html instead?

CODE: https://github.com/mcl-playz/grifeng
WEBSITE: https://grifeng.org
The related code is in words.json, server.js, templates/words.html, templates/word.html

Zoom SDK Works in ZoomMtg.init(), but not ZoomMtg.join()

I am developing an HTML and JS project for joining a meeting room created using ZoomWorkplace app. Right now I’m getting console logs for ZoomMtg.init(), but I’m not getting any logs for ZoomMtg.join(). Its also worth noting that when I press the ‘Start Session’ button, the tab detects that it uses camera, but no Zoom element appears. So my guess is it doesn’t run the ZoomMtg.join() function. For the signature, I generated using this link: https://developers.zoom.us/docs/meeting-sdk/auth/ with role type of participant. How can I make the ZoomMtg.join() functioning? Thank you in advance.

Source Code:

<!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>
        body {
            background-color: black;
            color: white;
            text-align: center;
        }
        #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;
        }
        .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">
        <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>
// Utility functions for debugging
const testTool = {
    parseQuery: () => {
        return {
            sdkKey: document.getElementById("sdkKey").value.trim(),
            meetingNumber: document.getElementById("meetingNumber").value.trim(),
            passWord: document.getElementById("passWord").value.trim(),
            userName: document.getElementById("userName").value.trim(),
            signature: document.getElementById("signature").value.trim(),
        };
    },
    detectOS: () => {
        const userAgent = navigator.userAgent.toLowerCase();
        if (userAgent.indexOf('win') > -1) return 'Windows';
        if (userAgent.indexOf('mac') > -1) return 'Mac';
        if (userAgent.indexOf('linux') > -1) return 'Linux';
        return 'Unknown OS';
    },
    getBrowserInfo: () => {
        const ua = navigator.userAgent;
        let tem;
        let M = ua.match(/(chrome|safari|firefox|msie|trident(?=/))/?s*(d+)/i) || [];
        return M[1] || "Unknown";
    }
};

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

    console.log("Zoom SDK Loaded");
    console.log(JSON.stringify(ZoomMtg.checkFeatureRequirements()));

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

    document.getElementById("startSession").addEventListener("click", function () {
        const tmpArgs = {
            sdkKey: document.getElementById("sdkKey").value.trim(),
            meetingNumber: document.getElementById("meetingNumber").value.trim(),
            passWord: document.getElementById("passWord").value.trim(),
            userName: document.getElementById("userName").value.trim(),
            signature: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBLZXkiOiJtR3RYTGJHU09DdWcyblZKY3dKV0EiLCJzZGtLZXkiOiJtR3RYTGJHU09DdWcyblZKY3dKV0EiLCJtbiI6Ijk4NTczOTIyNzkiLCJyb2xlIjoxLCJ0b2tlbkV4cCI6MTc0MDQ2MTQ0OCwiaWF0IjoxNzQwNDU3ODQ4LCJleHAiOjE3NDA0NjE0NDh9.03gMA9N6pgSrT7862rylgQGrmQZg-GE7hJ1RalYqeto',
        };

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

        console.log("Meeting Config:", tmpArgs);
        console.log("Generated Signature:", tmpArgs.signature);

        ZoomMtg.init({
            leaveUrl: window.location.href,
            isSupportAV: true,
            success: function () {
                console.log("Success init. Attempting to join meeting...");
                console.log("About to call ZoomMtg.join with params:", {
                    meetingNumber: tmpArgs.meetingNumber,
                    userName: tmpArgs.userName,
                    sdkKey: tmpArgs.sdkKey,
                    // Omitting signature and password for security
                });
                ZoomMtg.join({
                    meetingNumber: tmpArgs.meetingNumber,
                    userName: tmpArgs.userName,
                    signature: tmpArgs.signature,
                    sdkKey: tmpArgs.sdkKey,
                    passWord: tmpArgs.passWord,
                    success: function (res) {
                        console.log("Successfully joined meeting", res);
                        document.getElementById("zmmtg-root").style.display = "block";
                        document.querySelector(".controls").style.display = "none";
                    },
                    error: function (res) {
                        console.error("Error joining meeting:", res);
                        alert("Error joining meeting: " + JSON.stringify(res));
                    }
                });
            },
            error: function (res) {
                console.error("Zoom SDK Init Error:", res);
            }
        });

        // Add event listeners for debugging
        ZoomMtg.inMeetingServiceListener("onUserJoin", function (data) {
            console.log("User joined:", data);
        });

        ZoomMtg.inMeetingServiceListener("onUserLeave", function (data) {
            console.log("User left:", data);
        });

        ZoomMtg.inMeetingServiceListener("onMeetingStatus", function (data) {
            console.log("Meeting status changed:", data);
        });
    });
});

</script>
</html>

Requesting Firebase Storage Always Results in request.auth Being Null

I have set up Firebase Storage security rules as follows:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /video/{allPaths=**} {
      allow read: if request.auth != null;
    }
  }
}

When testing with a UID in the Firebase Playground, access is granted as expected. The issue arises when I try to access the storage on a hosted website, where the user is logged in with the same UID. I encounter the following error:

VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) The media could not be loaded, either because the server or network failed or because the format is not supported.

Here is the JavaScript code that loads Video.js:

let vPlayer = videojs('my-video');
if (vPlayer) vPlayer.dispose();

$("#video-wrapper").empty();
$("#video-wrapper").append(`<div class="video-overlay"></div>`);
$("#video-wrapper").append(`<video-js id="my-video" class="vjs-default-skin" controls preload="auto" controlsList="nodownload" oncontextmenu="return false;"></video-js>`);

vPlayer = videojs('my-video', {
  playbackRates: [0.5, 1, 1.5, 2]
});
vPlayer.src({
  src: vUrl,
  type: 'video/mp4',
  crossorigin: 'anonymous'
});

The vUrl is a URL to a file in the video folder in Firebase Storage, like this:
https://firebasestorage.googleapis.com/v0/b/{bucket}/o/video%2Ftest.mp4?alt=media.
I removed the token parameter from the URL.

I also tried using getDownloadURL to get a signed URL, but I get the error (storage/unauthorized), indicating insufficient permissions. Moreover, when using a signed URL, it seems to generate a publicly accessible URL temporarily, which I do not want to use.

Additionally, I found a post stating that request.auth is correctly passed when using an older version, so I installed version 11.1.0 with npm install [email protected], verified the installation with npm list firebase, and deployed it, but the issue remains unchanged.

The reason I downgraded to 11.1.0 is that I believe there were no issues when I previously used that version, so I installed it based on the initial version recorded in the package.json.

Even when checking the authentication state with onAuthStateChanged, the console.log shows that the auth information is stored correctly.

How can I ensure that request.auth is correctly passed to the security rules during this request?

How to Use Different Namespaces in an Angular Library?

I want to create a library in Angular, this will have two sections: UI and Services, UI is for the components that can be reused in the projects where the packages are installed, same case for the services, these can be used in the projects in which the package is installed, now, what I need is that when I go to import a component the import looks like this:

import { Component } from '@contoso/core/ui';

And when importing a service, the import should appear like this:

import { Service } from '@contoso/core/services';

Let’s assume my project is currently named “@contoso/core”. In the public-api.ts file, I would include all the components and services I want to export. This would result in both components and services being imported like this:

import { Component/Service } from '@contoso/core';

So, I would like to know how I can add the namespace for UI and Services. It would be very helpful if you could help me solve this problem please.

Posthog Reverse Proxy Issue

I am setting up a reverse proxy I have followed the steps exactly for the cloudflare worker but then when i go to my website console i get “Loading failed for the with source “https://colinfrankel.com/cloudflare-worker.colinfrankel.com/static/array.js”.” which makes me think i configured it wrong so i look back at the docs and follow it to exactly

    <script>
    !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug getPageViewId captureTraceFeedback captureTraceMetric".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
    posthog.init('THISMAYBESECRET?', {
    api_host: 'cloudflare-worker.colinfrankel.com',
    ui_host: "us.i.posthog.com",
    person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well
    })
    </script>

and also tried the ui_host to be us.posthog.com or https://us.posthog.com but didnt change anything and i checked the cloudflare worker is getting hit and there is a posthog object in window. but when doing window.posthog.LIB_VERSION i get undefined and here is my

Debug Info

Session: https://us.posthog.com/project/sTMFPsFhdP1Ssg/replay/01953a8b-8073-701a-bb25-a27e22af0bcf?t=8381
Admin: http://go/adminOrgUS/01953a96-ba2e-0000-06fa-000918518fc2 (project ID 131822)
Sentry: http://go/sentryUS/131822

I am unsure of what the issue could be?

Delay when loading transacions (BLS) SAP MII

I’m reaching out because I’m encountering an issue when making a request for 3 objects (BLS – SAP MII Transactions) to display information on the screen. The problem is that the last object doesn’t load automatically, but only after I click anywhere on the screen (for example, on one of the tabs like “Performance” or “Sources”). I’ve already checked the code, and there is no delay . It is this action of clicking anywhere on the screen that allows the request to be executed or for the result of the GET to be returned.

enter image description here

I’m not attaching the .js code because I’m mainly considering that it could be a problem with libraries or something similar. If anyone has experienced something similar, please let me know how you resolved it.