Using ng-class to remove in Angular

I’m using ng-class to add a disabled class to a CTA in certain scenarios, but there is a split second delay before it gets applied, which means if the user is quick enough they can click the CTA. See code below:

 <div ng-class="{'disabled-class': !testFunction(name)}">

If I was to change it to the below, obviously I can use JQuery / Vanilla JS to remove the class when needed, but is there a way to do something similar in Angular?

<div class="disabled-class">

Dynamically created SVG element not rendered by the browser

Similar Dynamically created SVG elements are not rendered by the browser but my code already corresponded to their solutions.

I am trying to use SVG markup in my HTML in order to render some texts. The problem was very tricky because I just realized that the issue is when generating the SVG programmatically.

The markup

What I want to end up in my page is this fragment of code:

html {
  background-color: #151515;
  color: white;
  background-image: url(https://picsum.photos/id/210/500/500);
  background-size: 500px 500px;
}

body {
  margin: 0;
  padding: 0;
}

#navbar {
  font-family: 'Katahdin Round', 'Arial Narrow Bold', sans-serif;
  list-style: none;
}

#navbar .letter-path {
  fill: white;
  stroke: transparent;
  stroke-width: 2.5;
  stroke-dasharray: 400;
  stroke-dashoffset: 400;
  transition: fill 0.1s ease-in 0.1s, stroke 0.25s ease-out, stroke-dashoffset 0s linear 0.25s;
}

#navbar svg:hover .letter-path {
  fill: rgba(255, 255, 255, 0.3);
  stroke: white;
  stroke-dashoffset: 0;
  transition: fill 0.25s ease-out, stroke 0s, stroke-dashoffset 0.5s ease-in 0.1s;
}

#blurred-clip-background {
  clip-path: url(#blur-letter-paths);
  filter: blur(3px);
}
<body>
  <ul id="navbar" class="relative inline-block">
    <li><svg xmlns="http://www.w3.org/2000/svg" height="100" viewBox="0 0 279.468 100">
                <defs>
                    <path id="letter-path-0" class="letter-path-T"
                        d="M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z"
                        transform="translate(0, 0)"></path>
                    <path id="letter-path-1" class="letter-path-E"
                        d="M56.6 74.805c0 3.127-1.564 4.69-4.692 4.69H7.191c-3.127 0-4.691-1.563-4.691-4.69V7.051c0-3.127 1.563-4.69 4.69-4.69h44.718c3.128 0 4.691 1.563 4.691 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H22.2v9.694h21.89c3.127 0 4.69 1.564 4.69 4.69v8.861c0 3.127-1.563 4.69-4.69 4.69H22.2v9.8h29.707c3.128 0 4.691 1.563 4.691 4.69z"
                        transform="translate(69.187, 0)"></path>
                    <path id="letter-path-2" class="letter-path-X"
                        d="M38.05 23.104l11.987-17.72c1.356-1.981 3.336-3.024 5.733-3.024h13.342c3.857 0 5.212 2.502 3.023 5.733L49.933 40.615l22.515 33.148c2.19 3.231.833 5.733-3.023 5.733H56.083c-2.397 0-4.378-1.042-5.734-3.023l-12.3-18.241-12.404 18.241c-1.356 1.98-3.335 3.023-5.733 3.023H6.57c-3.857 0-5.212-2.502-3.023-5.733l22.515-33.148L3.86 8.093C1.67 4.862 3.027 2.36 6.883 2.36h13.342c2.396 0 4.378 1.043 5.733 3.023z"
                        transform="translate(131.286, 0)"></path>
                    <path id="letter-path-3" class="letter-path-T"
                        d="M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z"
                        transform="translate(210.281, 0)"></path>
                    <clipPath id="blur-letter-paths">
                        <use href="#letter-path-0"></use>
                        <use href="#letter-path-1"></use>
                        <use href="#letter-path-2"></use>
                        <use href="#letter-path-3"></use>
                    </clipPath>
                </defs>
                <image id="blurred-clip-background" x="-40" y="-16" width="500" height="500"
                    href="https://picsum.photos/id/210/500/500"></image>
                <use href="#letter-path-0" class="letter-path"></use>
                <use href="#letter-path-1" class="letter-path"></use>
                <use href="#letter-path-2" class="letter-path"></use>
                <use href="#letter-path-3" class="letter-path"></use>
            </svg></li>
    <!-- <li>PROJECTS</li>
        <li>CONTACT</li> -->
  </ul>

  <!-- <script src="script.js"></script> -->
</body>

If you take this and paste it inside a page, all is fine and the text is rendered!

Creating the SVG dynamically

But I want to create this content using JavaScript, so I have this:

// Dictionary of paths for each letter
const letterPaths = {
    E: "M56.6 74.805c0 3.127-1.564 4.69-4.692 4.69H7.191c-3.127 0-4.691-1.563-4.691-4.69V7.051c0-3.127 1.563-4.69 4.69-4.69h44.718c3.128 0 4.691 1.563 4.691 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H22.2v9.694h21.89c3.127 0 4.69 1.564 4.69 4.69v8.861c0 3.127-1.563 4.69-4.69 4.69H22.2v9.8h29.707c3.128 0 4.691 1.563 4.691 4.69z",
    T: "M27.934 79.496c-3.127 0-4.69-1.564-4.69-4.69V22.06H7.19c-3.127 0-4.691-1.563-4.691-4.69V7.05c0-3.127 1.563-4.69 4.69-4.69h51.807c3.127 0 4.69 1.563 4.69 4.69v10.32c0 3.127-1.563 4.69-4.69 4.69H42.944v52.744c0 3.127-1.563 4.69-4.69 4.69z",
    X: "M38.05 23.104l11.987-17.72c1.356-1.981 3.336-3.024 5.733-3.024h13.342c3.857 0 5.212 2.502 3.023 5.733L49.933 40.615l22.515 33.148c2.19 3.231.833 5.733-3.023 5.733H56.083c-2.397 0-4.378-1.042-5.734-3.023l-12.3-18.241-12.404 18.241c-1.356 1.98-3.335 3.023-5.733 3.023H6.57c-3.857 0-5.212-2.502-3.023-5.733l22.515-33.148L3.86 8.093C1.67 4.862 3.027 2.36 6.883 2.36h13.342c2.396 0 4.378 1.043 5.733 3.023z",
};

const letterWidths = {
    E: 54.099,
    T: 61.187,
    X: 70.995,
}

// Letter spacing constant
const LETTER_SPACING = 8;
const HEIGHT = 100;

// Function to create SVG for a word
function createWordSVG(word)
{
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
    const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
    clipPath.setAttribute('id', 'blur-letter-paths');
    const image = document.createElementNS('https://www.w3.org/2000/svg', 'image')
    image.setAttribute('id', 'blurred-clip-background');
    image.setAttribute('x', '-40'); // TODO: dynamic depending on svg pos
    image.setAttribute('y', '-16'); // TODO: dynamic depending on svg pos
    image.setAttribute('width', '500');
    image.setAttribute('height', '500');
    image.setAttribute('href', 'https://picsum.photos/id/210/500/500'); // xlink:href didn't work either
    
    svg.appendChild(defs);
    svg.appendChild(image);

    let totalWidth = 0;
    word.toUpperCase().split('').forEach((letter, index) =>
        {
            if (letterPaths[letter])
            {
                defs.appendChild(createLetterPath(letter, index));
                clipPath.appendChild(createLetterUse(index, false))
                svg.appendChild(createLetterUse(index, true))

                totalWidth += letterWidths[letter] + LETTER_SPACING;
            }
        });

    defs.appendChild(clipPath);

    function createLetterPath(letter, index) {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute("id", `letter-path-${index}`)
        path.setAttribute("class", `letter-path-${letter}`)
        path.setAttribute("d", letterPaths[letter]);
        path.setAttribute("transform", `translate(${totalWidth}, 0)`);
        return path
    }

    function createLetterUse(index, isPath) {
        const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
        use.setAttribute("href", `#letter-path-${index}`)
        if (isPath) {
            use.setAttribute("class", `letter-path`)
        }
        return use
    }

    svg.setAttribute("height", `${HEIGHT}`);
    svg.setAttribute("viewBox", `0 0 ${totalWidth} ${HEIGHT}`);

    return svg;
}

// Main function to replace text in list items with SVGs
function replaceTextWithSVG()
{
    const listItems = document.querySelectorAll('li');

    listItems.forEach(li =>
    {
        const text = li.textContent.trim();
        const svg = createWordSVG(text);
        li.textContent = ''; // Clear the text
        li.appendChild(svg);
    });
}

// Call the function when the document is ready
document.addEventListener('DOMContentLoaded', replaceTextWithSVG);
html {
    background-color: #151515;
    color: white;
    background-image: url(https://picsum.photos/id/210/500/500);
    background-size: 500px 500px;
}

body {
    margin: 0;
    padding: 0;
}

#navbar {
    font-family: 'Katahdin Round', 'Arial Narrow Bold', sans-serif;
    list-style: none;
}

#navbar .letter-path {
    fill: white;
    stroke: transparent;
    stroke-width: 2.5;
    stroke-dasharray: 400;
    stroke-dashoffset: 400;
    transition: fill 0.1s ease-in 0.1s, stroke 0.25s ease-out, stroke-dashoffset 0s linear 0.25s;
}

#navbar svg:hover .letter-path {
    fill: rgba(255, 255, 255, 0.3);
    stroke: white;
    stroke-dashoffset: 0;
    transition: fill 0.25s ease-out, stroke 0s, stroke-dashoffset 0.5s ease-in 0.1s;
}

#blurred-clip-background {
    clip-path: url(#blur-letter-paths);
    filter: blur(3px);
}
<body>
    <ul id="navbar" class="relative inline-block">
        <li>TEXT</li>
        <!-- <li>PROJECTS</li>
        <li>CONTACT</li> -->
    </ul>

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

Well, try to execute this in fiddle or in your browser and you’ll see it will not be rendered. When you inspect the HTML, you see that the -tag is not taking any space.

What is the problem?

This post is almost the same as the one made by Andry a while ago, where the problem was the creation of svg-elements using createElement instead of createElementNS. This isn’t my problem though, because in my case all svg-elements are already generated using the correct way. This becomes apparent, because everything else works, not the -tag though.

How to make cache with string keys without memory leak?

