Using puppeteer to access body in HttpResponse designed to be downloaded as attachment

I wish to use puppeteer to simulate clicking on a link which posts data to the server and returns a csv file designed to be downloaded by the user. When that response arrives, I would like to read and process the response payload.

The response header includes:

content-disposition: attachment; filename=GVG-SearchResults-20231205121846.csv
content-type: text/csv

the code I am currently using throws an error because there is no body detected in the response – I am assuming because of the content-disposition above:

const url = 'https://www.greenvehicleguide.gov.au/Vehicle/Search';
browser = await puppeteer.launch({
    headless: false,
});
let page = await browser.newPage();

// select Toyota then Corolla and click submit
await page.goto(url, {timeout: 180000, waitUntil: 'networkidle0'});
await page.waitForSelector('#VS_4_SelectedManufacturer option[value="3"]');
await page.select('#VS_4_SelectedManufacturer', '3');
const waitForSelectPop = 'https://www.greenvehicleguide.gov.au/Vehicle/GetNamesForSelectList';
await page.waitForResponse(waitForSelectPop);
await page.waitForSelector('#VS_4_VehicleModel option[value="Corolla"]');
await page.select('#VS_4_VehicleModel', 'Corolla');
await page.click('#submitType4');
await page.waitForNavigation();

// wait until the csv download link is available, click it and intercept response
const csvSelector = 'a.csv';
await page.waitForSelector(csvSelector);
const responsePromise = page.waitForResponse(
    response =>  response.headers()['content-disposition'] 
        && response.headers()['content-disposition'].startsWith('attachment')
);
await page.click(csvSelector);
const response = await responsePromise;
if (response.status() === 200) {
    const text = await response.text(); // throws
    // const buffer = await response.buffer(); // also throws
    // ... do stuff here which needs to run in a node context - processing and saving to fileSystem or database
}

this throws on the response.buffer() (or response.text()) with:

...myProjectnode_modulespuppeteer-corelibcjspuppeteercdpHTTPResponse.js:103
                    return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
                                                ^

TypeError: Cannot read properties of undefined (reading 'body')

In summary – how do I obtain the body of the response and use it within the NodeJS context running pupeteer, overcoming the content-disposition of the response being attachment?

Dropzone required Using Parsley js

I have a multi-step form which has a dropzone js, I use Parsley js to validate each form step input fields so whenever the user clicks next button it validates the current block before going to the next step. But when it comes to the dropzone step i can’t figure how to check if the user has uploaded an image or not before going to the next step. Is it even possible to do this using parsley js?

I just couldn’t get it to work.

Loading Widget in HTML with Param from Browser

I have a widget that I have hosted. I am able to successfully call it, and it loads. But When I am trying to pass it params from browser its not loading. The main difference is how I am calling the script.

The following code works but then its hard coded params

<div id="idealy_widget"></div>
  <link href="https://xxxxxxxx/jscript/index.css" rel="stylesheet"/>
   <script id="widget-params" src="https://xxxxxxxxx/jscript/index.js" 
  clientId="xxxxxxxxxxxxxxxxxxxxx" 
  clientSecret="xxxxxxxxxx" 
   username="xxxxxxxxxxxxx"
    product_id="xxxxxxxxxx" type="module">
</script>      

The following code doesnt work. it doesnt show the form which is part of the widget. It print the params.

    <div id="idealy_widget"></div>
  <script type="module">
    const clientId = new URLSearchParams(window.location.search).get('clientId');
    const clientSecret = new URLSearchParams(window.location.search).get('clientSecret');
    const username = new URLSearchParams(window.location.search).get('username');
    const product_id = new URLSearchParams(window.location.search).get('product_id');
    
    console.log('Params:', clientId, clientSecret, username, product_id);

    const widgetScript = document.createElement('script');
    widgetScript.src = `https://xxxxxxxxxxxxx/jscript/index.js?clientId=${clientId}&clientSecret=${clientSecret}&username=${username}&product_id=${product_id}`;
    widgetScript.type = 'module';
    widgetScript.id = 'widget-params';

    widgetScript.onload = () => {
      console.log('Script loaded successfully');
      // You may not need to manually call initWidget since it's called internally in index.js
    };

    document.head.appendChild(widgetScript);
  </script>

