Failed to load resource: the server responded with a status of 500 (Internal Server Error) on react And nodejs. with failed to comment posts

Error in Post Comment Route—Need Help:

I’m facing the error in my post comment route, and while I understand it’s coming from the catch block, I can’t find the exact issue. All other functionalities in my code work perfectly—only this part fails. I’ve tried multiple approaches but still can’t resolve it.

Frontend (React) – onSubmit Handler
When a user submits the form, this function executes:

const onSubmit = async (data) => {
  try {
    console.log("Comment is ", data.Comment);
    const response = await postComment(id, { Comment: data.Comment });
    console.log("Response data", response.data);
    setComments(prevComments => [...prevComments, response.data]);
    toastSuccess("New comment added successfully");
    reset();
  } catch (error) {
    toastError(error.message);
    if (error.response?.status === 401) {
      navigate("/login");
      toastError(error?.response?.message);
    }
  }
};
  • I’m using React Hook Form for form handling.
  • Previously, I used axios.post directly, but later moved it to a separate api.jsx file and imported postComment from there.
  • The id comes from .
  • On success, I update the comments list using the spread operator, show a success toast, and reset the form.
  • If the user isn’t logged in (401), they’re redirected to the login page.

Backend (Express) – postComment Controller

module.exports.postComment = async (req, res) => {
  try {
    let postId = req.params.id;
    
    if (!req.user || !req.user._id) {
      console.log("You must be logged in first");
      return res.status(401).json({ message: "Unauthorized: Please log in first." });
    }

    const userId = req.user?._id || null;
    let newComment = new CommentListing({
      Comment: req.body.Comment,
      userName: userId,
      date: new Date(),
      postId: postId
    });

    let savedComment = await newComment.save();
    res.status(200).json(savedComment);
  } catch (error) {
    console.error("Error in catch block:", error);
    res.status(500).json({ 
      message: "Error posting comment", 
      error: error.message 
    });
  }
};

This is my route. JS backend:
router.post( wrapAsync(postComment))

This is api.jsx for reference:

export const postComment = (id, commentData) => API.post(`/tools/${id}/comment`, commentData);

  • Everything else (sessions, CORS, Axios) works fine—only the comment route fails..

  • The error appears in the catch block, but I can’t determine its origin.

  • I’ve added a screenshot of the error for reference.

    enter image description here

How to record audio from speaker (and not microphone)?

I am trying to record audio from speaker only using TypeScript or javascript.

Expected: it is recording audio from speaker only not microphone.

Actual: it is recording audio from microphone

Please help me what is the problem in my code?

let audioChunks: any = [];
navigator.mediaDevices.getUserMedia({ audio: true })
  .then(stream => {
    audioChunks = []; 
    let rec = new MediaRecorder(stream);

    rec.ondataavailable = e => {
      audioChunks.push(e.data);
      if (rec.state == "inactive") {
        let blob = new Blob(audioChunks, { type: 'audio/x-mpeg-3' });
        downloadFile(blob, "filename.wav");
      }
    }

    rec.start();  

    setTimeout(() => {
      rec.stop();
    }, 10000);
  })
  .catch(e => console.log(e));

How to open a custom registered Windows protocol handler?

I’m trying to trigger a custom Windows application from my PHP code using a custom protocol handler (videoplugin). I’ve registered the protocol in the Windows Registry and confirmed that it works when I run it manually from the Windows Run dialog.

What works:
Running this from Win + R works and launches the application successfully:

"C:Program Files (x86)IPRPLocalServiceComponentsIPRP Video PluginVideoPlugin.exe" "IP:100.100.210.16;Port:90;UserName:admin;Password:P@ssword1;AccountID:8518;ZoneNo.:501;AlarmTime:2025-04-08T15:01:46;AlarmInfo:Motion Detection Alarm;PCAlarmTimeTag:0;"

And here is my registry config (.reg file):

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOTvideoplugin]
@="URL:VideoPlugin Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOTvideopluginshell]

[HKEY_CLASSES_ROOTvideopluginshellopen]

[HKEY_CLASSES_ROOTvideopluginshellopencommand]
@="C:\Program Files (x86)\IPRPLocalServiceComponents\IPRP Video Plugin\VideoPlugin.exe %1"

What fails:

From my PHP code, I’m trying to trigger the URL like this:

if (isset($_POST['triggerVideo'])) {
    $firstFourDigits = substr($accountno, 0, 4);
    $formattedStartDate = ($DateTimeStart instanceof DateTime) ? $DateTimeStart->format('Y-m-dTH:i:s') : date('Y-m-dTH:i:s');
    
    $videoUrl = "videoplugin:IP:100.100.210.16;Port:90;UserName:admin;Password:P@ssword1;AccountID:$firstFourDigits;ZoneNo.:501;AlarmTime:$formattedStartDate;AlarmInfo:Motion Detection Alarm;PCAlarmTimeTag:0;";
    
    echo "<script>console.log('$videoUrl');</script>";
    echo "<script>window.location.href = '$videoUrl';</script>";
    exit;
}

When I run this in the browser, nothing happens. The browser either ignores it or throws a permission error. No exceptions are caught in the try…catch block either.

What I’ve tried:

  • Verified the protocol is correctly registered in regedit.
  • Manually tested the command — works.
  • Ensured browser allows custom protocol handlers (tried in Chrome and Edge).

Confirmed that JavaScript is executing by adding console.log.

Question:

How can I reliably open my custom videoplugin: protocol handler from PHP via browser (ideally Chrome or Edge)? Is this behavior blocked by the browser, or is there a better way to launch a native app from PHP?

How can I get prettier not to break when using component syntax?

When I say component syntax, what I’m referring to is this: https://flow.org/en/docs/react/component-syntax/

which looks like this:

export default component MyComponent(someProp: string) {

prettier doesn’t recognize component as a valid keyword (understandably so) and so it breaks and doesn’t check anything in the file

It doesn’t have to be prettier, I’d use another formatter if it works better with Flow’s component syntax, and I’m also open to in depth solutions like cloning the prettier repo and making the changes to support component syntax myself.

Custom Point Tooltip for Nivo Area Bump Chart

I’m trying to create Custom Point Tooltip for Nivo Area Bump Chart some like this for Bump Chart
https://nivo.rocks/storybook/iframe.html?globals=backgrounds.grid:!false&args=&id=bump–custom-point-tooltip&viewMode=story.

{
  render: () => {
    const data = generateDataWithHoles();
    return <Bump<{
      x: number;
      y: number | null;
    }> {...commonProps} useMesh={true} debugMesh={true} pointTooltip={PointTooltip} data={data} />;
  }
}

I don’t know if it’s even possible with Area Bump. Any other suggestions?

Is there a way to pause then continue a WebTransport request using Chrome Dev Tools Protocol?

I have previously intercepted, paused, inserted a body, then continued a fetch() request made from any tab or window using Fetch.enable, listening for Fetch.requestPaused and other Fetch domain definitions, something like this

chrome.debugger.onEvent.addListener(async ({
  tabId
}, message, params) => {
  console.log(tabId, message, params);
  if (
    message === 'Fetch.requestPaused' &&
    /^chrome-extension|ext_stream/.test(params.request.url)
  ) {
    await chrome.debugger.sendCommand({
      tabId
    }, 'Fetch.fulfillRequest', {
      responseCode: 200,
      requestId: params.requestId,
      requestHeaders: params.request.headers,
      body: bytesArrToBase64(
        encoder.encode(
          JSON.stringify([...Uint8Array.from({
            length: 1764
          }, () => 255)])
        )
      ),
    });

    await chrome.debugger.sendCommand({
      tabId
    }, 'Fetch.continueRequest', {
      requestId: params.requestId,
    });
  } else {
    await chrome.debugger.sendCommand({
      tabId
    }, 'Fetch.continueRequest', {
      requestId: params.requestId,
    });
  }
});

const tabId = tab.id;
await chrome.debugger.attach({
  tabId
}, '1.3');
await chrome.debugger.sendCommand({
  tabId
}, 'Fetch.enable', {
  patterns: [{
    requestStage: "Request",
    resourceType: "XHR",
    urlPattern: '*ext_stream'
  }]
});

Now I’m working on intercepting a WebTransport request, then start a local WebTransport server using Native Messaging, then continue the request.

First iteration of code. I ordinarily wouldn’t ask questions about my first draft. Generally I hack away at for a while before asking about any part of the process. Here goes, anyway, using Network.webTransportCreated

globalThis.port;
chrome.debugger.onEvent.addListener(async ({
  tabId
}, message, params) => {
  if (message === "Network.webTransportCreated" &&
    params.url ===
    "https://localhost:4433/path") {
    if (globalThis.port === undefined) {
      globalThis.port = chrome.runtime.connectNative(chrome.runtime.getManifest().short_name);
      port.onMessage.addListener(async (e) => {
        console.log(e);
      });
      port.onDisconnect.addListener(async (e) => {
        if (chrome.runtime.lastError) {
          console.log(chrome.runtime.lastError);
        }
        console.log(e);
      });
    } else {
      // ...
    }
  }
});

At least this is my initial concept for what I want to do. If the Dev Tools Protocol, or other extension API’s don’t have an explicit or implicit way to do this, then I’ll just do this another way. I’ve done this different ways in the past.

I looked. Didn’t find anything that was obvious to me in the Network domain that will facilitate the process I’m able to do with Fetch domain and fetch().

The Network.webTransportCreated event is dispatched when the request is made from the arbitrary Web page console or Snippets. The code looks something like this

(async () => {
  try {
    const serverCertificateHashes = [
      {
        "algorithm": "sha-256",
        // Hardcoding the certificate here
        // after serilizing to JSON for import with {type: "json"}...
        "value": Uint8Array.of(
          0, 1, 2, ...
        ),
      },
    ];

    const client = new WebTransport(
      `https://localhost:4433/path`,
      {
        serverCertificateHashes,
      },
    );
    // ...

The request doesn’t pause though. It usually takes between 200 to 300 milliseconds to start a node or deno or bun, etc. server using Native Messaging to launch the application; startup time of the executable. So the server is not started in time to handle the request.

I also tried making two (2) WebTransport requests. The first just to start the local WebTransport server. Then the error has to be handled, which involves including async code in error handling code. I think the double request to do one thing pattern has some technical debt. If all else fails, I’ll retry that, though.

Just curious if I’m missing something in the Network domain or extension API’s that could be used for this case – pausing a created WebTransport request, then continuing that request, programmatically?

Error while using setData containing HTML tags on a custom ckeditor5 build

I’m trying to set-up a custom UMD build for CKEditor5 and the relevant files are available here:

The issue can be seen live at https://saurabhnanda.in/ckeditor-bug/ While I am able to do editor.setData('string without HTML'), a string containing even the most basic HTML, such as, editor.setData('<strong>foobar</strong>'); throws the following error:

Uncaught CKEditorError: pattern is undefined
Read more: https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html#error-pattern is undefined
    _isElementMatching matcher.js:165
    match matcher.js:95
    node_modules custom-ckeditor.js:14875
    fire emittermixin.js:145
    _convertItem upcastdispatcher.js:221
    _convertChildren upcastdispatcher.js:250
    convertChildren upcastdispatcher.js:154
    node_modules custom-ckeditor.js:14597
    fire emittermixin.js:145
    _convertItem upcastdispatcher.js:227
    convert upcastdispatcher.js:189
    node_modules custom-ckeditor.js:8481
    change model.js:192
    toModel datacontroller.js:396
    node_modules custom-ckeditor.js:98901
    fire emittermixin.js:145
    node_modules custom-ckeditor.js:98904
    parse datacontroller.js:379
    node_modules custom-ckeditor.js:8445
    _runPendingChanges model.js:822
    enqueueChange model.js:215
    set datacontroller.js:354
    node_modules custom-ckeditor.js:98901
    fire emittermixin.js:145
    node_modules custom-ckeditor.js:98904
    setData editor.js:600
    <anonymous> (index):15
    setInterval handler* (index):14
    promise callback* (index):9
3 ckeditorerror.js:65

I’ve tried with multiple permutations of plugins and including/excluding the GeneralHtmlSupport plugin, but nothing seems to work.

I think I am missing something very basis due to a blind spot. Any help would be appreciated.

Why is my JS animation not being detected as starting by my ‘animationstart’ event listener?

I have an animation that I am declaring and playing using JS, but when the animation plays, my event listener doesn’t fire.

Here’s the HTML file:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body id="body">
    <div id="anim"></div>
    <style>
      #anim {
        top: 500px;
        left: 500px;
        width: 100px;
        height: 100px;
        background-color: black;
        position: absolute;
      }
    </style>
    <script>
      let anim = document.getElementById("anim");

      const animkeyFrames = new KeyframeEffect(
        anim,
        [
          { transform: `translate3d(0px, 0px, 0px)` }, // keyframe
          { transform: `translate3d(0px, ${200}px, 0px)` },
        ],
        {
          // keyframe options
          duration: 1000,
          iterations: "1",
        },
      );
      let animation = new Animation(animkeyFrames, document.timeline);
      
      animation.play();
      
      anim.addEventListener('animationstart', () => {
        console.log("STARTED");
      })
      </script>
  </body>
</html>

I want “STARTED” to be logged when the animation plays but nothing happens.

Doughnut ChartJs shrink when hover

I’m using doughnut chart from chartJS for my project. For some reason, it become smaller when I hover into center area of the chart. Below i have a link to the video how its looks like. I want to find what is the cause and how to fix it.

https://imgur.com/0v9FKzl

options: {
interaction: {
    mode: 'nearest',
    intersect: false
},
responsive: true,
maintainAspectRatio: false,
plugins: {
    tooltip: {
        enabled: true
    }
},
elements: {
    arc: {
        hoverOffset: 0
    }
}

Any ideas would be appreciated! Thank you.

Why does using the `lazy` attribute of `el-image` inside `el-carousel` cause several blank pages when flipping to the right?

Why does using the lazy attribute of el-image inside el-carousel cause several blank pages when flipping to the right?

Online Demo

Element Plus Playground 1

Example Code

<script setup lang="ts">

</script>

<template>
   <div>
<div style="width: 100%; height: 2000px; display: block">lazy load header</div>
<el-carousel height="2000px">
  <el-carousel-item>
    <el-row>
      <el-col :span="24">
        <el-image
          src="https://dummyimage.com/2000x2000/aaa/fff&text=2000x2000"
          lazy
        />
      </el-col>
    </el-row>
  </el-carousel-item>
  <el-carousel-item>
    <el-row>
      <el-col :span="24">
        <el-image
          src="https://dummyimage.com/2000x2000/bbb/fff&text=2000x2000"
          lazy
        />
      </el-col>
    </el-row>
  </el-carousel-item>
  <el-carousel-item>
    <el-row>
      <el-col :span="24">
        <el-image
          src="https://dummyimage.com/2000x2000/ccc/fff&text=2000x2000"
          lazy
        />
      </el-col>
    </el-row>
  </el-carousel-item>
  <el-carousel-item>
    <el-row>
      <el-col :span="24">
        <el-image
          src="https://dummyimage.com/2000x2000/ddd/fff&text=2000x2000"
          lazy
        />
      </el-col>
    </el-row>
  </el-carousel-item>
  <el-carousel-item>
    <el-row>
      <el-col :span="24">
        <el-image
          src="https://dummyimage.com/2000x2000/eee/fff&text=2000x2000"
          lazy
        />
      </el-col>
    </el-row>
  </el-carousel-item>
</el-carousel>
  </div>
</template>

<style>

Currently, I’m not sure how to make an attempt. The expected result is that when using the lazy attribute of el-image inside el-carousel, there won’t be any blank pages when flipping to the right.

Circular Progress and Graph

I’ve been trying to create this design using HTML, CSS and JS (particularly chart.io). I am wondering if there is a better way to do this without relying much on JS. The reason for this is that, this particular screenshot below is for a report. So I need to make sure that the design renders graciously with all browsers. And if a user decides to save it as PDF, the report should not have any problem rendering on different PDF viewers. I’m worried about having too much JS as I’ve had issues before where the designs break in iOS and Acrobat.

For starters, I’ll use 12 o’clock as the top most part of the progress.

  • If the progress bar is exactly 12 o’clock, it has a blue gradient.
  • If it less than 12 oclock, it has a faint green gradient and not filling up the whole progress.
  • But if it goes beyond 12 o’clock, it’s replaced by a purple gradient with the end having darker gradient and a curved end. The endings of the bar is also determined by how far it is from 12’oclock.

enter image description here

This is what I currently have at the moment.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Age Progress and Graph</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>
  <style>
    body {
      font-family: sans-serif;
      display: flex;
      align-items: center;
      justify-content: space-between;
      background: #fff;
      margin: 0;
      padding: 2rem;
      width: 700px;
    }

    .progress-container {
      position: relative;
      width: 160px;
      height: 160px;
      margin-bottom: 2rem;
    }

    .progress-container svg {
      transform: rotate(-90deg);
    }

    .progress-text {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      text-align: center;
    }

    .progress-text h2 {
      margin: 0;
      font-size: 2rem;
    }

    .chart-container {
      width: 440px;
      height: 150px;
    }
  </style>
</head>
<body>
  <div class="progress-container">
    <svg width="160" height="160">
      <circle
        cx="80"
        cy="80"
        r="70"
        stroke="#eee"
        stroke-width="8"
        fill="none"
      ></circle>
      <circle
        cx="80"
        cy="80"
        r="70"
        stroke="url(#gradient)"
        stroke-width="8"
        fill="none"
        stroke-dasharray="440"
        stroke-dashoffset="{{DASH_OFFSET}}"
        stroke-linecap="round"
      ></circle>
      <defs>
        <linearGradient id="gradient" x1="1" y1="0" x2="0" y2="1">
          <stop offset="0%" stop-color="#a646d7" />
          <stop offset="100%" stop-color="#e84fd1" />
        </linearGradient>
      </defs>
    </svg>
    <div class="progress-text">
      <h2 style="font-weight:300;">44.37</h2>
      <small>Older ⤴</small>
    </div>
  </div>

  <div class="chart-container">
    <canvas id="ageChart"></canvas>
  </div>

  <script>
    Chart.register(ChartDataLabels);

    const currentAge = 42;
    const biologicalAge = 44.37;
    const fullCircle = 440;
    const percent = Math.min(biologicalAge / currentAge, 1);
    const offset = fullCircle * (1 - percent);

    document.querySelector("circle[stroke-dashoffset='{{DASH_OFFSET}}']")
            .setAttribute("stroke-dashoffset", offset);

    const ctx = document.getElementById('ageChart').getContext('2d');
    const dataPoints = [40.44, 45.54, 44.37];

    const ageChart = new Chart(ctx, {
      type: 'line',
      data: {
        labels: ['Jan 2024', 'Apr 2024', 'Jul 2024'],
        datasets: [{
          label: 'Biological Age',
          data: dataPoints,
          fill: false,
          tension: 0.4,
          borderColor: function(context) {
            const chart = context.chart;
            const {ctx, chartArea} = chart;
            if (!chartArea) return;
            const gradient = ctx.createLinearGradient(chartArea.left, 0, chartArea.right, 0);
            gradient.addColorStop(0, '#36d1dc');
            gradient.addColorStop(1, '#a646d7');
            return gradient;
          },
          borderWidth: 3,
          pointRadius: 0
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        layout: {
          padding: {
            left: 20,
            right: 20
          }
        },
        scales: {
          y: {
            suggestedMin: 35,
            suggestedMax: 50,
            ticks: {
              stepSize: 5
            },
            grid: {
              drawTicks: true,
              drawOnChartArea: true
            }
          },
          x: {
            grid: {
              drawTicks: false,
              drawOnChartArea: false,
              drawBorder: false,
              color: 'transparent',
              lineWidth: 0
            },
            border: {
              display: false
            },
            ticks: {
              padding: 8
            },
            offset: true
          }
        },
        plugins: {
          legend: {
            display: false
          },
          tooltip: {
            callbacks: {
              label: ctx => `Age: ${ctx.raw}`
            }
          },
          datalabels: {
            align: 'center',
            anchor: 'center',
            formatter: (value) => value.toFixed(2),
            backgroundColor: (context) => context.dataIndex === context.dataset.data.length - 1 ? '#000' : '#fff',
            borderRadius: 999,
            color: (context) => context.dataIndex === context.dataset.data.length - 1 ? '#fff' : '#000',
            font: {
              weight: 'bold'
            },
            padding: 6,
            borderWidth: 1,
            borderColor: '#000'
          }
        }
      },
      plugins: [ChartDataLabels]
    });
  </script>
</body>
</html>

I would greatly appreciate if you could provide some recommendations or if you could help me with this. Thank you in advance.

Programmatically select a subset of characters from a content editable element

I have a list of items where one of the attributes can be double clicked and then edited.

I have to use [contenteditable="true"] instead of using a standard input.

The format of the data which is editable may also include a unit of measure, e.g. “150mm”.

I’m currently using .select() which selects everything, including the unit of measure.

See example here https://codepen.io/c01gat3/pen/GgRbwoj

I want to select just the numeric values, or the length of the string less the characters at the end.

Happy for plain javascript or jQuery answers for this project.

$('body').on('dblclick', '.value', function(){
  $(this).attr('contenteditable', true);
  //*** this is where I need some help   
  $(this).focus().select();
  //How to only select the numbers, not include the "mm"?
  //or how to select (length - 2) characters?
});
$(document).mouseup(function(e){
  var element = $('body').find('.value[contenteditable="true"]');
  if (!element.is(e.target) && element.has(e.target).length === 0){
    $('.value').attr('contenteditable', false);
  }
});
$(document).keypress(function(e) {
  if($('.value[contenteditable="true"]')){
    if(e.which == 13){ //enter
      $('.value').attr('contenteditable', false);
    }
  }
});
.list{
  display: flex;
  flex-direction: column;
  gap: 10px;
  .item{
    display: flex;
    .id{
      width: 20px;
      font-weight: 600;
    }
    &:has(.value[contenteditable="true"]){
      .id{
        color: red;
      }
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div class="list">
  <div class="item">
    <span class="id">1</span>
    <span class="value">100mm</span>
  </div>
  <div class="item">
    <span class="id">2</span>
    <span class="value">2,500mm</span>
  </div>
  <div class="item">
    <span class="id">3</span>
    <span class="value">340mm</span>
  </div>
</div>

Getting Video Length / Duration from Youtube without Youtube API

I am trying to find a robust way to get a YouTube video duration/length without using the YouTube API. The main problem is when the video starts with an ad because then document.querySelector(".ytp-time-duration") or document.querySelector("video").duration returns the ad’s duration, not the actual video duration.

Obviously, there are many APIs out there that provide much more information than just video duration, but I don’t think all of these APIs actually rely on the YouTube Data API. So, how can I get the actual video length even if the video starts with ads?

Any solution that works on browser console (JavaScript) is good enough for me.

newbie at javascript trying to decode a school project [closed]

For context, I’m incredibly new (talking one week) to javascript. I’m involved in a school project to make a choose your own adventure game and am taking it way too seriously. Anyway, I’ve started a game based on mexican folklore and am trying to get a “book” button to work. So you can read in game about the people you meet :). However, one of the “ghost” sprites (la planchada, or planchadafullsprite) will not hide, and the book background will not show up over that sprite’s designated background (planchadabg). This is kind of a trivial question lol but I’m super open to learning about javascript! Here is my code. For reference, I’m doing this on replit. the issues are happening after “showbookscreen” is executed. Sorry for any mistakes I’m super new to coding! 🙂

Also, the button seems to work fine on another screen, with lloronafullsprite and the corresponding background etc. So it means half of the code piece that’s hiding the sprites is working and half of it isn’t? I’m not sure.

//Press a button to choose your path
//See the README file for more information

/* VARIABLES */
let enterButton;
let a1Button;
let a2Button;
let b1Button;
let b2Button;
let screen = 0;
let music;
let softwind;
let lloronaSound;
let musicStopped = false;
let introBg;
let textScreen;
let startButton;
let nextButton;
let charroHead;
let charroFull;
let lloronaHead;
let lloronaFull;
let planchadaHead;
let planchadaFull;
let planchadaBg;
let charroBg;
let lloronaBg;
let forwardButton;
let backButton;
let bookButton;
let bookBg;
let blackBg;
let screenBeforeBook;
let exitButton;

function preload() {
  //music
  soundFormats("mp3");
  music = loadSound("sweetwater.mp3");
  lloronaSound = loadSound("Assets/llorona.mp3");
  //images
  introBg = loadImage("/Assets/introbg.png");
  textScreen = loadImage("/Assets/textscreen.png");  
  startButton = loadImage("/Assets/startbutton.png");  
  nextButton = loadImage("/Assets/nextbutton.png");
  charroHead = loadImage("/Assets/charrohead.png");
  charroFull = loadImage("/Assets/charrofull.png");
  lloronaHead = loadImage("/Assets/lloronahead.png");
  lloronaFull = loadImage("/Assets/lloronafull.png");
  lloronaBg = loadImage("/Assets/lloronabg.png");
  planchadaBg = loadImage("/Assets/planchadabg.png");
  charroBg = loadImage("/Assets/charrobg.png");
  forwardButton = loadImage("/Assets/forwardbutton.png");
  backButton = loadImage("/Assets/backbutton.png");
  planchadaFull = loadImage("/Assets/planchadafull.png");
  bookButton = loadImage("/Assets/bookbutton.png");
  bookBg = loadImage("/Assets/bookbg.png");
  blackBg = loadImage("/Assets/blackbg.png");
  exitButton = loadImage("/Assets/exitbutton.png");
}

/* SETUP RUNS ONCE */
function setup() {
  createCanvas(780, 520);
  textAlign(CENTER);
  textSize(20);
  noStroke();

  // Set up the home screen
  background(46, 26, 10);
  //Draw background Image
  image(introBg, 0, 0);

  // Create buttons for all screens
  startButton = new Sprite(width/2, height/2 + 200);
  nextButton = new Sprite(width + 400, height + 400);
  //Resize Images
  forwardButton.resize(0,45);
  backButton.resize(0,45);
  bookButton.resize(0,45);
  exitButton.resize(0,45);
  // Create sprites 
  charroFullSprite = new Sprite(charroFull, -2000,-2000)
  lloronaFullSprite = new Sprite(lloronaFull, -2000,-2000)
  planchadaFullSprite = new Sprite(planchadaFull, -2000,-2000)
  charroHeadSprite = new Sprite(charroHead, -2000,-2000)
  lloronaHeadSprite = new Sprite(lloronaHead, -2000,-2000)
  planchadaHeadSprite = new Sprite(planchadaHead, -2000,-2000)
  forwardButtonSprite = new Sprite(forwardButton, -2000,-2000)
  backButtonSprite =  new Sprite (backButton, -2000,-2000)
  bookButtonSprite = new Sprite (bookButton, -2000,-2000)

}

/* DRAW LOOP REPEATS */
function draw() {

  // Display start button
  startButton.w = 80;
  startButton.h = 50;
  startButton.collider = "k";
  startButton.color = "white";
  startButton.text = "start";

  //Check start button
  if (startButton.mouse.presses()) {
    print("pressed");
    showIntroScreen0();
    screen = 0;
  }

  // Display Llorona screen
  if (screen == 0) {
    if (nextButton.mouse.presses()) {
      print("pressed");
      showLloronaScreen();
      screen = 1;
    }
  }

  //Go to charro screen from llorona
  if (screen == 1) {
    if (forwardButtonSprite.mouse.presses()) {
      showCharroScreen();
      screen = 2; 
    }
    if (backButtonSprite.mouse.presses()) {
      showPlanchadaScreen();
      screen = 3;
    }
  }
  //go to llorona screen from planchada
  if (screen == 3) {
    if (forwardButtonSprite.mouse.presses()) {
      print("pressed");
      showLloronaScreen();
      screen = 1;
    }
  }
  //go to llorona screen from charro
  if (screen == 2) {
    if (backButtonSprite.mouse.presses()) {
      print("pressed");
      showLloronaScreen();
      screen = 1;
    }
  }
  //go to book screen
  if (screen == 1) {
    if (bookButtonSprite.mouse.presses()) {
      print("pressed");
      showBookScreen();
      screen = 4;
      screenBeforeBook = 1;
    }
  }
  if (screen == 2) {
    if (bookButtonSprite.mouse.presses()) {
      print("pressed");
      showBookScreen();
      screen = 4;
      screenBeforeBook = 2;
    }
  }
  if (screen == 3) {
    if (bookButtonSprite.mouse.presses()) {
      print("pressed");
      showBookScreen();
      screen = 4;
      screenBeforeBook = 3;
    }
  }

  //hide buttons in book bg
  if (screen == 4) {
    forwardButtonSprite.pos = { x: -2000, y: -2000 };
    backButtonSprite.pos = { x: -2000, y: -2000 };
    bookButtonSprite.pos = { x: -2000, y: -2000 };
    lloronaFullSprite.pos = { x: -2000, y: -2000 };
    charroFullSprite.pos = { x: -2000, y: -2000 };
    planchadaFullSprite.pos = { x: -2000, y: -2000 };
  }
  //exit book screen
  if (screen == 4) {
    if (exitButtonSprite.mouse.presses())
      if (screenBeforeBook == 1) {
          showLloronaScreen();
          screen = 1;
        }
      if (screenBeforeBook == 2) {
          showCharroScreen();
          screen = 2;
        }
      if (screenBeforeBook == 3) {
          showPlanchadaScreen();
          screen = 3;
      }
  }
  
}

function mousePressed() {
  // Start audio context and play music only on first click and if not stopped
  if (!music.isPlaying() && !musicStopped) {
    userStartAudio();
    music.play();
    music.loop();
    music.setVolume(0.3);
  }
}

/* FUNCTIONS TO DISPLAY SCREENS */
function showIntroScreen0() {
  background(46, 26, 10)
  image(textScreen, 3, 0);
  fill("white");
  text(
    "Your aunt was murdered ten years ago",
    width / 2,
    height / 2 - 100
  );
  //Get rid of begin button
  startButton.pos = { x: -100, y: -100};
  //Add accept button
  nextButton.pos = { x: width/2, y: height/2 + 120};
  nextButton.w = 280;
  nextButton.h = 90;
  nextButton.collider = "k";
  nextButton.color = "white";
  nextButton.text = "Go searching for answers";
}

async function showLloronaScreen() {
  if (music.isPlaying()) {
    // Fade out over 1 second
    for (let vol = 0.3; vol >= 0; vol -= 0.01) {
      music.setVolume(vol);
      await delay(33); // approx 30fps
    }
    music.stop();
    musicStopped = true;
  }
  if (!lloronaSound.isPlaying()) {
    lloronaSound.play();
    lloronaSound.setVolume(0.05);
  }
  background(46, 26, 10); 
  image(lloronaBg, 3, 0);
  lloronaFullSprite.pos = { x: width/2, y: height/2 + 50 };
  lloronaFullSprite.scale = 0.1;
  lloronaFullSprite.rotation = 0;
  planchadaFullSprite.pos = { x: -2000, y: -2000 }; // Move planchada off screen
  charroFullSprite.pos = { x: -2000, y: -2000 }; // Move charro off screen
  fill("antiquewhite");
  textAlign(LEFT);
  textSize(20);
  text(
    "tutorial text here",
    width/2 - 250,
    height/2 - 100
  );
  //Get rid of accept button
  nextButton.pos = { x: -2000, y: -2000 };
  //Add forward button at bottom right
  forwardButtonSprite.pos = { x: width - 50, y: height - 50 };
  forwardButtonSprite.w = 45;
  forwardButtonSprite.h = 45;
  forwardButtonSprite.collider = "k";
  forwardButtonSprite.color = color(255, 255, 255, 0);
  forwardButtonSprite.rotation = 0;
  //Add back button at bottom left
  backButtonSprite.pos = { x: 50, y: height - 50 };
  backButtonSprite.w = 45;
  backButtonSprite.h = 45;
  backButtonSprite.collider = "k";
  backButtonSprite.color = color(255, 255, 255, 0);
  backButtonSprite.rotation = 0; //Corrected line
  //change music
  //Add book button at top left
  bookButtonSprite.pos = { x: 50, y: 50 };
  bookButtonSprite.w = 45;
  bookButtonSprite.h = 45;
  bookButtonSprite.collider = "k";
  bookButtonSprite.color = color(255, 255, 255, 0);
  // move exit button off screen
  exitButtonSprite.pos = { x: -2000, y: -2000 };
}

function showCharroScreen() {
  if (lloronaSound.isPlaying()) {
    lloronaSound.pause();
  }
  background(46, 26, 10); 
  image(charroBg, 3, 0);
  if (charroFullSprite) {
    charroFullSprite.remove();
  }
  charroFullSprite = new Sprite(charroFull, width/2, height/2);
  charroFullSprite.scale = 0.1;
  lloronaFullSprite.pos = { x: -2000, y: -2000 }; // Move Llorona off screen
  forwardButtonSprite.pos = { x: -2000, y: -2000 };
  fill("antiquewhite");
  textAlign(LEFT);
  textSize(20);
  text(
    "elcharro",
    width/2 - 250,
    height/2 - 100
  );
}

function showPlanchadaScreen() {
  if (lloronaSound.isPlaying()) {
    lloronaSound.pause();
  }
  background(46, 26, 10); 
  image(planchadaBg, 3, 0);
  planchadaFullSprite.pos = { x: width/2, y: height/2 + 50 };
  planchadaFullSprite.scale = 0.1;
  planchadaFullSprite.rotation = 0;
if (screen == 3) {
  lloronaFullSprite.pos = { x: width/2, y: height/2 };
} else {
  lloronaFullSprite.pos = { x: -2000, y: -2000 };
}
// Move Llorona off screen
  backButtonSprite.pos = { x: -2000, y: -2000 };
  fill("antiquewhite");
  textAlign(LEFT);
  textSize(20);
  text(
    "laplanchada",
    width/2 - 250,
    height/2 - 100
  );  
}
function showBookScreen() {
  background(46, 26, 10);
  image(bookBg, 3, 0);
  
  console.log("Planchada position:", planchadaFullSprite.pos);
  console.log("Charro position:", charroFullSprite.pos);
  // Add exit button at top right
  exitButtonSprite = new Sprite(exitButton, width - 50, 50);
  exitButtonSprite.w = 45;
  exitButtonSprite.h = 45;
  exitButtonSprite.collider = "k";
  exitButtonSprite.color = color(255, 255, 255, 0);
}```