Official JSON Schema for npm audit –json Output?

I’m looking for an officially documented JSON schema that defines the structure of the output from the npm audit --json command. While I understand that the output includes keys like actions, advisories, metadata, etc., I couldn’t find a formal schema in the npm documentation or repository.

Is there an official JSON schema or response type definition available? If not, is the CLI source code the only reference for the expected structure? Is the output structure stable, or can it change between versions?

Change the settings of Sortablejs after its set

I am having trouble to change my sortablejs settings after its set.
The initialization of this function is done within an other script(this script may not change) and is done when the page is loaded.
what i have for code in other script is the following:

//initialize sortable
function initSortable() {
   $(".menu-item-list").each(function (index) {
      var id = this.id;
      var options = {
         animation: 150,
         group: "menu-item-list",
         chosenClass: "sortable-chosen",
         ghostClass: "sortable-ghost",
         filter: ".empty-area-text",
         cancel: ".empty-area-text",
         onAdd: function (e) {
            //moved to the new column. save the items position
            saveItemsPosition();
            removeEmptyAreaText(e.to);
            addEmptyAreaText(e.from);
            adjustHeightOfItemsContainer();
            removeSubmenuClass(e.item, e.to);
         },
         onUpdate: function (e) {
            //moved to the same column. save the items position
            saveItemsPosition();
            adjustHeightOfItemsContainer();
            removeSubmenuClass("", "");
         }
      };
      Sortable.create($("#" + id)[0], options);
   });
}

State Not Updating in useEffect When Clearing Filters

I have a global filter clearing function in a React app that should reset the filter dropdowns, input fields, and UI markers when a “Clear Filters” button is clicked.

The problem is that my clearFiltersTrigger remains undefined in the useEffect inside filter-utils.tsx, even though it is toggled in Container.tsx.

Even though console logs show that clearFiltersTrigger updates, the UI state (textboxes and filter markers) is not resetting as expected.

What am I doing wrong?

