Making metaballs from compound path / Paper.js

I have the following code:

const data = [
  { text: "Forage" },
  { text: "Fourndollar" },
  // { text: 'Interiorndesign' },
  // { text: 'Salviannormcore' },
  // { text: 'Typewriter' },
  // { text: 'Bicyclenrights' },
  // { text: 'Try-hardnenamel' },
  // { text: 'Grailed' },
  // { text: 'Fingerstache' },
];

const canvas = document.getElementById("metaballs__canvas");
let circles = [];
let blob = null;

const random = (min, max) => Math.random() * (max - min) + min;

const orbitRadius = random(8, 16);
const orbitSpeed = random(0.001, 0.005);

paper.setup(canvas);
paper.view.autoUpdate = true;

blob = new paper.CompoundPath({
  children: [],
  strokeColor: "white"
});

const createCircles = (content, position) => {
  const text = new paper.PointText({
    point: position,
    content: content,
    fontFamily: "Instrument Sans",
    fontWeight: "400",
    fontSize: 24,
    justification: "center",
    fillColor: "white"
  });

  const circle = new paper.Path.Circle({
    center: position,
    radius: text.bounds.width / 2 + 48,
    fillColor: "transparent"
  });

  const group = new paper.Group([text, circle]);

  group.onMouseDrag = (event) => {
    group.position = group.position.add(event.delta);
  };

  circles.push({
    group: group,
    circle: circle,
    text: text,
    angle: random(0, 360),
    center: position,
    orbitRadius: orbitRadius,
    orbitSpeed: orbitSpeed
  });
};

const createBlob = () => {
  blob.removeChildren();

  const unitedPath = circles.reduce((acc, {
    circle
  }) => {
    if (acc) return acc.unite(circle);
    else return circle;
  }, null);

  if (unitedPath) blob.addChild(unitedPath);
};

const animateCircles = () => {
  circles.forEach(
    ({
        group,
        circle,
        text,
        angle,
        center,
        orbitRadius,
        orbitSpeed
      },
      index
    ) => {
      angle += orbitSpeed;

      const x = center.x + orbitRadius * Math.cos(angle);
      const y = center.y + orbitRadius * Math.sin(angle);

      circle.position = new paper.Point(x, y);
      text.position = new paper.Point(x, y);

      group.position = new paper.Point(x, y);

      circles[index].angle = angle;
    }
  );
};

data.forEach((item) => {
  createCircles(
    item.text,
    new paper.Point(
      random(0, paper.view.bounds.width),
      random(0, paper.view.bounds.height)
    )
  );
});

paper.view.onFrame = (event) => {
  createBlob();
  // animateCircles();
};

paper.view.update();
@import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap");
#metaballs {
  padding: 32px;
}

