Multi-Select FileField is not returning list of files based on the order i selected them

I am trying to upload multiple images at once using the filefield xtype but when I get the list of selected files, the list is not in the order I selected them in. It looks to be automatically sorting them based on some other criteria. Is there a way to get list of file names based on the order I selected them?

xtype: 'filefield',
id: 'figureFileUpload',
buttonText: 'Browse Files From Device',
handler: 'onBrowseFiles',
buttonOnly: true,
multiple: true,
height: 45,
listeners: {
    change: 'figureFilefieldChange',
    render: function (me, eOpts) {
        var el = document.getElementById(me.id+'-button-fileInputEl');
        if(me.multiple) {
            el.multiple = true;
        }
    }
}

Getting a not a function error inside

Currently working on a project but getting an error that the code used is not a function which is inside a <form></form>

Our project is a simple registration with auto compute function where in when I input the value on the attendee count, it would automatically show the value below the registration, it also has a view function for us to view the form first before we hit the submit button

<html>
<head>
    <title>Registration Form</title>
    <meta name="viewport" content="width=device-width">
    <link href="C:UsersAdminDesktopRegistration1galvanRegistration111.css" rel="stylesheet" type="text/css">
</head>

<script language="javascript">

function viewInput()
    {
        document.getElementById("KEG1").innerHTML = document.getElementById("KFname").value;
        document.getElementById("KEG2").innerHTML = document.getElementById("KLname").value;
        document.getElementById("KEG3").innerHTML = document.getElementById("KEmail").value;
        document.getElementById("KEG4").innerHTML = document.getElementById("KArea").value;
        document.getElementById("KEG5").innerHTML = document.getElementById("KPtn").value;
        document.getElementById("KEG6").innerHTML = document.getElementById("KAddress").value;
        document.getElementById("KEG7").innerHTML = document.getElementById("KCity").value;
        document.getElementById("KEG8").innerHTML = document.getElementById("KState").value;
        document.getElementById("KEG9").innerHTML = document.getElementById("KZip").value;
        document.getElementById("KEG10").innerHTML = document.getElementById("KCountry").value;
        document.getElementById("KEG11").innerHTML = document.getElementById("KCompany").value;
        
    }

    var numOne, numTwo, res, temp;
function compute()
{
  numOne = parseInt(document.getElementById("value1").value);
  numTwo = parseInt(document.getElementById("KAttendee").value);
  if(numOne && numTwo)
  {
    temp = document.getElementById("res");
    temp.style.display = "block";
    res = numOne * numTwo;
    document.getElementById("compute").value = "$" + res;
  }
}

function checkforblank() {

if (document.getElementById('KFname').value == "") {
    alert('Please enter your FIRST NAME');

    return false;   
}

if (document.getElementById('KLname').value == "") {
    alert('Please enter your LAST NAME');
    return false;
}

if (document.getElementById('KEmail').value == "") {
    alert('Please enter your EMAIL ADDRESS');
    return false;
}

if (document.getElementById('KArea').value == "") {
    alert('Please enter the AREA CODE');
    return false;
}

if (document.getElementById('KPtn').value == "") {
    alert('Please enter your PHONE NUMBER');
    return false;
}

if (document.getElementById('KAddress').value == "") {
    alert('Please enter your ADDRESS');
    return false;
}

if (document.getElementById('KCity').value == "") {
    alert('Please enter your CITY');
    return false;
}

if (document.getElementById('KState').value == "") {
    alert('Please enter your STATE');
    return false;
}

if (document.getElementById('KZip').value == "") {
    alert('Please enter the ZIP CODE');
    return false;
}

if (document.getElementById('KCountry').value == "") {
    alert('Please enter your COUNTRY');
    return false;
}

if (document.getElementById('KCompany').value == "") {
    alert('Please enter the name of the COMPANY');
    return false;
}

if (document.getElementById('KAttendee').value == "") {
    alert('Please enter the count of ATTENDEES');
    return false;
}

}

</script>

<body style="background-color: #ccd1d1"; text-align="MIDDLE">

<h1>Registration Form</h1>


<form method="post" action="C:UsersAdminDesktopRegistration1galvanMainPage.html" >
<table Name="Full Name">
 
<tr>
    <td><label>Full Name :</label></td>
    <td><input required type="text" id="KFname"><br></td>
    <td><input required type="text" id="KLname"><br></td>
</tr>

<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">First Name</label></td>
    <td><label class="lowerTxt">Last Name</label></td>
</tr>
</table>

<table Name="Email">
<tr>
    <td><label>E-Mail :</label></td>
    <td><input type="text" class="long" id="KEmail"><br></td>
</tr>
    
<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">[email protected]</label></td>
    </tr>
</table>

<table Name="Phone Number" Class="TablePhone">
<tr>
    <td><label>Phone Number :</label></td>
    <td><input type="number" id="KArea"
        oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);" maxlength="3"><br></td>
    <td><input type="number" id="KPtn"
        oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);" maxlength="10"><br></td>
</tr>
    
<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">Area Code</label></td>
    <td><label class="lowerTxt">Phone Number</label></td>
</tr>
</table>

<table Name="TableAddress">
<tr>
    <td><label>Address :</label></td>
    <td><input type="text" class="long" id="KAddress"><br></td>
</tr>
    
<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">Street Address</label></td>
</tr>
</table>    

<table Name="TableCity">
<tr>
    <td><label></label></td>
    <td><input type="text" id="KCity"><br></td>
    <td><input type="text" id="KState"><br></td>
</tr>

<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">City</label></td>
    <td><label class="lowerTxt">State/Province</label></td>
</tr>
</table>

<table class="TableZip">
<tr>
    <td><label></label></td>
    <td><input type="number" id="KZip"
        oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);" maxlength="4"><br></td>
    <td><input type="text" id="KCountry"><br></td>
</tr>
    
<tr>
    <td><label></label></td>
    <td><label class="lowerTxt">Postal/Zip Code</label></td>
    <td><label class="lowerTxt">Country</label></td>
</tr>
</table>

<table Name="Company">
<tr>
    <td><label>Company Name :</label></td>
    <td><input type="text" class="long" id="KCompany"><br></td>
</tr>
</table>

<table Name="Attendee">
<tr>
    <td><label>Attendee Count :</label></td>
    <input id="value1" type="hidden" value="500">
    <td class="dollar">$500 x <input type="number" class="value2" id="KAttendee"
        oninput="javascript: if (this.value.length > this.maxLength) this.value = this.value.slice(0, this.maxLength);" maxlength="2" min='1' max='10'
        onclick="compute()" onkeyup="compute()" required></td>
</tr>
<tr>
    <td><label></label></td>
    <td class="Max">Max 10 Attendee</td>
</tr>
    

<script for ="value2">
        document.getElementsByClassName('value2')[0].oninput = function () {
        var max = parseInt(this.max);
        if (parseInt(this.value) > max) {
            this.value = max; 
        }
    }
</script>
</tr>
</table>

<br>

<div Name="Button1">
    <button class="Button1" type="submit" value="VIEW" onclick="viewInput(), checkforblank(); return false">VIEW</button>
</div>




<br>

<h1>Your Information</h1>

