Closing an alert dialog using Puppeteer

I want to join a Zoom meeting using Puppeteer. Everytime I go to a meeting, I am given a modal that blocks me from running any other code. I try to close it, but nothing happens. Is this a different type of modal that I should be handling differently? My code:

Note: The logic to close it happens before we visit the link, as advised by some GitHub issues.

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  page.on('dialog', async (dialog) => {
    console.log(dialog.message());
    dialog.dismiss();
  });
  //Dialog will not close?

  await page.goto(
    'https://us05web.zoom.us/j/82821789760?pwd=vaRSUcEPsJCbh0EUrh40aKb0v6ugHp.1'
  );

  // Note: The meeting host must enable Show a "Join from your browser" link for their participants.
  // Settings -> In Meeting (Advanced) -> Show a "Join from your browser" link

  await page
    .waitForSelector(
      '#zoom-ui-frame > div.bhauZU7H > div > div.pUmU_FLW > h3:nth-child(2) > span > a'
    )
    .then((btn) => btn?.click());
  await browser.close();
})();

Modal

Sprout “Javascript Player API”, throws “Uncaught Can not find video iframe” errror on wordpress website

Can someone help me troubleshoot this issue, I have a sprout video embedded in iframe, in a slick slider, and I have set this iframe to be draggable.

There is a div overlay(.iframe-overlay), that can be swipe and click. It is working fine on JSfiddle, but somehow it throws an “Uncaught Can not find video iframe” error on wordpress webiste.

JSFiddle: https://jsfiddle.net/markbanong/9myn6akd/27/

Site: https://getgoally.com/ (on the “What Parents Are Saying…” section)

Below is my code

html:

<div class="parents-saying template-row row-margin-top">
    <div class="container">
                <h2>What Parents Are Saying...</h2>
        <div class="slider-grandparent-container element-margin-top">
            <div class="slider-parent-container">
                <div class="slider">
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/119fd4ba181fe5cc98/858cee23708a3b72" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="119fd4ba181fe5cc98/858cee23708a3b72"></div>
                            </div>
                        </div>
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/ea90d3b01a10e2cb63/8cd05d2e6c368787" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="ea90d3b01a10e2cb63/8cd05d2e6c368787"></div>
                            </div>
                        </div>
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/7990d2b61a1ae3c0f0/aeb336123490329c" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="7990d2b61a1ae3c0f0/aeb336123490329c"></div>
                            </div>
                        </div>
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/d390d2b61a1ae3c25a/872e74b9a3a9ea24" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="d390d2b61a1ae3c25a/872e74b9a3a9ea24"></div>
                            </div>
                        </div>
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/1190d3b01a10e2c498/d983b9381d41e3bc" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="1190d3b01a10e2c498/d983b9381d41e3bc"></div>
                            </div>
                        </div>
                                            <div class="slider__item">
                            <div class="iframe-container" style="position:relative;height:0;padding-bottom:177.94253938832253%">
                                <iframe allow="autoplay" class="sproutvideo-player thirteenth-row-iframe" src="https://videos.sproutvideo.com/embed/0690d2b51f1be2c58f/bd697fcb0fcb2c64" ?playertheme="dark&amp;playerColor=2f3437" style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:8px;" frameborder="0" allowfullscreen="" referrerpolicy="no-referrer-when-downgrade">
                                </iframe> 
                                <div class="iframe-overlay" data-sprout-id="0690d2b51f1be2c58f/bd697fcb0fcb2c64"></div>
                            </div>
                        </div>
                                    </div>
            </div>
        </div>
    </div>
</div>

JS:

/**
 * Initializes and manages a Slick slider with video playback functionality.
 * Videos are played when clicking on an iframe overlay, and the script 
 * handles both original and cloned slider items.
 */
