HTML and Javascript: Clock number orbit animation is lagging after 1s

First time poster (please be gentle)

Using HTML/Javascript canvas I have drawn the 12 numbers of the clock face, and I am now making them rotate in a circle, like orbiting planets. The code is silky smooth for about 0.5 seconds with console.log showing 7ms interval between logs, but then it slows until it logs at 30ms intervals after just two rotations, with further lag beyond this. I have also tried with setTimeout and setInterval but I can’t figure out how to stop this lag and get a consistent timing.

Thank you <3

HTML:
<canvas id="clock" width="400px" height="400px"></canvas>

const clk = document.getElementById("clock").getContext("2d");

clk.font = "20px Arial";
clk.strokeStyle = "black";
clk.textAlign = "center";

let rotationCounter = 0;       // increment rotation for each function call

function drawNumbers() {
    clk.clearRect(0, 0, 400, 400);      // draw the clock base
    clk.fillStyle = "lightgrey";
    clk.arc(200, 200, 180, 0, Math.PI * 2);
    clk.fill();

    clk.fillStyle = "black";
    clk.translate(200, 200);       // set origin to center, for drawing numbers
        
                                        // draw the 12 numbers
    for (let i = 1; i < 13; i++) {
        let ang = (i * 30) + rotationCounter;
        clk.rotate((ang * Math.PI) / 180);    
        clk.translate(0, -150);                 
        clk.rotate((-ang * Math.PI) / 180)       
        clk.fillText(i.toString(), 0, 0);        
        clk.rotate((ang * Math.PI) / 180)
        clk.translate(0, 150);
        clk.rotate((-ang * Math.PI) / 180);

    }

    clk.translate(-200, -200);      // return canvas to original position

    rotationCounter += 1;

    if (rotationCounter >= 720) {
        return;                         // escape
    }

    window.requestAnimationFrame(drawNumbers);

}

window.requestAnimationFrame(drawNumbers);      // initialise

what does “Uncaught ReferenceError: createCookie is not defined,” mean?

On my website, when I press the submit button it spits back this error “Uncaught ReferenceError: createCookie is not defined” at line 61, what do I do to fix it? for context,
this is the full HTML script:

gggggggg.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mestre de Inglês - Curso Completo</title>
</head>
<body>
  <h1 align=center style="height: 20px;">Mestre de Inglês - Curso Completo</h1>
  <h2 align=center style="height: 25px;">Simples, Facil, e Grátis</h2>
  <h1 align=center style="height: 75px;">Aprenda Inglês dentro de um ano</h1>
  
  <div>
    <div>
      <h2 align=center style="height: 20px;">Aplicar para participação</h2>
    </div>
    <div>
      <h3 align=center style="height: 10px;">Email</h2>
    </div>
    
    <Email>
      <div style="height: 10px; display: flex; flex-direction: row; align-items: center; justify-content: center;">
        <input type="email" name="Email" id="Email" autocomplete="on">
      </div>
    </Email>

    <div>
      <h3 align=center style="height: 10px;">Username</h2>
    </div>

    <Username>
      <div style="height: 10px; display: flex; flex-direction: row; align-items: center; justify-content: center;">
        <input type="text" name="Username" id="Username" maxlength="20" minlength="5" autocomplete="on">
      </div>
    </Username>
    
    <div>
      <h3 align=center style="height: 10px;">Password</h2>
    </div>

    <Password>
      <div style="height: 10px; display: flex; flex-direction: row; align-items: center; justify-content: center;">
        <input type="text" name="Pass" id="Password" maxlength="20" minlength="5">
      </div>
    </Password>
    
    <div>
      <h3 align=center style="height: 10px;">Skill Level</h2>
    </div>

    <Skill style="height: 10px; display: flex; flex-direction: row; align-items: center; justify-content: center;">
      <select name="Skill Level" id="Skill">
      <option value="Noob">Noob</option>
      <option value="Beginner">Beginner</option>
      <option value="Intermidiate">Intermidiate</option>
    </select>
    </Skill>
    
    <Button>
      <div style="display: flex; flex-direction: row; align-items: center; justify-content: center;">
        <input type="button" onclick="createCookie()" value="submit">
      </div>
    </Button>
    
    
  </div>
    