<div name="viewform">
    <p><label class="info1">FIRST NAME:</label>
    <span class="info2" id='KEG1' style="text-transform: uppercase;"></span></p>
    
    <p><label class="info1">LAST NAME:</label>
    <span class="info2" id='KEG2' style="text-transform: uppercase;"></span></p>
        
    <p><label class="info1">EMAIL:</label>
    <span class="info2" id='KEG3' style="text-transform: uppercase;"></span></p>
        
    <p><label class="info1">PHONE NUMBER:</label>
    <span class="info2" id='KEG4' style="text-transform: uppercase;"></span>
    <span class="info2" id='KEG5' style="text-transform: uppercase;"></span>
    </p>
        
    <p><label class="info1">ADDRESS:</label>
    <span class="info2" id='KEG6' style="text-transform: uppercase;"></span>
    <span class="info2" id='KEG7' style="text-transform: uppercase;"></span>
    <span class="info2" id='KEG8' style="text-transform: uppercase;"></span>
    <span class="info2" id='KEG9' style="text-transform: uppercase;"></span>
    <span class="info2" id='KEG10' style="text-transform: uppercase;"></span>
    </p>
        
    <p><label class="info1">COMPANY NAME:</label>
    <span class="info2" id='KEG11' style="text-transform: uppercase;"></span></p>
        
    <p><label class="info1">ATTENDEE COUNT:</label>
    <span class="info2" id='KEG12' style="text-transform: uppercase;"></span>
    </p>

    <p id="res"><label class="info1">TOTAL AMOUNT:</label>
    <input class="add" id="compute">
    </p>
</div>

<br>

<div Name="Button2">
    <input class="Button2" type="submit" onclick="viewInput()">
</div>


</form>
</body>

</html>

With Material UI Text Field of type number, how to make the spin buttons darker?

I am creating a number Text Field with Material UI, and it is not using my theme for the colors of the spin buttons.

Here is the example code from the Material UI docs and a screenshot of it on their site:

<TextField
  id="outlined-number"
  label="Number"
  type="number"
  InputLabelProps={{
    shrink: true,
  }}
/>

Material UI docs site example number TextField

Here is the screenshot from my React app when I copy and paste their example:

Material UI number TextField in my test app

My reproducible example code has two files, App.js and ThemeUtil.js.

App.js:

import React from 'react';

import darkTheme, {lightTheme, ThemeContext} from "./ThemeUtil";
import {Box, CssBaseline, TextField, ThemeProvider} from "@mui/material";

function App() {
  const initialTheme = localStorage.getItem('theme') === 'light' ? lightTheme : darkTheme;

  const [theme, setTheme] = React.useState(initialTheme);

  const toggleTheme = () => {
    setTheme(prevTheme => {
      const newTheme = prevTheme === darkTheme ? lightTheme : darkTheme
      localStorage.setItem('theme', newTheme === darkTheme ? 'dark' : 'light');

      return newTheme;
    });
  }

  return (
      <ThemeContext.Provider value={{theme, toggleTheme}}>
        <ThemeProvider theme={theme}>
            <CssBaseline/>
            <Box className={"TestAppContainer"} sx={{display: "flex", justifyContent: "center", alignItems: "center", width: "100vw", height: "100vh"}}>
                <TextField
                    label="Number"
                    type="number"
                    InputLabelProps={{
                        shrink: true,
                    }}
                />
            </Box>
        </ThemeProvider>
      </ThemeContext.Provider>
  );
}

export default App;

ThemeUtil.js:

import React from "react";

import {createTheme} from '@mui/material/styles';

const darkTheme = createTheme({
    palette: {
        mode: "dark",
    },
    typography: {
        fontSize: 16,
    },
});

const lightTheme = createTheme({
    palette: {
        mode: "light",
    },
    typography: {
        fontSize: 16,
    },
});

const ThemeContext = React.createContext({
    theme: darkTheme, toggleTheme: () => {
    }
});

export default darkTheme;
export {lightTheme, ThemeContext};

How do I make the spin buttons appear dark, similar to the example on the Material UI site? Can the theme be modified to set those to a specified color?

Problem with Image uploading website reloading

the problem is that I’m trying to pass an image file in a formdata object to my server, but as a result of this; the webpage keeps reloading. I’ve tried using preventdefault but it doesn’t work in an onchange event.
Here is my HTML:

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link type="text/css" rel="stylesheet" href="css/image_uploader.css">
    <link rel="icon" href="img/devchallenges.png">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.0.0-alpha.1/axios.min.js"></script>
    <title>Image-Uploader</title>
</head>
<body>
    <div id="main">
        <div class="box">
            <span id="upload"> Upload your image </span><br>
             <span id="file">File should be jpeg, png...</span> <br>
             <div id="overlay"></div>
             <span id="drag">
                 <span id="drag2">
                     <img src="img/image.svg" alt="image icon">
                     Drag & Drop your image here</span>
             </span> <br>
             <span id="or">Or</span>
                <form action="" id="form">
                    <input type="file" accept="image/*" name="image" id="filed" onload="loadfile()" style="display: none;" >
                </form>
             <label id="choose" for="filed">Choose a file</label>
             <div id="input"></div>
             <button id="label" type="submit" for="input">Copy</button>
         </div>
         <div class="message">
            Image uploaded successfully.
            <button onclick="func1()" class="okay">Okay</button>
         </div>
        <!-- Progress bar -->
<div id="prog">
    <label id="prog-label" for="progress-bar">0%</label>
    <progress id="progress-bar" value="0" max="100"></progress>
</div>
         <footer> Philip Joe Yakubu</footer>
    </div>

const dropArea = document.querySelector("#drag");
const dragText = document.querySelector("#drag2");
const hide = document.getElementById("prog")
const overlay = document.getElementById('overlay');
const message = document.querySelector(".message");
const form = document.getElementById('form');
const bar = document.getElementById('progress-bar');
let button = document.querySelector("#choose");
let input = document.querySelector("#filed");
var copyButton = document.querySelector("#label");
var inp = document.getElementById("input");
let file;

input.addEventListener("change", function (e) {
  e.preventDefault();
  hide.style.display = "flex";
  file = input.files[0];
  dropArea.classList.add("active");
  console.log("successful1")
  console.log("it's successful");
  inp.innerHTML = " " + URL.createObjectURL(file) + " ";
  console.log("no problem so far")
  console.log(URL.createObjectURL(file))
  document.getElementById("label").addEventListener("click", function() {
    var copyText = URL.createObjectURL(file);
    navigator.clipboard.writeText(copyText)
      .then(function() {
        alert("Text copied to clipboard: " + copyText);
      })
      .catch(function(error) {
        alert("Failed to copy text: " + error);
      });
  });

  // Create a new FormData instance
  var formData = new FormData();

  // Append the file to the FormData
  formData.append("image", file);

// Axios
const config = {
  onUploadProgress: function(progressEvent) {
    progressEvent.preventDefault();
    const percentCompleted = Math.round((progressEvent.loaded / progressEvent.total)*100);
   
      bar.setAttribute('value', percentCompleted);
      bar.previousElementSibling.textContent = `${percentCompleted}%`
      if (percentCompleted === 100) {
         setTimeout(() => {
        document.getElementById("prog").style.display = "none";
        displayFile();
        overlay.style.display = 'block';
        message.style.display = "grid"
      }, 100);
      inp.style.display = "flex";
      copyButton.style.display = "flex";
      }
       // adjust the delay time as needed
  }
}

console.log(file);

axios.post('http://localhost:7777/upload', formData, config)
  .then(res => {
    console.log("Upload successful:", res);
    console.log(res.data);
  })
  .catch(err => {
    console.log(file);
    console.error("Error uploading image:", err);
});

});