#metaballs__canvas {
  position: relative;
  border-radius: 8px;
  width: 640px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background-color: hsla(0, 0%, 0%, 1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.18/paper-full.min.js"></script>

<div id="metaballs">
  <canvas id="metaballs__canvas"></canvas>
</div>

With 2 items, it works fine. However, whenever I uncomment more items, I encounter performance issues, probably caused by the compound path creation on every frame. Is there a more sophisticated solution for this?

I also want to smooth out the connections of the circles. I found a plugin called paperjs-round-corners, but it’s really buggy when applied to the segments. An example of what I’m aiming for is the option in Adobe Illustrator where you unite two circles, select the path, and apply a corner radius operation.

Corner Radius applied on compound path in Illustrator

Generating deterministic ECDSA keys based on seed and salt using zero-dependencies JavaScript and SubtleCrypto

Given seed and salt how do I produce a set of deterministic ECDSA keys using zero-dependencies JavaScript and SubtleCrypto?
This is a continuation of this question, which I was advised to form as a new question. Therefore I publish no sample code, my understanding is that there’re 2 major ways to go about it:

  1. Generate the key material and then somehow import it as ECDSA keys.
  2. Generate x & y required to generate ECDSA keys (I’m not sure how, given the generateKey call accepting just named curve, maybe via jwk(?))

I’d like the solution to rely as much as possibly on SubtleCrypto API with as little custom code as possible.

Generate deterministic ECDSA keys based on seed and salt using zero-dependencies JavaScript and SubtleCrypto

Given seed and salt how do I produce a set of deterministic ECDSA keys using zero-dependencies JavaScript and SubtleCrypto?
This is a continuation of this question, which I was advised to form as a new question. Therefore I publish no sample code, my understanding is that there’re 2 major ways to go about it:

  1. Generate the key material and then somehow import it as ECDSA keys.
  2. Generate x & y required to generate ECDSA keys (I’m not sure how, given the generateKey call accepting just named curve, maybe via jwk(?))

I’d like the solution to rely as much as possibly on SubtleCrypto API with as little custom code as possible.

highcharts in react customization with plotlines and custom marker only on new data point

I am using highcharts and highcharts-react-official to create chart for eth Realtime price. I also show user currentround details that i am getting in startedRoundData from partent component and it render like this (image attached) also added plotlines for startTime and endTime. as you can see plotline text is corpped.
enter image description here

This is the exact chart I want to copy with all details and functionality.
enter image description here

Here is my code

import React, { useEffect, useState } from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import moment from "moment";

const Chart = ({ data, startedRoundData }) => {
  const [chartOptions, setChartOptions] = useState({});

  useEffect(() => {
    const pricesData = data?.prices || [];
    const chartData = pricesData
      .slice(0, -1)
      .map((item) => [item.timestamp * 1000, parseFloat(item.price)]);

    const lastTimestamp = chartData[chartData.length - 1]?.[0] || 0;
    const futureTimestamp = lastTimestamp + 10000; // 10 seconds in the future

    const options = {
      chart: {
        type: "spline",
        backgroundColor: "#131722",
        height: "400px",
        marginRight: 10,
        marginLeft: 0,
        marginBottom: 30,
        marginTop: 10,
        animation: Highcharts.svg,
      },
      title: null,
      xAxis: {
        type: "datetime",
        lineColor: "#2a2e39",
        tickColor: "#2a2e39",
        labels: {
          style: { color: "#787b86", fontSize: "10px" },
          y: 20,
          formatter: function () {
            return moment(this.value).format("HH:mm:ss");
          },
        },
        tickLength: 0,
        minPadding: 0,
        maxPadding: 0,
        gridLineWidth: 1,
        gridLineColor: "#2a2e39",
        tickInterval: 10000, // 10 seconds interval
        tickAmount: 6,
        min: lastTimestamp - 50000, // Show 50 seconds of past data
        max: futureTimestamp, // Extend to 10 seconds in the future
        plotLines: [
          {
            color: "#ff9800",
            width: 1,
            value: startedRoundData?.startTime
              ? moment(startedRoundData?.startTime).utc().valueOf()
              : null, // Convert to UTC
            dashStyle: "dash",
            zIndex: 5,
            label: {
              useHTML: true, // Allows for custom HTML
              align: "center",
              y: 0,
              x: 0,
              formatter: function () {
                return `
                      <div class="custom-plotline-label">
                        <span class="label-text">Current</span>
                      </div>
                    `;
              },
            },
            events: {
              render: function () {
                // Access and style after rendering
                const plotLineLabel = this.plotLinesAndBands[0].label.element;
                plotLineLabel.style.transform = "rotate(270deg)";
                plotLineLabel.style.transformOrigin = "100% 12px";
                plotLineLabel.style.position = "absolute";
                plotLineLabel.style.left = "150px";
                plotLineLabel.style.top = "10px";
              },
            },
          },

          {
            color: "#ff9800",
            width: 1,
            value: startedRoundData?.lockTime
              ? moment(startedRoundData?.lockTime).utc().valueOf()
              : null, // Convert to UTC
            dashStyle: "dash",
            zIndex: 5,
            label: {
              text: "End",
              align: "center",
              style: { color: "#ffffff", fontSize: "12px" },
              y: 15,
            },
          },
        ],
      },
      yAxis: {
        title: null,
        labels: {
          align: "right",
          x: 45,
          style: { color: "#787b86", fontSize: "10px" },
          formatter: function () {
            return this.value.toFixed(2);
          },
        },
        gridLineColor: "#2a2e39",
        gridLineWidth: 1,
        tickAmount: 5,
        opposite: true,
        plotLines: [
          {
            color: "#ff9800",
            width: 1,
            value: startedRoundData?.startTime
              ? moment(startedRoundData?.startTime).utc().valueOf()
              : null, // Convert to UTC
            dashStyle: "dash",
            zIndex: 5,
            label: {
              text: startedRoundData?.startPrice
                ? startedRoundData?.startPrice
                : null,
              align: "center",
              style: {
                color: "#ffffff",
                fontSize: "12px",
                whiteSpace: "nowrap", // To ensure the text doesn't break
              },
              y: 15,
            },
          },
        ],
      },
      legend: { enabled: false },
      series: [
        {
          name: "Price",
          data: chartData,
          color: "#00ff00",
          lineWidth: 2,
          marker: {
            enabled: false,
          },
        },
      ],
      tooltip: {
        enabled: false,
      },
      credits: { enabled: false },
      plotOptions: {
        spline: {
          animation: {
            duration: 1000,
            easing: "easeOutQuart",
          },
        },
      },
    };

    setChartOptions(options);
  }, [data]);

  useEffect(() => {
    if (chartOptions.series) {
      const chart = Highcharts.charts[0];
      if (chart) {
        const series = chart.series[0];
        const lastPoint = series.data[series.data.length - 1];
        if (lastPoint) {
          lastPoint.update(
            {
              marker: {
                enabled: true,
                radius: 5,
                symbol: "circle",
                fillColor: "#00ff00",
                lineColor: "#00ff00",
                lineWidth: 2,
                states: {
                  hover: {
                    enabled: true,
                    radius: 5,
                    fillColor: "#00ff00",
                    lineColor: "#00ff00",
                    lineWidth: 2,
                  },
                },
              },
            },
            false
          );
          chart.redraw();
        }
      }
    }
  }, [chartOptions]);

  return (
    <div className="bg-[#131722] p-4 rounded-lg h-[485px]">
      <HighchartsReact highcharts={Highcharts} options={chartOptions} />
    </div>
  );
};

export default Chart;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

Event working but class not reacting to the event

In my website, when you click in an element, an nav list open just above. This list closes when the user clicks in another part of the page (body), scroll the page (body) and when clicks again in the element named “Produtos”.

I did the body part and with the last one it isn’t working!

let listaProdutos = document.querySelector(".lista-produtos");
let checkBoxProdutos = document.querySelector("#menu");
let produtos = document.getElementById("link-produtos");

checkBoxProdutos.addEventListener("change", function () {
  x = window.matchMedia("(max-width: 849px)");
  if (x.matches) {
    if (this.checked) {
      listaProdutos.classList.add("visivel");
      listaProdutos.classList.remove("invisivel");

      // Para fechar a lista:
      let body = document.querySelector("body");
      body.addEventListener("click", function () {
        listaProdutos.classList.add("invisivel");
        listaProdutos.classList.remove("visivel");
        checkBoxProdutos.checked = true;
      });
      body.addEventListener("mousewheel", function () {
        listaProdutos.classList.add("invisivel");
        listaProdutos.classList.remove("visivel");
      });

      produtos.addEventListener("click", function () {
        alert("ola");
        listaProdutos.classList.add("invisivel");
        listaProdutos.classList.remove("visivel");
      });
    } else {
      listaProdutos.classList.add("invisivel");
      listaProdutos.classList.remove("visivel");
    }
  }
});
.visivel {
  display: block;
}

.invisivel {
  display: none;
}
<div class="cabecalho-lista">
  <ul class="cabecalho__lista">
    <li class="cabecalho__item">
      <a class="cabecalho__item__link" href="index.html">Home</a>
    </li>
    <div class="cabecalho__item-produtos">
      <li class="cabecalho__item cabecalho__item__produtos">
        <input class="produtos-celular" type="checkbox" id="menu" />
        <label for="menu">
          <a
            class="cabecalho__item__link"
            id="link-produtos"
            href="produtos.html"
            >Produtos</a
          >
        </label>

        <ul class="lista-produtos invisivel">
          <li class="lista-produtos__item">
            <a href="produtos.html#ignite">Linha Ignite</a>
          </li>
          <li class="lista-produtos__item">
            <a href="produtos.html#elfbar">Linha Elfbar</a>
          </li>
          <li class="lista-produtos__item">
            <a href="produtos.html#oxbar">Linha Oxbar</a>
          </li>
          <li class="lista-produtos__item">
            <a href="produtos.html#lost">Linha Lost Mary</a>
          </li>
        </ul>
      </li>
    </div>
    <li class="cabecalho__item">
      <a class="cabecalho__item__link" href="contato.html">FAQ</a>
    </li>
  </ul>
</div>

When the listener are listening click in “produtos”, the alert works well but the class that cleans the nav list simple doesnt work!

The list only disappear when the user scrool or click the body.

GROQ query to Sanity returns undefined for whyChoseExamples field (Next.js 14 + Sanity)

I’m working with Sanity.io and using a GROQ query to fetch data for my application. I’m trying to retrieve the whyChoseExamples field from my main document type, but it’s returning undefined.

schema.ts

export const mainPage = {
  name: 'main',
  title: 'Strona główna',
  type: 'document',
  fields: [
    {
      name: 'header',
      title: 'Nagłówek',
      type: 'string',
    },
    {
      name: 'subheader',
      title: 'Podtytuł',
      type: 'string',
    },
    {
      name: 'buttonText',
      title: 'Text na przycisku',
      type: 'string',
    },
    {
      name: 'backgroundImage',
      title: 'Obraz tła',
      type: 'image',
      options: { hotspot: true },
      fields: [{ name: 'alt', title: 'Tekst alternatywny (SEO)', type: 'string' }],
    },
    {
      name: 'whyChose',
      title: 'Dlaczego warto nas wybrać',
      type: 'string',
    },
    {
      name: 'whyChoseTitle',
      title: 'Dlaczego warto nas wybrać - Tytuł',
      type: 'string',
    },
    {
      name: 'whyChoseSubtitle',
      title: 'Dlaczego warto nas wybrać - Podtytuł',
      type: 'string',
    },
    {
      name: 'whyChoseExamples',
      title: 'Dlaczego warto nas wybrać - Zalety współpracy',
      type: 'array',
      of: [
        {
          type: 'object',
          name: 'textWithImage',
          title: 'Karta zalet',
          fields: [
            {
              name: 'header',
              title: 'Tytuł',
              type: 'string',
            },
            {
              name: 'subHeader',
              title: 'Podtytuł',
              type: 'string',
            },
            {
              name: 'image',
              title: 'Image',
              type: 'image',
              options: {
                hotspot: true,
              },
              fields: [
                {
                  name: 'alt',
                  title: 'Tekst alternatywny (SEO)',
                  type: 'string',
                },
              ],
            },
          ],
        },
      ],
    },
  ],
};

groq query

export const getMainPageContent = async (): Promise<MainPage[]> => {
  const client = createClient({
    projectId: 'myprojectId',
    dataset: 'production',
    apiVersion: '2024-09-17',
  });

  return client.fetch(groq`
  *[_type == "main"]{
  header,
  subheader,
  buttonText,
  backgroundImage {
    asset->{
      _id,
      url
    },
    alt,
  },
  whyChose,
  whyChoseTitle,
  whyChoseSubtitle,
  whyChoseExamples{
    header,
    subHeader,
    image {
      asset->{
        _id,
        url
      },
      alt,
    }
  },
}
`);
};

When I run this query and try to access whyChoseExamples, it comes back as undefined.

What I’ve Tried:

Checked Field Names:

Ensured that all field names in my query match those in my Sanity schema exactly.
Verified that it’s whyChoseExamples, not whyChooseExamples.
Confirmed Data Entry:

In Sanity Studio, the whyChoseExamples field is populated with data in my main document.

Used Sanity’s Vision Tool:

Ran the query in the Vision tool within Sanity Studio.
The whyChoseExamples field does appear in the results.

Additional Information:

Other fields like header, subheader, and whyChose are being returned correctly.
There are no conditional fields or permissions set that would hide whyChoseExamples.
I’m using the latest versions of the Sanity and Next.js

What could be causing the whyChoseExamples field to return undefined in my GROQ query, even though it’s populated in the Sanity Studio and correctly defined in the schema? How can I modify my query or schema to successfully retrieve this field?

Any help would be greatly appreciated!

Simulating IN clause for NoSQL database

So I want to simulate a SELECT * FROM TABLE WHERE ID IN {LIST_OF_IDS} query, I know Im using a NoSQL database (dynamoDB) but I have an index on the field for which I want to execute this query. So mainly I want to get all the posts from a user where the userID (index column) is the given list.

I am using Amplify as my managed backend and using graphql as my API. I know there is no IN keyword to use for graphql (at least nog APPSYNC). I have tried creating a custom resolver from a template provided by AWS like the following

import { util } from '@aws-appsync/utils';

/**
 * Gets items from the DynamoDB tables in batches with the provided `id` keys
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {import('@aws-appsync/utils').DynamoDBBatchGetItemRequest} the request
 */
export function request(ctx) {
    return {
        operation: 'BatchGetItem',
        tables: {
            Posts: {
                keys: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
                consistentRead: true,
            },
        },
    };
}

/**
 * Returns the BatchGetItem table items
 * @param {import('@aws-appsync/utils').Context} ctx the context
 * @returns {[*]} the items
 */
export function response(ctx) {
    if (ctx.error) {
        util.error(ctx.error.message, ctx.error.type);
    }
    return ctx.result.data.Posts;
}

But this doesnt seem to work. I would also like to avoid to run a query for each user in the list to retrieve the posts. All help is greatly appreciated, also if you have another architectural solution/proposal to solve this issue I am all ears!

how do i manipulate my object with its own properties

Currently learning JS, im a beginner.
I created an object to tinker with, and i want to give it some properties that are dependent on th previous properties of the same object.

I have managed to solve it by working about it yet, i know, i am sure there is a way to do it with in the object definition.

please help me, as a beginner i don’t know what key words to used when googling so this is my best bet.

By doing this :


airMax1 = {
    price : 200,
    appreciation : 8,
    taxRate : 0.2,
    realPrice : ((this.price+this.appreciation)*this.taxRate)+this.price+this.appreciation,
}
console.log(airMax1)

console.log(airMax1.realPrice)

i was expecting this output :

{ price: 200, appreciation: 8, taxRate: 0.2, realPrice: 249.6 } 249.6

yet what i get is this :

{ price: 200, appreciation: 8, taxRate: 0.2, realPrice: NaN }
NaN

but when i do it differently like this :

airMax1 = {
    price : 200,
    appreciation : 8,
    taxRate : 0.2,
}

airMax1.realPrice=((airMax1.price+airMax1.appreciation)*airMax1.taxRate)+airMax1.price+airMax1.appreciation

console.log(airMax1)

console.log(airMax1.realPrice)

it works and gives me the disered output of

{ price: 200, appreciation: 8, taxRate: 0.2, realPrice: 249.6 }
249.6

how can the same math formula produce a number when its outside the object and not produce a number when its inside the object.

i have swapped the object name with the this. method and all

How to add a class and remove when clicked, or when clicked elsewhere?

I have a caret dropdown that is supposed to do an up and down animation on 4 different tabs. In general scheme of things, these tabs populate different content when clicked on. The problem here is that I successfully am able to add and remove a class active when I click (with the .toggle functionality). The problem here is it only does it when I have one, not when i add to all 4 tabs. I need to remove ‘active‘ when clicked again on the same one, but when a different tab is clicked, i need it to add it that tab as well, and remove it on the previous tab. Do I need to add a forEach and loop on each instance of the #caret-dropdown ?

//Tab toggle for full page modal
const workMarketToggle = document.querySelector("#tab-toggle--workmarket");
const wmButton = document.querySelector("button");
const tabs = document.querySelector(".wrapper");
const tabButton = document.querySelectorAll(
  ".tab-button, .toggle-text, .toggle-img"
);
const contents = document.querySelectorAll(".content");
tabs &&
  tabs.addEventListener("click", (e) => {
    const button = e.target.closest("button");
    if (!button) return;

    contents.forEach((content) => content.classList.remove("active"));
    tabButton.forEach((btn) => btn.classList.remove("active"));

    button.classList.add("active");
    const element = document.getElementById(button.dataset.id);
    element.classList.add("active");
  });

// Caret Dropdown for tab toggle in Full Page Modal
const caretDropdown = document.querySelector(
  "#dropdown-trigger .caret-dropdown"
);
if (caretDropdown) {
  console.log("hello caret is here");
}
caretDropdown.addEventListener("click", function (event) {
  event.preventDefault();
  caretDropdown.classList.toggle("active");
});
#tab-toggle--workmarket .container,
#tab-toggle--profservices .container {
  margin: 30px auto;
}