const Container: React.FunctionComponent = () => {
   const handleClearFilters = () => {
        console.log("Global Clear Filters Triggered!");
        setFilters({});
        setSelectedKeys([])
        setSelectedValues([]); 
    
        setClearFiltersTrigger((prev) => {
            console.log("Toggling clearFiltersTrigger to:", !prev); true
            return !prev; 
        });
    
        refetch(0, JOBS_TABLE_PAGE_LENGTH, false, undefined, undefined, {});
    };

   return (
        <ThatView
         clearFiltersTrigger={clearFiltersTrigger ?? false}
        />

In ThatView.tsx, we pass the state to ThatTable:

<ScansTable
   slot="Table"
   clearFiltersTrigger={clearFiltersTrigger ?? false} />

In ThatTable we pass the state to MyTable with columns:

<MyTable
   columns={MyTableColumns(refetch, jobs.length, clearFiltersTrigger)}

In MyTableColumns, we pass parameters to the function from filter-utils.tsx, which use useEffect to handle state updates.

{
key: 'name',
filterDropdown: getTextFilterDropdown("Search Scan Name", "Scan Name", "scan_name", refetch, jobsCount, clearFiltersTrigger)
}

getTextFilterDropdown:

export const getTextFilterDropdown = (
...
clearFiltersTrigger: boolean
...
   /** Reset input when global clear is triggered */
       React.useEffect(() => {
        console.log("useEffect triggered by clearFiltersTrigger:", clearFiltersTrigger); -- undefined
    
        if (clearFiltersTrigger) {
            setInputValue("");
            setSelectedKeys([]);
        }
    }, [clearFiltersTrigger]);

Why is useEffect inside filter-utils.tsx not triggering properly when clearFiltersTrigger updates?

How can I ensure the input box text and filter icon reset properly when clicking “Clear Filters”?

Is there a better way to handle global state updates across multiple components in this case?

React State Not Updating in useEffect When Clearing Filters

I have a global filter clearing function in a React app that should reset the filter dropdowns, input fields, and UI markers when a “Clear Filters” button is clicked.

The problem is that my clearFiltersTrigger remains undefined in the useEffect inside filter-utils.tsx, even though it is toggled in Container.tsx.

Even though console logs show that clearFiltersTrigger updates, the UI state (textboxes and filter markers) is not resetting as expected.

What am I doing wrong?

const Container: React.FunctionComponent = () => {
   const handleClearFilters = () => {
        console.log("Global Clear Filters Triggered!");
        setFilters({});
        setSelectedKeys([])
        setSelectedValues([]); 
    
        setClearFiltersTrigger((prev) => {
            console.log("Toggling clearFiltersTrigger to:", !prev); true
            return !prev; 
        });
    
        refetch(0, JOBS_TABLE_PAGE_LENGTH, false, undefined, undefined, {});
    };

   return (
        <ThatView
         clearFiltersTrigger={clearFiltersTrigger ?? false}
        />

In ThatView.tsx, we pass the state to ThatTable:

<ScansTable
   slot="Table"
   clearFiltersTrigger={clearFiltersTrigger ?? false} />

In ThatTable we pass the state to MyTable with columns:

<MyTable
   columns={MyTableColumns(refetch, jobs.length, clearFiltersTrigger)}

In MyTableColumns, we pass parameters to the function from filter-utils.tsx, which use useEffect to handle state updates.

{
key: 'name',
filterDropdown: getTextFilterDropdown("Search Scan Name", "Scan Name", "scan_name", refetch, jobsCount, clearFiltersTrigger)
}

getTextFilterDropdown:

export const getTextFilterDropdown = (
...
clearFiltersTrigger: boolean
...
   /** Reset input when global clear is triggered */
       React.useEffect(() => {
        console.log("useEffect triggered by clearFiltersTrigger:", clearFiltersTrigger); -- undefined
    
        if (clearFiltersTrigger) {
            setInputValue("");
            setSelectedKeys([]);
        }
    }, [clearFiltersTrigger]);

Why is useEffect inside filter-utils.tsx not triggering properly when clearFiltersTrigger updates?

How can I ensure the input box text and filter icon reset properly when clicking “Clear Filters”?

Is there a better way to handle global state updates across multiple components in this case?

Remove Spacing from the bars for Q1 and Q2 I am using chartjs version 2.7.2 and angular chart js version 1

In your Chart.js bar chart, experiencing unwanted spacing between the labels Q1 and Q2 when the corresponding data values are 0. Even though the bars with 0 values are not visually displayed, Chart.js still reserves space for them, creating unnecessary gaps. You have already attempted to use null, undefined, and NaN to remove these spaces, but the issue persists. The goal is to ensure that Q1 and Q2 appear side by side without any empty space caused by 0 values, so the chart maintains a clean and compact appearance.

var app = angular.module("chartApp", ["chart.js"]);

app.controller("ChartController", function ($scope) {
    $scope.chartJsProps = {
        data: [
            [100, null], [110, null], [200, null], [210, null], [210, null], [220, 0],
            [0, 300], [null, 290], [null, 320], [null, 340], [null, 350], [null, 370]
        ],
        type: "bar",
        colors: [
            "#803690", "#00ADF9", "#46BFBD", "#fb3b3b", "#FDB45C",
            "#949FB1", "#4D5360", "#4cbfbc", "#98bbcc", "#ff8d30",
            "#824670", "#dcdcdc"
        ],
        labels: ["Q1", "Q2"],
        labelsCol: "quarter",
        series: [],
        options: {
            responsive: true,
            maintainAspectRatio: true,
            title: {
                display: true,
                text: "Chart",
                position: "top"
            },
            legend: {
                display: false,
                position: "bottom",
                labels: {
                    fontColor: "rgb(66, 66, 66)",
                    fontSize: 12,
                    padding: 10,
                    boxWidth: 40
                }
            },
            layout: {
                padding: 20
            },
            tooltips: {
                enabled: true,
                mode: "nearest",
                intersect: true
            },
            elements: {
                point: {
                    radius: 3,
                    hoverRadius: 4,
                    hitRadius: 2
                }
            },
            scales: {
                xAxes: [{
                    type: "category",
                    stacked: true,
                    categoryPercentage: 0.8,
                    barPercentage: 0.9,
                    gridLines: { display: false },
                    ticks: { fontSize: 11 }
                }],
                yAxes: [{
                    stacked: true,
                    gridLines: { display: false },
                    ticks: { fontSize: 11, fontColor: "#803690", beginAtZero: false }
                }]
            }
        },
        datasetOverride: [
            {
                label: "jan_actual(sum)",
                type: "bar",
                backgroundColor: "#803690",
                yAxisID: "y-axis-0",
                stack: "grp_A"
            },
            {
                label: "jan_target(sum)",
                type: "bar",
                backgroundColor: "#00ADF9",
                yAxisID: "y-axis-0",
                stack: "grp_T"
            },
            {
                label: "feb_actual(sum)",
                type: "bar",
                backgroundColor: "#46BFBD",
                yAxisID: "y-axis-0",
                stack: "grp_A"
            },
            {
                label: "feb_target(sum)",
                type: "bar",
                backgroundColor: "#fb3b3b",
                yAxisID: "y-axis-0",
                stack: "grp_T"
            },
            {
                label: "mar_actual(sum)",
                type: "bar",
                backgroundColor: "#FDB45C",
                yAxisID: "y-axis-0",
                stack: "grp_A"
            },
            {
                label: "mar_target(sum)",
                type: "bar",
                backgroundColor: "#949FB1",
                yAxisID: "y-axis-0",
                stack: "grp_T"
            },
            {
                label: "apr_actual(sum)",
                type: "bar",
                backgroundColor: "#4D5360",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_A"
            },
            {
                label: "apr_target(sum)",
                type: "bar",
                backgroundColor: "#4cbfbc",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_T"
            },
            {
                label: "may_actual(sum)",
                type: "bar",
                backgroundColor: "#98bbcc",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_A"
            },
            {
                label: "may_target(sum)",
                type: "bar",
                backgroundColor: "#ff8d30",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_T"
            },
            {
                label: "jun_actual(sum)",
                type: "bar",
                backgroundColor: "#824670",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_A"
            },
            {
                label: "jun_target(sum)",
                type: "bar",
                backgroundColor: "#dcdcdc",
                yAxisID: "y-axis-0",
                stack: "grp_Q2_T"
            }
        ],
        labelsColType: "category"
    };
    $scope.chartJsProps.data = $scope.chartJsProps.data.filter(dataSet => 
  dataSet.some(value => value !== 0)
);
$scope.chartJsProps.options.scales.yAxes[0].ticks = {
  suggestedMin: 1, // Ensure the minimum is not zero
  callback: function (value) {
    return value !== 0 ? value : null; // Hide zero values
  }
};

$scope.chartJsProps.datasetOverride.forEach(dataset => {
  dataset.spanGaps = true;
});




});
<!DOCTYPE html>
<html lang="en" ng-app="chartApp">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AngularJS Chart</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.9/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-chart.js/1.1.1/angular-chart.min.js"></script>
</head>
<body ng-controller="ChartController">

        <canvas  
                        id="base" class="chart-base" 
                        chart-click="onChartJsClick" 
                        chart-data="chartJsProps.data" 
                        chart-labels="chartJsProps.labels" 
                        chart-colors="chartJsProps.colors" 
                        chart-pause="'running'" 
                        chart-dataset-override="chartJsProps.datasetOverride" 
                        chart-options="chartJsProps.options" 
                        chart-series="chartJsProps.series" 
                        chart-type="chartJsProps.type">
                </canvas>

    <script src="app.js">
    </script>
</body>
</html>
enter code here

Outlook Addin Mobile get Email as Base64

In a Mobile Outlook Addin, what is a good replacement for the unsupported (on Mobile) function Office.context.mailbox.item.getAsFileAsync?

*the function above gets the currenty open Email as base64

*no additional Microsoft Azure token-nightmares preferably

I have a working Desktop Outlook Addin that gets the Email as Base64 file via this function:

Office.context.mailbox.item.getAsFileAsync((asyncResult) => {
    if (asyncResult.status === Office.AsyncResultStatus.Failed) {
        console.log(`Error encountered during processing: ${asyncResult.error.message}`);
        return;
    }
    var emlBase64 = asyncResult.value;
});

enter image description here

Puppeteer Not Working in Node.js Project After Deployment to Azure (Works Locally)

I am using Puppeteer in my Node.js project, and it works perfectly fine on my local machine. However, after deploying the project to Azure, I am encountering issues where Puppeteer does not seem to function properly.

Issue:

The error message I get is:

Could not find Chrome (ver. 131.0.6778.85). This can occur if either
 1. you did not perform an installation before running the script (e.g. `npx puppeteer browsers install chrome`) or
 2. your cache path is incorrectly configured (which is: /root/.cache/puppeteer).
For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.

This suggests that Puppeteer is unable to locate a Chromium instance in the Azure environment.

Environment Details:

Local Machine: Works fine without any issues.

Azure Deployment: Fails to find Chrome.

Steps I Have Tried:

Installing Puppeteer with Chromium:

npm install puppeteer

Setup the api

app.get("/_api/screenshot", async (req, res) => {
  try {
    // Get the URL from query string, default to example.com if not provided
    const targetUrl = req.query.url || "https://example.com";

    console.log("Starting browser launch...");

    // Launch Puppeteer with necessary arguments for cloud environments
    const browser = await puppeteer.launch({
      headless: true,
      args: [
        "--no-sandbox",
        "--disable-setuid-sandbox",
        "--disable-dev-shm-usage",
        "--single-process",
        "--no-zygote",
      ],
      // Use the path from environment variable or let Puppeteer find it
      executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
    });

    console.log("Browser launched successfully");

    const page = await browser.newPage();
    console.log("Navigating to:", targetUrl);

    // Navigate to the target URL and wait until network is idle
    await page.goto(targetUrl, { waitUntil: "networkidle0" });
    console.log("Page loaded, taking screenshot");

    // Take a screenshot as a base64 string
    const screenshotBuffer = await page.screenshot({ encoding: "base64" });
    console.log("Screenshot taken, closing browser");

    await browser.close();

    // Return the screenshot as an HTML image
    res.send(`<img src="data:image/png;base64,${screenshotBuffer}" />`);
  } catch (error) {
    console.error("Error in /api/screenshot:", error);
    res.status(500).json({
      error: "Failed to take screenshot",
      message: error.message,
      stack: error.stack,
      puppeteerInfo: process.env.PUPPETEER_EXECUTABLE_PATH || "No path set",
    });
  }
});

But still getting the above error

Note: We’re using the Github workflow to deploy the updated code to Azure, if anyone know or have done this please let us know what will be proper method to do this, would be great help 🙂

Can anyone tell what to do ? I tried building a calculator with javascript and html, things are working fine but issues are there

I want the code to display history in the text box after the calc funciton has run, the issue here is i want to be able to browse through history while clicking history button and also calculate at any point after clicking a sign to calculate from history or select the history.
I will add a history add and history hide button which will be shown after a initial calculation is done.

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Calculator </title>
  <style>
    /* body {} */

    .container {
      box-shadow: 1px 1px 7px 1px rgb(133, 135, 136);
      position: relative;
      align-content: center;
      border: 4px #58585853;
      padding: 14px;
      margin-top: 10% !important;
      margin: auto;
      border-radius: 3.78%;
      /* border: rgba(177, 177, 177, 0.77), 2px; */
      background-color: rgb(237, 244, 251);
      height: fit-content;
      width: fit-content;
      text-align: center
    }


    .screen {
      font-size: 20px;
      background-color: rgb(92, 169, 92);
      text-align: right;
      text-overflow: inherit;
      overflow-wrap: break-word;
      color: rgba(255, 255, 255, 0.999);
      height: auto;
      max-height: auto;
      width: 80%;
    }

    .clear {
      margin-left: 3px;
      background-color: rgb(255, 255, 255);
      height: 40px;
      color: red;
      font-size: 20px;
      font-weight: bold;
      max-height: fit-content;
      position: inherit;
      margin: 2%;
      border-radius: 15%;
      padding: 2%;


    }

    .history {
      height: 40px;
      color: rgb(255, 255, 255);
      font-size: 20px;
      border-radius: 10%;
      background-color: rgb(69, 68, 68);
      font-weight: bold;
    }

    .deletehistory {
      height: 40px;
      color: red;
      font-size: 15px;
      font-weight: bold;
    }

    .historyd {
      width: 200px;
      margin: auto;
      text-align: left;
      border: 1px solid #ccc;
      padding: 10px;
      max-height: 200px;
      overflow-y: auto;
      margin-top: 10px;
    }

    .history-item {
      border-bottom: 1px solid #eee;
      padding: 5px;
    }

    .disp {
      padding: 10px;
      width: 100%;
      display: inline-block !important;

    }

    td .numbers {
      box-shadow: 1px 1px 3px 1px rgb(93, 94, 94);
      width: 80px;
      height: 8vh;
      background-color: rgba(222, 210, 218, 0.395);
      border-radius: 15.45%;
      box-shadow: 1px;
      font-size: 20px;
    }

    td .sign {
      box-shadow: 1px 1px 3px 1px rgb(101, 101, 101);
      width: 80px;
      height: 8vh;
      font-size: 20px;
      font-weight: bold;
      background-color: rgba(17, 9, 14, 0.395);
      border-radius: 15.45%;
      box-shadow: 1px;
    }
  </style>
</head>

<body id="checkkey" onkeydown="checkeyfn(event)">
  <div style="text-align:center; position: relative;">
    <div class="container">
      <form>
        <div style="display: inline-block;">
          <div class="disp">
            <input class="screen" id="display" type="" value=""></input>
            <input type="button" class="clear" value="Clear" onclick="clear1()">
          </div>
          <div>

            <table>
              <tr>
                <td><input type="button" class="history" value="History" onclick="showHistory()"></td>
                <td><input type="button" class="deletehistory" value="DEL" onclick="Del()"></td>
                <td><input type="button" class="sign" style="left: 80px; position: relative;" value="="
                                onclick="calculate()"></td>

              </tr>
              <tr class="inputs">
                <td><input type="button" class="numbers" value="9" onclick="display.value+= '9'"></td>
                <td><input type="button" class="numbers" value="8" onclick="display.value+= '8'"></td>
                <td> <input type="button" class="numbers" value="7" onclick="display.value+= '7'"></td>
                <td><input type="button" class="sign" value="/" onclick="display.value+= '/'"></td>
              </tr>
              <tr>
                <td><input type="button" class="numbers" value="6" onclick="display.value+= '6'"></td>
                <td><input type="button" class="numbers" value="5" onclick="display.value+= '5'"></td>
                <td><input type="button" class="numbers" value="4" onclick="display.value+= '4'"></td>
                <td> <input type="button" class="sign" value="*" onclick="display.value+= '*'"> </td>
              </tr>
              <tr>
                <td><input type="button" class="numbers" value="3" onclick="display.value+= '3'"></td>
                <td><input type="button" class="numbers" value="2" onclick="display.value+= '2'"></td>
                <td><input type="button" class="numbers" value="1" onclick="display.value+= '1'"></td>
                <td><input type="button" class="sign" value="+" onclick="display.value+= '+'"></td>
              </tr>
              <tr>
                <td><input type="button" class="numbers" value="0" onclick="display.value+= '0'"></td>
                <td><input type="button" class="sign" value="." onclick="display.value+= '.'"></td>
                <td><input type="button" class="sign" value="%" onclick="display.value+= '%'"></td>
                <td><input type="button" class="sign" value="-" onclick="display.value+= '-'"></td>
              </tr>
            </table>
          </div>
          <div style="border:2px red dotted;position:relative;display:block;">Test

          </div>
      </form>
    </div>
  </div>

  </div>

</body>
<footer>
  <script>
    let history = [];

    function checkeyfn(event) {
      var display = document.getElementById('display').value
      //  console.log(event);
      var charCode = event.charCode;
      var key = String.fromCharCode(charCode);

      var key = event.key;

      var allowedKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '+', '-', '*', '/', '%', '.', '(', ')'
      ];
      if (allowedKeys.includes(key)) {
        document.getElementById("display").value += key;

      } else if (key === 'Enter' || key === '=') {
        calculate();
      } else if (key === 'Backspace') {
        Del();
      }

      // Prevent default actions for some keys (optional)
      if (['+', '-', '*', '/', '%'].includes(key)) {
        event.preventDefault();
      }
    }


    function calculate() {
      var screenvalue = document.getElementById('display').value;
      var historyvalue = history;
      try {
        if (screenvalue.length > 1 && history.length > 0) {
          var lastdigit = historyvalue[0].split('=');
          console.log(historyvalue + ' History value');
        }
        if (screenvalue.match(/^[0-9+-*/%().]+$/)) {
          var result = eval(screenvalue);
          document.getElementById('display').value = result;
          var equatsto = result
          if (screenvalue === result) {
            console.log('This 2')
          }
          // adds to history
          history.push(screenvalue + "=" + result);
        } else {
          document.getElementById('display').value = "";
          alert('Invalid Input');
        }
      } catch (e) {
        document.getElementById("display").value = '!Undefined';
        9
        console.log(e);
        // setTimeout(clear(),500);

      }

    }

    function clear1() {
      document.getElementById("display").value = '0'
    }

    function Del() {
      var display = document.getElementById("display");
      display.value = display.value.slice(0, -1);
    }

    function showHistory() {
      var historyDisplay = document.getElementById('display');
      // historyDisplay.value = '';
      // historyDisplay.style.display = 'block'; 
      console.log('ifhistory', history);

      for (var i = history.length - 1; i >= 0; i--) {
        console.log(history.length, 'checking lenght', i);

        historyDisplay.value = history[i];
        var splits = history.value.split('=');
        console.log('Checking splits', splits);
        console.log(history[i]);
      }

    }
  </script>
</footer>

</html>

How to implement custom rendering of ECharts legend as a drop-down selection box?

Product Request Pic
Regarding the legend in this chart (Tracking Index / CSI 300) and rendering it as a Select component, I would like to ask how this is achieved through ECharts’ configuration or API, or if a custom render can be used to render it.

I couldn’t find a way to do custom rendering for the legend in the official documentation, so I am asking for some guidance on this.

I have searched through the legend-related API and configuration documentation, and besides finding that the formatter parameter allows HTML rendering for legend labels, I haven’t found any other relevant interfaces or documentation.

Additionally, I have seen similar needs in other PC-side website renders and noticed that the Select Legend isn’t rendered together with ECharts DIV.

Product Request Pic for Select Components DIV
Product Request Pic for Charts Div

Could you help specify whether this type of legend rendering can be achieved through ECharts’ own configuration, or if it’s necessary to define a custom component to handle this part of the rendering?

// prettier-ignore
const rawData = [['2015/12/31', '3570.47', '3539.18', '-33.69', '-0.94%', '3538.35', '3580.6', '176963664', '25403106', '-'], ['2015/12/30', '3566.73', '3572.88', '9.14', '0.26%', '3538.11', '3573.68', '187889600', '26778766', '-']].reverse();
function calculateMA(dayCount, data) {
  var result = [];
  for (var i = 0, len = data.length; i < len; i++) {
    if (i < dayCount) {
      result.push('-');
      continue;
    }
    var sum = 0;
    for (var j = 0; j < dayCount; j++) {
      sum += +data[i - j][1];
    }
    result.push(sum / dayCount);
  }
  return result;
}
const dates = rawData.map(function (item) {
  return item[0];
});
const data = rawData.map(function (item) {
  return [+item[1], +item[2], +item[5], +item[6]];
});
option = {
  legend: {
    data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30'],
    inactiveColor: '#777',
    selected: {
    // 选中'系列1'
    'MA5': true,
    'MA10': false,
    'MA20': false,
    'MA30': false,
    },
  selectorLabel:{
    show:false,
  },

    // selectedMode:false,  # 控制是否可以取消展示图例
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      animation: false,
      type: 'cross',
      lineStyle: {
        color: '#376df4',
        width: 2,
        opacity: 1
      }
    }
  },
  xAxis: {
    type: 'category',
    data: dates,
    axisLine: { lineStyle: { color: '#8392A5' } }
  },
  yAxis: {
    scale: true,
    axisLine: { lineStyle: { color: '#8392A5' } },
    splitLine: { show: false }
  },
  grid: {
    bottom: 80
  },
  dataZoom: [
    {
      textStyle: {
        color: '#8392A5'
      },
      dataBackground: {
        areaStyle: {
          color: '#8392A5'
        },
        lineStyle: {
          opacity: 0.8,
          color: '#8392A5'
        }
      },
      brushSelect: true
    },
    {
      type: 'inside'
    }
  ],
  series: [
    {
      type: 'candlestick',
      name: 'Day',
      data: data,
      itemStyle: {
        color: '#FD1050',
        color0: '#0CF49B',
        borderColor: '#FD1050',
        borderColor0: '#0CF49B'
      }
    },
    {
      name: 'MA5',
      type: 'line',
      data: calculateMA(5, data),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA10',
      type: 'line',
      data: calculateMA(10, data),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA20',
      type: 'line',
      data: calculateMA(20, data),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    },
    {
      name: 'MA30',
      type: 'line',
      data: calculateMA(30, data),
      smooth: true,
      showSymbol: false,
      lineStyle: {
        width: 1
      }
    }
  ]
};

dyGraphs is possible to fill the area with differents colors based on the y value?

Graph Image

With dyGraphs is possible to fill the area of a graphic with differents color based on y value ?

0-49.9 green
50.0 149.9 yellow
150 and up red.

I made this example https://tbr2.it/radon/index.html but with no different fill color all blue. I didn’t find any example in the documentation like this.

this is the code used

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
   <link href="/radon/dygraph.min.css" rel="stylesheet" />
<body>
<h3>Radon Soggiorno</h3>
<div style="width:2000px;height:1500px">
  <p style="text-align:center;">
    Zoom in: double-click, scroll wheel<br>
    Zoom out: ctrl-double-click, scroll wheel<br>
    Standard Zoom: shift-click-drag
    Standard Pan: click-drag<br>
    Restore zoom level: press button<br>
  </p>
  <button id="restore3">Ripristina</button>
  <div id="div_g3" style="width:2000px; height:1000px;"></div>
</div>

<script src="/radon/dygraph.min.js"></script>
<script src="/radon/dy_data.js"></script>
<script src="https://canvasjs.com/assets/script/jquery-1.11.1.min.js"></script>
<script>

function downV3(event, g, context) {
  context.initializeMouseDown(event, g, context);
  if (event.altKey || event.shiftKey) {
    Dygraph.startZoom(event, g, context);
  } else {
    Dygraph.startPan(event, g, context);
  }
}

function moveV3(event, g, context) {
  if (context.isPanning) {
    Dygraph.movePan(event, g, context);
  } else if (context.isZooming) {
    Dygraph.moveZoom(event, g, context);
  }
}

function upV3(event, g, context) {
  if (context.isPanning) {
    Dygraph.endPan(event, g, context);
  } else if (context.isZooming) {
    Dygraph.endZoom(event, g, context);
  }
}

// Take the offset of a mouse event on the dygraph canvas and
// convert it to a pair of percentages from the bottom left.
// (Not top left, bottom is where the lower value is.)
function offsetToPercentage(g, offsetX, offsetY) {
  // This is calculating the pixel offset of the leftmost date.
  var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
  var yar0 = g.yAxisRange(0);

  // This is calculating the pixel of the higest value. (Top pixel)
  var yOffset = g.toDomCoords(null, yar0[1])[1];

  // x y w and h are relative to the corner of the drawing area,
  // so that the upper corner of the drawing area is (0, 0).
  var x = offsetX - xOffset;
  var y = offsetY - yOffset;

  // This is computing the rightmost pixel, effectively defining the
  // width.
  var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;

  // This is computing the lowest pixel, effectively defining the height.
  var h = g.toDomCoords(null, yar0[0])[1] - yOffset;

  // Percentage from the left.
  var xPct = w === 0 ? 0 : (x / w);
  // Percentage from the top.
  var yPct = h === 0 ? 0 : (y / h);

  // The (1-) part below changes it from "% distance down from the top"
  // to "% distance up from the bottom".
  return [xPct, (1-yPct)];
}

function dblClickV3(event, g, context) {
  // Reducing by 20% makes it 80% the original size, which means
  // to restore to original size it must grow by 25%

  if (!(event.offsetX && event.offsetY)){
    event.offsetX = event.layerX - event.target.offsetLeft;
    event.offsetY = event.layerY - event.target.offsetTop;
  }

  var percentages = offsetToPercentage(g, event.offsetX, event.offsetY);
  var xPct = percentages[0];
  var yPct = percentages[1];

  if (event.ctrlKey) {
    zoom(g, -0.25, xPct, yPct);
  } else {
    zoom(g, +0.2, xPct, yPct);
  }
}

var lastClickedGraph = null;

function clickV3(event, g, context) {
  lastClickedGraph = g;
  event.preventDefault();;
}

function scrollV3(event, g, context) {
  if (lastClickedGraph != g) {
    return;
  }
  var normal = event.detail ? event.detail * -1 : event.wheelDelta / 40;
  // For me the normalized value shows 0.075 for one click. If I took
  // that verbatim, it would be a 7.5%.
  var percentage = normal / 50;

  if (!(event.offsetX && event.offsetY)){
    event.offsetX = event.layerX - event.target.offsetLeft;
    event.offsetY = event.layerY - event.target.offsetTop;
  }

  var percentages = offsetToPercentage(g, event.offsetX, event.offsetY);
  var xPct = percentages[0];
  var yPct = percentages[1];

  zoom(g, percentage, xPct, yPct);
  event.preventDefault();
}

// Adjusts [x, y] toward each other by zoomInPercentage%
// Split it so the left/bottom axis gets xBias/yBias of that change and
// tight/top gets (1-xBias)/(1-yBias) of that change.
//
// If a bias is missing it splits it down the middle.
function zoom(g, zoomInPercentage, xBias, yBias) {
  xBias = xBias || 0.5;
  yBias = yBias || 0.5;
  function adjustAxis(axis, zoomInPercentage, bias) {
    var delta = axis[1] - axis[0];
    var increment = delta * zoomInPercentage;
    var foo = [increment * bias, increment * (1-bias)];
    return [ axis[0] + foo[0], axis[1] - foo[1] ];
  }
  var yAxes = g.yAxisRanges();
  var newYAxes = [];
  for (var i = 0; i < yAxes.length; i++) {
    newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
  }

  g.updateOptions({
    dateWindow: adjustAxis(g.xAxisRange(), zoomInPercentage, xBias),
    valueRange: newYAxes[0]
    });
}

var v4Active = false;
var v4Canvas = null;

function downV4(event, g, context) {
  context.initializeMouseDown(event, g, context);
  v4Active = true;
  moveV4(event, g, context); // in case the mouse went down on a data point.
}

var processed = [];

function moveV4(event, g, context) {
  var RANGE = 7;

  if (v4Active) {
    var graphPos = Dygraph.findPos(g.graphDiv);
    var canvasx = Dygraph.pageX(event) - graphPos.x;
    var canvasy = Dygraph.pageY(event) - graphPos.y;

    var rows = g.numRows();
    // Row layout:
    // [date, [val1, stdev1], [val2, stdev2]]
    for (var row = 0; row < rows; row++) {
      var date = g.getValue(row, 0);
      var x = g.toDomCoords(date, null)[0];
      var diff = Math.abs(canvasx - x);
      if (diff < RANGE) {
        for (var col = 1; col < 3; col++) {
          // TODO(konigsberg): these will throw exceptions as data is removed.
          var vals =  g.getValue(row, col);
          if (vals === null || vals === undefined) { continue; }
          var val = vals[0];
          var y = g.toDomCoords(null, val)[1];
          var diff2 = Math.abs(canvasy - y);
          if (diff2 < RANGE) {
            var found = false;
            for (var i in processed) {
              var stored = processed[i];
              if(stored[0] == row && stored[1] == col) {
                found = true;
                break;
              }
            }
            if (!found) {
              processed.push([row, col]);
              drawV4(x, y);
            }
            return;
          }
        }
      }
    }
  }
}

function upV4(event, g, context) {
  if (v4Active) {
    v4Active = false;
  }
}

function dblClickV4(event, g, context) {
  restorePositioning(g);
}

function drawV4(x, y) {
  var ctx = v4Canvas;

  ctx.strokeStyle = "#000000";
  ctx.fillStyle = "#000000";
  ctx.beginPath();
  ctx.arc(x,y,5,0,Math.PI*2,true);
  ctx.closePath();
  ctx.stroke();
  ctx.fill();
}

function captureCanvas(canvas, area, g) {
  v4Canvas = canvas;
}

function restorePositioning(g) {
  g.updateOptions({
    dateWindow: null,
    valueRange: null
  });
}



$(document).ready(function() {
          var g3 = new Dygraph(document.getElementById("div_g3"),
                     NoisyData, { errorBars : true, interactionModel : {
                      'mousedown' : downV3,
                      'mousemove' : moveV3,
                      'mouseup' : upV3,
                      'click' : clickV3,
                      'dblclick' : dblClickV3,
                      'mousewheel' : scrollV3
                },
                color: 'blue',
            fillGraph: true,
            fillAlpha: 0.9,
                axes: {
                      y: {
                        ticker: function(min, max, pixels) {
                          ticks = [];
                          for (var i = 0; i <= max; i = i + 50) {
                            ticks.push({v: i});
                            ticks.push({label_v: i, label: i});
                          }
                          return ticks;
                        }
                      }
                    }
              });
                document.getElementById("restore3").onclick = function() {
  enter image description here                restorePositioning(g3);
                };
    }
);
</script>

</body>

</html>

Using Jquery for binding data in HTML table getting NAN values

I have implemented the below logic for displaying calculation with data. So while displaying the data in Maintenance Point column I am getting values as NaN. But there should be no values. What is wrong in below column?

function displaySignOffSheetFTTX(ReportType, Month, DataList) {
    var _reportType = (ReportType == 'ALL') ? "PAN INDIA" : ReportType;
    var _month = Month;

    var table = $('#grdCicleDatatable');
    $(table).empty();
    var thead = "";
    var datalist = JSON.parse(DataList);

    
        thead = "<thead><tr><th rowspan='2' class='text-left'>Sr.No</th><th rowspan='2' class='text-left'>Maintenance Point</th><th align='center' colspan='5'>Billable Scope Approved by CMM</th></tr><tr><th class='text-left'>UG Scope (KM)</th><th class='text-left'>Aerial Scope (KM)</th><th class='text-left'>MDU Scope (KM)</th><th class='text-left'>Aerial + MDU Scope (KM)</th><th class='text-left'>Total Scope (KM)</th></tr></thead>";
    

    var tbody = "<tbody>";
    table.append(thead);
    table.append(tbody);

    if (datalist != null && datalist.length > 0) {
      
        var AerialMDUTotal = 0;
        var UGAerialMDUTotal = 0;

        $.each(datalist, function (key, val) {

            key++;

            val.UG_LENGTH = val.UG_LENGTH == null ? 0 : parseFloat(val.UG_LENGTH);
            val.UG_LENGTH = val.UG_LENGTH.toFixed(2);

            val.AR_LENGTH = val.AR_LENGTH == null ? 0 : parseFloat(val.AR_LENGTH);
            val.AR_LENGTH = val.AR_LENGTH.toFixed(2);

            val.MDU_LENGTH = val.MDU_LENGTH == null ? 0 : parseFloat(val.MDU_LENGTH);
            val.MDU_LENGTH = val.MDU_LENGTH.toFixed(2);

            AerialMDUTotal = parseFloat(val.AR_LENGTH) + parseFloat(val.MDU_LENGTH);

            UGAerialMDUTotal = parseFloat(val.UG_LENGTH) + AerialMDUTotal;
            UGAerialMDUTotal = UGAerialMDUTotal.toFixed(2);

            

            var tr = "<tr><td>" + key + " </td><td>" + val.ITEM + "</td><td>" + val.UG_LENGTH + "</td><td>" + val.AR_LENGTH + "</td><td>" + val.MDU_LENGTH + "</td><td>" + AerialMDUTotal + "</td><td>" + UGAerialMDUTotal + "</td></tr>";

            table.append(tr);
        });

        table.append('</tbody>');
    }
    var footer = "<tfoot><th colspan='1' style='text-align:center'><b>Total:</b></th><th class='text-center'></th><th class='text-center'></th><th class='text-center'></th><th class='text-center'></th><th class='text-center'></th><th class='text-center'></th></tfoot>";

    table.append(footer);

    oTable = $(table).dataTable({
        dom: 'tp',
        "dom": 'tp<"bottom"B><"clear">',
        "searching": false,
        responsive: true,
        "autoWidth": true,
        "bDestroy": true,
        "pageLength": 6,
        paging: false,
        "columnDefs": [
            { "width": "7.7%", "targets": 0 },
            { "width": "7.7%", "targets": 1 },
            { "width": "7.7%", "targets": 2 },
            { "width": "7.7%", "targets": 3 },
            { "width": "7.7%", "targets": 4 },
            { "width": "7.7%", "targets": 5 },
            { "width": "7.7%", "targets": 6 }

        ],
        buttons: [
            {
                "extend": "excelHtml5", "text": "Export to Excel", "filename": _reportType + "_SignOffSheet_" + _month,
                title: 'Sign Of Sheet of ' + _reportType + ' Circle for ' + _month + ' Month',
                exportOptions: {
                    columns: ':visible',
                    format: {
                        header: function (data, columnindex, trDOM, node) {
                            return GetColumnPrefixFTTX(columnindex) + data;
                        }
                    }
                }
            }
        ],
        "footerCallback": function (row, data, start, end, display) {
            var api = this.api(), data;

            var intVal = function (i) {
                return typeof i === 'string' ? i.replace(/[$,]/g, '') * 1 : (typeof i === 'number' ? i : 0);
            };

            var FTTXUGTotal = api.column(1).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);
            var FTTXARTotal = api.column(2).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);
            var FTTXMDUTotal = api.column(3).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);
            var TotFTTXUGTotal = api.column(4).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);
            var TotFTTXARTotal = api.column(5).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);
            var TotFTTXMDUTotal = api.column(6).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0).toFixed(2);

            $(api.column(0).footer()).html('Total');
            $(api.column(1).footer()).html(FTTXUGTotal);
            $(api.column(2).footer()).html(FTTXARTotal);
            $(api.column(3).footer()).html(FTTXMDUTotal);
            $(api.column(4).footer()).html(TotFTTXUGTotal);
            $(api.column(5).footer()).html(TotFTTXARTotal);
            $(api.column(6).footer()).html(TotFTTXMDUTotal);
        },

        initComplete: function () {
            var btns = $('.dt-button');
            btns.addClass('btn btn-danger button');
            btns.removeClass('dt-button');
        }
    });

    $('.buttons-excel').css("display", "none");

}