I tried passing the file in the input as a constant itself

const file = input.files[0];

axios.post('http://localhost:7777/upload', file, config)
  .then(res => {
    console.log("Upload successful:", res);
    console.log(res.data);
  })
  .catch(err => {
    console.log(file);
    console.error("Error uploading image:", err);
});

but it seems my axios HTTPrequest will only accept multipart/formdata requests.

Modify available choices for a multiple Django from based on current selection

I have a form that works with these base choices:

class ShockForm(forms.Form):
    sector = forms.ChoiceField(choices=[
            ('c1', 'Choice 1'),
            ('c2', 'Choice 2'),
            ('c3', 'Choice 3'),
    ])
    amount = forms.FloatField()

the form is rendered a number of times depending on the user’s previous selection, it displays n forms if n was inputed by the user on the previous screen.
I want to make the forms dynamic, ie, if an user selects one choice in the first form that choice is not available on the other form and so on, so there cannot be duplicated choices.
I belive JS is the solution for this as I am not saving every form one by one but all in one go (see views below).
However I have very little knowledge of JS and all I tried did not work. Any guidance?

Here is the template relevant content:

        <form method="post">
            {% csrf_token %}
            {% for form in shocks_forms %}
                <h4>Sector {{ forloop.counter }}</h4>
                {{ form.as_p }}
            {% endfor %}
            <button type="submit">Submit</button>
        </form>   

And here the full backend view to understan how forms are send and render:

def simulation_shocks_view(request, pk):
    simulation = get_object_or_404(Simulation, pk=pk)
    number_of_sectors_to_shock = simulation.number_of_sectors_to_shock
    
    if request.method == 'POST':
        form_list = [ShockForm(request.POST, prefix=str(i+1)) for i in range(number_of_sectors_to_shock)]
        
        if all(form.is_valid() for form in form_list):
            shocks_data = {}
            for form in form_list:
                sector = form.cleaned_data['sector']
                amount = form.cleaned_data['amount']
                shocks_data[sector] = amount
            simulation.shocks = shocks_data
            simulation.save()
            return redirect('simulation_details', pk=simulation.id)
        else:
            pass
    
    else:
        form_list = [ShockForm(prefix=str(i+1)) for i in range(number_of_sectors_to_shock)]
    
    context = {
        'shocks_forms': form_list,
    }
    return render(request, 'shock_form.html', context)

How to Properly Handle Logout page Followed by Login page in React without Causing Page Re-renders?

I’m working on a React application where I need to handle user inactivity by logging them out and then redirect them back to login page. However, I’m encountering an issue where the page renders twice, causing a noticeable flicker or delay.

The component like this:

export const LogoutInactivityTimeout = () => {
  const { search, pathname } = useLocation()
  const { loginWithInactiveTimeout, logout } = useAuthenticationContext()
  const redirect = new URLSearchParams(search).get('redirect') ?? ''
  const isReturningFromLogout =
    new URLSearchParams(search).get('isReturningFromLogout') === 'true' || false

  useEffect(() => {
    if (!isReturningFromLogout) {
      logout({
        returnTo: `${window.location.origin}${pathname}?redirect=${redirect}&isReturningFromLogout=true`,
      })
    } else {
      loginWithInactiveTimeout(redirect)
    }
  }, [
    logout,
    loginWithInactiveTimeout,
    isReturningFromLogout,
    pathname,
    redirect,
  ])

  return (
    <AuthLoadingPage>
...
    </AuthLoadingPage>
  )
}

When isReturningFromLogout is false, the logout function is triggered, which logs the user out and redirects them back to the same component with the isReturningFromLogout parameter set to true. At this point, the loginWithInactiveTimeout function is called to direct the user to login page.

This approach causes the page to render twice—once during the logout process and again during the login process. This creates a flicker in the user experience that I’d like to avoid.

How can I handle the logout and login process in a way that makes sure the login is called after the logout finished to prevent the page from rendering twice?

I’ve tried wrapping the logout function in a Promise, and use .then() but since the logout function from Auth0 doesn’t return a Promise, this approach didn’t work as expected.

Is there a more efficient way to handle this process in React, or should I be considering a different approach altogether?

Error using npm create vite@latest: “require() of ES modules is not supported”

I’m trying to set up a new Vite project using the command:

npm create vite@latest

After confirming to proceed with the installation of [email protected], I get the following error:

(node:58326) Warning: require() of ES modules is not supported.
require() of /Users/username/.npm/_npx/1415fee72ff6294b/node_modules/create-vite/index.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/username/.npm/_npx/1415fee72ff6294b/node_modules/create-vite/package.json.
/Users/username/.npm/_npx/1415fee72ff6294b/node_modules/create-vite/index.js:3
import './dist/index.mjs'
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:983:16)
    at Module._compile (internal/modules/cjs/loader.js:1033:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1103:10)
    at Module.load (internal/modules/cjs/loader.js:914:32)
    at Function.Module._load (internal/modules/cjs/loader.js:822:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1143:12)
    at internal/main/run_main_module.js:16:11
npm error code 1
npm error path /Users/username
npm error command failed
npm error command sh -c create-vite
npm error A complete log of this run can be found in: /Users/username/.npm/_logs/2024-08-14T02_51_37_222Z-debug-0.log

Package.json:

{
  "name": "project-name",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "framer-motion": "^10.16.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.10.1",
    "react-scroll": "^1.8.9"
  },
  "devDependencies": {
    "@types/react": "^18.2.15",
    "@types/react-dom": "^18.2.7",
    "autoprefixer": "^10.4.15",
    "eslint": "^8.45.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.3",
    "postcss": "^8.4.28",
    "tailwindcss": "^3.3.3",
    "vite": "^5.4.0"
  }
}

Steps to Reproduce:
Run npm create vite@latest.
Confirm installation when prompted.

Environment:
Node.js version: v22.6.0
npm version: 10.8.2
Operating System: macOS Sonoma

What I’ve Tried:

  • Uninstalled and reinstalled all Node.js-related tools to ensure they were up-to-date.
  • Ran brew update to ensure Homebrew and its packages were current.
  • Manually updated the versions in package.json to their latest.
  • Attempted to revert to CommonJS by removing the “type”: “module” field.
  • Deleted node_modules and package-lock.json, then ran npm install.
  • Ran npm cache clean --force
  • I renamed the file as suggested, but it didn’t resolve the issue.
  • I tried to modify the code to use dynamic import() statements instead of require(), but i kept getting the error.
  • I removed the “type”: “module” from the package.json in the relevant directories, but this led to other compatibility issues or just flat out did not fix the problem