The problem is that the ordinary Map would prevent old cache entries from being garbage collected and the WeakMap won’t allow string as key.

Cache could be cleared by GC when the GC decides that there’s not enough free memory, cleared fully or partially, in arbitrary order.

Use case – optimisation for calculating custom hash codes for strings, used in custom HashMap, HashSet, Unique, etc.

Not working code, playground

const cache = new WeakMap<string, number>() // <= Error primitives can't be keys

function cachedCalculation(s: string) {
  let v = cache.get(s)
  if (v == undefined) {
    v = complexCalculation(s)
    cache.set(s, v)
  }
  return v
}

function complexCalculation(s: string): number {
  console.log(`complex calculation for ${s}`)
  return s.length
}

console.log(cachedCalculation('a'))
console.log(cachedCalculation('a'))

Possible solution, wonder if there’s something more sensible and also simple.

const cache = new Map<string, number>()
setInterval(() => cache.clear(), 1000)

Chrome does not expire cookie at Max-Age

The following code, when run in Chrome/Chromium, keeps outputting <i> 'testme=Banana' indefinitely:

document.cookie = "testme=Banana; Max-Age=4";
let i = 0
setInterval(() => {
  i++
  console.log(i, document.cookie)
}, 1000)

In Firefox, it starts outputting empty string from i=4 (as one would expect).

What’s the deal with Chrome? Is it a bug?

UPDATE: Looks like this is the bug reported here.

I am trying to download csv file from react app, I am getting the headings as label from my json file but I need to change the headers with spaces

I am trying to download csv file from react app, I am getting the headings as label from my json file
but I need to change the headers with spaces
I tried giving it as column headers still it did not work

this is what I am getting
enter image description here
expecting these headers with spaces
enter image description here
I tried giving it as column headers still it did not work enter image description here
what am I missing here

Problem with removing extra items from an array [duplicate]

I need to write a function to remove the remaining items of an existing one in an array – in console returns to me [ 1, 2, 2, 3 ] instead [1,2,3]. I don’t find my mistake.

function removeRemainItemArray(arr) {
  var new_arr = [];
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] in new_arr) {
      continue
    } else {
      new_arr.push(arr[i])
    }
  }
  return new_arr
}
console.log(removeRemainItemArray([1, 2, 2, 2, 2, 3, 3, 1, 3, 2]))

How to calculate or get the response time of an API?

using cypress i’m intercepting an API call and want to get how much the response time it took

cy.intercept("PATCH", "https://api.website.com/b2b2/**").as("myAPP")
cy.wait("@myAPP").then(({ request, response }) => {
    expect(response.duration).to.be.lessThan(2000)// => expected undefined to be a number or a date
    expect(response.statusCode).to.equal(200)
}) 

How to securely read and store password in memory in nodejs

I want to take a password from user in console, then derive a key from that password.
In Java, we can store passwords in a char array and zeroize the array after use.
What is the right way of doing this in nodejs where no extra copies of the password are created in memory and the password is garbage collected as soon as possible.

In simple terms, how to securely handle sensisitve data such as passwords and keys in a nodejs application?
(assume that I am making a cli desktop app entirely in nodejs and typescript/javascript without any 3rd party libraries)

Does Sentry JS filter the messages according to sample rate?

I’m using Sentry in JS application (web extension) to track the errors. Now i’d like to have some log of the events that happened before the error, so i’ve added scope.captureMessage() calls every here and there.

I have a “beforeSend” callback can see not all the messages are sent. Is it because of sample rate applied to messages too?

Here is the initialization code:

  const manifest = browser.runtime.getManifest();

  // filter integrations that use the global variable
  const integrations = getDefaultIntegrations({}).filter(
    (defaultIntegration: { name: string }) => {
      return ![
        "BrowserApiErrors",
        "TryCatch",
        "Breadcrumbs",
        "GlobalHandlers"
      ].includes(defaultIntegration.name);
    }
  );

  const client = new BrowserClient({
    dsn,
    environment,
    release: info.addonVersion,
    transport: makeFetchTransport,
    stackParser: defaultStackParser,
    initialScope: {
      tags: {
        manifestVersion: manifest.manifest_version
      }
    },
    integrations,
    sampleRate: sampleRate ?? 0.01,
    beforeSend(event) {
      console.warn("Event", event);
      lastEvent = event;
      return event;
    }
  });

  scope = new Scope();
  scope.setClient(client);
  client.init();

Here is how i send the messages:

scope.captureMessage(message);

Only few messages are actually sent:
enter image description here

How can one pass all the messages? Is it possible to set some “messages sample rate” to 1? Is it possible to send the messages only if specific error happens (to reduce the traffic)? Are Sentry messages limited with any quotas or cost per amount?

How to use value=”{{ old(‘value’, default_value) }}” when appending code from JavaScript to Blade on Laravel 10

I’m working on the project which I have to make my users to be able to add and remove items; in this case is the reward information. I’m developing this on Laravel 10 and I’ve tried so many attempts to get the old values from the controller when the validation is failed by the rules I declared.

In my JavaScript, I have addMoreItem() function and removeItem() function. In addMoreItem() function, I can used value=”{{ old(‘reward_name’) }}”. After the failure, I have to check for the old value(s) by using @json(‘reward_details’) which contains the array of reward data like this :