How to display the output of a JavaScript random image generator horizontally

I’m making a random image generator in JavaScript following this online guide. I’m looking to generate multiple random images as shown in example 2 of the guide but am unsure how to make these images display horizontally instead of vertically. Any help is appreciated!

Here’s the code in question below:

function getRandomImage() {

  var randomImage = new Array();

  randomImage[0] = "https://wi.wallpapertip.com/wsimgs/15-155208_desktop-puppy-wallpaper-hd.jpg";
  randomImage[1] = "http://www.petsworld.in/blog/wp-content/uploads/2014/09/running-cute-puppies.jpg";
  randomImage[2] = "https://wi.wallpapertip.com/wsimgs/156-1564365_golden-retriever-puppy-desktop-wallpaper-desktop-wallpaper-puppy.jpg";
  randomImage[3] = "https://wi.wallpapertip.com/wsimgs/156-1564140_free-puppy-wallpapers-for-computer-wallpaper-cave-cute.jpg";
  randomImage[4] = "https://wi.wallpapertip.com/wsimgs/156-1565522_puppies-desktop-wallpaper-desktop-background-puppies.jpg";
  randomImage[5] = "https://wi.wallpapertip.com/wsimgs/156-1566650_cute-puppies-desktop-wallpaper-cute-puppies.jpg";

  for (let i = 0; i < 5; i++) {

    var number = Math.floor(Math.random() * randomImage.length);

    document.getElementById("result").innerHTML += '<img src="' + randomImage[number] + '" style="width:150px" />';
  }
}

getRandomImage()
<div id="result"></div>

Blocking fallback form when javascript is enabled

I’m trying to setup a page where people can submit data in a form, but the presentation of the form is based on whether the user has javascript and/or CSS enabled or not.

Currently I have a code similar to the following code to achieve my requirements:

<a href="/non-javascript-form" onclick="javascriptform()">Fill in data</a>
<div ID="javascriptform">
    <form action="submit.php" method="post">
        Enter data: <input type="text" name="data">
        <input type="submit" value="send data">
    </form>
</div>
<script>
function javascriptform(){
    document.getElementById("javascriptform").display="block";
    return false;
}
</script>

Then on the /non-javascript-form page I have centents similar to this:

<form action="submit.php" method="post">
    Enter data: <input type="text" name="data">
    <input type="submit" value="send data">
</form>

and in CSS, I would have:

#javascriptform{display:none}

I’m trying to avoid forcing those without javascript to load a second page to access the form when the form is already in the code on the first page.

My idea works if both CSS and Javascript are disabled, then I could change the href value to a hash while people could see the form. Also, it works if CSS and Javascript are both enabled.

But what about the case where CSS is enabled and javascript is disabled?

If I were to avoid the link going to the separate page, the people in this situation will have no way of seeing the form unless they turn CSS off.

The only other way I could pull this off which could wreck CEO guidelines is to make a script that causes the form to disappear AFTER its loaded, thereby causing a bit of flicker.

Is there a way I can make a piece of javascript code that causes an element to be hidden before it has the chance to appear on screen and without requiring visitors to upgrade their browsers?

Limiting mouse scroll in viewbox

I have the below code for zooming in and out of a svg viewbox. Is there any way to modify this to limit the zoom out to the view size that is initially loaded on the page?

window.addEventListener("DOMContentLoaded", (event) => {
const svg = document.querySelector('svg');

// zooming
svg.onwheel = function (event) {
    event.preventDefault();

    // set the scaling factor (and make sure it's at least 10%)
    let scale = event.deltaY / 1000;
    scale = Math.abs(scale) < .1 ? .1 * event.deltaY / Math.abs(event.deltaY) : scale;

    // get point in SVG space
    let pt = new DOMPoint(event.clientX, event.clientY);
    pt = pt.matrixTransform(svg.getScreenCTM().inverse());

    // get viewbox transform
    let [x, y, width, height] = svg.getAttribute('viewBox').split(' ').map(Number);

    // get pt.x as a proportion of width and pt.y as proportion of height
    let [xPropW, yPropH] = [(pt.x - x) / width, (pt.y - y) / height];
    
    // calc new width and height, new x2, y2 (using proportions and new width and height)
    let [width2, height2] = [width + width * scale, height + height * scale];
    let x2 = pt.x - xPropW * width2;
    let y2 = pt.y - yPropH * height2;        

    svg.setAttribute('viewBox', `${x2} ${y2} ${width2} ${height2}`);
}

})