Question:
How can I resolve this error and successfully create a new Vite project using the latest version? Is there a configuration or workaround needed to avoid these ES module issues? I’ve attached my package.json of a current vite project that I have downloaded as well to show that doesn’t work either.

Any help would be greatly appreciated!

Remove gridlines in AG Grid and Streamlit app

I am making an application using Streamlit and AG Grid in a Python project.

I have below table which I have been able to format as I wanted, BUT i can’t seem to get rid of the ‘gridlines’ in the table. When I try to pass some custom css to the AG Grid table, the borders that I’m editing are the ones ‘inside’ the cell, and not the ones that you can see here. e.g. I want the red row to just be redd all the way across without the lines in between each cell.

Can someone help me on how to do that?

Example of the table where I need the gridlines to disappear

I am only using the plugin:@typescript-eslint/recommended preset rule set,Why am I getting no-var errors?

1、When I use ESLint to check my code, there is a rule that targets the var keyword, and the rule name is no-var. the no-var rule is a native rule of ESLint. However, I am only using the ‘plugin:@typescript-eslint/recommended’ preset rule set and haven’t used the native ESLint rule set. Why am I getting no-var errors?

“Please help me solve the problems mentioned above.”

Why are my screen sizes affecting my footer?

I am using Next.js and tailwindcss and have been sitting here for an hour trying to figure out what the issue is (finally becoming a programmer). In my tailwind config.js file I have some screen sizes under theme and extend. For some reason, the md and xl screen sizes only are affecting my footer, causing my footer to become uncentered. Does anyone know why this is happening?

Also, I imported the footer.js code into _app.js where it is then applied to all of my pages.

tailwind.config.js screen sizes code snippet:

 screens: {
        "2xl": { max: "1535px" },
        // => @media (max-width: 1535px) { ... }
    
        xl: { max: "1279px" },
        // => @media (max-width: 1279px) { ... }
    
        lg: { max: "1023px" },
        // => @media (max-width: 1023px) { ... }
    
        md: { max: "767px" },
        // => @media (max-width: 767px) { ... }
    
        sm: { max: "639px" },
        // => @media (max-width: 639px) { ... }
    
        xs: { max: "479px" },
        // => @media (max-width: 479px) { ... }
    },

Footer.js code:

import { useState, useEffect } from "react";
import Layout from "./Layout";
import Link from "next/link";
import { useRouter } from "next/router";
import { motion } from "framer-motion";

const CustomLink = ({ href, title, className = "", target }) => {
  const router = useRouter();
  return (
    <motion.a
      href={href}
      target={target}
      whileTap={{ scale: 0.9 }}
      className={`${className} relative group inline-block`}
    >
      {title}
      <span
        className={`h-[1px] inline-block bg-white
                absolute left-0 -bottom-0.5
                group-hover:w-full transition-[width] ease duration-300
                ${router.asPath === href ? "w-full" : "w-0"}
                `}
      >
        &nbsp;
      </span>
    </motion.a>
  );
};

const Footer = () => {
  const [isVisible, setIsVisible] = useState(false);

  const scrollToTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const toggleVisibility = () => {
    if (window.scrollY > 0.1) {
      // Change 300 to any value you want
      setIsVisible(true);
    } else {
      setIsVisible(false);
    }
  };

  useEffect(() => {
    window.addEventListener("scroll", toggleVisibility);
    return () => window.removeEventListener("scroll", toggleVisibility);
  }, []);

  return (
    <footer className="relative w-full border-t-2 border-solid border-dark font-medium text-lg bg-gray-900 text-white">
      <div className="max-w-screen-xl mx-auto py-12 grid grid-cols-1 md:grid-cols-4 gap-8 text-center">
        <div className="md:col-span-2 flex flex-col items-center md:items-start text-center md:text-left">
          <h3 className="text-2xl font-bold mb-6">About Me</h3>
          <p className="text-lg mb-6">
            Lorem ipsum odor amet, consectetuer adipiscing elit. Sed elementum phasellus aliquet, lectus netus mauris. Sapien ac nisl eu euismod tincidunt gravida. Mollis natoque posuere convallis lacinia ligula est molestie. Per nisl ornare; nisl ultrices urna mi. Tristique aptent placerat hendrerit quis eget porta litora. Hac nibh suscipit fusce bibendum primis curae duis ut. Sapien habitant class donec potenti aenean. Cursus metus integer porta vulputate in; est enim. Velit laoreet eros quis dis finibus.
          </p>
          <span className="text-lg">
            Copyright &copy; {new Date().getFullYear()} In the Lens of the
            Public. All rights reserved.
          </span>
        </div>

        <div className="flex flex-col items-center">
          <h3 className="text-2xl font-bold mb-6">Site Links</h3>
          <ul className="space-y-3 text-lg">
            <li>
              <CustomLink
                href="/"
                title="Home"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="/about"
                title="About"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="/projects"
                title="Projects"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="/resume"
                title="Resume"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="/contact"
                title="Contact"
                className="hover:text-gray-400"
              />
            </li>
          </ul>
        </div>

        <div className="flex flex-col items-center">
          <h3 className="text-2xl font-bold mb-6">Contact</h3>
          <ul className="space-y-3 text-lg">
            <li>
              <CustomLink
                href="https://www.instagram.com/"
                title="Instagram"
                target="_blank"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="https://x.com/"
                title="X"
                target="_blank"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="https://www.linkedin.com/"
                title="LinkedIn"
                target="_blank"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="https://github.com/"
                title="GitHub"
                target="_blank"
                className="hover:text-gray-400"
              />
            </li>
            <li>
              <CustomLink
                href="mailto:[email protected]"
                title="Email"
                target="_blank"
                className="hover:text-gray-400"
              />
            </li>
          </ul>
        </div>
        <div className="col-span-4 mt-8 flex items-center justify-center">
          <CustomLink
            href="/privacy"
            title="Privacy Policy"
            className="hover:text-gray-400"
          />
          <span className="mx-2">|</span>
          <CustomLink
            href="/terms"
            title="Terms of Service"
            className="hover:text-gray-400"
          />
        </div>
      </div>
      <button
        onClick={scrollToTop}
        className={`fixed bottom-8 right-8 p-3 rounded-full bg-gray-700 text-white shadow-lg hover:bg-gray-600 transform transition-transform duration-300 ${
          isVisible ? "translate-y-0 opacity-100" : "translate-y-20 opacity-0"
        }`}
        aria-label="Scroll to top"
        style={{ transitionProperty: "transform, opacity" }} // Added to ensure both transform and opacity transition together
      >
        ↑
      </button>
    </footer>
  );
};

export default Footer;

I’ve tried rewriting the screen sizes and changing where my footer is located within my file and changing the contents of my footer but nothing is working.

jQuery Module Not Loading on Click from Module Buttons

I am working on a WordPress project where I have a set of modules that should load dynamically when clicking on module buttons. However, the modules are not loading as expected. Below is the relevant code:

single-coure.php