$(document).ready(function() {
  var isDragging = false;
  var clickStartX;

  /**
   * Initializes the Slick slider with specified settings.
   */
  $('.parents-saying .slider').slick({
    slidesToShow: 3,
    slidesToScroll: 1,
    arrows: true,
    dots: true,
    infinite: false,
    variableWidth: true,
    prevArrow: "<img class='a-left slider-arrow slick-prev' src='/wp-content/uploads/2023/08/home-left-arrow.webp'>",
    nextArrow: "<img class='a-right slider-arrow slick-next' src='/wp-content/uploads/2023/08/home-right-arrow.webp'>",
    responsive: [{
      breakpoint: 1024,
      settings: {
        arrows: false,
      }
    }]
  });

  /**
   * Attaches event handlers to detect dragging on the slider.
   * This helps differentiate between drag and click events.
   */
  $('.parents-saying .slider')
    .on('mousedown', function(e) {
      isDragging = false;
      clickStartX = e.pageX;
    })
    .on('mousemove', function(e) {
      if (Math.abs(e.pageX - clickStartX) > 5) {
        isDragging = true;
      }
    })
    .on('mouseup', function() {
      isDragging = false;
    });

  /**
   * Global click event listener for overlay elements.
   * This listener ensures that the click event on overlays triggers
   * video playback, regardless of whether the element is original or cloned.
   */
  $(document).on('click', '.parents-saying .slider__item .iframe-overlay', function(e) {
    if (!isDragging && $(e.target).closest('.slick-slide').hasClass('slick-active')) {
      playVideo($(this));
    }
  });

  /**
   * Plays the video associated with the clicked overlay.
   * @param {jQuery Object} item - The clicked overlay jQuery object.
   */
  function playVideo(item) {
    const sproutId = item.attr('data-sprout-id');
    console.log('SPROUT ID: ', sproutId);
    const player = new SV.Player({
      videoId: sproutId
    });
    player.play();
  }
});

Also I have added, the sprout javascript player api, in the header as per documentation.

enter image description here

Mui(mui.com) Data Grid – How add a Summary row

I am using Data Table from MUI, docs below.

https://mui.com/material-ui/react-table/#data-table
https://mui.com/

I am trying to add a summary row in my table, but I dont understand how.

Any Idea is apreciated.

My actual code:

import React, { useEffect, useState } from 'react';
import { DataGrid, GridRowsProp, GridColDef } from '@mui/x-data-grid';

import './Invoice.scss';

const Invoice = () => {

  const rows = [
    { id: 1, col1: '1', col2: '', col3: 'Power Sweeping1', col4: '', col5: '1', col6: '$1.350,00', col7: '$1.350,00' },
    { id: 1, col1: '1', col2: '', col3: 'Power Sweeping2', col4: '', col5: '1', col6: '$1.350,00', col7: '$1.350,00' },
    { id: 1, col1: '1', col2: '', col3: 'Power Sweeping3', col4: '', col5: '1', col6: '$1.350,00', col7: '$1.350,00' },
  ];

  const columns = [
    { field: 'col1', headerName: '#', width: 10 },
    { field: 'col2', headerName: 'Date', width: 150 },
    { field: 'col3', headerName: 'Product or service', width: 250 },
    { field: 'col4', headerName: 'SKU', width: 150 },
    { field: 'col5', headerName: 'Qty', width: 150 },
    { field: 'col6', headerName: 'Rate', width: 150 },
    { field: 'col7', headerName: 'Amount', width: 150 },
  ];

  return  (
    <div className="container">
      <h1>Invoice</h1>
      <div id='table' className='row'>
        <div style={{ height: 300, width: '100%' }}>
          <DataGrid rows={rows} columns={columns} 
            hideFooterPagination="true"
            hideFooterSelectedRowCount="true"/>
        </div>
      </div>
    </div>
)};

export default Invoice; 

Expectation:
enter image description here

my firefox extension’s background script isn’t being loaded in

i’ve been working on a firefox extension in which i created a devtools panel where i show some DOM data.

I want to refresh the data in my extension panel every time the url of the current tab changes.

I tried creating a background script which looks at tabupdates and then triggers an event that my panel script should be able to listen to, but it seems like the background script isnt being loaded in.

background.js
I’ve tested this by putting some console logs in and outside of the function but nothing ever gets triggered.

browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  // Check if the URL has changed
  if (changeInfo.url) {
    // Send a message to the devtools panel to trigger a refresh
    browser.tabs.sendMessage(tabId, { action: "refresh" });
  }
});

manifest.json