Age restriction using Javascript on Select HTML tag

I’m been trying to make a signup form with a restriction of age between 18 and 90 years old.

I’m newly on using JS, or any type of programming language but I think it could be possible to use JS for restricting people signup below 18 years old. I don’t really know what should I type in. I have tried several options found it on internet but none of them works.

Can anyone could help me out please ? 🙂

Thank you for your help and support . I appreciate that.

I’m trying to manipulate the HTML select element to restrict the signup below 18 years old.

HTML:

    <form class="signup" id="signup" netlify>
        <a href="#" onclick="hide('signup')"><ion-icon class="ion-icon" name="close-outline" class="iconic"></ion-icon></a>
        <div class="filling-up">
                <input id="surname" type="text" placeholder="First Name" name="surname" required>
                <input id="name" type="text" placeholder="Last Name" name="name" required><br>
                <input id="username" type="text" placeholder="Username" name="name" required>
        </div>
        <div class="filling-up">
            <input id="email" type="email" placeholder="Email Address" name="email" required>
            <input id="password" type="password" placeholder="New Password" name="password" required>
        </div>
        <p>date of birth</p><br>
        <div class="birthday">
            <span class="Day">
                <select aria-label="day" name="birthday_day" id="day" 
                title="day" class="selection_birthday">
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
                <option value="6">6</option>
                <option value="7">7</option>
                <option value="8">8</option>
                <option value="9">9</option>
                <option value="10">10</option>
                <option value="11">11</option>
                <option value="12">12</option>
                <option value="12">13</option>
                <option value="12">14</option>
                <option value="12">15</option>
                <option value="12">16</option>
                <option value="12">17</option>
                <option value="12">18</option>
                <option value="12">19</option>
                <option value="12">20</option>
                <option value="12">21</option>
                <option value="12">22</option>
                <option value="12">23</option>
                <option value="12">24</option>
                <option value="12">25</option>
                <option value="12">26</option>
                <option value="12">27</option>
                <option value="12">28</option>
                <option value="12">29</option>
                <option value="12">30</option>
                <option value="12">31</option>
            </select>
            </span>
            <span id="month">
                <select aria-label="Month" name="birthday_month" id="month" 
                title="month" class="selection_birthday">
                <option value="1">January</option>
                <option value="2">February</option>
                <option value="3">March</option>
                <option value="4">April</option>
                <option value="5">May</option>
                <option value="6">June</option>
                <option value="7">July</option>
                <option value="8">August</option>
                <option value="9">September</option>
                <option value="10">October</option>
                <option value="11">November</option>
                <option value="12">December</option>
            </select>
            </span>
            <span id="year">
                <select aria-label="Year" name="birthday_year" id="year" 
                title="year" class="selection_birthday">
                <option value="0">2024</option>
                <option value="1">2023</option>
                <option value="2">2022</option>
                <option value="3">2021</option>
                <option value="4">2020</option>
                <option value="5">2019</option>
                <option value="6">2018</option>
                <option value="7">2017</option>
                <option value="8">2016</option>
                <option value="9">2015</option>
                <option value="10">2014</option>
                <option value="11">2013</option>
                <option value="12">2012</option>
                <option value="13">2011</option>
                <option value="14">2010</option>
                <option value="15">2009</option>
                <option value="16">2008</option>
                <option value="17">2007</option>
                <option value="18">2006</option>
                <option value="19">2005</option>
                <option value="20">2004</option>
                <option value="21">2003</option>
                <option value="22">2002</option>
                <option value="23">2001</option>
                <option value="24">2000</option>
                <option value="25">1999</option>
                <option value="26">1998</option>
                <option value="27">1997</option>
                <option value="28">1996</option>
                <option value="29">1995</option>
                <option value="30">1994</option>
                <option value="31">1993</option>
                <option value="32">1992</option>
                <option value="33">1991</option>
                <option value="34">1990</option>
                <option value="35">1989</option>
                <option value="36">1988</option>
                <option value="37">1987</option>
                <option value="38">1986</option>
                <option value="39">1985</option>
                <option value="40">1984</option>
                <option value="41">1983</option>
                <option value="42">1982</option>
                <option value="43">1981</option>
                <option value="44">1980</option>
                <option value="45">1979</option>
                <option value="46">1978</option>
                <option value="47">1977</option>
                <option value="48">1976</option>
                <option value="49">1975</option>
                <option value="50">1974</option>
                <option value="51">1973</option>
                <option value="52">1972</option>
                <option value="53">1971</option>
                <option value="54">1970</option>
                <option value="55">1969</option>
                <option value="56">1968</option>
                <option value="57">1967</option>
                <option value="58">1966</option>
                <option value="59">1965</option>
                <option value="60">1964</option>
                <option value="61">1963</option>
                <option value="62">1962</option>
                <option value="63">1961</option>
                <option value="64">1960</option>
                <option value="65">1959</option>
                <option value="66">1958</option>
                <option value="67">1957</option>
                <option value="68">1956</option>
                <option value="69">1955</option>
                <option value="70">1954</option>
                <option value="71">1953</option>
                <option value="72">1952</option>
                <option value="73">1951</option>
                <option value="74">1950</option>
                <option value="75">1949</option>
                <option value="76">1948</option>
                <option value="77">1947</option>
                <option value="78">1946</option>
                <option value="79">1945</option>
                <option value="80">1944</option>
                <option value="81">1943</option>
                <option value="82">1942</option>
                <option value="83">1941</option>
                <option value="84">1940</option>
                <option value="85">1939</option>
                <option value="86">1938</option>
                <option value="87">1937</option>
                <option value="88">1936</option>
                <option value="89">1935</option>
                <option value="90">1934</option>
                <option value="91">1932</option>
                <option value="92">1931</option>
                <option value="93">1930</option>
            </select>
            </span>

        </div>
      <button type="submit" class="btn--submit">Submit</a></button>