</body>
</html>

this is the script responsible for user login and cookies
gggggggg.js

function createCookie(email,username,pwd,skill){
    let email = document.getElementById("Email");
    let username = document.getElementById("Username");
    let pwd = document.getElementById("Password");
    let skill = document.getElementById("Skill");
  
  
    today = new Date();
    var expire = new Date();
    expire.setTime(today.getTime() + 3600000*24*15);
   
  
    document.cookie = "email="+encodeURI(email.value)+";path=/" + ";expires="+expire.toUTCString();
    document.cookie = "username="+username.value+";path=/" + ";expires="+expire.toUTCString();
    document.cookie = "password="+encodeURI(pwd.value)+";path=/" + ";expires="+expire.toUTCString();
    document.cookie = "skill="+skill.value+";path=/" + ";expires="+expire.toUTCString();
    //can only write one entity at a time (name, pass, skill)
  }  
  
  
  //event handler for page load - runs on every refresh
  window.onload = function(){
  
    document.getElementById('Username').value = uname;
    document.getElementById('Password').value = pass;
    document.getElementById('Skill').value = skill;
    
    console.log(email,username,pwd,skill)
  }

I used a truck load of research for the html and copy-pasted an answer I found here with some edits for the javascript, but when I press the submit button I get back an error saying the function is undefined, any help would be appreciated

i didnt really try much, I emmidiately thought “I should go to stack overflow”

Next.js (React) Component Filter table results without re-rendering input

I have a Next.js client component which shows a data table and has a search bar which can be used to filter the table. The issue is that whenever I type in the search bar, it causes the whole component to re-render, which means the search loses focus and interrupts typing. I have the search debounced but it still has an effect for longer queries.

'use client'

import { useEffect, useState } from "react";
import { PartsTable as PartsRow } from "@/app/lib/definitions";
import PartsTable from "@/app/ui/parts/table";
import NoQueryParamSearch from "../no-query-param-search";

export default function PartSearch({
    onPartSelected
}: {
    onPartSelected?: any
}) {
    const [parts, setParts] = useState([] as PartsRow[]);
    const [search, setSearch] = useState('');
    useEffect(() => {
        fetch(`/api/parts?query=${search}`).then(async (response) => {
            const parts = await response.json();
            setParts(parts);
        });
    }, [search]);
    const handlePartRowClicked = (part: any) => {
        onPartSelected(part);
    }
    const handleQueryChanged = (query: string) => {
        setSearch(query);
    }
    return (
        <div>
            <NoQueryParamSearch placeholder="Search ID, description, year, etc..." onChange={handleQueryChanged} />
            <div className="border border-solid border-gray-200 rounded-md my-2">
                <PartsTable parts={parts.slice(0, 5)} onRowClick={handlePartRowClicked} disableControls />
            </div>
        </div>
    );
}
'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useDebouncedCallback } from 'use-debounce';

export default function NoQueryParamSearch({ placeholder, onChange, autoComplete = true }: { placeholder: string, onChange: any, autoComplete?: boolean }) {

    const handleSearch = useDebouncedCallback((term) => {
        onChange(term);
    }, 300);

    return (
        <div className="relative flex flex-shrink-0">
            <label htmlFor="search" className="sr-only">
                Search
            </label>
            <input
                className="peer block w-96 rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500"
                placeholder={placeholder}
                onChange={(e) => {
                    handleSearch(e.target.value);
                }}
                autoComplete={autoComplete ? undefined : 'off'}
                autoCorrect={autoComplete ? undefined : 'off'}
            />
            <MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
        </div>
    );
}

This needs to be a client component because of onPartSelected. When a row is selected it needs to send that to the parent component.

How can I update this app script to send SMS from rotating Twilio phone numbers?

Currently, I have app script that texts a column of phone numbers, “PHONE_NUMBER” from 1 Twilio number,+18778516645.

