Refilling a form with saved user selection

In my application I have a form that initially has default values. The user can choose other selections in the form. The user can also save the selections into localStorage by clicking the Save button and using the CacheData() function. My application has an option to navigate to another page once the form selections are made. Upon leaving the initial page, the form data is saved once again using CahceData(), incase the user made any changes without saving before navigating. From the second page, upon returning to the original page, the application reloads the form with the function dataLoad() with the selections saved.

My form has three sections set up as ‘s, Location, Date and Prayers. Each fieldset is made essentially a set of radio buttons. For example, in Location the radio buttons are Auto, City, and Manual. Under the City radio button, I have a selection input, and a checkbox for Daylight Saving. Under the Manual radio button there is there are two number input fields and under each number input field two more radio buttons. The form code is below.

<form id="formSettings" class="dropdown contents" autocomplete="on" target="hiddeniFrame">
                <script> // this script formats the dropdown menu of the form when the settings button is pressed
                var coll = document.getElementsByClassName("options");
                var i;
                for (i = 0; i < coll.length; i++) {
                  coll[i].addEventListener("click", function() {
                    this.classList.toggle("open");
                    var content = this.nextElementSibling;
                    if (content.style.display === "grid") {
                      content.style.display = "none";
                      //content.style.overflow = "hidden";
                    } else {
                      content.style.display = "grid";
                      content.style.background = "#457900";
                        content.style.maxWidth = "inherit";
                        content.style.color = "#000";
                        content.style.fontSize = "25px";
                        content.style.fontFamily = "brioso-pro-display, serif";
                        content.style.padding = "10px";
                        content.style.border = "#000 1px solid";
                        content.style.borderRadius = "20px";
                        content.style.textAlign = "center";
                        content.style.justifyContent = "center";
                        content.style.gridTemplateColumns = "1fr 1fr 1fr";
                        content.style.gridTemplateRows = "auto";
                        content.style.gridColumn = "1/3";
                        content.style.gridRow = "2/3";
                        //content.style.button.border ="#000 2px solid";
                        //content.style.button.borderRadius = "15px";
                        //content.style.button.fontFamily = "brioso-pro-display, serif";
                        //content.style.button.fontSize = "20px";
                        //content.style.button.color = "black";
                      //content.style.overflow = "visible";
                    }
                  });
                }
                </script>
                <fieldset class="locfield">
                    <legend>Location<br></legend>
                    <input type="radio" name="location" value="auto" id="autoloc" checked><label for="autoloc"> Auto</label><br><hr class="settingsDivider">
                    <input type="radio" name="location" value="city" id="cityloc"><label for="cityloc"> City</label><br>
                    Select City:<br>
                    <select name="cities" class="locinput" style="width:6em">
                        <option value="0" selected>U.S. Cities</option>
                        <option value="1">Albuquerque, NM</option>
                        <option value="2">Anchorage, AK</option>
                        <option value="3">Atlanta, GA</option>
                        <option value="4">Austin, TX</option>
                        <option value="5">Birmingham, AL</option>
                        <option value="6">Bismark, ND</option>
                        <option value="7">Boston, MA</option>
                        <option value="8">Boulder, CO</option>
                        <option value="9">Chicago, IL</option>
                        <option value="10">Dallas, TX</option>
                        <option value="11">Denver, CO</option>
                        <option value="12">Detroit, MI</option>
                        <option value="13">Honolulu, HI</option>
                        <option value="14">Houston, TX</option>
                        <option value="15">Indianapolis, IN</option>
                        <option value="16">Jackson, MS</option>
                        <option value="17">Kansas City, MO</option>
                        <option value="18">Los Angeles, CA</option>
                        <option value="19">Menomonee Falls, WI</option>
                        <option value="20">Miami, FL</option>
                        <option value="21">Minneapolis, MN</option>
                        <option value="22">New Orleans, LA</option>
                        <option value="23">New York City, NY</option>
                        <option value="24">Oklahoma City, OK</option>
                        <option value="25">Philadelphia, PA</option>
                        <option value="26">Phoenix, AZ</option>
                        <option value="27">Pittsburg, PA</option>
                        <option value="28">Portland, ME</option>
                        <option value="29">Portland, OR</option>
                        <option value="30">Raleigh, NC</option>
                        <option value="31">Richmond, VA</option>
                        <option value="32">Saint Louis, MO</option>
                        <option value="33">San Antonio, TX</option>
                        <option value="34">San Diego, CA</option>
                        <option value="35">San Francisco, CA</option>
                        <option value="36">Seattle, WA</option>
                        <option value="37">Washington DC</option>
                        <option value="38" >WORLD CITIES</option>
                        <option value="39">Abuja, Nigeria</option>
                        <option value="40">Algiers, Algeria</option>
                        <option value="41">Ankara, Turkey</option>
                        <option value="42">Baghdad, Iraq</option>
                        <option value="43">Bamako, Mali</option>
                        <option value="44">Beijing, China</option>
                        <option value="45">Berlin, Germany</option>
                        <option value="46">Buenos Aires, Argentina</option>
                        <option value="47">Cairo, Egypt</option>
                        <option value="48">Canberra, Australia</option>
                        <option value="49">Cape Town, South Africa</option>
                        <option value="50">Caracas, Venezuela</option>
                        <option value="51">Damascus, Syria</option>
                        <option value="52">Darwin, Australia</option>
                        <option value="53">Dublin, Ireland</option>
                        <option value="54">Helsinki, Finland</option>
                        <option value="55">Hong Kong, Hong Kong</option>
                        <option value="56">Iquique, Chile</option>
                        <option value="57">Islamabad, Pakistan</option>
                        <option value="58">Jakarta, Indonesia</option>
                        <option value="59">Jerusalem, Israel</option>
                        <option value="60">Kabul, Afghanistan</option>
                        <option value="61">Khartoum, Sudan</option>
                        <option value="62">Kuala Lumpur, Malaysia</option>
                        <option value="63">Lima, Peru</option>
                        <option value="64">London, United Kingdom</option>
                        <option value="65">Madrid, Spain</option>
                        <option value="66">Mecca, Saudi Arabia</option>
                        <option value="67">Mexico City, Mexico</option>
                        <option value="68">Mogadishu, Somalia</option>
                        <option value="69">Moscow, Russia</option>
                        <option value="70">Mumbai, India</option>
                        <option value="71">New Delhi, India</option>
                        <option value="72">Nouakchott, Mauritania</option>
                        <option value="73">Ottawa, Canada</option>
                        <option value="74">Paris, France</option>
                        <option value="75">Perth, Australia</option>
                        <option value="76">Rabat, Morocco</option>
                        <option value="77">Rio de Janeiro, Brazil</option>
                        <option value="78">Riyadh, Saudi Arabia</option>
                        <option value="79">Rome, Italy</option>
                        <option value="80">Singapore, Singapore</option>
                        <option value="81">Sydney, Australia</option>
                        <option value="82">Tehran, Iran</option>
                        <option value="83">Tokyo, Japan</option>
                        <option value="84">Tripoli, Libya</option>
                        <option value="85">Vancouver, Canada</option>
                        <option value="86">Warsaw, Poland</option>
                        <option value="87">Winnipeg, Canada</option>
                        <option value="88">Zürich, Switzerland</option>
                    </select><br>
                    <!--<span><label for="Citydst" style="font-size:15px">Daylight Saving?</label></span><br>-->
                    <label for="Citydst" style="font-size:15px">Daylight Saving?</label><br>
                    <input type="checkbox" name="CityDST" value="DST" id="Citydst"><br>
                    <hr class="settingsDivider">
                    
                    <input type="radio" name="location" value="manual" id="manualloc"><label for="manualloc"> Manual</label><br>
                    
                    Latitude:<br> <input type="number" name="latitude" maxlength="10" class="locinput" style="width:6em"><br>
                    <input type="radio" name="NS" value="north" id="North"><label for="North"> North</label><br>
                    <input type="radio" name="NS" value="south" id="South"><label for="South"> South</label><br>
                    Longitude:<br> <input type="number" name="longitude" class="locinput" style="width:6em"><br>
                    <input type="radio" name="EW" value="east" id="East"><label for="East"> East</label><br>
                    <input type="radio" name="EW" value="west" id="West"><label for="West"> West</label><br>
                    Elevation:<br> <input type="number" name="landelev" class="locinput" style="width:6em"><br>
                    <input type="radio" name="elevunits" value="feet" id="feet"><label for="feet"> Feet</label><br>
                    <input type="radio" name="elevunits" value="meters" id="meters"><label for="meters"> Meters</label><br>
                    Time Zone:<br> <select name="timezones">
                        <option value="-12">UTC-12</option>
                        <option value="-11">UTC-11</option>
                        <option value="-10">UTC-10</option>
                        <option value="-9">UTC-9</option>
                        <option value="-8">UTC-8</option>
                        <option value="-7">UTC-7</option>
                        <option value="-6">UTC-6</option>
                        <option value="-5">UTC-5</option>
                        <option value="-4">UTC-4</option>
                        <option value="-3">UTC-3</option>
                        <option value="-2">UTC-2</option>
                        <option value="-1">UTC-1</option>
                        <option value="0">UTC+0</option>
                        <option value="1">UTC+1</option>
                        <option value="2">UTC+2</option>
                        <option value="3">UTC+3</option>
                        <option value="4">UTC+4</option>
                        <option value="5">UTC+5</option>
                        <option value="6">UTC+6</option>
                        <option value="7">UTC+7</option>
                        <option value="8">UTC+8</option>
                        <option value="9">UTC+9</option>
                        <option value="10">UTC+10</option>
                        <option value="11">UTC+11</option>
                        <option value="12">UTC+12</option>
                    </select><br>
                    <!--<span><label for="Manualdst" style="font-size:15px">Daylight Saving?</label></span><br>-->
                    <label for="Manualdst" style="font-size:15px">Daylight Saving?</label><br>
                    <input type="checkbox" name="ManualDST" value="DST" id="Manualdst">
                </fieldset>
                <fieldset class="datefield">
                    <legend>Date</legend>
                    <input type="radio" name="dateMode" value="auto" id="autodate" checked><label for="autodate"> Auto</label><br><hr class="settingsDivider">
                    <input type="radio" name="dateMode" value="manual" id="manualdate"><label for="manualdate"> Manual</label><br>
                    Select Date:<br>
                    <input type="date" name="dateInput" class="dateinput" style="width:6em"><br><hr class="settingsDivider">
                    <!--<label for="dst" style="font-size:15px">Daylight Saving?</label></span><br>
                    <input type="checkbox" name="DST" value="DST" id="dst"><span style="font-size: 13px">-->
                </fieldset>
                <fieldset class="prayerfield">
                    <legend>Prayers</legend>
                    <!--<span style="text-decoration: underline">Subh Cond:</span><br>-->
                    <span>Subh Cond:</span><br>
                    <input type="radio" name="subhMode" value="12" id="subhurban" checked><span><label for="subhurban"> Urban</label></span><br>
                    <input type="radio" name="subhMode" value="15" id="subhwild"><span style="font-size: 17px"><label for="subhwild"> Wilderness</label></span><br><hr class="settingsDivider">
                    <span class="dhuhurtext">Dhuhur Shadow:</span><br>
                    <input type="radio" name="dhuhurMode" value="42" id="dhuhursandal"><span style="font-size: 15px"><label for="dhuhursandal"> Sandal Strap</label></span><br>
                    <input type="radio" name="dhuhurMode" value="21" id="dhuhurhand" checked><span style="font-size: 17px"><label for="dhuhurhand"> Hand Span</label></span><br>
                    <input type="radio" name="dhuhurMode" value="7" id="dhuhurfoot"><span style="font-size: 17px"><label for="dhuhurfoot"> Foot Span</label></span><br><hr class="settingsDivider">
                    <!--<span style="text-decoration: underline">Asr Shadow:</span><br>-->
                    <span>Asr Shadow:</span><br>
                    <input type="radio" name="asrMode" value="1" id="asrmajority" checked><span style="font-size: 20px"><label for="asrmajority"> Majority</label></span><br>
                    <input type="radio" name="asrMode" value="2" id="asrhanafi"><span><label for="asrhanafi"> Hanafi</label></span><br><hr class="settingsDivider">
                    <!--<span style="text-decoration: underline">Isha Cond:</span><br>-->
                    <span>Isha Cond:</span><br>
                    <input type="radio" name="ishaMode" value="12" id="ishaurban"><span><label for="ishaurban"> Urban</label></span><br>
                    <input type="radio" name="ishaMode" value="15" id="ishawild" checked><span style="font-size: 17px"><label for="ishawild"> Wilderness</label></span><br>
                </fieldset>
                <div class="set-section">
                    <button type="button" name="prayertimes" value=" Save " onclick="saveForm(this.form)">Save</button>
                </div>
                <div class="update-section">
                    <button type="button" name="updateprayertimes" value=" Update " onclick="chooseMethod(this.form)">Update</button>
                </div>
                <div class="reset-section">
                    <button type="reset" value=" Reset ">Reset</button>
                </div>