{"1":
    {
        "reward_award_status":"1",
        "reward_type_id":"1",
        "reward_owner_name":"International Kid Scientist Programme",
        "reward_owner_description":"Example of Description",
        "reward_date":"2024-10-04",
        "reward_name":"International Kid Scientist",
        "reward_education_level_id":"1",
        "reward_academic_year":"2024",
        "reward_academic_semester":"1",
        "reward_prize_amount":null
    },
"2":
    {
        "reward_award_status":"1",
        "reward_type_id":"3",
        "reward_owner_name":"National Kid Scientist Programme",
        "reward_owner_description":"Example of Description",
        "reward_date":"2024-10-04",
        "reward_name":"National Kid Scientist",
        "reward_education_level_id":"1",
        "reward_academic_year":"2024",
        "reward_academic_semester":"1",
        "reward_prize_amount":null
    },
}

After I got the old values as above, I will work in JavaScript like this :

var reward_items = @json(old('reward_total_items', 0));
if(reward_items > 0){
    const reward_details = @json(old('reward'));
    Object.entries(reward_details).forEach(([key, value]) => {
        $('#reward_details').append(
            '<div id="reward-' + key + '" class="card border-dark mt-3 mb-3' + '">' +
                        '<div class="card-header bg-warning">' +
                            '<div class="row align-items-center">' +
                                '<div class="col d-flex justify-content-start">' +
                                    '<strong>Reward Information</strong>' +
                                '</div>' +
                                '<div class="col d-flex justify-content-end">' +
                                    '<a class="btn btn-clear text-center" onclick="removeReward('reward-' + key + '');">' +
                                        '<i class="fa-solid fa-trash"></i>' +
                                        '<strong>Remove This Item</strong>' +
                                    '</a>' +
                                '</div>' +
                            '</div>' +
                        '</div>' +

                        '<input type="text" id="reward_' + key + '_reward_award_status" name="reward[' + key + '][reward_award_status]" class="form-control" value="{{ old("reward.' + key + '.reward_award_status", ' + value.reward_award_status + ') }}" style="display: none;"/>' + 

                        '<div class="card-body">' +
                            '<div class="row row-cols-1">' +
                                '<div class="col mt-1 mb-1">' +
                                    '<label for="reward_' + key + '_reward_type_id" class="form-label">' +
                                        '<strong>Reward Type <span style="color: red;">*</span> : </strong>' +
                                    '</label>' +
                                    '<select id="reward_' + key + '_reward_type_id" name="reward[' + key + '][reward_type_id]" class="form-select form-control search-on-selection ' +
                                        '@error("reward.' + key + '.reward_type_id") is-invalid @enderror" style="width: 100%;">' +
                                        '<option value="" selected disabled>กรุณาเลือก/พิมพ์เพื่อค้นหา</option>' +
                                        reward_type_list.map(reward_type => {
                                            return '<option value="' + reward_type.id + '" ' +
                                                (value.reward_type_id == reward_type.id ? 'selected' : '') + 
                                                ' data-is-get-reward="' + reward_type.is_get_reward + '">' +
                                                reward_type.name + '</option>';
                                        }).join('') + 
                                    '</select>' +
                                    '@error("reward.' + key + '.reward_type_id")' +
                                        '<span class="invalid-feedback" role="alert">' +
                                            '<strong>{{ $message }}</strong>' +
                                        '</span>' +
                                    '@enderror' +
                                '</div>' +
                            '</div>' +

                            '<div id="reward_' + key + '_reward_details" name="reward_' + key + '_reward_details" class="@if(' + value.reward_award_status + ' == "1")' + ' d-block @endif">' +
                                '<div class="row row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-lg-2">' +
                                    '<div class="col mt-1 mb-1">' +
                                        '<label for="reward_' + key + '_reward_owner_name" class="form-label">' +
                                            '<strong>Reward Owner Name <span style="color: red;">*</span> : </strong>' +
                                        '</label>' +
                                        '<input type="text" id="reward_' + key + '_reward_owner_name_th" name="reward[' + key + '][reward_owner_name]" class="form-control ' +
                                            '@error("reward.' + key + '.reward_owner_name") is-invalid @enderror" ' +
                                            'placeholder="Please Type Reward Owner Name" ' +
                                            'value="{{ old('reward.' + key + 'reward.owner_name', ) }}"' +
                                            'onkeyup="typeOnlyEN('reward_' + key + '_reward_owner_name');" ' +
                                            'onkeydown="typeOnlyEN('reward_' + key + '_reward_owner_name');"/>' +
                                        '<span class="invalid-feedback type_alert_reward_reward_owner_name" role="alert" style="display: none;">' +
                                            '<strong>Please specify in English</strong>' +
                                        '</span>' +
                                        '@error("reward.' + key + 'reward.reward_owner_name")' +
                                            '<span class="invalid-feedback" role="alert">' +
                                                '<strong>{{ $message }}</strong>' +
                                            '</span>' +
                                        '@enderror' +
                                    '</div>' + ...

I want to use old function like the code above but I can’t use it. I don’t know why it is not working but I assume that it is because of the different of how Blade and JavaScript read the code in different side. However, I need to use the old function. If you have any ideas about this, please inform me. I’d love to know every opinions from you guys. Please help me. I don’t really know what to do next. If you want further information, please contact me.

How can I create a user inputted field in a dropdown that then adds the inputted field as a dropdown option

I am just getting into coding for my university degree, and while I am loving it so far, I cannot help but feel like what this web engineering course asks for is ridiculously more advanced than what they teach in labs and lectures. For the assignment, I’m creating a form for user’s of the website to enter their 2nd hand products into. It has a dropdown menu, with the option to choose “custom”, which then should allows the users to enter a custom category, and have that become an option in the dropdown menu.I have virtually no idea how to do this, I’m very new to javascript and have tried to make some sort of text input that is originally hidden and pops up when the user selects custom, but whenever trying to click enter on that custom field, it tries to submit the form. I would appreciate if someone could write the function to add this field for me.

<html lang="en">

<head>
    <title>DOM Practice (Task 4.1)</title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
    <h1>Submit your 2nd hand Product!</h1>
    <form method="POST" action="../submit.php" onsubmit="return validateForm()" onreset="resetForm()">
        <!-- form for submitting products-->
        <fieldset class="form">
            <fieldset class="fieldSet">
                <legend>Enter your personal information</legend>
                <label for="firstName">*First Name:</label>
                <input type="text" id="firstName" required><br>
                <label for="lastName">*Last Name:</label>
                <input type="text" id="lastName" required><br>
                <label for="pemail">*Email:</label>
                <input type="email" id="pemail" required>
                <br><!-- validate as valid email when entered -->
                <label for="submitted">*Have you submitted a product before?</label>
                <select name="submitted" id="submitted" required>
                    <option value="yes">
                        I have submitted a product before.
                    </option>
                    <option value="no" selected>
                        I have not submitted a product before.
                    </option>

                </select>
            </fieldset>
            <fieldset class="fieldSet">
                <legend>Enter the details of the product</legend>

                <label for="productName">*Product name:</label><br>
                <input type="text" id="productName" required><br>

                <label for="description">*Product Description:</label><br>
                <textarea name="description" required>*Please describe the product briefly here.</textarea><br>

                <label for="priceInput">*Product Price:</label><br>
                <input type="text" id="priceInput" onChange="displayPricing()" required><br>

                <label for="priceOutput">Price Range:</label><br>
                <input type="text" id="priceOutput" readonly><br>

                <label for="picture">*Upload a picture of the product:</label><br>
                <input type="file" id="picture" name="picture" onChange="testType()" required><br>

                <label for="url">*Upload a URL of a webpage associated with the product:</label><br>
                <input type="url" id="url" required><br>

                <p>Price Matched?</p>
                <label for="priceMatched">Yes</label>
                <input type="radio" id="priceMatched" name="priceMatching" value="wasPriceMatched"><br>

                <label for="notPriceMatched">No</label>
                <input type="radio" id="notPriceMatched" name="priceMatching" value="notPriceMatched"><br>

                <label for="easeOfDiy">*Ease of DIY:</label><br>
                <input type="text" id="easeOfDiy" required onChange="validateEaseofDiy"><br>

                <label for="toolsIncluded">*Tools Included:</label><br>
                <select name="toolsIncluded" id="toolsIncluded" required>
                    <option value="toolsAreIncluded">Tools are Included</option>
                    <option value="toolsNotIncluded">Tools are not Included</option>
                </select><br>

                <label for="productCategory">*What category is this product?</label><br>
                <select name="productCategory" id="productCategory" onChange="createNewCategory()" required>
                    <option value="kitchen">Kitchen</option>
                    <option value="gardening">Gardening</option>
                    <option value="custom">Custom</option>
                </select><br>

                <label for="checkedTerms">I have read the <a href="terms-and-conditions">terms and
                        conditions</a></label>
                <input type="checkbox" id="checkedTerms" name="terms" value="haveCheckedTerms" required><br>

                <fieldset class="searchTags"><!-- a box for adding search tags-->
                    <legend>Search Tags</legend>
                    <input type="text" name="custom_entry" id="custom_entry"><input type="button"
                        value="Enter a new search tag" onclick="addSearchTag()"></input>
                </fieldset>
                <fieldset id="Reviews"> <!-- a box for adding reviews-->
                    <legend>Reviews</legend>
                    <input type="button" value="New Review" onclick="addReview()" onChange="validateReview()">
                </fieldset>
            </fieldset>
            <input type="submit" value="submit">
            <input type="reset" value="Reset">
        </fieldset>

        <script type="text/javaScript">

            createNewCategory()
            {
                
            }

            function displayPricing() //validates valid numeric input, plus produces price category field based on price
            {
                var priceInput = document.getElementById('priceInput');
                var priceDisplay = document.getElementById('priceOutput');
                var pattern = /^-?d+(.d+)?$/; //testing for number
                
                
                if(!(pattern.test(priceInput.value))) //if not valid number
                {
                    alert("Please enter a numeric value.");
                    priceInput.value = '';
                    return; //clear the input field
                }
                
                
                var price = parseFloat(priceInput.value); //turning text/string output to double
                
                if(price < 0) //alert if less than 0
                {
                    alert("Price cannot be below 0.");
                    priceInput.value = 0; //set to 0
                }
                
                
                if(price == 0) //if 0
                {
                    priceDisplay.value = 'Free';
                }
                else if(price<25) //if under 25
                {
                    priceDisplay.value = 'Cheap';
                }
                else if(price<=100) //if 25 to 100 inclusive
                {
                    priceDisplay.value = 'Affordable';
                }
                else //anything over 100
                {
                    priceDisplay.value = 'Expensive';
                }

            }
            
            function testType() //function that validates file types
            {
                var picture = document.getElementById('picture'); //getting input file
                
                
                const fileTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']; //has to be one of these types
                
                
                if(!(fileTypes.includes((picture.files[0]).type)))
                {
                    alert('File type '+(picture.files[0]).type + ' is not a valid file type! Please upload a JPEG, JPG, PNG, or GIF for the product image.');
                    picture.value = ''; //clear
                }
                
                
            }
                      
            function validateEaseofDiy() //function to validate easeofDiy as Easy, Average, or Hard
            {
                var diyInput = document.getElementById('easeOfDiy'); //getting inputted ease of DIY
                
                if((diyInput.value != "easy") && (diyInput.value != "average") && (diyInput.value != "hard")) //if input isnt easy, average, or hard
                {
                    alert('Invalid ease of DIY. The entered difficulty must be "easy", "average", or "hard". The entered input is case sensitive!');
                    diyInput.value = ''; //clear entered ease of DIY
                }
                
                
            }

            function addReview() //function that adds reviews with button click, was solution to zero to many selector. 
            {
                var newReview = document.createElement('textarea');
                var lineBreak = document.createElement('br');
                var deleteButton = document.createElement("button");
                deleteButton.textContent = "Delete";
                var reviewsContainer = document.getElementById('Reviews');
                deleteButton.onclick = function()
                {
                    newReview.remove(); //needs to delete the text area and the delete button
                    deleteButton.remove();
                    lineBreak.remove();
                }
                 


                newReview.placeholder = 'Enter Review Here';
                newReview.className = 'review-input'; //use to style, and to delete upon reset

                reviewsContainer.appendChild(lineBreak);
                reviewsContainer.appendChild(newReview);
                reviewsContainer.appendChild(deleteButton);

                
            }

            validateReview()
            {
                var pattern = /d+(.d+)$/ // a regex to test 0.0-5 out of 5 stars

            }

            function addSearchTag() //function to add search tags
            {
                var customSearchTag = document.getElementById("custom_entry").value;

                if(customSearchTag == '') //to ensure empty search tags arent added, important for when IDs are grabbed
                {
                    alert('Added Search Tags cannot be empty'); //not sure why not alerting
                    customSearchTag = ''; //clear input
                    return; //exit method
                }

                var newSearchTag = document.createElement("input");
                var tagContainer = document.createElement("div");

                newSearchTag.type = 'text'; //creating new search tag
                newSearchTag.name = 'searchTag';
                newSearchTag.value = customSearchTag;
                newSearchTag.id = customSearchTag;
                newSearchTag.className = 'search-Tag-Input';

                //creating delete button for new tag
                var deleteButton = document.createElement("button");
                deleteButton.textContent = "Delete";
                deleteButton.onclick = function() 
                {
                    deleteSearchTag(tagContainer); //needs to delete the entire container
                };


                tagContainer.appendChild(newSearchTag); //appending all elements to one container, was necessary to remove delete button upon delete
                tagContainer.appendChild(deleteButton);
                tagContainer.appendChild(document.createElement("br"));


                document.getElementById("custom_entry").insertAdjacentElement("beforebegin", tagContainer);
            }

            function deleteSearchTag(tagContainer) //function delete search tag
            {
                tagContainer.remove(); // Remove the entire container

            }

            function resetSearchTags() //deletes all search tags
            {
                var tagContainer = document.querySelectorAll('div');
                
                tagContainer.forEach(function(container) {
                    container.remove(); // Remove the entire container
                });
            }

            function resetReviews() //deletes all reviews
            {
                var reviewsContainer = document.getElementById('Reviews');
                
                // Remove all review elements indivdually
                var reviewInputs = reviewsContainer.querySelectorAll('.review-input');
                reviewInputs.forEach(function(review) {
                    review.remove();
                });
                // Also remove any line breaks and delete buttons
                var lineBreaks = reviewsContainer.querySelectorAll('br');
                lineBreaks.forEach(function(lineBreak) {
                    lineBreak.remove();
                });
                var deleteButtons = reviewsContainer.querySelectorAll('button');
                deleteButtons.forEach(function(button) {
                    button.remove();
                });
            }

            function validateForm() //functions that runs mutliple validation functions on form submission
            {
                validateReview();
            }

            function resetForm() // function that runs resets form elements that dont reset normally upon reset button
            {
                
                resetReviews();
                resetSearchTags();
            }


        </script>
    </form>

</body>

min steps to make all elements equal to each other

Here is the detail:

Eg: [1,1,2,4] (init) -> gen1 [2,1,2,4] -> gen2 [4,1,2,4] -> gen3 [4,2,2,4] -> gen4 [4,4,2,4] -> gen5 (no change) -> gen6 [4,4,4,4]

each generation:

odd step: +1 or zero

even step: +2 or zero

in order for all eles to be equaled each other, we need min 6 gens (example). Write an algo to solve this.

Here is one of the comments, I feel puzzled.

we want to fill each slot(odd and even generation) greedily with 1 and 2 alternatively until it crosses the total difference. 
In other words, we need an equal number of 1 and 2. 
If we need n number of 1 and n number of 2, 1 n + 2 n = total_difference or n = total_difference / 3. 
so we need 2n generation. 
additionally, we need 1 more generation if total_diff - 3n == 1 or 
we need 2 more generation if total_diff - 3*n == 2.
total difference is sum of (max(arr) - arr[i]) for i 0 to len(arr) - 1.

we want to fill each slot(odd and even generation) greedily with 1 and 2 alternatively until it crosses the total difference.

I understand

In other words, we need an equal number of 1 and 2.

I don’t understand

If we need n number of 1 and n number of 2, 1 n + 2 n = total_difference or n = total_difference / 3.

I understand

so we need 2n generation.

I don’t understand

additionally, we need 1 more generation if total_diff – 3n == 1 or
we need 2 more generation if total_diff – 3*n == 2.

I don’t understand

// Here is one of solutions from comments and looks like working based on the comment above.
// * g: [1, 2, 2, 4]
const ans = (arr) => {
    const eachCycleVal = 1 + 2; /*cycle contains decreasing 1 followed by 2*/
    const maxVal = Math.max(...arr);
    let totalCycles = 0;
    for (let i = 0; i < arr.length; i++) {
        arr[i] = maxVal - arr[i];
        arr[i] = Math.ceil(arr[i] / eachCycleVal);
        totalCycles += arr[i];
    }
    return 2 * totalCycles;
}

const arr = [1, 2, 2, 4]; // 6
const out = ans(arr);
console.log(out)

original from https://leetcode.com/discuss/interview-question/5798402/Amazon-OA-questions

Continously call function on button long press javascript

I have a custom input component that shows a value, it has 2 icons that increase or decrease the value. But now I want to be able to call the increment or decrement function when the user long presses on the button such that the input just increases/decreases in real-time. Just like how the default input field with type number works.

The input looks like this

enter image description here

This is the component

import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'
import { cn } from '@/lib/utils'
import { useLongPress } from '@uidotdev/usehooks'

interface AdjustableQuantityProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string
  inputWrapperStyle?: string
  inputStyle?: string
  value: number | undefined
  valueSuffix?: string
  onChangeHandler: (value: number | undefined) => void
  onIncrementHandler?: (value: number | undefined) => void
  onDecrementHandler?: (value: number | undefined) => void
  min?: number
  max?: number
  hideZeroValue?: boolean
  enableLongPress?: boolean
}

const AdjustableQuantity: React.FC<AdjustableQuantityProps> = ({
  label = '',
  inputWrapperStyle = '',
  inputStyle = '',
  value,
  valueSuffix = '',
  onChangeHandler,
  onIncrementHandler,
  onDecrementHandler,
  min = 1,
  max = 100,
  hideZeroValue = false,
  enableLongPress = false,
  ...rest
}) => {
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const inputValue = e.target.value.replace(/[^0-9]/g, '')
    const numericValue = inputValue === '' ? undefined : Number(inputValue)
    onChangeHandler(numericValue)
  }

  const handleIncrement = () => {
    if (hideZeroValue && value === undefined) {
      onChangeHandler(0)
    } else if (value !== undefined && value < max) {
      onIncrementHandler ? onIncrementHandler(value) : onChangeHandler(value + 1)
    }
  }

  const handleDecrement = () => {
    if (hideZeroValue && value === 0) {
      onChangeHandler(undefined)
    } else if (value !== undefined && value > min) {
      onDecrementHandler ? onDecrementHandler(value) : onChangeHandler(value - 1)
    }
  }

  const longPressIncrement = useLongPress(() => handleIncrement(), {
    onStart: (event) => console.log('Press started'),
    onFinish: (event) => console.log('Press Finished'),
    onCancel: (event) => console.log('Press cancelled'),
    threshold: 500
  })

  // const longPressDecrement = useLongPress(() => handleDecrement(), {
  const longPressDecrement = useLongPress(() => handleDecrement(), {
    onStart: (event) => console.log('Press started'),
    onFinish: (event) => console.log('Press Finished'),
    onCancel: (event) => console.log('Press cancelled'),
    threshold: 500
  })

  return (
    <div className="flex items-center">
      {label && <p className="mr-2 text-14 capitalize">{label}</p>}

      <div
        className={cn(
          'mr-3 flex items-center justify-between rounded-[0.313rem] border border-tenzr-neutral-400 p-2 h-[2.25rem]',
          inputWrapperStyle
        )}
      >
        <input
          type="text"
          onChange={(e) => {
            if (hideZeroValue) {
              handleChange(e)
            } else {
              onChangeHandler(Number(e.target.value))
            }
          }}
          value={hideZeroValue && value === undefined ? '' : value}
          min={0}
          className={`flex w-1/5 items-center justify-center  bg-transparent focus:outline-none ${inputStyle}`}
          {...rest}
        />
        {valueSuffix}
        <div className="flex items-center space-x-2">
          <KeyboardArrowDownIcon
            {...(enableLongPress && longPressDecrement)}
            onClick={handleDecrement}
            className={`${
              (hideZeroValue && value === 0) || (value !== undefined && value > min)
                ? 'cursor-pointer'
                : 'cursor-not-allowed text-gray-300'
            }`}
          />
          <KeyboardArrowUpIcon
            {...(enableLongPress && longPressIncrement)}
            onClick={handleIncrement}
            className={`${
              (value === undefined && hideZeroValue) || (value !== undefined && value < max)
                ? 'cursor-pointer'
                : 'cursor-not-allowed text-gray-300'
            }`}
          />
        </div>
      </div>
    </div>
  )
}

export default AdjustableQuantity

Currently, the function is called onLongpress but the result does not continue to update. It just calls once.

This is how the component is being called.

                value={weight}
                onChangeHandler={(value: number | undefined) => setWeight(value)}
                inputWrapperStyle="h-[2.5rem] w-[7.625rem] pl-4"
                inputStyle="!w-full"
                min={0}
                hideZeroValue={true}
                enableLongPress
              />

Enhancing Script for Better Visual Presentation

is anyone here who can help improve my script? I want the results to be more visually presentable. Is is possible to include in HTML code?

function showAEdata() {
  var ui = SpreadsheetApp.getUi();
  var input = ui.prompt("Please enter your First Name and Last Name.", ui.ButtonSet.OK_CANCEL);

  if (input.getSelectedButton() == ui.Button.OK) {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var ws = ss.getSheetByName("Monitoring (Database)");
    var data = ws.getRange("A2:X" + ws.getLastRow()).getValues();
    var userSelectedRep = input.getResponseText().toLowerCase();

    var newData = data.filter(function (r) {
      return r[6].toLowerCase() == userSelectedRep;
    });

    var yearInput = ui.prompt("Please enter the year (e.g., 2024):", ui.ButtonSet.OK_CANCEL);

    if (yearInput.getSelectedButton() == ui.Button.OK) {
      var selectedYear = yearInput.getResponseText();
      var filteredYearData = newData.filter(function (r) {
        var date = new Date(r[5]);
        return date.getFullYear() == selectedYear;
      });

      var selectedColumns = filteredYearData.map(function (r) {
        return [r[1], r[2], r[4], r[5], r[9], r[10], r[11], r[17]];
      });

      if (filteredYearData.length > 0) {
        var newWs = ss.insertSheet(userSelectedRep);
        var headers = ["INSTITUTION", "MONTH", "JO NUMBER", "DATE RELEASED", "TYPE OF USER", "PLATFORM", "TYPE OF ACCESS", "NUMBER OF ACCESS CODE"];

        newWs.getRange(4, 3, selectedColumns.length, selectedColumns[0].length).setValues(selectedColumns);
        newWs.getRange(3, 3, 1, headers.length).setValues([headers]);

      } else {
        ui.alert("No matching data found for the entered year.");
      }
    } else {
      ui.alert("Year input canceled.");
    }
  } else {
    ui.alert("Operation Canceled.");
  }
}

This the current result of my Script I want to enhance it by using HTML etc.

enter image description here

Request for Help: Enhancing Script for Better Visual Presentation

is anyone here who can help improve my script? I want the results to be more visually presentable. Is is possible to include in HTML code?

function showAEdata() {
  var ui = SpreadsheetApp.getUi();
  var input = ui.prompt("Please enter your First Name and Last Name.", ui.ButtonSet.OK_CANCEL);

  if (input.getSelectedButton() == ui.Button.OK) {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var ws = ss.getSheetByName("Monitoring (Database)");
    var data = ws.getRange("A2:X" + ws.getLastRow()).getValues();
    var userSelectedRep = input.getResponseText().toLowerCase();

    var newData = data.filter(function (r) {
      return r[6].toLowerCase() == userSelectedRep;
    });

    var yearInput = ui.prompt("Please enter the year (e.g., 2024):", ui.ButtonSet.OK_CANCEL);

    if (yearInput.getSelectedButton() == ui.Button.OK) {
      var selectedYear = yearInput.getResponseText();
      var filteredYearData = newData.filter(function (r) {
        var date = new Date(r[5]);
        return date.getFullYear() == selectedYear;
      });

      var selectedColumns = filteredYearData.map(function (r) {
        return [r[1], r[2], r[4], r[5], r[9], r[10], r[11], r[17]];
      });

      if (filteredYearData.length > 0) {
        var newWs = ss.insertSheet(userSelectedRep);
        var headers = ["INSTITUTION", "MONTH", "JO NUMBER", "DATE RELEASED", "TYPE OF USER", "PLATFORM", "TYPE OF ACCESS", "NUMBER OF ACCESS CODE"];

        newWs.getRange(4, 3, selectedColumns.length, selectedColumns[0].length).setValues(selectedColumns);
        newWs.getRange(3, 3, 1, headers.length).setValues([headers]);

      } else {
        ui.alert("No matching data found for the entered year.");
      }
    } else {
      ui.alert("Year input canceled.");
    }
  } else {
    ui.alert("Operation Canceled.");
  }
}

This the current result of my Script I want to enhance it by using HTML etc.

enter image description here