</form>

I’ve posted on Codepen: https://codepen.io/cornelius89/pen/xxMZjza

Tracking “Mailchimp for WordPress” form submissions in Google Analytics and Reddit Ads

I have a WordPress landing page that uses the “Mailchimp for WordPress” plugin. I’m trying to track the form submission events in Google Analytics and Reddit Ads (via the Reddit Pixel). Unfortunately, after setting this up, I’m noticing that there are more form submission than there are tracked events in both Google Analytics and the Reddit Ads dashboard. It seems like not all of the events are being tracked.

If anyone could verify my setup and let me know if there are any obvious things to fix, I’d greatly appreciate it!

This is my setup:

  • I installed a Google Tag Manager container on the landing page.

  • Within the “Mailchimp for WordPress” form settings where you define the HTML for the form, I include the following JavaScript code to send form events to the dataLayer.

<script type="text/javascript">
// started
mc4wp.forms.on('started', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_started' });
});
 
// submitted
mc4wp.forms.on('submitted', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_submitted' });
});

// error
mc4wp.forms.on('error', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_error' });
});
  
// success
mc4wp.forms.on('success', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_success' });
});
  
// subscribed
mc4wp.forms.on('subscribed', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_subscribed' });
});

  // unsubscribed
mc4wp.forms.on('unsubscribed', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_unsubscribed' });
});
  
  
// updated_subscriber
mc4wp.forms.on('updated_subscriber', function(form) {
   window.dataLayer = window.dataLayer || [];
   window.dataLayer.push({ 'event' : 'mailchimp_form_updated_subscriber' });
});
</script>
  • In the Google Tag Manager, I set up triggers for each of those custom events. They look like so:

Example trigger

  • In Google Tag Manager, I then set up GA4 Event tags to send the events to Google Analytics. They look like so:

Example GA4 tag

  • In Google Tag Manager, I then set up a Reddit Pixel tag to send Sign Up events to Reddit Ads. It looks like so:

Example Reddit tag