I would like to update this apps script to send from a column of Twilio Phone Numbers on a rotating basis. The “Send From” phone number is in column B. The “Send To” phone number is in Column C. The “Send To” number is already implemented in the script. How do I implement the “Send From” numbers?

Also is it necessary to implement a “pause” after ‘X’ messages to respect rate limits? Or would Twilio account for this already? If I should add a pause, how do I add Utilities.Sleep()?

Finally, I would like the script to start with a message box that reminds me of several “checks” to be completed. If I click “yes”, the the script runs. If “no”, then the script is cancelled. How can I do this?

I’m just getting started in coding. Sorry if this is not a great question. Thank you for your help!

// Spreadsheet column names mapped to 0-based index numbers.
var TIME_ENTERED = 0;
var PHONE_NUMBER = 1;
var MESSAGE = 2;
var STATUS = 3;

// Enter your Twilio account information here.
var TWILIO_ACCOUNT_SID = '';
var TWILIO_SMS_NUMBER = '+18778516645';
var TWILIO_AUTH_TOKEN = '';

/**
 * Installs a trigger in the Spreadsheet to run upon the Sheet being opened.
 * To learn more about triggers read: https://developers.google.com/apps-script/guides/triggers
 */
function onOpen1() {
  // To learn about custom menus, please read:
  // https://developers.google.com/apps-script/guides/menus
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Send SMS')
      .addItem('Send to all', 'sendSmsToAll')
      .addToUi();
};  

function onOpen() {
  // To learn about custom menus, please read:
  // https://developers.google.com/apps-script/guides/menus
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('GetReplies')
      .addItem('GetReplies', 'GetReplies')
      .addToUi();
};  

/**
 * Sends text messages listed in the Google Sheet
 *
 */
function sendSmsToAll() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var rows = sheet.getDataRange().getValues();
  
  // The `shift` method removes the first row and saves it into `headers`.
  var headers = rows.shift();
  
  // Try to send an SMS to every row and save their status.
  rows.forEach(function(row) {
    row[STATUS] = sendSms(row[PHONE_NUMBER], row[MESSAGE]);
  });
  
  // Write the entire data back into the sheet.
  sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);
}


/**
 * Sends a message to a given phone number via SMS through Twilio.
 * To learn more about sending an SMS via Twilio and Sheets: 
 * https://www.twilio.com/blog/2016/02/send-sms-from-a-google-spreadsheet.html
 *
 * @param {number} phoneNumber - phone number to send SMS to.
 * @param {string} message - text to send via SMS.
 * @return {string} status of SMS sent (successful sent date or error encountered).
 */
function sendSms(phoneNumber, message) {
  var twilioUrl = 'https://api.twilio.com/2010-04-01/Accounts/' + TWILIO_ACCOUNT_SID + '/Messages.json';

  try {
    UrlFetchApp.fetch(twilioUrl, {
      method: 'post',
      headers: {
        Authorization: 'Basic ' + Utilities.base64Encode(TWILIO_AUTH_TOKEN)
      },
      payload: {
        To: phoneNumber.toString(),
        Body: message,
        From: TWILIO_SMS_NUMBER,
      },
    });
    return 'sent: ' + new Date();
  } catch (err) {
    return 'error: ' + err;
  }
}

How to make the same request multiple times?

I have to make an API request to an endpoint and see if it returns a specific object. How can I make the same API request multiple times over a 60 second period until it returns that object using playwright? Here’s what I tried

await this.page.waitForResponse(
    async resp =>
        resp.url().includes("test") &&
        resp.status() === 200 &&
        ((await resp.json()) as any).libraries.some((lib: { name: string }) => lib.name === libraryName),
        {
            timeout: 60000
        }
);

But I am pretty sure it doesn’t make the same request multiple times

Persist dark mode in Nuxt 3

I use useDark from VueUse to implement the dark mode. When I go to another route the dark mode doesn’t persist, possibly due to SSR, since the server doesn’t know about the current mode.

I tried to solve the issue by using useCookie to store the mode in cookie, but it doesn’t seem to work:

const darkModeCookie = useCookie("dark-mode", {
  path: "/",
  sameSite: "lax",
});