<div class='course_wrap container_wrap container_wrap_first main_color sidebar_left'>
    <div class='container'>
        <main id="course-content" class='template-page template-course content <?php avia_layout_class('content'); ?> units' <?php avia_markup_helper(array('context' => 'content', 'post_type' => 'course')); ?>>
            <div id="module-content-wrap">
                <?php
                if (have_posts()) {
                    while (have_posts()) {
                        the_post();
                        the_content();
                    }
                }

                // Fetch related modules
                $course_id = get_the_ID();

                // Query to get modules related to the course
                $args = array(
                    'post_type' => 'module',
                    'meta_query' => array(
                        array(
                            'key' => 'module_course',
                            'value' => $course_id,
                            'compare' => '='
                        )
                    ),
                    'posts_per_page' => -1
                );
                $course_modules = new WP_Query($args);

                // Initialize array to hold modules by type
                $modules = array(
                    'Course Overview' => array(),
                    'Welcome' => array(),
                    'Course Video' => array(),
                    'Quiz Questions' => array(),
                    'Next Steps' => array()
                );

                // Loop through modules and categorize them
                if ($course_modules->have_posts()) {
                    while ($course_modules->have_posts()) {
                        $course_modules->the_post();
                        $module_id = get_the_ID();

                        // Get module type, title, and content
                        $module_types = maybe_unserialize(get_post_meta($module_id, '_module_types', true));
                        $module_title = get_post_meta($module_id, 'module_title', true); // Fetch the specific module section title
                        $module_content = get_post_meta($module_id, 'module_content', true); // Fetch the specific module section content

                        // Fallback to default content types
                        $course_overview_content = get_post_meta($module_id, 'course_overview_content', true);
                        $welcome_content = get_post_meta($module_id, 'welcome_content', true);
                        $course_video_content = get_post_meta($module_id, 'course_video_content', true);
                        $quiz_questions_content = get_post_meta($module_id, 'quiz_questions_content', true);
                        $next_steps_content = get_post_meta($module_id, 'next_steps_content', true);

                        // Use specific content based on module type
                        if (empty($module_content)) {
                            if (!empty($course_overview_content)) {
                                $module_content = apply_filters('the_content', $course_overview_content);
                            } elseif (!empty($welcome_content)) {
                                $module_content = apply_filters('the_content', $welcome_content);
                            } elseif (!empty($course_video_content)) {
                                $module_content = apply_filters('the_content', $course_video_content);
                            } elseif (!empty($quiz_questions_content)) {
                                $module_content = apply_filters('the_content', $quiz_questions_content);
                            } elseif (!empty($next_steps_content)) {
                                $module_content = apply_filters('the_content', $next_steps_content);
                            } else {
                                $module_content = '<p>No content available for this module.</p>';
                            }
                        }

                        // Assign content to the correct module section
                        if ($module_types && is_array($module_types)) {
                            foreach ($module_types as $module_type) {
                                if (isset($modules[$module_type])) {
                                    $modules[$module_type][] = array(
                                        'slug' => get_post_field('post_name', $module_id),
                                        'title' => !empty($module_title) ? $module_title : get_the_title(),
                                        'content' => $module_content // Assign the correct content based on the module type
                                    );
                                }
                            }
                        }
                    }
                }

                wp_reset_postdata();

                // Display the modules
                $isFirstModule = true;

foreach ($modules as $moduleType => $moduleList) {
    if (!empty($moduleList)) {
        echo '<section class="module_wrap_section">';
        echo '<div class="module_wrap">';
        echo '<div class="module_inner">';
        echo '<div class="module_inner_header">';
        echo '<h2>' . esc_html($moduleType) . '</h2>'; // Display the module type as a section header
        echo '</div>';

        foreach ($moduleList as $module) {
            if ($isFirstModule) {
                // Display the content for the first module
                echo '<div class="module_inner_content">';
                echo '<div class="module_content">' . $module['content'] . '</div>';
                echo '</div>';
                $isFirstModule = false; // Set to false after loading the first module
            } else {
                // Display the content for other modules
                echo '<div class="module_inner_content">';
                echo '<div class="module_content">' . $module['content'] . '</div>';
                echo '</div>';
            }
        }
        echo '</div>';
        echo '</div>';
        echo '</section>';
    }
}

                ?>
            </div>
        </main>

        <aside class='sidebar units <?php avia_layout_class('sidebar'); ?>' role="complementary" itemscope="itemscope" itemtype="https://schema.org/WPSideBar">
            <div class="module_menu">
                <div class="course-title">
                    <h1><?php the_title(); ?></h1>
                </div>
                <div class='inner_sidebar'>
                    <ul id="module-links">
                        <?php
                        $icons = array(
                            'Course Overview' => 'fas fa-book-open',
                            'Welcome' => 'fas fa-handshake',
                            'Course Video' => 'fas fa-video',
                            'Quiz Questions' => 'fas fa-question-circle',
                            'Next Steps' => 'fas fa-tasks'
                        );

                        $order = ['Course Overview', 'Welcome', 'Course Video', 'Quiz Questions', 'Next Steps'];

foreach ($order as $type) {
    if (!empty($modules[$type])) {
        if ($type == 'Course Overview' || $type == 'Welcome' || count($modules[$type]) == 1) {
            $item = $modules[$type][0];
            $icon = isset($icons[$type]) ? $icons[$type] : 'fas fa-file';
            echo '<li>';
            echo '<a href="#" data-module="' . esc_attr($item['slug']) . '" data-course-id="' . esc_attr($course_id) . '"><i class="' . esc_attr($icon) . '"></i> ' . esc_html($type) . '</a>'; // Display correct title
            echo '</li>';
        } else {
            echo '<li class="dropdown">';
            $icon = isset($icons[$type]) ? $icons[$type] : 'fas fa-file';
            echo '<a href="#" class="dropdown-toggle"><i class="' . esc_attr($icon) . '"></i> ' . esc_html($type) . '</a>';
            echo '<ul class="dropdown-menu">';
            foreach ($modules[$type] as $item) {
                echo '<li><a href="#" data-module="' . esc_attr($item['slug']) . '" data-course-id="' . esc_attr($course_id) . '">' . esc_html($item['title']) . '</a></li>'; // Display correct title
            }
            echo '</ul>';
            echo '</li>';
        }
    }
}

                        ?>
                    </ul>
                </div>
            </div>
        </aside>
    </div><!-- end container -->

    <div class="module_bottom_bar">
        <div class="module_bottom_bar_buttons">
            <div id="next-module-container">
                <button id="prev-module">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="main-grid-item-icon" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
                        <polygon points="19 4 9 12 19 20 19 4" />
                        <line x1="5" x2="5" y1="5" y2="19" />
                    </svg>
                    <span>Previous</span>
                </button>
                <button id="next-module">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="main-grid-item-icon" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
                        <polygon points="5 4 15 12 5 20 5 4" />
                        <line x1="19" x2="19" y1="5" y2="19" />
                    </svg>
                    <span>Next</span>
                </button>
            </div>
        </div>
    </div>
</div><!-- end course_wrap -->

<?php
get_footer();
?>