*Note: I’m not sure if it makes a difference, but when a “Mailchimp for WordPress” form is submitted, it reloads the page. I’m not sure if that would affect this setup.

*Note: I did try to debug events with the “Tag Assistant Companion” and “Reddit Pixel Helper” Chrome extensions. The events seem to fire correctly when debugging with the use of those tools. It’s only on the Google Analytics and Reddit Ads dashboards that events seem to be underreported.

Thank you!

cancel any event or listener for a DOM element but not including mine

I’m building a chrome extension which sets the style to “none” of any element the user clicks, it looks like this:

function handleMouseOver(e) {
// Change the background color when the mouse hovers over an element
e.target.style.backgroundColor = "lightblue";
}

function handleMouseOut(e) {
  // Reset the background color when the mouse leaves an element
  e.target.style.backgroundColor = ""; // Set it to an empty string to reset to default
}

function clicked(e) {
  console.log(e.target);

  // Remove the other event listeners
  document.removeEventListener("click", clicked);
  document.removeEventListener("mouseover", handleMouseOver);
  document.removeEventListener("mouseout", handleMouseOut);
  var result = confirm("Do you want to remove this element?");

  if (result) {
    // If the user confirms, hide the clicked element
    e.target.style.display = "none";
  } else {
    // If the user cancels, reset the background color
    e.target.style.backgroundColor = "";
  }

}

   async function toggleClickListener(varFromPopup) {
     if (varFromPopup === true) {
     document.addEventListener("click", clicked);
     document.addEventListener("mouseover", handleMouseOver);
     document.addEventListener("mouseout", handleMouseOut);
    } 
  }

chrome.runtime.onMessage.addListener(message => {
  toggleClickListener(message.myVar);
  if(message.myVar === false){
  document.removeEventListener("mouseover", handleMouseOver);
  document.removeEventListener("mouseout", handleMouseOut);
  document.removeEventListener("click", clicked);
  }
});

if the user clicks on a button on my popup, it would send a message to my main script, and then activate the eventListener for the clicked function, and it works fine, the problem is, if i use some methods like event.stopPropagation() or event.preventDefault(), to stop the button from being clicked before or after setting its style to none, it also prevents my “clicked” function to be called, so i couldn’t find another way to cancel the actions of a dom element but not cancelling my function too!
i hope my explanation isn’t confusing

CKEditor mentions (autocompletion) having a different text content and mention id?

I am trying to add a plugin into CK Editor where I can alter the textContent of the mention to be the value of the attribute label.

    function MENTION_ENGINE(
        editor: Parameters<
            // @ts-ignore
            Parameters<typeof ClassicEditor.create>[1]["plugins"][number]
        >[0]
    ) {
        editor.conversion.for("upcast").elementToAttribute({
            view: {
                name: "span",
                classes: "mention",
                attributes: {}
            },
            model: {
                key: "mention",
                value: (viewItem) => {
                    console.log({ viewItem });
                    const mentionAttribute = editor.plugins
                        .get("Mention")
                        .toMentionAttribute(viewItem, {
                            // Add any other properties that you need.
                        });

                    return mentionAttribute;
                }
            },
            converterPriority: "high"
        });

        editor.conversion.for("downcast").attributeToElement({
            model: "mention",
            view: function (modelAttributeValue, { writer }) {
                if (!modelAttributeValue) {
                    return;
                }

                console.log({modelAttributeValue})

                return writer.createAttributeElement(
                    "span",
                    {
                        class: "mention",
                        "data-mention": modelAttributeValue.id,
                        "data-jsonpath": modelAttributeValue.jsonpath,
                        "data-label": modelAttributeValue.label
                    },
                    {
                        priority: 20, // Mention's priority in relation to other attributes
                        id: modelAttributeValue.uid // Unique ID to prevent merging mentions
                    }
                );
            },
            converterPriority: "high"
        });
    }

The upcast is model value function is also not running.

Expected the writer.createAttributeElement to return a HTML Element where I could just directly modify the textContent and set the label

Encountering GeneralException RichApi.Error in Office Add-in using Excel API: An internal error has occurred