The problem that I am having, is that if the user selects anything other than the defaults, it seems like CacheData() saves all the selections in the form. Upon reloading the selections into the form, loadData() always ends up checking the default radio button in the list of radio buttons in any given fieldset. For example, in the Location filedset, the default is Auto, but if the user selects City, and selects a city from the selection list, and its saved, up reloading, the selected city is in the selection box, but the radio button that is checked is Auto.

Upon carefully using the debugger in Chrome, I discovered that in loadData() when it cones to the radio buttons, when I assign field.checked = value; the debugger is showing input#autoloc… as the radio button, and not input#cityloc…

How can I specify which radio button needs to be checked, given that I know the value of the radio button named ‘name=location’ but the id is ‘id=cityloc’? Likewise for the other radio buttons.

Is my error in how I save the selections from the form in CacheData() that is it is not specifying the id’s or is it in loadData(), that it is not specifying the correct id’s?

Here are the functions CaacheData() and loadData() that I am using.

function dataLoad() 
        {
            
            if (typeof(Storage) !== "undefined")
            {
                if (localStorage.getItem("formSettings") !== null)
                {

                    var inputParse = JSON.parse(localStorage.getItem("formSettings"));
                    $.each(inputParse, function(key, value) {
                      var field = document.querySelector("[name=" + key + "]");
                      if (field.type == 'radio' || field.type == 'checkbox') {
                          if (field.checked)
                          {
                              field.value = value;
                              field.checked = true;
                          }
                      } else {
                        field.value = value;
                      }
                    });
                }
                
            }
            else 
            {
              document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Storage...";
            }
function CacheData() 
        {
            alert("in CahceData");
            if (typeof(Storage) !== "undefined")
            {   
                jQuery.fn.serializeObject = function() {
                    var formData = {};
                    var formArray = this.serializeArray();
                    for (var i = 0, n = formArray.length; i < n; ++i)
                        formData[formArray[i].name] = formArray[i].value;
                    formArray = {};
                    return formData;
                };
                var formObject = $("#formSettings").serializeObject();
                console.log(formObject);
                localStorage.setItem("formSettings", JSON.stringify(formObject));
                
                return JSON.stringify(formObject);
            }
            else 
            {
                document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Storage...";
            }

        }

I was expecting that when the form gets reloaded with loadData() with the selections saved in CacheData() that the correct radio buttons and check boxes would be checked, and input fields and selections would contain those entered by the user.

How to set the type for Class Variable

In the below code, the accountType can be Individual or Joint. In my automation test codebase, some classes will set accountType to Individual and some will set accountType to Joint and run tests.

Every line where I set the accountType to Joint and access coApplicantName, it shows me a warning :

Property 'coApplicantName' does not exist on type 'Individual | Joint'. Did you mean 'applicantName'?.

I am also unable to navigate to any of the Joint class Property/methods using CMD+Click. This also applies to TrustAccount properties

I’m able to overcome this by setting the type (a.accountType as Joint).coApplicantName = "Dave", but this needs be done every line where I’m accessing the Joint class property. In my actual code, this will be more than 100 lines where I would have to specify the type across different classes.

Another workaround I tried is const jointAcc = a.accountType and then access it using jointAcc.coApplicantName, but since this is needed across multiple files, I would have to create this new variable across multiple files

Is there any way I can set the type at the top level to avoid specifying the type at each line

class AccountDetails
{
    accountType: Individual | Joint | TrustAccount;
    accountType: number;
}

class Individual {
   applicantName: string
}

class Joint extends Individual{
   coApplicantName: string
}

class TrustAccount extends Individual{
   TrustName: string
}
let a = new AccountDetails()

function JointAccountTest(){
   a.accountType = new Joint()
   a.accountType.applicantName = "John"  //No Warning is shown
   a.accountType.coApplicantName = "Dave" //Warning Shown
}

How to send electron menu data to react component?

In my react/electronjs project, here is what I would like to accomplish:

  • In the Menu aspect of the electron js window, I want a function that gets text content of a file using the electron dialog and fs package, and I want that text content sent and displayed to a react component

Issues Im facing:

  1. Im fairly new to electronjs and I don’t completely understand how I can send the text content data of a file to the component
  2. I don’t how I can render the content within the component properly.
// main.js
const { app, BrowserWindow, dialog, Menu, ipcMain } = require('electron')
const fs = require('fs')
const os = require('node:os')
const path = require('node:path')
require('@electron/remote/main').initialize()
const isMac = process.platform === 'darwin'

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    titleBarStyle: "default",
    webPreferences: {
      contextIsolation: true,
      nodeIntegration: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.setRepresentedFilename(os.homedir())
  win.setDocumentEdited(true)
  win.loadURL('http://localhost:3000')

  const template = [
    ...(isMac
      ? [{
        label: app.name,
        submenu: [
          { role: 'about' },
          { type: 'separator' },
          { role: 'services' },
          { type: 'separator' },
          { role: 'hide' },
          { role: 'hideOthers' },
          { role: 'unhide' },
          { type: 'separator' },
          { role: 'quit' }
        ]
      }]
      : []),
    // { role: 'fileMenu' }
    {
      label: 'File',
      submenu: [
        {
          label: 'Open File',
          click: () => {

            const files = dialog.showOpenDialogSync(win,
              {
                properties: ['openFile'],
                filters: [{ name: "Text Files", extensions: ['txt'] }]
              }
            );
            // If no files 
            if (!files) return;


            const file = files[0];
            //const fileContent = fs.promises.readFile(file, 'utf-8');
            const fileContent = fs.readFileSync(file).toString();
            win.webContents.send('open-file', fileContent)
          }
        }

      ]
    }
  ]
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
  return win
}

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.whenReady().then(() => {
  createWindow()
  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})
// preload.js 

const { contextBridge, ipcRenderer } = require('electron/renderer');

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: (callback) => ipcRenderer.on('open-file', (_event, content) => callback(content))
})