Screenshot:

enter image description here

how to share Azure AD auth between two different sites

I have two websites:

1.  Main Site: Developed using Next.js with NextAuth for authentication.
2.  Child Site: Developed using React with MSAL-React for authentication.

Both applications share the same Azure AD client ID and have the same subdomain.

Authentication Flow

•   When a user logs in on the main site, it stores the accessToken in a cookie with the subdomain scope.
•   The child site can retrieve this accessToken from the cookie.

Goal

I want the child site to skip the login process in MSAL-React and use the accessToken stored in the cookie instead.

Question

Can I achieve this authentication bypass using only an accessToken, or do I need additional configurations?

Current Approach in the Child Site

•   Retrieve accessToken from the cookie.
•   If the token exists, store it in sessionStorage and skip MSAL authentication.
•   Otherwise, proceed with the MSAL authentication flow, attempting ssoSilent before falling back to a redirect-based login.

my msal-react config looks like this

export const msalConfig: Configuration = {
  auth: {
    // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
    clientId: import.meta.env.VITE_MSAL_CLIENT_ID,
    // Full directory URL, in the form of https://login.microsoftonline.com/<tenant-id>
    authority: `https://login.microsoftonline.com/${import.meta.env.VITE_MSAL_TENANT_ID}`,
    // Full redirect URL, in form of http://localhost:3000
    redirectUri: isMobile
      ? window.location.origin + '/login'
      : window.location.origin + '/oauth2/redirect',
    navigateToLoginRequestUrl: true,
  },
  cache: {
    cacheLocation: 'localStorage', // This configures where your cache will be stored
    storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
  },
  system: {
    allowNativeBroker: false, // Disables WAM Broker
    loggerOptions: {
      loggerCallback: (level, message, containsPii) => {
        if (containsPii) {
          return
        }
        if (!(window as any).showMsalLog) {
          return
        }
        switch (level) {
          case LogLevel.Error:
            console.error(message)
            return
          case LogLevel.Info:
            console.info(message)
            return
          case LogLevel.Verbose:
            console.debug(message)
            return
          case LogLevel.Warning:
            console.warn(message)
            return
          default:
            return
        }
      },
    },
    allowRedirectInIframe: true,
    tokenRenewalOffsetSeconds: 300, // 5 minutes before token expiry
    // pollIntervalMilliseconds: 0,
  },
}