I’m developing an Office Add-in using the Excel JavaScript API and I’ve encountered a problem when the Excel workbook is shared using Excel’s legacy share feature. All calls to context.sync() fail with a GeneralException error.

Here’s a snippet of my code:

await Excel.run(async function (context) {
    // Get the current workbook name property
    const workbook = context.workbook.load("name");

    // Sync to get the loaded properties
    await context.sync();

    debugger;
    
    result = workbook.name;
});

When I run this code in a shared workbook, I receive a GeneralException error at the context.sync() line. It doesn’t seem to matter what I ‘load’ any context.sync() results in the same error.

I have confirmed that Toolbar / Review / Unshare Workbook fixes the problem.

Looking for a way the add-in can check and inform user. Any insights would be greatly appreciated.

How to implement dynamic routing on Angular?

I have a project in Angular and we are separating business subjects by modules, for example we have two flows: hiring and activation, in hiring we have a component for credit card selection list and this component/page is being accessed by three different components(routes) and I’m not sure what is the best way to redirect the user when he chooses to go to the previous page. I made something like this:

import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter, pairwise } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class PreviousRouteService {
  private previousUrl: string;

  constructor(private router: Router) {
    this.router.events
      .pipe(
        filter((event: any) => event instanceof NavigationEnd),
        pairwise()
      )
      .subscribe((events: [NavigationEnd, NavigationEnd]) => {
        this.previousUrl = events[0].urlAfterRedirects;
        console.log('previous url', this.previousUrl);
      });
  }

  public getPreviousUrl(): string {
    return this.previousUrl;
  }

}

It’s a service to get the previous route, but for example I have 4 pages, and the 3rd is the credit card selection list, when I get to the 4th and go back to the third I cannot go back to the second one because the previous would be the 4th. How can I fix this problem?

Why am I getting a heap error when I run this program for large inputs?

I am working on this problem https://leetcode.com/problems/pseudo-palindromic-paths-in-a-binary-tree/description/ and I have created a solution that works fine for all my small testcases but the program causes a heap allocation error when it is run with a large input.

FATAL ERROR: MarkCompactCollector: young object promotion failed Allocation failed - JavaScript heap out of memory

Here’s my solution:

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var pseudoPalindromicPaths = function (root) {
    var total = 0
    var dfs = (node, parents) => {
        parents.push(node.val)
        if (!node.left && !node.right) {
            if (isPalindromic(parents)) {
                total++
            }
            return;
        }
        if (node.left) {
            dfs(node.left, [...parents])
        }
        if (node.right) {
            dfs(node.right, [...parents])
        }
    }
    dfs(root, [])
    return total;
};

const isPalindromic = (arr) => {
    let numberCounts = {
        "1": 0,
        "2": 0,
        "3": 0,
        "4": 0,
        "5": 0,
        "6": 0,
        "7": 0,
        "8": 0,
        "9": 0
    }
    for (var i = 0; i < arr.length; i++) {
        numberCounts[arr[i]]++
    }
    var res = Object.values(numberCounts)
    var oddCount = 0
    for (var i = 0; i < res.length; i++) {
        if (res[i] % 2 !== 0) {
            oddCount++
        }
        if (oddCount > 1) {
            return false
        }
    }
    return true
}

How can I make this more efficient so that it runs within the memory limit?

Improving Efficiency in Google Apps Script for Cell Retrieval

I have a Google Apps Script that retrieves values from a spreadsheet based on certain conditions. While the script works as intended, I’m looking for suggestions on how to make it more efficient.

Here’s the current implementation:

var beoordelingsDataSheet; // Declare this as a global variable


function initializeDataSheet() {    


  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();

  beoordelingsDataSheet = spreadsheet.getSheetByName('Beoordelings Data');

}