{
  "description": "xxxx",
  "manifest_version": 3,
  "name": "xxxx",
  "version": "xxxx",
  "author": "xxxx",
  "icons": {
    "48": "icons/star.png"
  },
  "background": {
    "scripts": ["/background.js"],
    "persistent": false
  },
  "permissions": ["<all_urls>"],
  "devtools_page": "devtools/devtools-page.html"
}

panel.js listener snippet

browser.runtime.onMessage.addListener((message) => {
  if (message.action === "refresh") {
    browser.devtools.inspectedWindow.eval(`console.log('test');`);
  }
});

Typewriter effect and container sizing

I made a typewriter effect where text “types” onto the screen, backspaces, and writes different text. It works great, but it’s causing the width of any containers below to resize along with the typewriter text’s container.

      document.addEventListener("DOMContentLoaded", function () {
        var phrases = [
          "phrase 1",
          "phrase 2",
          "phrase 3",
          "phrase 4",
        ];

        var speed = 50;
        var flickerSpeed = 600;
        var pauseBetweenPhrases = 2000;

        var container = document.querySelector(".typewriter");
        var bar = document.querySelector(".typewriter-bar");
        var currentPhraseIndex = 0;
        var charIndex = 0;

        function typeWriter() {
          if (charIndex < phrases[currentPhraseIndex].length) {
            container.innerHTML +=
              phrases[currentPhraseIndex].charAt(charIndex);
            charIndex++;
            setTimeout(typeWriter, speed);
          } else {
            setTimeout(function () {
              backspace();
            }, pauseBetweenPhrases);
          }
        }

        function backspace() {
          if (charIndex >= 0) {
            container.innerHTML = phrases[currentPhraseIndex].substring(
              0,
              charIndex
            );
            charIndex--;
            setTimeout(backspace, speed);
          } else {
            currentPhraseIndex = (currentPhraseIndex + 1) % phrases.length;
            setTimeout(typeWriter, speed);
          }
        }

        function flicker() {
          bar.style.visibility =
            bar.style.visibility === "visible" ? "hidden" : "visible";
          setTimeout(flicker, flickerSpeed);
        }

        flicker();
        typeWriter(); // Start the typewriter effect
      });
    
html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  padding: 0;
  font-family: 'Hind Madurai', Arial, Helvetica, sans-serif;
}

h1 {
  font-family: 'Poppins', Arial, Helvetica, sans-serif;
  font-size: 10vh;
  line-height: 1;
}

h2 {
  font-family: 'Signika', Arial, Helvetica, sans-serif;
}

header {
  background-color: white;
  color: #222;
  padding: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

header h2 {
  margin: 0;
  font-weight: 300;
}

nav ul {
  list-style: none;
  padding: 0;
  margin: 0;
  text-align: right;
}

nav li {
  display: inline-block;
  margin-right: 20px;
}

nav a {
  text-decoration: none;
  color: #222;
  transition: color 0.3s ease;
}

nav a:hover {
  color: #3ca6a6;
  text-decoration: underline;
}

@media only screen and (max-width: 600px) {
  header {
    flex-direction: column;
    text-align: center;
  }
  nav {
    margin-top: 10px;
  }
  nav ul {
    flex-direction: column;
    text-align: center;
  }
  nav li {
    display: block;
    margin: 10px 0;
  }
}

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  position: relative;
  margin: 0;
  padding: 0;
}

.content {
  flex: 1;
}

footer {
  background-color: #026773;
  color: white;
  text-align: center;
  padding: 10px;
  position: relative;
  z-index: 1;
  margin-top: auto;
}