const isDark = useDark({ disableTransition: false });
const toggleDark = useToggle(isDark, () => {
  darkModeCookie.value = isDark.value ? "true" : "false";
});

if (darkModeCookie.value === true) {
  isDark.value = true;
} else {
  isDark.value = false;
}

I’m looking for a possible solution.

How to send data to GAS function?

I am using Google Apps Script to write a small web app where I can upload an image (Business Cards) which is then stored in my Google Drive.
The image should be utilized by ChatGPT which extract the information and save them into Google Spreadsheets.

I have this Index.html where I can select and image and upload it. It saved to my Google Drive successfully but I get an error Uncaught at uploadImageToServer (Code:34).

Here is the HTML

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      #status {
        margin-top: 10px;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <form id="uploadForm">
      <input type="file" id="fileInput" accept="image/*">
      <input type="button" value="Upload" onclick="uploadImage()">
    </form>
    <pre id="result"></pre>
    <div id="status"></div>
    <script>
      function uploadImage() {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        const statusDiv = document.getElementById('status');

        if (!file) {
          alert("Please select a file before uploading.");
          return;
        }

        statusDiv.textContent = "Uploading image, please wait...";

        const reader = new FileReader();

        reader.onload = function(e) {
          const dataUrl = e.target.result;
          console.log("Data URL:", dataUrl);

          if (dataUrl) {
            google.script.run.withSuccessHandler(function(response) {
              document.getElementById('result').textContent = JSON.stringify(response, null, 2);            
            }).uploadImageToServer(dataUrl);

            statusDiv.textContent = "Image uploaded successfully. Processing...";
          } else {
            console.error("Failed to generate Data URL.");
            statusDiv.textContent = "Error uploading image.";
          }
        };

        reader.onerror = function(error) {
          console.error("Error reading file:", error);
          statusDiv.textContent = "Error uploading image.";
        };

        reader.readAsDataURL(file);
    </script>
  </body>
</html>

I put a console.log and Data URL exists but it is somehow not pass to my GAS function uploadImageToServer().

Here is my Code.gs script

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function uploadImageToServer(dataUrl) {
  Logger.log("Upload function started");

  try {
    Logger.log("Received dataUrl: " + dataUrl);

    if (!dataUrl) {
      throw new Error("No dataUrl received");
    }

    const matches = dataUrl.match(/^data:(.+);base64,(.*)$/);
    if (!matches || matches.length !== 3) {
      throw new Error('Could not parse the data URL.');
    }

    const contentType = matches[1];
    const data = matches[2];
    const blob = Utilities.newBlob(Utilities.base64Decode(data), contentType, "uploaded_image");

    const folder = DriveApp.getFolderById('xxx');
    const file = folder.createFile(blob);
    Logger.log("Image uploaded to Google Drive");

    const imageUrl = file.getUrl();
    Logger.log("Image URL: " + imageUrl);

    // Call Google Cloud Vision API
    const apiKey = 'xxx';
    const visionUrl = `https://vision.googleapis.com/v1/images:annotate?key=${apiKey}`;
    const image = UrlFetchApp.fetch(imageUrl).getBlob().getBytes();
    const encodedImage = Utilities.base64Encode(image);
    Logger.log("Encoded image for Vision API");

    const visionPayload = {
      requests: [
        {
          image: {
            content: encodedImage
          },
          features: [
            {
              type: "TEXT_DETECTION"
            }
          ]
        }
      ]
    };

    const visionOptions = {
      method: "post",
      contentType: "application/json",
      payload: JSON.stringify(visionPayload)
    };

    const visionResponse = UrlFetchApp.fetch(visionUrl, visionOptions);
    Logger.log("Vision API response received");

    const visionData = JSON.parse(visionResponse.getContentText());
    Logger.log("Vision API data parsed: " + JSON.stringify(visionData));

    if (!visionData.responses || !visionData.responses[0] || !visionData.responses[0].fullTextAnnotation) {
      throw new Error("No text found by Vision API");
    }

    const text = visionData.responses[0].fullTextAnnotation.text;
    Logger.log("Extracted text: " + text);

    // Call OpenAI API
    const openaiApiKey = 'xxx';
    const openaiUrl = 'https://api.openai.com/v1/engines/davinci/completions';
    const prompt = `Extract the information from this business card text:nn${text}nnThe information should be in JSON format with keys 'Company', 'Name', 'Title', 'Phone', 'Website', 'Email', and 'Address'.`;

    const openaiPayload = {
      prompt: prompt,
      max_tokens: 150,
      n: 1,
      stop: null,
      temperature: 0.5
    };

    const openaiOptions = {
      method: 'post',
      contentType: 'application/json',
      headers: {
        'Authorization': `Bearer ${openaiApiKey}`
      },
      payload: JSON.stringify(openaiPayload)
    };

    const openaiResponse = UrlFetchApp.fetch(openaiUrl, openaiOptions);
    Logger.log("OpenAI API response received");

    const openaiData = JSON.parse(openaiResponse.getContentText());
    Logger.log("OpenAI API data parsed: " + JSON.stringify(openaiData));

    const jsonResponse = openaiData.choices[0].text.trim();
    Logger.log("OpenAI response parsed: " + jsonResponse);

    const parsedData = JSON.parse(jsonResponse);

    if (!parsedData.Company || !parsedData.Name) {
      throw new Error("Parsed data incomplete");
    }
    
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    const row = [parsedData.Company, parsedData.Name, parsedData.Title, parsedData.Phone, parsedData.Website, parsedData.Email, parsedData.Address];
    sheet.appendRow(row);
    Logger.log("Data appended to Google Sheet");

    return parsedData;
  } catch (error) {
    Logger.log("Error: " + error.message);
    throw error;
  }
}

Here is the Execution Log when I run this code

12:37:37 AM Notice Execution started
12:37:06 AM Info Upload function started
12:37:06 AM Info Received dataUrl: undefined
12:37:06 AM Info Error: No dataUrl received
12:37:37 AM Error

Error: No dataUrl received
uploadImageToServer @ Code.gs:12

Import a ES6 module in Angular

I’m trying to use H5 to convert C# code into JS and then use the result in an Angular project, but I’m having trouble importing the generated JS module.

I wrote a simple C# class like this:

namespace TW3.Shared {
    public class Class1 {
        public int Sum(int x, int y) {
            return x + y;
        }
    }
}

…and using the following h5.json configuration file:

{
    "module": {
        "type": "ES6",
        "name": "XXX"
    },
    "generateTypeScript": true
}

…I got these 2 files:

// SharedJS.js

/**
 * @compiler H5 24.2.45744+d8342060ba1fec4b36b7d0c2865c74ad945e2889
 */
H5.assemblyVersion("SharedJS","1.0.0.0");
H5.assembly("SharedJS", function ($asm, globals) {
    "use strict";

    (function () {
        var XXX = { };
        H5.define("TW3.Shared.Class1", {
            $metadata : function () { return {"att":1048577,"a":2,"m":[{"a":2,"isSynthetic":true,"n":".ctor","t":1,"sn":"ctor"},{"a":2,"n":"Sum","t":8,"pi":[{"n":"x","pt":System.Int32,"ps":0},{"n":"y","pt":System.Int32,"ps":1}],"sn":"Sum","rt":System.Int32,"p":[System.Int32,System.Int32],"box":function ($v) { return H5.box($v, System.Int32);}}]}; },
            $scope: XXX,
            $module: "XXX",
            methods: {
                Sum: function (x, y) {
                    return ((x + y) | 0);
                }
            }
        });
        export {XXX};
    }) ();

});
// SharedJS.d.ts

namespace TW3.Shared {
    interface Class1 {
        Sum(x: number, y: number): number;
    }
    interface Class1Func extends Function {
        prototype: Class1;
        new (): Class1;
    }
    var Class1: Class1Func;
}

So, what do I need to do in order to call Class1.Sum() inside Angular?

I tried adding the generated .js and .ts files to angular.json:

"scripts": [
  "src/assets/js/SharedJS/h5.js",
  "src/assets/js/SharedJS/h5.meta.js",
  "src/assets/js/SharedJS/SharedJS.js",
  "src/assets/js/SharedJS/SharedJS.meta.js",
  "src/assets/js/SharedJS/SharedJS.d.ts"
]

but if I do that, then I get an error when I try to build the angular project:

X [ERROR] Unexpected "export"

    angular:script/global:scripts.js:51675:8:
      51675 │         export {XXX};
            ╵         ~~~~~~

How to customize elements in a #shadow-root (open)

I am trying to customize a third-party code to match the colors on our Squarespace website. The third party has an open #shadow-root on their Footer, but I’m still unable to customize part of the code. What I’m trying to do is change the background-color to #ff0000;

I’ve tried to target .formFooter in CSS, but from what I’ve tried, it hasn’t worked. Here is their code below through Inspector:

<div>
 #shadow-root (open)
 <style></style>
<div class="formFooter-heightMa sk"></div>
<div class="formFooter f6 branding21 ">∞</div>flex)
== $0
<style id="action-styles">
</style>
</div>
</form>