#tab-toggle--workmarket #tab1,
#tab-toggle--workmarket #tab2,
#tab-toggle--workmarket #tab3,
#tab-toggle--workmarket #tab4,
#tab-toggle--workmarket #tab5,
#tab-toggle--workmarket #tab6,
#tab-toggle--profservices #tab1,
#tab-toggle--profservices #tab2,
#tab-toggle--profservices #tab3,
#tab-toggle--profservices #tab4,
#tab-toggle--profservices #tab5,
#tab-toggle--profservices #tab6 {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  gap: 50px;
  padding-top: 50px;
}

#tab-toggle--workmarket .wrapper,
#tab-toggle--profservices .wrapper {
  max-width: 1330px;
  margin: auto;
  border-radius: 10px;
}

#tab-toggle--profservices .buttonWrapper {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  background-color: #20347d;
  border-radius: 10px;
  height: 81px;
  max-width: 848px;
  margin: 0 auto;
  position: relative;
  z-index: 12;
}

#tab-toggle--profservices .no-bg {
  background-color: #eff5ff;
  height: auto;
}

#tab-toggle--profservices .contentWrapper {
  max-width: 1220px;
  margin: 0 auto;
}

#tab-toggle--workmarket button.tab-button,
#tab-toggle--profservices button.tab-button {
  font-family: var(--font-family-base);
  color: #fff;
}