<script>
jQuery(document).ready(function($) {
    var modules = <?php echo json_encode($modules); ?>;
    var moduleOrder = [];

    // Flatten the modules array to get a list of modules in the order they are displayed
    $.each(modules, function(type, moduleGroup) {
        $.each(moduleGroup, function(index, module) {
            moduleOrder.push({
                slug: module.slug,
                content: module.content
            });
        });
    });

    var currentModuleIndex = 0;

    function loadModule(moduleSlug) {
        $('#module-content-wrap').html('<div class="loading-indicator">Loading module...</div>');

        $.ajax({
            url: '<?php echo admin_url('admin-ajax.php'); ?>',
            type: 'POST',
            data: {
                action: 'load_module_content',
                nonce: '<?php echo wp_create_nonce('rp_ajax_nonce'); ?>',
                module: moduleSlug
            },
            success: function(response) {
                if (response.success) {
                    $('#module-content-wrap').html(response.data.content);
                } else {
                    console.error('Error loading module content:', response.data.message);
                    $('#module-content-wrap').html('<p>Error loading module content. Please try again later.</p>');
                }
            },
            error: function(xhr, status, error) {
                console.error('AJAX Error:', error);
                $('#module-content-wrap').html('<p>An unexpected error occurred while loading the module. Please try again later.</p>');
            }
        });
    }

    // Initial load of the first module
    loadModule(moduleOrder[currentModuleIndex].slug);

    // Handle sidebar link clicks
    $('#module-links a').on('click', function(e) {
        e.preventDefault();
        var moduleSlug = $(this).data('module');
        currentModuleIndex = moduleOrder.findIndex(module => module.slug === moduleSlug);
        loadModule(moduleOrder[currentModuleIndex].slug);
    });

    // Handle Next and Previous button clicks
    $('#next-module').on('click', function(e) {
        e.preventDefault();
        currentModuleIndex++;
        if (currentModuleIndex >= moduleOrder.length) {
            currentModuleIndex = 0; // Loop back to the first module
        }
        loadModule(moduleOrder[currentModuleIndex].slug);
    });

    $('#prev-module').on('click', function(e) {
        e.preventDefault();
        currentModuleIndex--;
        if (currentModuleIndex < 0) {
            currentModuleIndex = moduleOrder.length - 1; // Loop back to the last module
        }
        loadModule(moduleOrder[currentModuleIndex].slug);
    });
});
</script>

functions.php

function get_module_content($module_id)
{
    $module_content = get_post_meta($module_id, 'module_content', true);

    // Check if content is empty
    if (empty($module_content)) {
        return ''; // Or handle empty content as needed
    }

    // Basic formatting or filtering can be done here
    return apply_filters('the_content', $module_content);
}

function load_first_module($modules)
{
    $priority_order = ['Course Overview', 'Welcome', 'Course Video', 'Quiz Questions', 'Next Steps'];

    foreach ($priority_order as $module_type) {
        if (isset($modules[$module_type]) && !empty($modules[$module_type])) {
            $first_module = $modules[$module_type][0]; // Load the first available module of this type
            load_module($first_module['slug'], $first_module['course_id']);
            return;
        }
    }
}

function load_module_content()
{
    check_ajax_referer('rp_ajax_nonce', 'nonce');

    $module_slug = sanitize_text_field($_POST['module']);

    $args = array(
        'name' => $module_slug,
        'post_type' => 'module',
        'posts_per_page' => 1
    );

    $module_query = new WP_Query($args);

    if ($module_query->have_posts()) {
        $module_query->the_post();
        $module_id = get_the_ID();
        $module_content = apply_filters('the_content', get_post_meta($module_id, 'module_content', true));

        wp_send_json_success(array(
            'content' => $module_content
        ));
    } else {
        wp_send_json_error(array('message' => 'Module not found.'));
    }

    wp_die();
}
add_action('wp_ajax_load_module_content', 'load_module_content');
add_action('wp_ajax_nopriv_load_module_content', 'load_module_content');

function load_module_content_callback()
{
    if (!isset($_GET['module'])) {
        wp_send_json_error(['error' => 'No module specified.']);
    }

    $module_slug = sanitize_text_field($_GET['module']);
    $module_post = get_page_by_path($module_slug, OBJECT, 'module');

    if (!$module_post) {
        wp_send_json_error(['error' => 'Module not found.']);
    }

    $module_id = $module_post->ID;
    $module_title = get_the_title($module_id);
    $module_content = apply_filters('the_content', $module_post->post_content);

    $featured_image = get_the_post_thumbnail($module_id, 'full');

    $response = [
        'title' => $module_title,
        'content' => $module_content,
        'featured_image' => $featured_image ? $featured_image : ''
    ];

    wp_send_json_success($response);
}

add_action('wp_ajax_load_module_content', 'load_module_content_callback');
add_action('wp_ajax_nopriv_load_module_content', 'load_module_content_callback');


function display_module_section($module_type, $module_id, $video_labels)
{
    switch ($module_type) {
        case 'course_overview':
        case 'welcome':
        case 'quiz_questions':
        case 'next_steps':
            echo '<h2>' . esc_html(ucwords(str_replace('_', ' ', $module_type))) . '</h2>';
            echo apply_filters('the_content', get_post_meta($module_id, $module_type . '_content', true));
            break;

        case 'course_video':
            echo '<h2>Course Videos</h2>';
            if (!empty($video_labels)) {
                // ... existing code for displaying video labels ...
            } else {
                // Handle case where there are no video labels but the module type is 'course_video'
                $module_content = get_post_meta($module_id, 'course_video_content', true);
                if (!empty($module_content)) {
                    echo '<div class="module_content">' . apply_filters('the_content', $module_content) . '</div>';
                } else {
                    // Handle empty content for course video
                }
            }
            break;

        default:
            echo '<p>Invalid module type.</p>';
            break;
    }
}

function handle_module_loading()
{
    $course_id = isset($_POST['course_id']) ? intval($_POST['course_id']) : 0;

    if ($course_id) {
        $modules = get_course_modules($course_id);

        if (!empty($modules)) {
            // Process and return the modules
            wp_send_json_success(array('modules' => $modules));
        } else {
            wp_send_json_error('No modules found for this course.');
        }
    } else {
        wp_send_json_error('Invalid course ID.');
    }
}
add_action('wp_ajax_load_modules', 'handle_module_loading');
add_action('wp_ajax_nopriv_load_modules', 'handle_module_loading');

Problem: The modules are not loading when I click on the module buttons. I have checked the console for errors and found the following message: “Error loading module content: No content found for this module.”

What I’ve Tried:

Verified that the module slugs are correct.
Ensured that the AJAX request is being sent and received.
Checked the PHP function to ensure it is returning the correct content.
Question: What could be causing the modules not to load on click, and how can I fix this issue?

Any help would be greatly appreciated. Thank you!

How to check length of body element in iFrame

I have an iFrame that I would like to check the length of the body element within in it. My iFrame looks like this:

<iframe id='1' width='1' height='1' style={{visibility = 'hidden'}}></iframe>

When I console log out document.getElementById(‘1’), I get back this in the console:

<iframe id='1' width='1' height='1' style=visibility = 'hidden'>
#document
 <html>
    <head></head>
    <body></body>
 </html>
</iframe>

I would like to be able to check the length of the body element in this iframe and base some logic around this.

My main issue from looking for solutions on SO is that when I try to check the contentDocument of my iframe (such as document.getElementById('1').contentDocument or document.getElementById('1').contentWindow) I get back an error saying cannot read contentDocument or contentWindow of null. I would just like to see if the body element has anything in it. How would I go about this?