and what I’m trying in child project is like this

  useEffect(() => {
    const initializeAuth = async () => {
      try {
        // Get token from cookie if available
        const getCookieValue = (name: any) => {
          const value = `; ${document.cookie}`
          const parts = value.split(`; ${name}=`)
          if (parts.length === 2) return parts?.pop()?.split(';').shift()
          return null
        }

        // Check if accessToken exists in cookies
        const tokenFromCookie = getCookieValue('accessToken')

        if (tokenFromCookie) {
          // Use token from cookie and skip MSAL auth
          console.log('Using token from cookie')
          sessionStorage.setItem('accessToken', tokenFromCookie)
          setIsInitialized(true)
          return
        }

        // Proceed with normal MSAL authentication flow
        await instance.handleRedirectPromise()
        const accounts = instance.getAllAccounts()

        if (accounts.length > 0) {
          instance.setActiveAccount(accounts[0])
          setIsInitialized(true)
        } else {
          try {
            const silentRequest = {
              ...loginRequest,
              loginHint: '',
              redirectUri: window.location.origin + '/oauth2/redirect',
            }

            await instance
              .ssoSilent(silentRequest)
              .then((response) => {
                console.log('Silent login success', response.accessToken)
                sessionStorage.setItem('accessToken', response.accessToken)
              })
              .catch((error) => {
                console.error('Silent login failed', error)
                throw error
              })

            const accountsAfterSso = instance.getAllAccounts()
            if (accountsAfterSso.length > 0) {
              instance.setActiveAccount(accountsAfterSso[0])
            }
          } catch (e) {
            console.error('Silent login failed', e)
          }
        }
      } catch (error) {
        console.error('Auth initialization failed:', error)
      } finally {
        setIsInitialized(true)
      }
    }

    initializeAuth()
  }, [instance])