.tab-button.tab-button-img {
  background-color: #eff5ff !important;
  height: 100% !important;
}

#tab-toggle--profservices button.tab-button {
  border: none;
  padding: 10px;
  background-color: #20347d;
  color: #fff;
  font-size: 18px;
  cursor: pointer;
  transition: 0.5s;
  border-radius: 10px;
  width: 202px;
  height: 61px;
  margin: 0 20px;
}

#tab-toggle--workmarket button:hover,
#tab-toggle--profservices button:hover {
  background-color: #d5e3ff;
}

#tab-toggle--workmarket button.active,
#tab-toggle--profservices button.active {
  background-color: white;
  margin: 0 20px;
}

#tab-toggle--workmarket button:hover,
#tab-toggle--workmarket button.active,
#tab-toggle--profservices button:hover,
#tab-toggle--profservices button.active {
  color: #000;
}

#tab-toggle--profservices button:hover,
#tab-toggle--profservices button.active {
  width: 202px;
  color: #33478c;
}

#tab-toggle--workmarket .active,
#tab-toggle--profservices .active {
  background-color: #f3f4f6;
}

#tab-toggle--workmarket .content,
#tab-toggle--profservices .content {
  display: none;
  padding: 10px 20px;
}

#tab-toggle--profservices .content-regular.active {
  display: flex;
  justify-content: center;
  padding: 70px 20px;
  align-items: center;
  gap: 50px;
  background-color: #fff;
  border-radius: 10px;
  margin: 0px;
  box-shadow: rgba(0, 0, 0, 0.14) 0px 3px 15px;
}