footer::before {
  content: "";
  position: absolute;
  bottom: 100%;
  left: 0;
  right: 0;
  height: 400px;
  background: url(imgs/layered-peaks-haikei.svg) repeat-x;
  z-index: 2;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.skip-link {
  position: fixed;
  top: 0;
  left: 0;
  background: #fff;
  padding: 10px;
  z-index: 999;
}

.typewriter {
  overflow: hidden;
  max-width: 800px;
  margin: 0 auto;
}

.typewriter-bar {
  border-right: 1px solid #222;
}
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Hind+Madurai:wght@300&family=Poppins:ital,wght@0,600;1,600&family=Signika:wght@300;400&display=swap"
      rel="stylesheet"
    />
    <div class="skip-link">
      <a class="visually-hidden" href="#main-content">Skip to Main Content</a>
    </div>
    <header>
      <h2>Name</h2>
      <nav>
        <ul>
          <li><a href="#">Welcome</a></li>
          <li><a href="#">Selected Work</a></li>
          <li><a href="#">Resume</a></li>
        </ul>
      </nav>
    </header>
    <main id="main-content" class="container">
      <div class="content">
        <div class="typewriter-container">
          <h1>
            I'm a <span class="typewriter"></span
            ><span class="typewriter-bar"></span>
          </h1>
        </div>
        <h2>Hi there!</h2>
      </div>
    </main>
    <footer></footer>
    

I’ve tried wrapping the typewriter portion inside it’s own container/div and assigning a width to that, but any subsequent containers still resize with it.

Element is invisible but `getComputedStyle` says otherwise

I’m interacting with a React web app which sometimes hides an element within the page.

I’m trying to detect if that element is visible or not, using an injected script onto the page.

Problem is: even though the element is visually hidden, as per my eyes, it’s still on the DOM, and:

getComputedStyle(temp0).visibility
"visible"

getComputedStyle(temp0).display
"block"

getComputedStyle(temp0).opacity
"1"

I’ve also tried for temp0 parents, up to 3 level.

Any ideas why it’s not working?

Next-auth token bearer authorization

I was creating my own jwt, but I started using next-auth, so it generated one automatically and I stopped creating my own, as there was no need.
The problem is that this generated token does not work to pass as authorization for a post. What could I do?

this is the code for creating a context in the database, in the headers, I am passing a Bearer ${token}, which has the information of a jwt automatically generated by next-auth, but as said, it does not work.

const token = Cookie.get('next-auth.session-token')

async function createContent(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    try {
      const formData = new FormData(event.currentTarget)
  
      const contentBody = await api.post('/content', {
        content: formData.get('content'),
      }, {
        headers: {
          Authorization: `Bearer ${token}`
        }
      })
      setClicked(true);
      setId(contentBody.data.contentCreated.id)
    } catch (error) {
      console.error("Erro ao criar conteúdo:", error);
    }
    
  }  

I’m trying to create a simple Google dino game but I’m only getting the title of the game and a gray box

I’m new to JavaScript and I’m trying to create a simple Google dino game but I have been facing problems in that I can’t see the dinosaur nor the cacti. Upon opening the live server for the HTML file and get into the new window for the game, I clicked inspect on the window to see what exactly the error was at. The errors are found on the lines in which I use context.drawImage as shown below:

//dino
    velocityY += gravity; //velocity of jumping upwards weighed down with gravity
    dino.y = Math.min(dino.y + velocityY, dinoY); // applies gravity to current dino.y, so that dino doesn't exceed ground level
    context.drawImage(dinoImg, dino.x, dino.y, dino.width, dino.height);

    //cactus
    for (let i = 0; i <= cactusArray.length; i++) {
        cactusArray[i].x += velocityX;
        context.drawImage(cactusArray[i].img, cactusArray[i].x, cactusArray[i].y, cactusArray[i].width, cactusArray[i].height);

        if (detectCollision(dino, cactusArray[i])) {
            gameOver = true;
            dinoImg.src = "./img/dino-dead.png";
            dinoImg.onload = function() { 
                context.drawImage(dinoImg, dino.x, dino.y, dino.width, dino.height);
            }
        }
    }

In addition, these errors only occurred in function update(){…}

I have already ensured that all the images are placed correctly into the same folder. Moreover, I placed console.log(“Dino image loaded”); the line before context.drawImage. Even then, the message was not logged, so I do believe that there might be an issue with the image path or loading process.

React E-Commerce Product Page

I’m trying to make a logic that will display a page with specific product (along with its details), which is previously clicked on. I am having some trouble understanding how can I achieve this. I assume I should make a new component which will render the product and its details, but can’t seem to understand how to do that.

import React, { useEffect, useMemo, useState } from "react";
import "./VinylClocks.css";
import Navbar from "./Navbar.jsx";
import Footer from "./Footer.jsx";
import SortButton from "./SortButton.jsx";
import { data } from "./data.js";
import Product from "./Product.jsx";
import FilterButton from "./FilterButton.jsx";

export default function VinylClocks() {
  const [dataList, setDataList] = useState([]);
  const [selectedCategory, setSelectedCategory] = useState("");
  const [sortOption, setSortOption] = useState("latest");

  // Add default value on page load
  useEffect(() => {
    setDataList(data);
  }, []);

  // Function to get filtered and sorted list
  function getFilteredAndSortedList() {
    let filteredList = dataList;
    // Filter by category
    if (selectedCategory) {
      filteredList = dataList.filter((item) => item.cat === selectedCategory);
    }

    // Sort by 
    switch (sortOption) {
      case "highToLow":
        filteredList.sort((a, b) => b.price - a.price);
        break;
      case "lowToHigh":
        filteredList.sort((a, b) => a.price - b.price);
        break;
        case "oldest":
          filteredList.sort((a, b) => a.id - b.id);
        break;

      default:
        // Latest or default sorting
        case "latest":
        filteredList.sort((a, b) => b.id - a.id);
        break;
    }

    return filteredList;
  }

  // Avoid duplicate function calls with useMemo
  var filteredAndSortedList = useMemo(getFilteredAndSortedList, [
    selectedCategory,
    sortOption,
    dataList,
  ]);

  function handleCategoryChange(option) {
    setSelectedCategory(option);
  }

  function handleSortChange(option) {
    setSortOption(option);
  }

  return (
    <>
      <header className="header">
        <Navbar />
      </header>
      <div className="vc-container">
        <div className="filter-sort">
          <FilterButton onFilterChange={handleCategoryChange}/>
          <SortButton onSortChange={handleSortChange} />
        </div>
        <div className="products-container">
          <div className="catSort-container">
              <form onChange={handleCategoryChange} className="cat-form" action="#">
                <h5>Categories:</h5>
                <input type="checkbox" checked={selectedCategory === ""} onChange={() => setSelectedCategory("")} id="all" className="radio-input" name="" value=""></input>
                <label htmlFor="all">All</label><br/>
                <input type="checkbox" checked={selectedCategory === "movies"} onChange={() => setSelectedCategory( "movies")} id="movies" className="radio-input" name="" value="movies"></input>
                <label htmlFor="movies">Movies</label><br/>
                <input type="checkbox" checked={selectedCategory === "music"} onChange={() => setSelectedCategory("music")} id="music" className="radio-input" name="" value="music"></input>
                <label htmlFor="music">Music</label><br/>
                <input type="checkbox" checked={selectedCategory === "sport"} onChange={() => setSelectedCategory("sport")} id="sport" className="radio-input" name="" value="sport"></input>
                <label htmlFor="sport">Sport</label><br/>
                <input type="checkbox" checked={selectedCategory === "other"} onChange={() => setSelectedCategory("other")} id="other" className="radio-input" name="" value="other"></input>
                <label htmlFor="other">Other</label>
            </form>
            <form onSortChange={handleSortChange} className="sort-form" action="#">
                <h5>Sort by:</h5>
                <h3>Date:</h3>
                <input type="checkbox" checked={sortOption === "latest"} onChange={() => setSortOption("latest")}  
                       id="latest" className="radio-input" name="" value="latest"></input>
                <label>Latest</label><br/>
                <input type="checkbox" checked={sortOption === "oldest"} onChange={() => setSortOption( "oldest")} 
                       id="oldest" className="radio-input" name="" value="oldest"></input>
                <label>Oldest</label><br/>
                <h3>Price:</h3>
                <input type="checkbox" checked={sortOption === "highToLow"} onChange={() => setSortOption("highToLow")} 
                       id="lowest" className="radio-input" name="" value="highToLow"></input>
                <label>Highest</label><br/>
                <input type="checkbox" checked={sortOption === "lowToHigh"} onChange={() => setSortOption( "lowToHigh")} 
                       id="highest" className="radio-input" name="" value="lowToHigh"></input>
                <label>Lowest</label>
                
            </form>
          </div>
            <div className="products">
              {filteredAndSortedList.map((item, id) => (
                <Product {...item} key={id} />
              ))}
              </div>
        </div>
      </div>
      <Footer />
    </>
  );
}
import React from "react";
import "./VinylClocks.css";
import { Link } from "react-router-dom";

export default function Product({name, price, img}) {
    return (

            <div className="products-card">
                <div className="name-price-tag">
                <Link to="/productpage"><img src={img}/></Link>
                <div className="price-container">
                    <p className="product-name">{name}</p>
                    <span className="price">${price}</span>
                </div>
            </div>   
            </div>
    )        
}

Buttons require 2 taps on mobile – first tap is to trigger hover state. Second is to trigger onClick function

I have a button in my React application. This button is working as intended on the web application, but when I open the app on my Iphone in Chrome, the button requires 2 taps to work. The first click is to trigger the hover effect, and the second is to trigger the onClick effect. I have 2 svg files – one for the hovered state and unhovered state. The svg image will render based on the isHover value. Any help is appreciated!
Here are my codes:

const OptionsButton: FC<Props> = ({ 

    disabled=false, 
    className="options-button", 
    onClick, 
    style={cursor:'pointer',height:'34',width:'34',bottom:'15', right:'15',position:'absolute' } 

}) => {

    const [isHover, setIsHover] = useState(false);

    const handleMouseEnter = () => {
        setIsHover(true);
    }
    const handleMouseLeave = () => {
        setIsHover(false);
    }
    const handleClick = (e : React.MouseEvent<HTMLButtonElement>) => {
        onClick(e);
        e.stopPropagation();
    }
    

    return (
        <>
        { 
            ( isHover )? 
            <OptionsBtnPr 
                className = {className}
                onMouseLeave={handleMouseLeave}
                onClick={handleClick}
                style={style}
            /> :
            <OptionsBtn
                className = {className}
                onMouseEnter={handleMouseEnter} 
                onClick={handleClick}
                style={style}
            />
        }
        </>
    )
}

Making the ‘draw’ condition in Tic Tac Toe Game by JavaScript

Having trouble programming the ‘Draw’ condition in my Tic Tac Toe game on JavaScript. I put in my win conditions as function(), I’m struggling to see how to put in a ‘draw ‘condition. I’d appreciate some help in this regard.
Below is the code…

let boxes = document.querySelectorAll(".box");
let winner = document.querySelector(".hide");
let msg = document.querySelector(".msg");
let playBtn = document.querySelector(".playBtn");
let resetBtn = document.querySelector(".resetBtn");
let indicator_O = document.querySelector(".indicator_O");
let indicator_X = document.querySelector(".indicator_X");
let turnO = true;
let winPatterns = [
    [0, 1, 2],
    [0, 3, 6],
    [0, 4, 8],
    [1, 4, 7],
    [2, 5, 8],
    [2, 4, 6],
    [3, 4, 5],
    [6, 7, 8]
];

boxes.forEach(box => {
    box.addEventListener("click", () => {
        if(turnO){
            box.innerHTML = "O";
            indicator_O.style.display = "none";
            indicator_X.style.display = "block";
            turnO = false;
        }else{
            box.innerHTML = "X";
            box.style.backgroundColor = "#ff00ff";
            indicator_X.style.display = "none";
            indicator_O.style.display = "block";
            turnO = true;
        }
        box.disabled = true;
        checkWinner();
        playAudio();
    })

})

let checkWinner = () =>{
    for(let pattern of winPatterns){
        let pos1 = boxes[pattern[0]].innerText;
        let pos2 = boxes[pattern[1]].innerText;
        let pos3 = boxes[pattern[2]].innerText;

        if(pos1 != "" && pos2 != "" && pos3 != ""){
            if(pos1 === pos2 && pos2 === pos3){
                winner.classList.remove("hide");
                showWinner(pos1);
                turnO = true;
                disabledBox();
            }
        }
    }
}

let showWinner = (winner) => {
        msg.innerText = `Congratulations, user "${winner}" You won the Game!`;
}

let disabledBox = () => {
    for(let box of boxes){
        box.disabled = true;
    }
}

let enabledBox = () => {
    for(let box of boxes){
        turnO = true;
        box.disabled = false;
        box.innerHTML = "";
        winner.classList.add("hide");
        box.style.backgroundColor = "";
        indicator_O.style.display = "block";
        indicator_X.style.display = "none";
    }
}

resetBtn.addEventListener("click", enabledBox);
playBtn.addEventListener("click", enabledBox);

function playAudio(){
    new Audio("mixkit-opening-software-interface-2578.wav").play();
}

I want some help in this regard, that how to add draw condition.

Set default value in a “destination” sortable rank_list group in R Shiny App

I am using the R package sortable in a Shiny App in a similar way as described in this SO post, with the aim of drag-and-dropping values from one “source” rank_list to a “destination” rank_list.

I am facing the following issue : when the app starts, I would like the “destination” rank_list to be already filled with one of the value of the “source” rank_list. This means for the example here below that when the app starts the “Destination” box on the right already contains e.g. the “b” value.

Is it possible to achieve this ? How ? I guess there is probably a js solution, but I am really a newbie…

Thanks in advance for any help

library(shiny)
library(sortable)


ui <- fluidPage(
  
  fluidRow(
    column( 
      width = 6,
      uiOutput("main_rank")
    ),
    column( width =6,
            rank_list(
              text = "Destination",
              labels = c(),
              input_id = "other_rank",
              options = sortable_options(group = "my_shared_group"))
    )
    
    
  ) 
)

server <- function(input,output) {
  
  # Fixed Items as reactive
  rankvars = reactive({
    
    # Fixed Items
    fixedItems = c("a", "b", "c", "d")
    
    # used 
    used = c()
    used = c(unique(input$other_rank))
    
    # refill the list with unique items
    unique(c(setdiff(fixedItems, used),used))
  })
  
  # Make the main rank as a reactive object
  output$main_rank = renderUI({
    fluidRow(
      rank_list(
        text = "Source",
        labels = rankvars(), #colnames(Memory$data), #sample(paste0("list item ", 1:5)),
        input_id = "main_list",
        # be sure to have the same group name
        options = sortable_options(group = "my_shared_group")
      ) 
      
    )
    
  })
  
  
}

shinyApp(ui, server)

Blazor components wont execute any on click event handler. Blazorise ui framework and Blazor server app

@inject IJSRuntime _runtime;

<Button Clicked="OnUniClick">
    <Figure Size="FigureSize.Is128x128" Rounded>
        <FigureImage Source="@Logo" AlternateText="@Name" />
        <FigureCaption>@Name</FigureCaption>
    </Figure>
</Button> 

    @code {

    [Parameter]
    public string Logo { get; set; }

    [Parameter]
    public string Name { get; set; }


    public async Task OnUniClick()
    {
        Console.WriteLine("Clicked");
        await _runtime.InvokeVoidAsync("alert", "This is a test alert");
    }
}

neither the Console.WriteLine or the alert execute when the button is clicked. This is my first time using blazor and I am just trying to get a javascript alert to pop up. I believe that I am following everything described in these three posts: https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-8.0 https://learn.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-8.0
https://blazorise.com/docs/components/button

Im sure its some stupid thing that I am missing but if anyone could please help me out that would be very appreciated.

Upscaling an SVG with sharp and splitting into tiles as PNG – how to optimize performance?

I have a highly detailed SVG (around 100MB) of a world map and I want to scale it up, divide it into tiles (8k res max) and save them as PNGs. The SVG is 1024×1024, I’m doing this process 7 times, each time doubling the resolution.

For this example I’m at level 5, which results in 16 tiles at 8k resolution. It takes around 6 min to generate the tiles and save them to disk with the following script:

const i = 5
const image = sharp(imagePath, { density: 72 * 2 ** i, limitInputPixels: false, unlimited: true })

const { width, height } = await image.metadata()
const chunkCount = Math.ceil(width / maxChunkSize)
const chunkSize = Math.ceil(width / chunkCount)

for (let j = 0; j < chunkCount; j++) {
  for (let k = 0; k < chunkCount; k++) {
    let left = j * chunkSize
    let top = k * chunkSize
    image
      .clone()
      .extract({ left, top, width: chunkSize, height: chunkSize })
      .png({ effort: 1 })
      .toFile(`${destination}/${j}-${k}.png`)
  }
}

I tried using node’s multithreading API:

const workers = []
const maxWorkers = 16
let activeWorkers = 0
const i = 5
const image = sharp(imagePath, { density: 72 * 2 ** i, limitInputPixels: false, unlimited: true })

const { width, height } = await image.metadata()
const chunkCount = Math.ceil(width / maxChunkSize)
const chunkSize = Math.ceil(width / chunkCount)

for (let j = 0; j < chunkCount; j++) {
  for (let k = 0; k < chunkCount; k++) {
    let left = j * chunkSize
    let top = k * chunkSize
    workers.push({
      worker: new Worker('./workers/processChunk.js'),
      data: {
        x: j,
        y: k,
        left,
        top,
        size: chunkSize,
        imagePath,
        scale: 2 ** i,
        destination: `${destination}/chunks`,
      },
    })
  }
}

while (workers.length > 0) {
  if (activeWorkers < maxWorkers) {
    const worker = workers.shift()
    if (worker) {
      activeWorkers++
      console.log('Starting worker', worker.data.x, worker.data.y)
      worker.worker.postMessage(worker.data)
      worker.worker.on('message', (chunk) => {
        console.log('Created chunk', chunk.x, chunk.y, 'at', chunk.path)
        activeWorkers--
      })
    }
  } else {
    await new Promise((resolve) => setTimeout(resolve, 100))
  }
}

// Worker script
// Have to load the image with sharp again, because the Sharp object is not serializable
await sharp(imagePath, { density: 72 * scale, limitInputPixels: false, unlimited: true })
  .extract({ left, top, width: size, height: size })
  .png({ effort: 1 })
  .toFile(`${destination}/${x}-${y}.png`)

This cuts the time down to around 4 min. I expected a larger performance gain, but the fact that each worker has to load the SVG and upscale it again slows things down a lot. On scale levels 6 and 7 this takes hours.

With both examples my CPU is chilling at 20-30% usage. I tried with os.setPriority but it only helped a little bit. Is there a way to further give the script priority?

I can’t load the image only once and pass it to the workers, because it’s a Sharp object and it’s not serializable. I can’t upscale it first and send it, because it will exceed sharp’s pixel limit.

What can I do to optimize the process?

Issue with multiple javascript slideshows on the same page

I’m trying to make multiple slideshows to work with the same piece of javascript code.

Here is my HTML code:

<div class="slider-container">
    
    <div class="slider">
        <div class="slide">...</div>
        <div class="slide">...</div>
        <div class="slide">...</div>
        <div class="slide">...</div>
    </div>
    
    <div class="slider">
        <div class="slide">...</div>
        <div class="slide">...</div>
        <div class="slide">...</div>
        <div class="slide">...</div>
    </div>

</div>

Here is the main CSS:

.slider-container{
    width:50%;
}
        
.slider{
    width:100%;
    position:relative;
    padding-bottom:75%; /* aspect-ratio 4/3 */
}
        
.slide{
    width:100%;
    position:absolute;
    top:0;
    left:0;
    opacity:0;
    transition:opacity 0.5s ease-in;
}
        
.slide:first-child{
    opacity:1;
}

and here is the javascript :

const sliderContainer = document.querySelector(".slider-container");
const slide = sliderContainer.querySelectorAll(".slide");
let currentSlide = 0;
        
function changeSlide() {
    slide[currentSlide].style.opacity = 0;
    currentSlide = (currentSlide + 1) % slide.length;
    slide[currentSlide].style.opacity = 1;
}
        
function startSlider() {
    intervalId = setInterval(changeSlide, 2000);
}
        
function stopSlider() {
    clearInterval(intervalId);
}
        
startSlider();
sliderContainer.addEventListener("mouseenter", stopSlider);
sliderContainer.addEventListener("mouseleave", startSlider);

This javascript works well for one slideshow but II can’t get more than one to work on the same page. Ideally, I would like to keep the same class for all the slideshows and obviously make the enter/leave mouse events work individualy for each of them. Any help ?