Discord.js 14 Slash Commands Duplicated

Not really sure why, but seems that my slash commands are registering twice. I’ve tried various methods, yes using ChatGPT, but so far all of the results have duplicates.

Any suggestions? (Besides stop using ChatGPT since it’s helping me learn to do this).

As far as I can tell it should only be registering once so not really sure where to go from here.

// Initialize your commands collection
client.commands = new Collection();

// Read command files from ./commands/
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));

// Array to hold slash command definitions for registration
const slashCommandsData = [];

// Load each command file
for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file);
  const commandModule = require(filePath);

  // If a module exports multiple slash definitions:
  if (Array.isArray(commandModule.data)) {
    for (const slashDef of commandModule.data) {
      if (!slashDef.name) continue; // Skip invalid definitions
      slashCommandsData.push(slashDef.toJSON());
      client.commands.set(slashDef.name, commandModule);
    }
  } else {
    // Single slash command export
    const { data } = commandModule;
    if (!data.name) continue; // Skip if missing name
    slashCommandsData.push(data.toJSON());
    client.commands.set(data.name, commandModule);
  }
}

console.log(`Total commands loaded: ${client.commands.size}`);

// Register slash commands for a specific guild after the client is ready
client.once('ready', async () => {
  try {
    const guildId = '1134123338734764072';
    const guild = client.guilds.cache.get(guildId);
    if (!guild) throw new Error(`Guild with ID ${guildId} not found`);

    console.log(`Clearing existing commands for guild ${guildId}...`);
    // Clear all existing commands for this guild
    await guild.commands.set([]);
    console.log('Existing commands cleared.');

    console.log(`Now registering (/) commands for guild ${guildId}...`);
    const rest = new REST({ version: '10' }).setToken(BOT_TOKEN);
    const result = await rest.put(
      Routes.applicationGuildCommands(DISCORD_CLIENT_ID, guildId),
      { body: slashCommandsData }
    );
    console.log(`Successfully registered ${result.length} slash command(s) for the guild.`);
  } catch (error) {
    console.error('Error during command registration:', error);
  }
});