Converting this HTML-table-to-JSON function so it handles the Welsh Wiktionary table?

I have this function from a previous question on how to parse out Wiktionary data on complex tables:

// Function to convert an HTML table to a matrix, duplicating data where col/rowspans occur
function toMatrix(table) {
  // Get a 2-letter lang attribute from anywhere in the whole table. To be used when
  //   a data cell does not have an element with a lang attribute.
  const defaultLang =
    Array.from(table.querySelectorAll('[lang]'), (elem) =>
      elem.getAttribute('lang'),
    ).find(lang => lang.length == 2) ?? 'text'
  // Function to extract the content from a table cell, taking into account
  //    tag (td/th), IPA class, lang, row/colspan attributes, and italics.
  // Returns an array of duplicated objects (to reflect colspan)
  function get(cell) {
    const length = +(cell.getAttribute('colspan') ?? 1)
    const hasAlias = false
    const hasData = cell.tagName == 'TD'
    const span = +(cell.getAttribute('rowspan') ?? 1)
    let content = {}
    if (hasAlias) {
      content.alias = clean(cell)
    } else if (hasData) {
      const ipaEls = cell.querySelectorAll('.IPA')
      const ipa = []
      for (const el of ipaEls) {
        ipa.push(clean(el).replace(/[[]/]/g, ''))
      }
      if (ipa.length) {
        content.ipa = ipa
      }
      for (const version of cell.querySelectorAll('[lang]')) {
        content[version.getAttribute('lang')] = clean(version)
      }
      if (!Object.keys(content).length) {
        const lang = clean(cell)
        content = lang ? { [defaultLang]: lang } : null
      }
    } else {
      content = clean(cell, true)
    }

    return Array.from({ length }, () => ({ content, span }))
  }

  let cells
  const rows = [...table.rows]
  // Ignore first row if it is the clickable row to expand/collapse the rest of the table
  if (rows[0].cells[0].classList.contains('vsToggleElement')) {
    rows.shift()
  }
  // Ignore last row if it has notes
  if (rows.at(-1).cells[0].className.includes('-notes-')) {
    rows.pop()
  }
  return Array.from(rows, row => {
    // Get the current row, with colspans resolved into cells
    const buff = Array.from(row.cells, get).flat()
    // Integrate this row by taking into account the rowspans
    // of cells in previous row(s)
    cells =
      cells
        ?.map(cell =>
          --cell.span ? cell : buff.shift() ?? { span: 1 },
        )
        .concat(buff) ?? buff
    // Now that rowspans have served their purpose, just retain
    // the content:
    return cells.map(cell => cell.content)
  })
}