#tab-toggle--profservices .content.active {
  display: flex;
  justify-content: center;
  padding: 70px 20px;
  align-items: center;
  gap: 50px;
  background-color: #fff;
  border-radius: 10px;
  margin: -30px;
  box-shadow: rgba(0, 0, 0, 0.14) 0px 3px 15px;
}
#dropdown-trigger {
  display: block;
}
.caret-dropdown {
  cursor: pointer;
  top: 50%;
  transform: translateY(-50%);
  display: inline-block;
  height: 10px;
  left: 15px;
  margin-top: 2px;
  position: relative;
  text-align: left;
  transition: 0.4s ease;
  transform: rotate(0);
  width: 13px;
}
.caret-dropdown:after,
.caret-dropdown:before {
  background-color: transparent;
  border-bottom: 11px solid #444;
  box-sizing: content-box;
  content: "";
  display: inline-block;
  height: 8px;
  left: 0;
  position: absolute;
  top: 0;
  transition: 0.4s ease;
  width: 3px;
}
.caret-dropdown:before {
  transform: rotate(-135deg);
}
.caret-dropdown:after {
  transform: rotate(135deg);
}
.caret-dropdown.active {
  transform: rotate(0);
  transform: translate(0, -6px);
}
.caret-dropdown.active:before {
  transform: rotate(-45deg);
}
.caret-dropdown.active:after {
  transform: rotate(45deg);
}
<!-------- TAB TOGGLE SECTION -------->
<div class="bg-lightblue">
  <div id="tab-toggle--profservices">
    <div class="wrapper">
      <div class="buttonWrapper no-bg gap-100">
        <button class="tab-button tab-button-img active" data-id="implementation">

          <img src="#" width="150" class="toggle-img" />
          <h3 class="blue toggle-text">Implementation</h3>
             <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
        </button>
        <button class="tab-button tab-button-img" data-id="advisory">
   
          <img
            src="#" width="150" class="toggle-img" >
          <h3 class="blue toggle-text">Advisory</h3>
              <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
        </button>
        <button class="tab-button tab-button-img" data-id="integration">
          
                <img
                  src="#"
                  width="150"
                  class="toggle-img"
                />
                <h3 class="blue toggle-text">Integration</h3>
                    <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
              </button>
        <button class="tab-button tab-button-img" data-id="transformation">
      
                <img
                  src="#"
                  width="150"
                  class="toggle-img"
                />
                <h3 class="blue toggle-text">Transformation</h3>
                    <a href="#" id="dropdown-trigger"
                  ><span class="caret-dropdown"></span
                ></a>
              </button>
      </div>
      <div class="contentWrapper">
        <div class="content content-regular active" id="implementation">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">Info 1</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="advisory">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 2</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="integration">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 3</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                info 1
              </li>
              <li>
                info 2
              </li>
              <li>
                info 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
        <div class="content" id="transformation">
          <div class="pf-two-col-1">
            <p class="deep-red wfn-caps bold pb-0 mb-0">info 4</p>
            <h2 class="pt-0 mt-0">
              information here
            </h2>
            <ul class="profservices">
              <li>
                list 1
              </li>
              <li>list 2</li>
              <li>
                list 3
              </li>
            </ul>
          </div>
          <div class="pf-two-col-2">
            <img src="#" class="pf-img-col2" skiplazy="" />
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Twilio.s Pitchshifting voice in Audiocall in audioContext.createMediaStreamSource(stream);

I’m banging my head up against this issue for hours missing something simple. I’m trying to implement a checkbox in my html page to toggle pitchshifting my microphone input for privacy and am working on combining Twilio.js with Pitchshifter located here:

Original Twilio-voice: [https://github.com/twilio/twilio-voice.js/releases](Twilio JS DIST github) (located in dist folder)

twilio-voice.js-2.12.1 located in the dist folder
Pitchshifter: [https://github.com/urtzurd/html-audio/blob/gh-pages/static/js/pitch-shifter.js](Pitchshifter on Github)

Upon answering a call, I see the console.log(s) being triggered, but no change in voice adjustment like the Pitchshift demo. I believe it has something to do with the way I am connecting the audio processor but after multi multi attempts the voice passing through is still unmodified.

I’ll worrie about adding the checkbox later to enable and disable the pitchshift once I get this part figured out.

Any pointers would be much appreciated as to why.

I attempted the modified code above and expected to hear my voice being modified and unrecognizable. I am doubtful but ultimately unsure if there is some post event that is overwriting the processor from taking effect.

var pitchShifterProcessor;
var initProcessor = function (audioContext) {
var validGranSizes = [256, 512, 1024, 2048, 4096, 8192];
var grainSize = validGranSizes[1];
var pitchRatio = 2.0;
var overlapRatio = 0.50;

console.log("1");
var hannWindow = function (length) {
    var window = new Float32Array(length);
    for (var i = 0; i < length; i++) {
        window[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / (length - 1)));
    }
    return window;
};

console.log("2");
var linearInterpolation = function (a, b, t) {
    return a + (b - a) * t;
};

console.log("3");
if (pitchShifterProcessor) {
    pitchShifterProcessor.disconnect();
}

console.log("4");
if (audioContext.createScriptProcessor) {
    pitchShifterProcessor = audioContext.createScriptProcessor(grainSize, 1, 1);
    console.log("audioContext.createScriptProcessor");
} else if (audioContext.createJavaScriptNode) {
    pitchShifterProcessor = audioContext.createJavaScriptNode(grainSize, 1, 1);
    console.log("audioContext.createJavaScriptNode");
}

console.log("5");
pitchShifterProcessor.buffer = new Float32Array(grainSize * 2);
pitchShifterProcessor.grainWindow = hannWindow(grainSize);
console.log("6");
pitchShifterProcessor.onaudioprocess = function (event) {
    var inputData = event.inputBuffer.getChannelData(0);
    var outputData = event.outputBuffer.getChannelData(0);

    for (i = 0; i < inputData.length; i++) {
        // Apply the window to the input buffer
        inputData[i] *= this.grainWindow[i];

        // Shift half of the buffer
        this.buffer[i] = this.buffer[i + grainSize];

        // Empty the buffer tail
        this.buffer[i + grainSize] = 0.0;
    }

    // Calculate the pitch shifted grain re-sampling and looping the input
    var grainData = new Float32Array(grainSize * 2);
    for (var i = 0, j = 0.0; i < grainSize; i++, j += pitchRatio) {
        var index = Math.floor(j) % grainSize;
        var a = inputData[index];
        var b = inputData[(index + 1) % grainSize];
        grainData[i] += linearInterpolation(a, b, j % 1.0) * this.grainWindow[i];
    }

    // Copy the grain multiple times overlapping it
    for (i = 0; i < grainSize; i += Math.round(grainSize * (1 - overlapRatio))) {
        for (j = 0; j <= grainSize; j++) {
            this.buffer[i + j] += grainData[j];
        }
    }

    // Output the first half of the buffer
    for (i = 0; i < grainSize; i++) {
        outputData[i] = this.buffer[i];
    }
};
console.log("pitchShifterProcessor.connect()");
return pitchShifterProcessor;
};


/**
* Update the stream source with the new input audio stream.
* @param {MediaStream} stream
* @private
*/
PeerConnection.prototype._updateInputStreamSource = function (stream) {
if (this._inputStreamSource) {
    this._inputStreamSource.disconnect();
}
try {
    this._inputStreamSource = this._audioContext.createMediaStreamSource(stream);
    this._inputStreamSource.connect(this._inputAnalyser);
    this._inputStreamSource.connect(this._inputAnalyser2);

    console.log("initprocessor");
    // Initialize pitch shifting processor
    var pitchShifterNode = initProcessor(this._audioContext);

    // Connect the pitch shifter to the input stream
    this._inputStreamSource.connect(pitchShifterProcessor);
    pitchShifterNode.connect(this._audioContext.destination);
    console.log("this._inputStreamSource.connect(pitchShifterProcessor);");
}
catch (ex) {
    console.log(ex);
    this._log.warn('Unable to update input MediaStreamSource', ex);
    this._inputStreamSource = null;
}
};

How to run async function in parallel but with a timeout after the first one resolves? | Promise.all with timeout after first resolves

Let’s say I have async functions calling external APIs with variable response times defined as below:

async function promise1() { /* API Call */ }
async function promise2() { /* API Call */ }
async function promise3() { /* API Call */ }
// ... up to promiseN

If I wanted to run them all in parallel and wait for all of them to resolve, I would run them like result = Promise.all([promise1, promise2, promise3,...,promiseN]).

However, I have a slightly different requirement:

  1. I want to start all the promises in parallel.
  2. As soon as the first one resolves (whichever that is), I want to wait 500 milliseconds for the other promises to resolve.
  3. After that 500 ms window, I want to ignore any remaining unresolved promises.
  4. result should contain all the promises that resolved by the time the 500 ms window ends.

So for example, if promise2 resolves first at time T, I want to wait until T + 500 ms for the others to resolve. Any promises that haven’t resolved by then should be ignored.

How can I accomplish this in JavaScript?

Frappe gantt stopped working, error giving as Gantt not defined,

I am using the frappe gantt chart library in my html file, it was all working fine till 13th sep 2024, after that it suddenly throwing me error as –
Gantt not defined, though I’ve used online cdn links of the library, those are fine, it was working fine with that same link before, can anyone guide me what to be done to fix this?
Need urgent resolution on this.I have tried to add this locally and then use it, it is still the same, in network tab I can see it is loading

links I am using –

I have tried to add that library locally, then as well I saw in my network tab the frappe gantt library is loading, it was working fine, after a release it stopped working from friday.

here this error is coming on loading of the gantt chart library, error coming as below, though in network tab we can see that.

jira/:338 Error initializing Gantt chart: ReferenceError: Gantt is not defined
    at HTMLDocument.<anonymous> (jira/:283:21)
    at e (jquery-3.6.0.min.js:2:30038)
    at t (jquery-3.6.0.min.js:2:30340)

code snippet –

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Frappe Gantt Chart Example</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.css">
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }

    #gantt {
      height: 500px;
      overflow: auto; /* Allow scrolling if needed */
    }
  </style>