// Listen for slash command interactions
client.on('interactionCreate', async interaction => {
  if (!interaction.isCommand()) return;
  const command = client.commands.get(interaction.commandName);
  if (!command) return;

  try {
    await command.execute(interaction);
  } catch (error) {
    console.error('Error executing command:', error);
    if (!interaction.replied && !interaction.deferred) {
      await interaction.reply({ content: 'There was an error executing this command!', ephemeral: true });
    } else {
      await interaction.followUp({ content: 'There was an error executing this command!', ephemeral: true });
    }
  }
});

How do I make my Vertical Image Parallax Smoother? [Javascript]

I’m trying to make a parallax effect similar to the hover-over effects on the images on this site: https://www.framer.com/gallery/categories/portfolio

and here is the code I have so far:

document.querySelectorAll('.image-container').forEach(item => {
    const img = item.querySelector('img');

    item.addEventListener('mousemove', (e) => {
        const rect = item.getBoundingClientRect();
        const mouseY = e.clientY - rect.top; // Mouse Y position relative to the item
        const halfHeight = rect.height / 2; // Midpoint of the element
        const imgHeight = img.height - rect.height
        const mousePercentage = mouseY/imgHeight*100

        img.style.transform = `translateY(-${mousePercentage}px)`; // Move up
        img.style.transition = "transform 0s ease-out"
        
    });

    item.addEventListener('mouseleave', () => {
        img.style.transform = 'translateY(0)'; // Reset position when mouse leaves
        img.style.transition = " transform 2s ease-out"
    });

    item.addEventListener('mouseenter', () =>{
        img.style.transition = "transform 2s ease-out"
    })
});
#display-grid{
    display: grid;
    width: 80%;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(24%, 1fr));

}