// Function to convert matrix to the desired flat object.
function toObject(matrix) {
  const result = []
  const tabQualifier = []
  const colQualifiers = []
  let data = true
  for (const [...row] of matrix) {
    // Find the column index where first data is found --
    // knowing that a data element is represented by an object
    // while a qualifier is represented by a string data type
    const dataAt = row.findIndex(content => typeof content === 'object')
    // Some heading rows have a TD element in first column (an inconsistency in the table for Arabic),
    // so only consider it data when in a non-first column
    if (dataAt < 1) {
      // No data; so this is a heading with only qualifier(s)
      // If the qualifier is present in the first column, it's a table qualifier (highest-level qualifier)
      if (row[0]) {
        tabQualifier[0] = row[0]
      } else {
        tabQualifier[0] = undefined
      }
      // If the previous row had data, we enter a new section... so reset the column-qualifiers
      if (data) {
        colQualifiers.length = 0
      }
      row.forEach((col, i) => (colQualifiers[i] ??= []).push(col))
    } else {
      // Row with data
      // For sections that have no column headers, create one dummy column qualifier
      if (!colQualifiers.length) {
        colQualifiers[dataAt] = []
      }
      const qualifier = [
        ...tabQualifier,
        ...row.splice(0, dataAt),
      ].filter(x => x)
      // For each column with data, join it with the qualifiers
      colQualifiers.slice(dataAt).forEach((colQualifier, i) => {
        if (row[i]) {
          result.push({
            // Create keys with true as value for each of the relevant qualifiers
            ...Object.fromEntries(
              Array.from([...qualifier, ...colQualifier], qual => [
                qual,
                true,
              ]),
            ),
            ...row[i],
          })
        }
      })
    }
    data = dataAt >= 1
  }
  return result
}

You call it on the Welsh mutation (like here), like this:

toObject(toMatrix(document.querySelector('.inflection-table'))

It should output something like this:

{
  'Welsh mutation / radical': 'achyddiaeth',
  'Welsh mutation / soft': 'unchanged',
  'Welsh mutation / nasal': 'unchanged',
  'Welsh mutation / h-prothesis': 'hachyddiaeth',
}

Basically, a map of the headers to the cell value (ignoring the notes section at the bottom).

What needs to change from in the toObject function, to support the Welsh table? It works on Latin conjugation tables, French ones, Arabic ones, and a few others, but not on this Welsh table. The goal is to create a generic HTML table parser, which correctly maps headers to cell content, given how complex and varied HTML tables can be.

I tried removing a section of the toObject function at the beginning, but it doesn’t return the correct result:

// Function to convert matrix to the desired flat object.
function toObject(matrix) {
  const result = []
  const tabQualifier = []
  const colQualifiers = []
  let data = true
  for (const [...row] of matrix) {
    // Find the column index where first data is found --
    // knowing that a data element is represented by an object
    // while a qualifier is represented by a string data type
    const dataAt = row.findIndex(content => typeof content === 'object')
    // Some heading rows have a TD element in first column (an inconsistency in the table for Arabic),
    // so only consider it data when in a non-first column
  
    // Row with data
    // For sections that have no column headers, create one dummy column qualifier
    if (!colQualifiers.length) {
      colQualifiers[dataAt] = []
    }

    const qualifier = [
      ...tabQualifier,
      ...row.splice(0, dataAt),
    ].filter(x => x)
    // For each column with data, join it with the qualifiers
    colQualifiers.slice(dataAt).forEach((colQualifier, i) => {
      if (row[i]) {
        result.push({
          // Create keys with true as value for each of the relevant qualifiers
          ...Object.fromEntries(
            Array.from([...qualifier, ...colQualifier], qual => [
              qual,
              true,
            ]),
          ),
          ...row[i],
        })
      }
    })

    data = dataAt >= 1
  }
  return result
}

Note: The toMatrix function works correctly on this table, resulting in:

[
  [
    "Welsh mutation",
    "Welsh mutation",
    "Welsh mutation",
    "Welsh mutation"
  ],
  [
    "radical",
    "soft",
    "nasal",
    "h-prothesis"
  ],
  [
    {
      "cy": "achyddiaeth"
    },
    {
      "cy": "unchanged"
    },
    {
      "cy": "unchanged"
    },
    {
      "cy": "hachyddiaeth"
    }
  ],
  [
    {
      "cy": "Note: Some of these forms may be hypothetical. Not every possible mutated form of every word actually occurs."
    },
    {
      "cy": "Note: Some of these forms may be hypothetical. Not every possible mutated form of every word actually occurs."
    },
    {
      "cy": "Note: Some of these forms may be hypothetical. Not every possible mutated form of every word actually occurs."
    },
    {
      "cy": "Note: Some of these forms may be hypothetical. Not every possible mutated form of every word actually occurs."
    }
  ]
]

However, ideally the note can be filtered out, if possible.

enter image description here

How can I structure my Classes and functions in JavaScript, starting from a backend for a better chance at loose coupling, SOLID principles and TDD?

I am building a simple website, where users collaborate and create a project. The projects act as a post, it can be upvoted, downvoted and has comments. The projects also have a ranking system.

I am unable to grasp where I should place my functions, classes and what patterns I should be using for minimum coupling. Are any classes apart from the obvious base classes needed?

  1. Users can post a comment so should I be going for a User.addComment(projectID, comment) or Project.addComment(userID,comment) or Comment.add(userID,projectID). This seems to me as if User and Project should not be used as they are a main component of the website and I would not want anyone to mess with those part of the code more than any other sections of the website.
  2. Similar to the case above I cannot identify where my Class.upvote(projectID) should be for minimal coupling. As an upvote also involves both the User and Project class.
  3. Lastly for now there will be a Regular and Super user. Further down the road I would like my code to be open for other roles and not hardcoded on what Instance of an inherited object I should be using.

Can’t get ElevenLabs API in Google AppScript To Return Valid Response

I’m using a Google AppScript to integrate Eleven Labs’ API into a Sheet.
Trying to get past this step so that I can do something with a returned file but I can’t get past this response code 422. I’m following their javascript example. I started it to lok

https://elevenlabs.io/docs/api-reference/text-to-speech

apiUrlEndPoint = "https://api.elevenlabs.io/v1/text-to-speech/Xb7hH8MSUJpSbSDYk0k2"


const options = {
  method: 'POST',
  headers: {
    'xi-api-key': myAPIKeyWhichIDoHave
    'Content-Type': 'application/json',
  },
  body: '{"text":"This is the text to convert","model_id":"eleven_multilingual_v2","voice_settings":{"stability":1,"similarity_boost":1}}',
  muteHttpExceptions: true
};

const response = UrlFetchApp.fetch(apiUrlEndPoint, options);

I’ve tried variations with stringify and without. Same result.

Response Code: 422
Response Body: {"detail":[{"loc":["body","text"],"msg":"field required","type":"value_error.missing"}]}
Request failed with status code 422

Unable to write Json schema to validate properties inside array

I am trying to write Json schema to validate json file with following content:

{
“number” : “1”,
“rootProperty”:
[
{
“property1″:”value1”
},
{
“property2″:”value2”
}
]
}

The schema should handle following scenarios:

  1. If property2 is present and property1 is missing, error should be reported mentioning that property1 is missing since its mandatory.
  2. If property1 is present and property2 is missing, error should be reported mentioning that property2 is missing since its mandatory.
  3. No error should be reported if both property1 and property2 are present.
  4. No error should be reported if property2 is mentioned first and then property1 is mentioned.
  5. Error should be reported if any property other than property1 and property2 is mentioned(additionalProperties:false?)
  6. property1 and property2 should not be repeated(uniqueItems:true?)

Could someone please assist?

Also, whether schema is the right way to handle this depth of validation?

When I tried using “items” keyword for the rootProperty array and mentioned property1 and property2 inside required condition, these conditions are evaluated for every element of the array, which results in error saying property2 is missing in first element and property1 is missing in second element.
Also, using “contains” results in error saying object contains property that couldnt be evaluated against any property or additionalProperties.

How to detect collision in pacman game?

Following is the boilerplate for Pacman game that I need to complete. I’m having problems with detecting when the player is going to collide with walls. The problem is I don’t know where the player is relative to the maze. Like if the player moved one tile from the maze array on keydown, I could work out the position and check adjacent tiles for collision. But since the player moves in “1px” increment on a keydown, I can’t track which tile it is currently on.

What I’ve tried:

I created two variables to store the player’s current row and column. Whenever the player moves, I check whether the player is completely inside a block using getBoundingClientRect() method, and when the player is completely inside the block i update the row and column trackers. But I ran into a bottleneck, as the player can move in between two tiles as well.

Please suggest me some ideas.

let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;

const main = document.querySelector("main");

//Player = 2, Wall = 1, Enemy = 3, Point = 0
let maze = [
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 2, 0, 1, 0, 0, 0, 0, 3, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
  [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
  [1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
  [1, 0, 0, 1, 0, 3, 0, 0, 0, 1],
  [1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
  [1, 3, 1, 0, 0, 0, 0, 0, 0, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

//Populates the maze in the HTML
for (let y of maze) {
  for (let x of y) {
    let block = document.createElement("div");
    block.classList.add("block");

    switch (x) {
      case 1:
        block.classList.add("wall");
        break;
      case 2:
        block.id = "player";
        let mouth = document.createElement("div");
        mouth.classList.add("mouth");
        block.appendChild(mouth);
        break;
      case 3:
        block.classList.add("enemy");
        break;
      default:
        block.classList.add("point");
        block.style.height = "1vh";
        block.style.width = "1vh";
    }

    main.appendChild(block);
  }
}

//Player movement

const lbutton = document.querySelector("#lbttn");
const rbutton = document.querySelector("#rbttn");
const ubutton = document.querySelector("#ubttn");
const dbutton = document.querySelector("#dbttn");

function keyUp(event) {
  if (event.key === "ArrowUp") {
    upPressed = false;
    ubutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowDown") {
    downPressed = false;
    dbutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowLeft") {
    leftPressed = false;
    lbutton.style.borderStyle = "outset";
  } else if (event.key === "ArrowRight") {
    rightPressed = false;
    rbutton.style.borderStyle = "outset";
  }
}

function keyDown(event) {
  if (event.key === "ArrowUp") {
    upPressed = true;
    ubutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowDown") {
    downPressed = true;
    dbutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowLeft") {
    leftPressed = true;
    lbutton.style.borderStyle = "inset";
  } else if (event.key === "ArrowRight") {
    rightPressed = true;
    rbutton.style.borderStyle = "inset";
  }
}

const player = document.querySelector("#player");
const playerMouth = player.querySelector(".mouth");
let playerTop = 0;
let playerLeft = 0;

setInterval(function () {
  if (downPressed) {
    playerTop++;
    player.style.top = playerTop + "px";
    playerMouth.classList = "down";
  } else if (upPressed) {
    playerTop--;
    player.style.top = playerTop + "px";
    playerMouth.classList = "up";
  } else if (leftPressed) {
    playerLeft--;
    player.style.left = playerLeft + "px";
    playerMouth.classList = "left";
  } else if (rightPressed) {
    playerLeft++;
    player.style.left = playerLeft + "px";
    playerMouth.classList = "right";
  }
}, 10);

function startGame() {
  document.addEventListener("keydown", keyDown);
  document.addEventListener("keyup", keyUp);
  startBtn.style.display = "none";
}

const startBtn = document.querySelector(".start");
startBtn.addEventListener("click", startGame);

Error uploading node.js project on azure web app through Github CI/CD

I am uploading my node.js code on Azure web app. After a lot of try, I was able to successfully do it. I am building my code on Visual Studio and pushing to Github and using CI/CD connected to Azure web app. Recently, I connected my code to Azure MySQL server and after that, I have started getting error. I found in the logs that port 8080 is not working. Here’s a snippet of the error:

[DATE] ERROR - Container [CONTAINER_ID] for site [SITE_NAME] has exited, failing site start

[DATE] ERROR - Container [CONTAINER_ID] didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.

[DATE] INFO  - Stopping site [SITE_NAME] because it failed during startup.

[DATE] Error details:

  code: 'MODULE_NOT_FOUND',

  requireStack: [ '/home/site/wwwroot/node_modules/express/lib/router/index.js', '/home/site/wwwroot/node_modules/express/lib/application.js', '/home/site/wwwroot/node_modules/express/lib/express.js', '/home/site/wwwroot/node_modules/express/index.js', '/home/site/wwwroot/server.js'

  ]

[DATE] INFO  - Starting container for site

[DATE] INFO  - docker run -d --expose=8080 --name [CONTAINER_NAME] [ENVIRONMENT_VARIABLES] appsvc/node:20-lts_[VERSION] node server.js

[DATE] INFO  - Initiating warmup request to container [CONTAINER_ID] for site [SITE_NAME]

[DATE] ERROR - Container [CONTAINER_ID] for site [SITE_NAME] has exited, failing site start

[DATE] ERROR - Container [CONTAINER_ID] didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.

[DATE] INFO  - Stopping site [SITE_NAME] because it failed during startup.

I am not using Docker. I am various measures to do it. Here’s a screenshot of my Web app config. I have added port as 8080:
enter image description here

Here’s my Github workflow:

name: Build and deploy Node.js app to Azure Web App

on:

  push:

    branches:

      - main

  workflow_dispatch:

jobs:

  build-and-deploy:

    runs-on: ubuntu-latest

    steps:

    - uses: actions/checkout@v2

    - name: Set up Node.js version

      uses: actions/setup-node@v1

      with:

        node-version: '20.15.1'

    - name: Create .npmrc file

      run: echo "engine-strict=false" > .npmrc

    - name: Install server dependencies

      run: npm ci

    - name: Build client

      run: |

        cd client

        npm ci

        npm run build

        cd ..

    - name: Create deployment package

      run: |

        zip -r deploy.zip . -x "*.git*" "node_modules/*" "client/node_modules/*" "client/src/*" "client/public/*"

    - name: 'Deploy to Azure Web App'

      uses: azure/webapps-deploy@v2

      with:

        app-name: 'job-search'

        publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}

        package: ./deploy.zip

    - name: Print server.js content

      run: |

        echo "Contents of server.js:"

        cat server.js

    - name: Print Node.js version

      run: node --version

Here’s my server.js:

console.log('Starting server.js');

console.log('Environment variables:', {

  NODE_ENV: process.env.NODE_ENV,

  PORT: process.env.PORT,

  DB_HOST: process.env.DB_HOST,

  DB_USER: process.env.DB_USER,

  DB_NAME: process.env.DB_NAME

  // Don't log DB_PASSWORD

});

process.on('uncaughtException', (err) => {

  console.error('Uncaught Exception:', err);

});

const express = require('express');

const cors = require('cors');

const cron = require('node-cron');

const path = require('path');

const { initializeDatabase } = require(path.join(__dirname, 'server', 'database'));

const { fetchAllJobs, searchJobs, getJobCategories, getCategoryCount, getTotalJobCount } = require(path.path.join(__dirname, 'server', 'jobLogic'));

const app = express();

const port = process.env.PORT || 8080;

// Middleware

app.use(cors());

app.use(express.json());

// Serve static files from the React app

app.use(express.static(path.join(__dirname, 'client/build')));

app.get('/health', (req, res) => {

  res.status(200).send('OK');

});

app.get('*', (req, res) => {

  res.sendFile(path.join(__dirname, 'client/build', 'index.html'));

});

// Error handling middleware

app.use((err, req, res, next) => {

  console.error(err.stack);

  res.status(500).send('Something broke!');

});

app.use((req, res, next) => {

  res.status(404).send("404 - Not Found");

});

// Initialize database and start server

initializeDatabase()

  .then(() => {

    console.log('Database initialized successfully');

    return new Promise((resolve) => {

      const server = app.listen(port, () => {

        console.log(`Server running at http://localhost:${port}`);

        resolve(server);

      });

    });

  })

  .then((server) => {

    console.log('Server started successfully');

    return fetchAllJobs();

  })

  .then(() => {

    console.log('Initial job fetch completed');

  })

  .catch(error => {

    console.error('Error during startup:', error);

    process.exit(1);

  });

// Schedule job fetching (every 6 hours)

//cron.schedule('0 */6 * * *', fetchAllJobs);

This is package.json for server level:

{
  "name": "job-search",
  "version": "0.1.0",
  "main": "server.js",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.2",
    "cheerio": "^1.0.0-rc.12",
    "cors": "^2.8.5",
    "csv-parser": "^3.0.0",
    "express": "^4.19.2",
    "lottie-react": "^2.4.0",
    "mysql2": "^3.10.3",
    "node-cache": "^5.1.2",
    "node-cron": "^3.0.3",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "source-map-support": "^0.5.21",
    "sqlite": "^5.1.1",
    "sqlite3": "^5.1.7",
    "us-state-codes": "^1.1.2",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "node server.js",
    "build": "cd client && npm install && npm run build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "dev": "react-scripts start",
    "postinstall": "npm run build"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "engines": {
    "node": "20.15.1"
  },
  "devDependencies": {
    "@babel/core": "^7.24.9",
    "@babel/preset-env": "^7.24.8",
    "babel-loader": "^9.1.3",
    "nodemon-webpack-plugin": "^4.8.2",
    "start-server-webpack-plugin": "^2.2.5",
    "webpack": "^5.93.0",
    "webpack-cli": "^5.1.4",
    "webpack-node-externals": "^3.0.0"
  }
}

I am trying various steps to solve it. But it seems my main issue is with port 8080. Any help with be appreciated.

process is undefined when trying to provide environment variables to client code through webpack

I’m trying to provide access to certain environment variables in the front end code package by webpack, but process is always undefined. The application I’m working on is doing a bunch of extra stuff in the webpack configuration that I thought might be creating issues, so I wanted to see how it worked with a default create react app instance, and then compare the two. However I’m seeing the same behavior with the create react app instance (process is always undefined in the client side code).

These are the steps I performed:

  1. Created a react app using the template cra-template-pwa:
    npx create-react-app my-app –template cra-template-pwa
  2. Performed an npm run eject to look at the webpack config files and confirmed that in this code from conifg/webpack.config.js, that certain environment variables should be available through process.env in the client side code:
// We will provide `paths.publicUrlOrPath` to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
// Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));

(then further down in webpack.config.js…)

// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV is set to production 
// during a production build. 
// Otherwise React will be compiled in the very slow development mode. 
new webpack.DefinePlugin(env.stringified), 

This section from config/env.js show that at the very least process.env.NODE_ENV=development, regardless of what it picks up from the underlying environment:

  const raw = Object.keys(process.env)
    .filter(key => REACT_APP.test(key))
    .reduce(
      (env, key) => {
        env[key] = process.env[key];
        return env;
      },
      {
        // Useful for determining whether we’re running in production mode.
        // Most importantly, it switches React into the correct mode.
        NODE_ENV: process.env.NODE_ENV || 'development',
        // Useful for resolving the correct path to static assets in `public`.
        // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
        // This should only be used as an escape hatch. Normally you would put
        // images into the `src` and `import` them in code to get their paths.
        PUBLIC_URL: publicUrl,
        // We support configuring the sockjs pathname during development.
        // These settings let a developer run multiple simultaneous projects.
        // They are used as the connection `hostname`, `pathname` and `port`
        // in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
        // and `sockPort` options in webpack-dev-server.
        WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
        WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
        WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
        // Whether or not react-refresh is enabled.
        // It is defined here so it is available in the webpackHotDevClient.
        FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
      }
    );
  // Stringify all values so we can feed into webpack DefinePlugin
  const stringified = {
    'process.env': Object.keys(raw).reduce((env, key) => {
      env[key] = JSON.stringify(raw[key]);
      return env;
    }, {}),
  };

  return { raw, stringified };
}
  1. Then in App.js I added console.log(process);
function App() {

  console.log(process);

  return (
    <div className="App">
      <header className="App-header">
  1. Ran npm start and then checked the console log in developer tools for http://localhost:3000 and I received the error:
ReferenceError: process is not defined
    at App (App.js:7:1)
    at renderWithHooks (react-dom.development.js:15486:1)
    at mountIndeterminateComponent (react-dom.development.js:20103:1)
    at beginWork (react-dom.development.js:21626:1)
    at beginWork$1 (react-dom.development.js:27465:1)
    at performUnitOfWork (react-dom.development.js:26596:1)
    at workLoopSync (react-dom.development.js:26505:1)
    at renderRootSync (react-dom.development.js:26473:1)
    at recoverFromConcurrentError (react-dom.development.js:25889:1)
    at performConcurrentWorkOnRoot (react-dom.development.js:25789:1)

How to get google search indexing results for react app?

I am making a website, and I really want the website to have good search engine optimization, so that when the user searches for my website on google “jermasearch.com” they get results with multiple clickable links, like these two examples:

enter image description here

But at the moment, my website has the incorrect icon, no search feature, and zero additional clickable links:
enter image description here

I’ve tried using the npm package helmet to give my react app component the meta elements that google indexes, here’s a snippet of my code (full code here)

import React, { useState, useEffect } from 'react';
import { Helmet } from "react-helmet";

...

const JermaSearch = () => {

return (<>
    <Helmet>
        <meta name="google-site-verification" content="8pFA2UWTUXgl_iMCwStnmG332el6U4vIeyyMEGcmG_g" />
        <meta charSet="utf-8" />
        <title>Jerma Search</title>
        <link rel="canonical" href="https://jermasearch.com/" />
        <meta name="description" content="Search through every Jerma985 stream." />
        <meta name="keywords"
            content="Jerma, Jerma985, Jerma search, Jerma stream search, search, logs, stream logs, transcript, subtitles" />
        <meta property="og:title" content="Jerma Search" />
        <meta property="og:description" content="A log of what Jerma has said in chat." />
        <meta property="og:type" content="website" />
        <meta property="og:url" content="https://jermasearch.com/" />
        <link rel="icon" href="jermasearch.ico" />
        <meta property="og:image" content="jermasearch.ico" />
        <script type="application/ld+json">
                    {`
            {
              "@context": "https://schema.org",
              "@type": "WebSite",
              "url": "https://jermasearch.com/",
              "name": "Jerma Search",
              "description": "Search through text of every Jerma985 stream.",
              "potentialAction": {
                "@type": "SearchAction",
                "target": {
                  "@type": "EntryPoint",
                  "urlTemplate": "https://jermasearch.com/jermasearch/search?query={search_term_string}"
                },
                "query-input": "required name=search_term_string"
              }
            }
          `}
                </script>


    </Helmet>

    <div>
        main content here
    </div>>
</>);
}

export default JermaSearch;

And I can see on google search console that my site has been indexed:
enter image description here

Do I need to edit my code and request a new indexing ?

Need to show array values from Javascript running in one php page in another php page

The javascript below runs in a php page. When the php page is opened, the javascript runs and the 10 values in the array in the javascript are shown one at a time in order every second in a div in the php page called “h1”. Both php pages will be on the same server, in the same website. The first php page will be opened by me on my computer and the second php page will be opened by multiple users around the world on their computers in different time zones. The first php page and the second php page should always show the exact same values, always.

QUESTION
How do I show the 10 values in a second php page in real-time so that if a user refreshes the second php page or leaves the second php page and comes back to it, the values still continue showing in real-time in the second php page?

<div id="h1"></div>
<script>
var dialog = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];

function changeText() {
  var timer = 0;
  for (let i = 0; i < dialog.length; i++) {
    setTimeout(() => document.getElementById('h1').innerHTML = dialog[i], timer);
    timer = timer + 1000;
  }
}

changeText();
</script>

Why is my website not allowing me to store in local storage?

I have a div that I want to display (then will collapse after a timeout) when one first visits my site, but after that I want that div to remain collapsed.

I found this jsfiddle that seems perfect for me:
https://jsfiddle.net/hibbard_eu/mjgdrg1f/

var hasSeenGreeting = localStorage.getItem("greeting");

I implemented it on my site, but after two hours I still can’t get the local storage variable to save! I created a test page that has ONLY the sample and it works just fine:
https://importarchive.com/test.php

But on my main site I just can’t get the variable to save into local storage. I have even put the “var hasSeenGreeting = localStorage.getItem(“greeting”);” as the first script in the head, but it’s not working.

Thanks in advance to anyone that can help!

(I know my site is a mess — it’s a side project that evolved over decades)

I expect the local storage key ‘greeting’ to be set as true as soon as the page loads. Instead, no variable is being saved at all.

Block div / element on webpage (chrome extension development)

I’m learning to create a chrome extension that blocks elements in social media website like feeds, comments etc.
For example, I want to block the Reddit Feed, there is a checkbox to toggle to hide or display the feed. This checkbox sends a message to the content script. Here I’m using var toggle_reddit_feed = document.querySelector("shreddit-feed") to get the feed. then hiding using toggle_reddit_feed.style.display = 'none' . This works when I first load the page, however if I go to a different page within reddit, say the popular feed, the feed doesn’t get hidden. It is only after I refresh the feed gets hidden.

So initially in my content script I was doing the following to load the script on DOM:

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', loadScript());
  console.log("DOM reloading");
} else {
  console.log("DOM loaded");

  loadScript();
}

But then doing some digging I read that to use MutationObserver()

const redditMain = document.body;
const config = {  attributes: true, childList: true };

var callback = function(mutationsList, observer) {
  for (var mutation of mutationsList) {
      if (mutation.type === "childList") {
        console.log("DOM updated");
        loadScript();
      }
  }
};

const observer = new MutationObserver(callback);
observer.observe(redditMain, config);

Both ways I’m not able to achieve hiding the page

I also was checking the Chrome Extension documentation. I saw from one of the beginner samples like the this one, it also has the same problem

I know these extensions / tool already exists but I want to learn about JavaScript and Chrome extension development.

element not showing after element [closed]

For some reason the <img> element is not showing.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <main>
      <div id="minimap">
        <canvas id="minimap-main"/>
        <img src="MiniMap.png" width="50px" height="50px" id="minimap-char">
      </div>
    </main>
    <script src="sketch.js"></script>
  </body>
</html>

It works if I place the element before the <canvas> element but then I can’t overlay them which I need to do.