And here is their style:

.formFooter {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    background-color: #0A1551;

Issues in Chrome setting scrollLeft when devicePixelRatio is not an integer

When I set element.scrollLeft while the devicePixelRatio is 1.25 (on Windows > Display > Scale = 125%), Chrome seems to round my values to what appear to be multiples of 0.8. Firefox does not exhibit the same behavior.

Here is an example script:

element.scrollLeft = 1;
console.log(element.scrollLeft); // 0.8
element.scrollLeft = 2;
console.log(element.scrollLeft); // 2.4
element.scrollLeft = 3;
console.log(element.scrollLeft); // 3.2
element.scrollLeft = 4;
console.log(element.scrollLeft); // 4

This rounding is causing me issues in some code I am developing. The goal is to maintain the visual position of something in a scrolled container while that container is moving. For example, if the box moves 10 pixels to the right on the screen, scroll it the same amount, so that the contents look like they haven’t moved. Kind of like looking out of a window.

The code works well at a devicePixelRatio of 1 or 2, but not at 1.25. The rounding causes the element’s position to jitter back-and-forth a fraction of a pixel.

I know that sub-pixel precision is expected in Element.scrollLeft, as part of the spec. I would like to know how to to account for it in some way, so that I can set the scrollLeft and know that it is maintaining my contents’ position.


GIF: https://imgur.com/a/UYE2KsH

Here is a CodeSandbox with a demo of the technique: https://codesandbox.io/p/sandbox/hardcore-lichterman-xydrpy

Verify the sequence of Time in JavaScript Selenium

I have a list of time as folllowing,
‘a day ago,
10 days ago,
3 months ago,
6 months ago’.

How to verify that these dates are ascending? in javascript Selenium

I cant think of trying anything, I have some ideas for dd/mm/yyyy or mm/dd/yyyy. But this Text format of the time made me confused.

Quantity field doesn’t work in a custom plugin for WooCommerce and Elementor

I’m making a plugin for Elementor. It must show a product from WooCommerce on the page. Picture, name, price, quantity window, +/- buttons, add to cart button for each product. The quantity window does not work, the quantity 1 initial digit is indicated there, it accepts it, and that’s it. If I enter the number 3, it will not record anything and add 1.

Build

  1. custom-product-widget.php
  2. script.js
  3. style.css
  4. folder “widgets”:
    • class-custom-product-widget.php

The main pludin file custom-product-widget.php:

<?php
/*
Plugin Name: Custom Product Widget for Elementor v1.00000000
Description: Додає віджет для Elementor, який відображає WooCommerce продукти.
Version: 1.00000000
Author: blvckfamily
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

// Connecting the necessary files
function cpw_enqueue_scripts() {
    wp_enqueue_style( 'cpw-style', plugin_dir_url( __FILE__ ) . 'style.css' );
    wp_enqueue_script( 'cpw-script', plugin_dir_url( __FILE__ ) . 'script.js', array('jquery'), null, true );
}
add_action( 'wp_enqueue_scripts', 'cpw_enqueue_scripts' );

// Widget registration
function cpw_register_widget() {
    require_once( plugin_dir_path( __FILE__ ) . 'widgets/class-custom-product-widget.php' );
    ElementorPlugin::instance()->widgets_manager->register_widget_type( new Custom_Product_Widget() );
}
add_action( 'elementor/widgets/widgets_registered', 'cpw_register_widget' );

The script.js file:

jQuery(function($) {
    $('.product-card').each(function() {
        var minusBtn = $(this).find('.minus'),
            plusBtn  = $(this).find('.plus'),
            qtyInput = $(this).find('.qty'),
            addToCartBtn = $(this).find('.add-to-cart');

        function updateAddToCartButton(qtyInput) {
            qtyInput.closest('.product-card').find('.add-to-cart').attr('data-quantity', qtyInput.val());
        }

        minusBtn.on('click', function () {
            var currentVal = parseInt(qtyInput.val());
            if (currentVal > 1) {
                qtyInput.val(currentVal - 1);
                updateAddToCartButton(qtyInput);
            }
        });

        plusBtn.on('click', function () {
            var currentVal = parseInt(qtyInput.val());
            qtyInput.val(currentVal + 1);
            updateAddToCartButton(qtyInput);
        });

        qtyInput.on('input', function () {
            updateAddToCartButton(qtyInput);
        });

        addToCartBtn.on('click', function() {
            var productId = $(this).data('product-id');
            var quantity = $(this).siblings('.quantity-box').find('.qty').val();
            $.ajax({
                url: wc_add_to_cart_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'woocommerce_ajax_add_to_cart',
                    product_id: productId,
                    quantity: quantity
                },
                success: function(response) {
                    if (response.error && response.product_url) {
                        window.location = response.product_url;
                    } else {
                        $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $this]);
                    }
                }
            });
        });
    });
});

The style.css file:

.custom-product-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
}

.product-card {
    text-align: center;
    padding: 10px;
    border: 1px solid #eee;
    margin-bottom: 15px;
}

.product-card img {
    max-width: 100%;
    height: auto;
}

.product-card h3 {
    font-size: 16px;
    margin: 10px 0;
}

.product-card .quantity-box {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 10px 0;
}

.product-card .quantity-box input {
    width: 50px;
    text-align: center;
    margin: 0 10px;
}

.product-card .add-to-cart {
    background-color: #000;
    color: #fff;
    padding: 10px 20px;
    text-transform: uppercase;
    border: none;
    cursor: pointer;
}

.product-card .add-to-cart:hover {
    background-color: #333;
}

.quantity-box button {
    background-color: #ddd;
    border: none;
    padding: 0 10px;
    cursor: pointer;
}

.quantity-box button:hover {
    background-color: #ccc;
}

/* Remove arrows from number input */
.quantity-box input[type="number"] {
    -moz-appearance: textfield;
    -webkit-appearance: none;
    appearance: none;
}

.quantity-box input[type="number"]::-webkit-inner-spin-button,
.quantity-box input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

In the folder “widgets”, the file “class-custom-product-widget.php”:

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

class Custom_Product_Widget extends ElementorWidget_Base {

    public function get_name() {
        return 'custom_product_widget';
    }

    public function get_title() {
        return __( 'Custom Product Widget', 'cpw' );
    }

    public function get_icon() {
        return 'eicon-product-grid';
    }

    public function get_categories() {
        return [ 'general' ];
    }

    protected function _register_controls() {
        // Add controls here if needed
    }

    protected function render() {
        $args = array(
            'post_type' => 'product',
            'posts_per_page' => 10
        );
        $products = new WP_Query( $args );
        if ( $products->have_posts() ) {
            echo '<div class="custom-product-grid">';
            while ( $products->have_posts() ) {
                $products->the_post();
                global $product;
                ?>
                <div class="product-card">
                    <?php if ( has_post_thumbnail() ) { ?>
                        <img src="<?php echo esc_url( get_the_post_thumbnail_url() ); ?>" alt="<?php the_title(); ?>" />
                    <?php } ?>
                    <h3><?php the_title(); ?></h3>
                    <div class="quantity-box">
                        <button type="button" class="minus">-</button>
                        <input type="number" class="input-text qty text" step="1" min="1" value="1" />
                        <button type="button" class="plus">+</button>
                    </div>
                    
                    <a href="<?php echo esc_url( $product->add_to_cart_url() ); ?>" class="add-to-cart button">
                        <?php echo esc_html( $product->add_to_cart_text() ); // Button "Add to cart" ?>
                    </a>

                </div>
                <?php
            }
            echo '</div>';
            wp_reset_postdata();
        }
    }
}

I’ve done so many things, even tried searching stackoverflow.

`drawArrays` not working when mode is POINTS

With the following code:

// where gl is WebGL2RenderingContext, but I tried WebGLRenderingContext as well.
gl.drawArrays(gl.POINTS, 0, points.length / 2);
#version 300 es

in vec2 position;

void main() {
    gl_Position = vec4(position, 0.0, 1.0);
}

My canvas isn’t rendering any of the points on the canvas whatsoever. However, when mode, the first argument, is gl.LINES, the interconnected points render just fine. I also noticed this issue only happens in hardware acceleration mode in browsers for certain GPUs—it worked fine on Linux but not on Windows.

How can I render the points correctly?

cypress javascript function for sorting based on event priority then event date and time

I need to validate sorting of an Event column with this value:
Event Name
Event date
Event time
your text
Order of column when sorted is based on priority of event name ( I > U > P)
then date and time should be sorted correctly based on ascending/descending.

I need a javascript code to run in Cypress to validate if sorting is correct based on event priority then date/time.
Chatgpt gave me some but it did not work

function validateEvents(events, priorityOrder, sortOrder = 'desc') {
const getDate = (dateStr) => new Date(dateStr);

// Compare function for date and time sorting
const compareDates = (a, b) => {
const dateA = getDate(a.date);
const dateB = getDate(b.date);
if (dateA.getTime() === dateB.getTime()) {
  return sortOrder === 'asc' ? a.time.localeCompare(b.time) : b.time.localeCompare(a.time);
}
return sortOrder === 'asc' ? dateA - dateB : dateB - dateA;
};

let previousPriorityIndex = -1;

// Group by event name and check priority and sorting
for (const event of events) {
const currentPriorityIndex = priorityOrder.indexOf(event.name);

// Ensure event names follow the priority order
if (currentPriorityIndex < previousPriorityIndex) {
  return false;
}

previousPriorityIndex = currentPriorityIndex;

// Sort events within the same name group
const groupedEvents = events.filter(e => e.name === event.name).sort(compareDates);

// Ensure events are in correct date/time order
if (!groupedEvents.every((e, i) => e === event)) {
  return false;
}
}
return true;
}

// Usage in Cypress test
cy.get('selector-for-events').then(($elements) => {
const events = [...$elements].map(el => ({
name: el.querySelector('.event-name').innerText.trim(),
date: el.querySelector('.event-date').innerText.trim(),
time: el.querySelector('.event-time').innerText.trim()
 }));

 const priorityOrder = ['I', 'U', 'P']; // Define the priority order
 const sortOrder = 'desc'; // or 'asc'

  expect(validateEvents(events, priorityOrder, sortOrder)).to.be.true;
});`

HTML Canvas Arc only drawing outer sliver of circle

I am working on a visualizer to show the area on a graph that is currently being checked for points from a starting points POV, and within a limited FOV. I am trying to show this with canvas arc, however, instead of filling in the arc area, it is filling in the outermost sliver of the arc.

I have tried drawing lines using the rad angle, and those are successful, it is only the arc that does not appear to work correctly. I would expect that it would complete the arc within the two angles, instead of seemingly subtracting the area of the triangle within the arc.

let startingLocation = {x:0,y:0};
let angle = 18.43494882292201;
let fovAngle = 30;
let r = 100;
//Convert start and end angles to radians
let theta = angle * (Math.PI / 180);
let theta2 = (angle + fovAngle) * (Math.PI / 180);

//Draw start to end arc area
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(startingLocation.x, startingLocation.y, r, theta, theta2, false);
ctx.fill();
ctx.closePath();

JSFiddle