.cell{
    border: 1px solid #EEEEEE;
    border-radius: 1rem;
    width: 100%;
    overflow-y: hidden;
    position: relative;
  overflow: hidden;
  border-radius: 12px;
  aspect-ratio: 1 / 1.2;
}

.cell .image-container{
  width: 100%;
  height: 90%;
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 1rem;
  overflow-y: hidden;

}

.cell img {
  width: 100%;
  height: auto;
  object-fit: cover;
  object-position: top;
  border-radius: 1rem;
}
<div id="display-grid">
    <div class="cell">
      <div class="image-container">
        <img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
      </div>
      <p>Image Title</p>
    </div>
    <div class="cell">
      <div class="image-container">
        <img id="website-preview" src="https://framerusercontent.com/images/B7ykrtzOQa5hEXGFIhcq8gyaE.jpg?scale-down-to=1024">
      </div>
      <p>InfoSwap</p>
    </div>
</div>

I have made it as far as having the image scroll within frame up and down a variable % depending on where the mouse is in the image-container, but I find when the mouse enters the image-container, the image jumps to the coordinates. If I add a transition time to my stylesheet or to the “mousemove” Event Listener, I find the image waits for the mouse to stop moving before slowly moving to where the image needs to be.

What can I so that the image moves smoothly and consistently on entry, moving, and leaving the container.