Why is my pagination buttons suddenly disappearing when I delete some javascript code?

In a commercial website I am making, I have been trying to test out the pagination buttons. But the problem is that for some reason the script that I applied to my index.html isn’t working. I am following along a youtube video and planned to work my way through that, but I encountered the following issues:

1 – When I delete code from my script.js, any code actually, it causes my pagination buttons to disappear.

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

    <head>
      
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Tech2etc Ecommerce Tutorial</title>
       
        <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" />

        <link rel="stylesheet" href="style.css">
        
        <link rel="stylesheet" href="pagination.css">

   
    </head>

    <body>


        <!-- Pagination Buttons -->

        <div class="pagination-buttons">
  <button type="button" class="page-btn prev-page">prev</button>
  <button type="button" class="page-btn start-page">start</button>
  <button type="button" class="page-btn active">1</button>
  <button type="button" class="page-btn">2</button>
  <button type="button" class="page-btn">3</button>
  <button type="button" class="page-btn">4</button>
  <button type="button" class="page-btn">5</button>
  <button type="button" class="page-btn">6</button>
  <button type="button" class="page-btn">7</button>
  <button type="button" class="page-btn">8</button>
  <button type="button" class="page-btn">9</button>
  <button type="button" class="page-btn next-page">next</button>
  <button type="button" class="page-btn end-page">end</button>
</div> 

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

        
    </body>

</html>
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  font-family: sans-serif;
  padding: 25px;
  background: #fff;
  color: #111;
  display: flex;
  justify-content: center;
  align-items: center;
}

*, *::after, *::before {
  box-sizing: border-box;
}

button {
  background: none;
  appearance: none;
  -webkit-appearance: none;
  -ms-appearance: none;
  -moz-appearance: none;
  border: none;
  cursor: pointer;
}

.page-btn {
  background: #ddd;
  color: #2c303a;
  height: 35px;
  border-radius: 2px;
  padding: 0 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transition: background 0.3s ease;
  margin: 0 2px
}

.page-btn:first-of-type {
  margin-left: 0;
}

.page-btn:last-of-type {
  margin-right: 0;
}

.page-btn:not([class$="-page"]) {
  width: 35px;
}

.pagination-buttons {
  display: flex;
  align-items: center;
}

.page-btn[class*="-page"] {
  background: #ccc;
  font-size: 0.6em;
  font-weight: 700;
}

.page-btn.active {
  background: #717790;
  color: #fff;
}

.page-btn[disabled] {
  opacity: 0.3;
}

2 – I also don’t understand just how using this code: would give me an extra pagination button since I don’t really see any code that connects to the index.html containing that pagination container.

Trouble

The closest seems to be this however this just creates an element, not retrieve it:

const paginationButtonContainer = document.createElement('div');
paginationButtonContainer.className = 'pagination-buttons';
const pageNumbers = (total, max, current) => {
    const half = Math.floor(max / 2);
    let to = max;
    
    if(current + half >= total) {
      to = total;
    } else if(current > half) {
      to = current + half ;
    }
    
    let from = Math.max(to - max, 0);
  
    return Array.from({length: Math.min(total, max)}, (_, i) => (i + 1) + from);
  }
  
  function PaginationButton(totalPages, maxPagesVisible = 10, currentPage = 1) {
    let pages = pageNumbers(totalPages, maxPagesVisible, currentPage);
    let currentPageBtn = null;
    const buttons = new Map();
    const disabled = {
      start: () => pages[0] === 1,
      prev: () => currentPage === 1 || currentPage > totalPages,
      end: () => pages.slice(-1)[0] === totalPages,
      next: () => currentPage >= totalPages
    }
    const frag = document.createDocumentFragment();
    const paginationButtonContainer = document.createElement('div');
    paginationButtonContainer.className = 'pagination-buttons';
    
    const createAndSetupButton = (label = '', cls = '', disabled = false, handleClick) => {
      const buttonElement = document.createElement('button');
      buttonElement.textContent = label;
      buttonElement.className = `page-btn ${cls}`;
      buttonElement.disabled = disabled;
      buttonElement.addEventListener('click', e => {
        handleClick(e);
        this.update();
        paginationButtonContainer.value = currentPage;
        paginationButtonContainer.dispatchEvent(new CustomEvent('change', {detail: {currentPageBtn}}));
      });
      
      return buttonElement;
    }
    
    const onPageButtonClick = e => currentPage = Number(e.currentTarget.textContent);
    
    const onPageButtonUpdate = index => (btn) => {
      btn.textContent = pages[index];
      
      if(pages[index] === currentPage) {
        currentPageBtn.classList.remove('active');
        btn.classList.add('active');
        currentPageBtn = btn;
        currentPageBtn.focus();
      }
    };
    
    buttons.set(
      createAndSetupButton('start', 'start-page', disabled.start(), () => currentPage = 1),
      (btn) => btn.disabled = disabled.start()
    )
    
    buttons.set(
      createAndSetupButton('prev', 'prev-page', disabled.prev(), () => currentPage -= 1),
      (btn) => btn.disabled = disabled.prev()
    )
    
    pages.map((pageNumber, index) => {
      const isCurrentPage = currentPage === pageNumber;
      const button = createAndSetupButton(
        pageNumber, isCurrentPage ? 'active' : '', false, onPageButtonClick
      );
      
      if(isCurrentPage) {
        currentPageBtn = button;
      }
      
      buttons.set(button, onPageButtonUpdate(index));
    });
    
    buttons.set(
      createAndSetupButton('next', 'next-page', disabled.next(), () => currentPage += 1),
      (btn) => btn.disabled = disabled.next()
    )
    
    buttons.set(
      createAndSetupButton('end', 'end-page', disabled.end(), () => currentPage = totalPages),
      (btn) => btn.disabled = disabled.end()
    )
    
    buttons.forEach((_, btn) => frag.appendChild(btn));
    paginationButtonContainer.appendChild(frag);
    
    this.render = (container = document.body) => {
      container.appendChild(paginationButtonContainer);
    }
    
    this.update = (newPageNumber = currentPage) => {
      currentPage = newPageNumber;
      pages = pageNumbers(totalPages, maxPagesVisible, currentPage);
      buttons.forEach((updateButton, btn) => updateButton(btn));
    }
    
    this.onChange = (handler) => {
      paginationButtonContainer.addEventListener('change', handler);
    }
  }
  
  const paginationButtons = new PaginationButton(10, 5);
  
  paginationButtons.render();
  
  paginationButtons.onChange(e => {
    console.log('-- changed', e.target.value)
  });
  

I tried commenting out blocks of code from the start of the script in order to figure out how the pagination button worked. I have been unable to do that however since any code that I deleted caused the buttons to keep on disappearing from my web page.

Can’t Instlling Angular on my MacBook Air M1 chip, its a problem i hav’t seen like before

The problem starts when I give the command

sudo npm install -g @angulat/cli@17

1- This strange error appears to me
enter image description here

2- Despite my choice of version 17, after querying the package, it appears as version 18
And Angular writes to me undefined
enter image description here

3- After that, if I create a new Angular project, assuming that the package has been downloaded, the folder appears, but with many files that do not exist
enter image description here