function GET_CELL(D6, period, baseNumber, huidige) {



  if (!beoordelingsDataSheet) {
    initializeDataSheet();
  }

  var loco = beoordelingsDataSheet.getDataRange().getValues();

  // Calculate the column index based on huidige, baseNumber, and periode
  var posOne = huidige ? baseNumber + ((period - 1) * 46) : baseNumber + ((period - 2) * 46);

  for (var i = 0; i < loco.length; i++) {
    Logger.log("hier: " + i);

         if (loco[i][0] === D6) {
            
            // Specify the row index (adding 1 because array indices start from 0)

                var rowIndex = i + 1;

      // Get the value in the specified cell

      var cellValue = beoordelingsDataSheet.getRange(rowIndex, posOne).getValue();  
     }
  }

 //Log the retrieved value
 Logger.log('Cell Value: ' + cellValue);
return cellValue; 
 }

      function currentRow(D6, period, baseNumber){
      
          return GET_CELL(D6, periode, baseNumber, true);
    }
 

          function previousRow(D6, period, baseNumber) {

         var ans = "";  // Initialize ans with an empty string
 
            if (period > 1) {
               
                ans = GET_CELL(D5, periode, baseNumber, false);

              } return ans;
    } 

there are a row of numbers and groupcolumns each groupcolumns represent a meeting and in those groupcolumns data (example 2,4,5,6 ,7) just basic ratings are put when a employees has had multiple meetings the previous meeting (example on the 2nd meeting the 1st meeting ratings are returned )(the current and previous ones ) D6 represents the selected name in the sheet with a dropdown and the period is then a number representing which meeting it is and the basecolumn is the numberColumn that is caught using
a =COLUMN(B1 any column can be filled in here)
The script essentially fetches a specific cell value based on a participant’s name (D6), a period (periode), a base number (baseNumber), and whether it’s the current row or the previous row.the PosOne variable then calculates the position of the cells where the ratings should be filled for both current and present

I’m particularly interested in optimizing the loop and data retrieval process. Are there better approaches or functions that could enhance the performance of this script?
could you please help me i’ve missed my deadline because of this

Any insights or examples would be greatly appreciated!

p.s sadly i cant share the sheets because its for the company i work for and it holds sensitive data

I’ve tried using the Vlookup function of google sheets itself and tried making a lookuptable myself but that didnt help either `

`function buildLookupTable(D7, periode, baseNumber, huidige) {
 
 if (!beoordelingsDataSheet) {
    initializeDataSheet();
  }

  lookupTable = {};
  for (let i = 0; i < loco.length; i++) {
    const firstName = loco[i][0];
    const lastName = loco[i][1];
    // ... access other relevant data from the first element ...

    const currentPosOne = huidige ? baseNumber : (periode - 1) * 46; // Adjust offset if needed
    if (periode === 1 && !huidige) {
      currentPosOne = null; // Handle first period and not huidige
    }

    lookupTable[loco[i][0]] = {
      row: i + 1,
      column: currentPosOne,
    };
  }
}

function GET_CELL(D6) {
  if (!beoordelingsDataSheet) {
    initializeDataSheet();
  }

  if (!lookupTable) {
    buildLookupTable();
  }

  const cellData = lookupTable[D6];
  if (!cellData) {
    // Handle missing value scenario
    return null;
  }

  if (!cellData.value) {
    cellData.value = beoordelingsDataSheet.getRange(cellData.row, cellData.column).getValue();
  }

  return cellData.value;
}
function getRatingData(D6, periode, baseNumber) {
  if (periode > 1) {
    return GET_CELL(D6, periode, baseNumber, true);
  } else if (periode === 1) {
    // Handle no previous ratings for first period
    return "Geen vorige beoordeling beschikbaar";
  } else {
    return GET_CELL(D6, periode, baseNumber, false);
  }
}

function getFirstName(D6, periode, baseNumber) {
  return GET_CELL(D6, periode, baseNumber, true)[0]; // Access first element for name
}

function getLastName(D6, periode, baseNumber) {
  return GET_CELL(D6, periode, baseNumber, true)[1]; // Access second element for lastname
}
`

Running multiple processes performing long tasks in the background of Electron app

I have an Electron app.

It is a Multi-Login browser that allows you to create multiple separate browsing sessions, each one with its own set of tabs.

See the demo below.

I need to run one “background” process for each session, to perform long tasks such as running long API calls, as well as communicating with each one of the open visible tabs on the same session.

It should also have access the the DOM.

How can I accomplish that?
Is it possible to have an additional renderer running in the background for each one of the sessions, and have them keep running in the background while the user switched to other sessions?