Thanks in advance!

Replicate a water droplet cursor effect

I am trying to replicate the background magnification cursor effect you can see on this website using html, css, js. Does anyone have an idea of how I can do this? Any help would be welcome.

I have managed to replicate the motion of the bubbles but not the magnification/distortion of the background image within the circles.

<head>
    <meta charset="UTF-8"/>
    <link rel="stylesheet" href="dropplet.css">
</head>


<body>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <script src="dropplet.js"></script>
</body>
const coords = { x: 0, y: 0 };
const circles = document.querySelectorAll(".circle");

circles.forEach(function (circle) {
    circle.x = 0;
    circle.y = 0;
});

window.addEventListener("mousemove", function(e){
  coords.x = e.clientX;
  coords.y = e.clientY;
});

function animateCircles() {

    let x = coords.x;
    let y = coords.y;
    let speed = 0.8;
    // var bw = 3;
    // var w = glass.offsetWidth / 2;
    // var h = glass.offsetHeight / 2;

    circles.forEach(function (circle, index) {
        circle.style.left = x - 20 + "px";
        circle.style.top = y - 20 + "px";
        circle.x = x;
        circle.y = y;

        // circle.style.backgroundSize = (img.width * zoom) + "px " + (img.height * zoom) + "px";
        // circle.style.backgroundPosition = "-" + ((x * zoom) - w + bw) + "px -" + ((y * zoom) - h + bw) + "px";
        circle.style.scale = ((1.5 - 1) * (index - 1) / (20 - 1)) + 1;

        const nextCircle = circles[index + 1] || circles[0];

        x += (nextCircle.x - x) * speed;
        y += (nextCircle.y - y) * speed;
    
    });

    requestAnimationFrame(animateCircles);
}


animateCircles();