I searched a lot and did not find a solution to this problem, and I did not find anything similar to it at least, so I do not know if there is a mistake in my laptop or if I just did not do it correctly
I hope to find someone to help me with this matter because it has greatly hindered my development in the field of front-end
Thank you in advance

I followed the steps in the videos correctly, but this problem appeared to me and I searched a lot and did not find anything similar or a solution.
My work was disrupted a lot because of this.

How to optimize this code that gets video frame as image

I am quite new to mp4 file. But here is my working attempt to extract image frame given video url and a timestamp.

In reality the input url is an 8K 10hours 200GB video, so I can’t download it all, I can’t load it to memory, and this is an API call so it has to be fast.

Is there anything else I can optimize ?

My doubts:

  • This line ffprobe -v error -select_streams v:0 -show_entries packet=pos,size,dts_time -read_intervals ${timestamp}%+5 -of csv=p=0 "${url}" I chose this clingy 5s, in which case would this fail ?

  • Same line, I don’t know what’s going on under the hood of this ffprobe command, but I tested it with the big 8K video and it seems to complete fast. So is it safe to assume that the entire 200GB was not downloaded? An explanation of how this ffprobe command work would be appreciated

  • Based on trial and error, I concluded that the intervals returned is parsable by ffmpeg only if the first frame until the timestamp is included. If I include only that the single frame interval, ffmpeg says it is an invalid file. (Makes sense cuz I don’t think I’ll get an image from a 4byte data.) However, how can I be sure that I am selecting the least number of intervals.

  • Worse bottleneck: The function extractFrame takes 6seconds on the big video. It seems to read the entire video segment fetched (by the preceding subrange step). I couldn’t find a way to jump to the last frame without computing. Is this how MP4 work ? I read somewhere that MP4 computes the current frame based on the previous. If that is true, does it mean there is no way to compute a specific frame without reading everything since the last keyframe ?

  • Finally, this ffmpeg line is fishy (I got it from SO Extract final frame from a video using ffmpeg) it says that it ovewrites the output at everyframe . Does it mean it is writing to the disk every time ? I experience severe degradation in performance when I used .png instead of .jpg. This tells me that the image is computed every
    frame. Can we compute only the final image at the very end ?

Here is the working code to optimize.

import path from "path";
import axios from "axios";
import ffmpeg from "fluent-ffmpeg";
import fs from "fs";
import {promisify} from 'util';
import {exec} from 'child_process';

const execPromise = promisify(exec);


// URL of the video and desired timestamp (in seconds)

const videoUrl = 'https://raw.githubusercontent.com/tolotrasamuel/assets/main/videoplayback.mp4';

console.log(videoUrl);
const timestamp = 30; // Example: 30 seconds into the video


// Function to get the byte range using ffprobe
const getByteRangeForTimestamp = async (url, timestamp) => {
    // Use ffprobe to get the offset and size of the frame at the given timestamp
    const command = `ffprobe -v error -select_streams v:0 -show_entries packet=pos,size,dts_time -read_intervals ${timestamp}%+5 -of csv=p=0 "${url}"`;
    console.log('Running command:', command);
    const {stdout} = await execPromise(command);


    // Parse the output
    const timeStamps = stdout.trim().split("n");
    const frames = timeStamps.map(ts => {
        const [dts_time, size, offset] = ts.split(',');
        const timeInt = parseFloat(dts_time);
        const offsetInt = parseInt(offset);
        const sizeInt = parseInt(size);
        return {dts_time: timeInt, size: sizeInt, offset: offsetInt};
    })

    if (frames.length === 0) {
        throw new Error('No frames found in the specified interval');
    }

    let closest;


    let i = 0
    while (i < frames.length) {
        if (i === frames.length) {
            throw new Error('No frames found in the specified 5s interval');
        }
        if (frames[i].dts_time >= timestamp) {
            const oldDiff = Math.abs(closest.dts_time - timestamp);
            const newDiff = Math.abs(frames[i].dts_time - timestamp);
            if (newDiff < oldDiff) {
                closest = frames[i];
            }
            break;
        }
        closest = frames[i];
        i++;
    }

    // I experimented with this, but it seems that the first frame is always at the beginning of a valid atom
    // anything after that will make the video unplayable
    const startByte = frames[0].offset;
    const endByte = closest.offset + closest.size - 1;

    const diff = Math.abs(closest.dts_time - timestamp);
    const size = endByte - startByte + 1;
    console.log("Start: ", startByte, "End: ", endByte, "Diff: ", diff, "Timestamp: ", timestamp, "Closest: ", closest.dts_time, "Size to fetch: ", size)


    const startTime = closest.dts_time - frames[0].dts_time;
    return {startByte, endByte, startTime};
};

// Download the specific segment
const downloadSegment = async (url, startByte, endByte, outputPath) => {
    console.log(`Downloading bytes ${startByte}-${endByte}...`);
    const response = await axios.get(url, {
        responseType: 'arraybuffer',
        headers: {
            Range: `bytes=${startByte}-${endByte}`,
        },
    });

    console.log('Segment downloaded!', response.data.length, "Written to: ", outputPath);
    fs.writeFileSync(outputPath, response.data);
};

// Extract frame from the segment
const extractFrameRaw = async (videoPath, timestamp, outputFramePath) => {


    const command = `ffmpeg -sseof -3 -i ${videoPath} -update 1 -q:v 1 ${outputFramePath} -y`;
    console.log('Running command:', command);
    const startTime = new Date().getTime();
    await execPromise(command);
    const endTime = new Date().getTime();
    console.log('Processing time:', endTime - startTime, 'ms');
    console.log('Frame extracted to:', outputFramePath);
};
const extractFrame = (videoPath, timestamp, outputFramePath) => {
    ffmpeg(videoPath)
        .inputOptions(['-sseof -5'])  // Seeks to 3 seconds before the end of the video
        .outputOptions([
            '-update 1', // Continuously update the output file with new frames
            '-q:v 1'     // Set the highest JPEG quality
        ])
        .output(outputFramePath)  // Set the output file path

        // log
        .on('start', function (commandLine) {
            console.log('Spawned Ffmpeg with command: ' + commandLine);
        })
        .on('progress', function (progress) {
            console.log('Processing: ' + progress.timemark + '% done', progress, 'frame: ', progress.frames);
        })
        .on('end', function () {
            console.log('Processing finished !');
        })
        .on('error', function (err, stdout, stderr) {
            console.error('Error:', err);
            console.error('ffmpeg stderr:', stderr);
        })
        .run();
};


const __dirname = path.resolve();

// Main function to orchestrate the process
(async () => {
    try {
        // ffmpeg.setFfmpegPath('/usr/local/bin/ffmpeg');
        const {startByte, endByte, startTime} = await getByteRangeForTimestamp(videoUrl, timestamp);
        const tmpVideoPath = path.resolve(__dirname, 'temp_video.mp4');
        const outputFramePath = path.resolve(__dirname, `frame_${timestamp}.jpg`);

        await downloadSegment(videoUrl, startByte, endByte, tmpVideoPath);
        await extractFrame(tmpVideoPath, startTime, outputFramePath);
    } catch (err) {
        console.error('Error:', err);
    }
})();