</head>

<body>
  <h2>Gantt Chart Example</h2>
  <div id="gantt"></div>

  <script src="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const tasks = [
        {
          id: "Task 1",
          name: "Task 1",
          start: "2023-09-01",
          end: "2023-09-05",
          progress: 100,
        },
        {
          id: "Task 2",
          name: "Task 2",
          start: "2023-09-03",
          end: "2023-09-10",
          progress: 50,
        },
        {
          id: "Task 3",
          name: "Task 3",
          start: "2023-09-06",
          end: "2023-09-12",
          progress: 25,
        },
      ];

      const gantt = new Gantt("#gantt", tasks, {
        on_click: (task) => {
          alert(`Task: ${task.name}`);
        },
        on_date_change: (task, start, end) => {
          console.log(`Task date changed: ${task.name} from ${start} to ${end}`);
        },
      });
    });
  </script>
</body>

</html>

Autoscale plotly chart using github, Javascript, html

I want to plot plotly chart on github pags. I am using html code and javascript. I am able to plot the chart but it is not autoscaling. Below is the code. First part is doing fine.

let data = {};

// Function to update message on the screen
function displayMessage(message) {
const messageDiv = document.getElementById("message");
messageDiv.innerHTML = message;
}

// Function to load and merge multiple JSON files
function loadData() {
const jsonFiles = ['data_1d_part_22.json', 'data_1d_part_9.json'];

const fetchPromises = jsonFiles.map(file => fetch(file).then(response =>        response.json()));

Promise.all(fetchPromises)
    .then(jsonParts => {
        // Merge all parts into the 'data' object
        jsonParts.forEach(part => {
            Object.assign(data, part);
        });
        displayMessage("All data successfully loaded from JSON files.");
    })
    .catch(error => displayMessage('Error loading JSON files: ' + error));
}

// Call loadData when the page loads
window.onload = loadData;

function plotGraph() {
const tick = document.getElementById("search-box").value.trim();

if (!tick) {
    displayMessage("Please enter a tick.");
    return;
}

if (tick in data) {
    const tickData = data[tick];
    displayMessage(`Data found for tick: ${tick}`);

    // Extract the necessary data
    const dates = tickData.map(entry => entry.Datetime);
    const opens = tickData.map(entry => entry.Open);
    const highs = tickData.map(entry => entry.High);
    const lows = tickData.map(entry => entry.Low);
    const closes = tickData.map(entry => entry.Close);
    const volumes = tickData.map(entry => entry.Volume);
    const k_values = tickData.map(entry => entry.k);
    const d_values = tickData.map(entry => entry.d);
    const signals = tickData.map(entry => entry.signal);

    // Candlestick trace
    const candlestick = {
        x: dates,
        open: opens,
        high: highs,
        low: lows,
        close: closes,
        type: 'candlestick',
        name: 'Price',
        xaxis: 'x',
        yaxis: 'y1'
    };

    // K and D oscillator traces
    const k_trace = {
        x: dates,
        y: k_values,
        mode: 'lines',
        name: 'K',
        line: { color: 'blue' },
        xaxis: 'x',
        yaxis: 'y2'
    };

    const d_trace = {
        x: dates,
        y: d_values,
        mode: 'lines',
        name: 'D',
        line: { color: 'orange' },
        xaxis: 'x',
        yaxis: 'y2'
    };

    // Volume bar chart trace
    const volume_trace = {
        x: dates,
        y: volumes,
        type: 'bar',
        name: 'Volume',
        marker: { color: 'rgba(100, 150, 250, 0.4)' },
        xaxis: 'x',
        yaxis: 'y3'
    };

    // Buy/Sell signals (arrows)
    const buy_signal_trace = {
        x: dates.filter((_, i) => signals[i] === 'Buy'),
        y: lows.filter((_, i) => signals[i] === 'Buy').map(low => low * 0.98),
        mode: 'markers',
        name: 'Buy Signal',
        marker: {
            symbol: 'triangle-up',
            color: 'green',
            size: 12
        },
        xaxis: 'x',
        yaxis: 'y1'
    };

    const sell_signal_trace = {
        x: dates.filter((_, i) => signals[i] === 'Sell'),
        y: highs.filter((_, i) => signals[i] === 'Sell').map(high => high * 1.02),
        mode: 'markers',
        name: 'Sell Signal',
        marker: {
            symbol: 'triangle-down',
            color: 'red',
            size: 12
        },
        xaxis: 'x',
        yaxis: 'y1'
    };

    // Layout for the chart
    const layout = {
        title: `Stock Data for ${tick}`,
        height: 800,
    dragmode: 'zoom',
        grid: {
            rows: 3,
            columns: 1,
            pattern: 'independent',
            roworder: 'top to bottom'
        },
        xaxis: {
            
            autorange: true,
            rangeslider: { visible: true },  // Disable the date slider
            range: [dates[dates.length - 50], dates[dates.length - 1]],  // Initial fixed range (last 50 dates)
            showticklabels: false,  // Remove date labels on the x-axis
        },
        yaxis1: {
            title: 'Price',
            autorange: true,
            domain: [0.5, 1],  // Height for candlestick panel
            anchor: 'x'
        },
        yaxis3: {
            title: 'Oscillator',
            autorange: true,
            domain: [0.2, 0.45],  // Height for K and D oscillator panel
            anchor: 'x'
        },
        yaxis2: {
            title: 'Volume',
            autorange: true,
            domain: [0, 0.19],  // Height for volume panel
            anchor: 'x'
        },
        showlegend: true,
        hovermode: 'x',  // Enable hovermode for dates to be shown only on hover
    };

    // Plot the chart
    Plotly.newPlot('plot', [candlestick, k_trace, d_trace, volume_trace, buy_signal_trace, sell_signal_trace], layout, {showSendToCloud: true});
    displayMessage(`Plot created successfully for ${tick}.`);
} else {
    displayMessage(`No data found for tick: ${tick}`);
}
};

This is the second part of the code that I added for auto scaling of plotly chart but it doesn’t seem to work.

var myPlot = document.getElementById('plot');

var isUnderRelayout = false;
myPlot.on('plotly_relayout',function(relayoutData){

if(isUnderRelayout != true)
    {
        isUnderRelayout = true;         
        
        // get the start and end dates of current 'view'
        var start = relayoutData['xaxis.range'][0];
        var end = relayoutData['xaxis.range'][1];   
        
        // get the index of the start and end dates
        var xstart = myPlot.data[0].x.map(function(e) { return e; }).indexOf(start.substring(0, 10));
        var xend = myPlot.data[0].x.map(function(e) { return e; }).indexOf(end.substring(0, 10));
        
        if (xstart < 0) { xstart = 0;} // sometimes the date is before the data and returns -1          
                    
        // get the min and max's
        var low = Math.min.apply(null, myPlot.data[0].low.slice(xstart, xend));
        var high = Math.max.apply(null, myPlot.data[0].high.slice(xstart, xend));
        
        // update the yaxis range and set flag to false
        var update = {'yaxis.range': [low, high]};  
        Plotly.relayout(myPlot, update).then(() => {isUnderRelayout = false})   
    }
 });

Is this the right method or should I try some other approach?

How would we get values in Preline Tailwind CSS ComboBoxes whenever selection updated?

I’m using pure HTML+JS+PrelineCSS and I have this code copied from here Example 1 a basic usage of ComboBox its working perfectly, however how would we get the value of the input whenever it changes? the value is always empty when we change selection unless it gets the focus!

I tried this <svg onclick="testValue2()" to force the input refocus:

function forcefocus() {
    document.getElementById("input1").focus();
}

It is working but it gives always the old value which means it is not ok! :s

Is there away or different approach to get the value on a function like this <input onblur="testValue(this)":

function testValue(el) {
    console.log(el.value);
    console.log(document.getElementById("input1").value); 
} 

The aim here to ( get the input ) then verify it before using/posting. So how would we get it please?

JSDOM unit testing

I have a project that calculates the distance between 2 semitones and I’m building a UI for it.

One of the things I have to do is have unit tests that make sure the game runs as it should.

I have run into an issue where one test keeps failing and I don’t understand why it’s failing.

For context, here is how the class works:
The JamBuddy class manages musical notes, allowing users to set current notes, validate them, and calculate distances between two notes in both clockwise and anticlockwise

let buddy = new JamBuddy();
buddy.setCurrentNotes(["D#", "C"]);
console.log(buddy.calculateDistance()); // Output: [10, 2] (clockwise and anticlockwise distances between D# and C)

Here is what I have so far:

I have this function that updates the UI with the relevant error or success message depending on if the user got the answer correct. It works as it should.

function checkCorrectAnswer() {
  const answer = parseInt(answerInputElement.value, 10);

  resultElement.textContent = "";

  if (answer.toString().includes(".")) {
    resultElement.textContent = errorMsgObj.answerFloat;
    clearDisplay();
  } else if (answer < 0) {
    resultElement.textContent = errorMsgObj.answerPositive;
    clearDisplay();
  } else if (answer > 12) {
    resultElement.textContent = errorMsgObj.invalidNumberRange;
    clearDisplay();
  } else {
    const isCorrectAnswer = jamBuddy.checkAnswer(answer);

    if (isCorrectAnswer) {
      resultElement.textContent = successMsgObj.correctAnswerMsg(answer);
      answerInputElement.disabled = true;
      checkAnswerBtn.disabled = true;
      streakCounterHandler();
      streakDisplay.innerHTML = `Current Streak: ${streakCounter}`;
    } else {
      resultElement.textContent = errorMsgObj.incorrectAnswer;
      answerInputElement.disabled = false;
      checkAnswerBtn.disabled = false;
      clearDisplay();
    }
  }
}

Here is my unit test:

it("should display a success message when the answer is correct when you click the check answer button", () => {
    jamBuddy.setCurrentNotes(["F", "A#"]);

    const [ans, ans1] = jamBuddy.calculateDistance();

    // Test the first possible answer
    answerInput.value = ans;
    checkAnswerBtn.click();
    clock.tick(1000);

    expect(resultElement.textContent).toBe(successMsgObj.correctAnswerMsg(ans)); //"Correct answer!"

The test fails expecting the resultElement.textContent to have “Incorrect Answer”

Where did I go wrong?

I tried re-writting the function using a switch statement but to no avail.

How can I get the test to pass?