Laravel 12, Event Listener First array member is not a valid class name or object error

I am trying to add multiple listeners on an event in Laravel 12 which is something like this:

AppServiceProvider.php

use AppEventsFrontendOTPVerificationEvent;
use AppListenersFrontend{OTPOnMobileListener, OTPOnWhatsappListener};

class AppServiceProvider extends ServiceProvider {
    ...

    /**
     * Bootstrap any application services.
     */
    public function boot(): void {
        Event::listen(OTPVerificationEvent::class, [
            [new OTPOnMobileListener, 'handle'],
            [new OTPOnWhatsappListener, 'handle'],
        ]);

        // --- OR ---

        Event::listen(OTPVerificationEvent::class, [
            new OTPOnMobileListener,
            new OTPOnWhatsappListener,
        ]);

        // --- OR ---

        Event::listen(OTPVerificationEvent::class, [
            OTPOnMobileListener::class,
            OTPOnWhatsappListener::class,
        ]);
    }
}

Events/Frontend/OTPVerificationEvent.php

class OTPVerificationEvent {
    /**
     * Public variables
     */
    public $mobile;
    public $mobileOtp;
    public $whatsappOtp;

    /**
     * Create a new event instance.
     */
    public function __construct($mobile, $mobileOtp, $whatsappOtp) {
        $this->mobile      = $mobile;
        $this->mobileOtp   = $mobileOtp;
        $this->whatsappOtp = $whatsappOtp;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, IlluminateBroadcastingChannel>
     */
    public function broadcastOn(): array {
        return [
            // new PrivateChannel('channel-name'),
        ];
    }
}

Listeners/Frontend/OTPOnMobileListener.php

class OTPOnMobileListener {
    /**
     * Handle the event.
     */
    public function handle(object $event): void {
        echo "<pre>";
        print_r($event);
        echo "</pre>n";
        exit;
    }
}

Listeners/Frontend/OTPOnWhatsappListener.php

class OTPOnWhatsappListener {
    /**
     * Handle the event.
     */
    public function handle(object $event): void {
        echo "<pre>";
        print_r($event);
        echo "</pre>n";
        exit;
    }
}

I am keep getting this error:

First array member is not a valid class name or object

Error

The text after the shipping label in WooCommerce

How do I place the text after the shipping label on the WooCommerce shopping cart and checkout page? I mean the “Shipping” label (Not a method).

cart

I can’t seem to find the answer to my question. I can add the code to the file cart-shipping.php but this is wrong.

I will be glad of your help!

New page in WordPress not showing the header menu

I have created a new page , and added a new menu . however, i am not able to see the menu at the top of the page. firstpage is the new page.enter image description here. i have to add a new menu thats newhomeheadermenu . I want to add this new menu to top of this new page . its not showing if i assigned the page to the menu .

Accents replaced with a strange character in Symfony 6.4 [duplicate]

For 3 days, the characters with French accents no longer appear and are replaced by a �.
Everything was fine before.
In phpmyadmin the display is correct.
It’s only on the remote server, in local all is fine.

In doctrine.yaml there are indeed the indications:

   default_table_options:
                charset: utf8mb4
                collation: utf8mb4_unicode_ci

and in the entities in the header:

#[ORM Table(options: ["collate" => "utf8mb4_unicode_ci", "charset" => "utf8mb4"])]
#[ORM Entity(repositoryClass: Equi‍peadminRepository::class)]

I updated the vendor via composer update.

How to correct this sudden problem?

Thank you for your answers.
Best regards

Javascript dynamically load Google auto script to header tags: error c is not defined

I need to dynamically load goolge’s new auto complete script to the head tags of a website. This is the new format of the script.

<script>
  (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
    key: "YOUR_API_KEY",
    v: "weekly",
    // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
    // Add other bootstrap parameters as needed, using camel case.
  });
</script>

The previous format was easy to dynamically load:

<script async
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&loading=async&callback=initMap">
</script>

This is how i previously dynamically loaded it

 let mapScript = document.createElement('script');
 mapScript.async = true;
 mapScript.setAttribute('src', 'https://maps.googleapis.com/maps/api/js?key='MY-Key'&loading=async&libraries=places&callback=initMap');

I am not sure how to dynamically load the new style script.

UPDATE:

Following the helpful advice from @biggujo I tried adding it as a textContent but have an error:

 Uncaught ReferenceError: c is not defined

This is how i tried to add it. I used template literals

const newScript = document.createElement('script');

        newScript.textContent = ` (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
    key: "myKey",
    v: "weekly",
    // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
    // Add other bootstrap parameters as needed, using camel case.
  });`


        newScript.onload = () => console.log(`${file} loaded successfully.`);
        newScript.onerror = () => console.error(`Error loading script: ${file}`);

        document.head.appendChild(newScript);

The ‘C’ the error refers to is within the script tag – its encased within another template literals:

;a.src=`https://maps.${c}apis.com/maps/api/js?` 

Javascript dynamically load a script to header tags

I need to dynamically load goolge’s new auto complete script to the head tags of a website. This is the new format of the script.

<script>
  (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
    key: "YOUR_API_KEY",
    v: "weekly",
    // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
    // Add other bootstrap parameters as needed, using camel case.
  });
</script>

The previous format was easy to dynamically load:

<script async
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&loading=async&callback=initMap">
</script>

This is how i previously dynamically loaded it

 let mapScript = document.createElement('script');
 mapScript.async = true;
 mapScript.setAttribute('src', 'https://maps.googleapis.com/maps/api/js?key='MY-Key'&loading=async&libraries=places&callback=initMap');

I am not sure how to dynamically load the new style script.

Script js in Blazor Web Assembly project not updated in web navigator

I am working on a blazor Web Assembly project. This projects contains javascript file. Some of them are located in wwwroot/js. And others are attached to a razor component in a collocated way (MyComponent.razor and MyComponent.razor.js).

In my index.html, my script located in wwwwroot/js are imported as below :
<script src="js/myScript.js?v={APP_VERSION}"></script>

For The js script attached to my razor component, it is invoked inside the component cs code like this :
_module = await JS.InvokeAsync<IJSObjectReference>("import", "./Pages/PathToMyComponent/MyComponent.razor.js");

When I modify these scripts, my web navigator (Edge or Chrome not tested with Firefox) does not take my modifications in account. The old version of my script are still loaded in the web navigator.

I don’t understand why. Thanks for your help

Can’t get fullcalendar events to show up in asp.net app

I have an asp.net app that is using fullcalendar, and I am trying to use the calendar javascript file to call the API endpoint and retrieve the list of events from the database that way, and then use those list of events to populate the calendar.

import { Calendar } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import { req } from 'superagent';

let eventsArr = [];


document.addEventListener('DOMContentLoaded', function () {
    const calendarEl = document.getElementById('calendar');

    const calendar = new Calendar(calendarEl, {
        plugins: [dayGridPlugin, timeGridPlugin, listPlugin],
        initialView: 'dayGridMonth',
        headerToolbar: {
            left: 'prev, next today',
            center: 'title',
            right: 'dayGridMonth, timeGridWeek, listWeek'
        },
        events: function (fetchInfo, successCallback, failureCallback) {
            fetch('/HealthCareTeams/patientList/1A0B71B3-61E4-4096-B647-70FAE91590DB', {
                headers: {
                    'Accept': 'application/json'
                }
            })
                .then(response => response.json())
                .then(data => successCallback(data))
                .catch(err => failureCallback(err)),
               }

    });

    calendar.render()
});

Here is the controller method:

 [HttpGet("patientList/{patientID}")]
 public async Task<IActionResult> ReturnPatientApptListAsync(Guid patientID)
 {
     var healthCareTeams = await healthCareTeamRepository.ReturnPatientApptListAsync(patientID);

     var calendarEvents = healthCareTeams.Select(hct => new
     {
         title = hct.Specialty ?? "Appoinment",
         start = hct.Appointment.ToString("s")
     });

     return new JsonResult(calendarEvents);
 }

HEre is the repo method:

public async Task<HealthCareTeam[]> ReturnPatientApptListAsync(Guid patientID)
{
    var healthCareTeams = await planMyMDVisitContext.HealthCareTeams.Where(hct => hct.PatientId == patientID).ToArrayAsync();

    return healthCareTeams;
}

Here is the JavaScript part of the cshtml page placing the calendar:

    <div id="calendar-container">
        <div id="calendar"></div>
    </div>
 </div>

<script type="module" src="~/dist/calendar.js"></script>

When I run the app, and place this URL in the browser…..https://localhost:7189/HealthCareTeams/patientList/1A0B71B3-61E4-4096-B647-70FAE91590DB, it is giving me a JSON array of the data contained in the database. I have looked at the console quite a few times, and no errors are showing up in there, and I have also looked at the network tab of chrome, and have seen this error many times:

(failed) net::ERR_BLOCKED_BY_ORB

I have tried to follow chatgpt suggestions to fix this error, including putting in proper CORS code into Program.cs, and that error still keeps popping up. Part of me thinks that’s the issue, part of me is not sure. But I feel like I have hit a wall.

How do I use custom phone number format with react-phone-input-2

I can add phone number in the input field and get the otp successfully. Issue is when I enter the phone number it display like this “+94 712 345 678”. But I want it to display like this “+94 71 234 5678”. I tried many time but couldn’t fix it.
this is the issue picture

Backend get it “+94712345678” and correct. Issue is frontend input field display format.

Here is the full code – Link

This is mobile number field i want to show like this -“+94 71 237 1234”, but showing “+94 712 371 234”. I want when typing number show like above format.

There are lots of codes. So I put a drive link for all the codes (Link). Can you refer it and help me to finish this? As a intern I don’t know how to solve this.

{
          <div className={`${styles.formGroup} ${styles.phone}`}>
            <label htmlFor="mobile">Phone Number</label>
            <PhoneInput
              country={"lk"}
              //value={displayMobile}
              value={formData.mobile}
              onChange={(value) => {
                setFormData((prev) => ({ ...prev, mobile: "+" + value }))
              }}
              /*onChange={(value) => {
                const digitsOnly = value.replace(/D/g, "").slice(0, 9); // max 9 digits
                setFormData((prev) => ({
                  ...prev,
                  mobile: "+94" + digitsOnly,
                }));
              }}*/
              inputProps={{
                name: "mobile",
                required: true,
                autoFocus: false,
                disabled: isLoading || otpSent,
              }}
              inputClass={`${styles.phoneInput} ${
                errors.mobile ? styles.errorInput : ""
              }`}
              containerStyle={{ width: "100%" }}
              /*enableAreaCodes={true}
              enableSearch={true}
              disableCountryCode={false}
              disableDropdown={false}
              autoFormat={true}*/
            />
            {errors.mobile && (
              <span className={styles.errorText}>{errors.mobile}</span>
            )}
          </div>
        }

JavaScript onload and onscroll nuance

Apologies if this has been asked, I couldn’t find it.

I have a JavaScript function that changes a DIV element’s .innerhtml onload. It basically gets JSON and iterates to build the page, and it works great. I also want to run another function after it that creates the player instance of a video player for each video tag that was created during the first onload function that changes .innerhtml.

When the function that finds video tags is run on onload, it doesn’t find any video tags. If I run it on onscroll then it works as expected.

If the document’s HTML is modified on onload, is there like some sort of internal function I need to run to update it or something that is happening either after onload is successful or before onscroll starts?

I’ve also tried putting my .innerhtml update function to be called by the DOMContentLoaded event, and the video tag function in onload, and I get the same results – which is that it doesn’t find any video tags, but again moving the video tags function to onscroll works just fine.

How to fetch databse in javascript

let usernames = document.querySelector("#usernames");
let clearsave = document.querySelector("#clearSaved");
let remembered = document.querySelector("#remembered");
let arr = JSON.parse(localStorage.getItem("user")) || []

fetch("https://jsonplaceholder.typicode.com/users")
.then(res=>res.json())
.then(users=>{
users.forEach(element => {

    let h1 = document.createElement("h1")
    h1.innerText = element.username
    usernames.appendChild(h1)

    let btn = document.createElement("button")
    btn.innerText = "Remember"
    h1.appendChild(btn)
    btn.addEventListener("click",function(){
        if (arr.includes(element.email)){
            alert("Email is remembered already")
            return
    }
    else{
       let character = document.createElement("li")
       character.innerText = element.email
       remembered.appendChild(character)
       arr.push(element.email)
       localStorage.setItem("user",JSON.stringify(arr))
       }
    })
});
});

   clearsave.addEventListener("click",function(){
localStorage.clear()`enter code here`
remembered.innerHTML = null
remembered.innerHTML = "Remembered: "
arr = []
    })

what is incorrect? I want you guys to review it and suggest any more efficient solution if possible. Also how can I minimze the code, like do the same in the most short code possible.

Add code snippet to textarea inside Jinja2 For-Loop

I’m trying to add a button “add code snippet” inside textarea. I have a working example in this picture 1.

textarea with button to add code snippets

I’, trying to apply the same code but this time the textarea is inside a modal and it’s generated by jinja2 for loop automatically.
It generates an edit form for all posts. In this case I added the specific ‘Id’ into buttons and textareas so when I click ‘add code’ open the correct modal with the correct textarea id to be edited, like this:

<a id="add_code-{{post.Id}}" href="#" class="fp-btn-posts fp-btn-post-view">
    Add Code
</a>  

 <textarea id="text-{{post.Id}}" type="text" name="question" required></textarea> 

The form it’s working. If the user type text or manually add code snippet like “`code here´´´, will save ok.

The problem is that I want to make the ‘add code’ button to work but when I click I get this error from my JS:

const startPosition = textarea.selectionStart; #null is not an object (evaluating ‘textarea.selectionStart’)

And I don’t understand why, since the other form works, I thought since this textarea (since is in a for loop) has unique Id would work fine.

Here is JS code to add code snippet block when the user click add code:

// script to add code snippet into textboxes
    var textarea = document.getElementById('text-{{post.Id}}');
    var insertCode = document.getElementById('add_code-{{post.Id}}');

    insertCode.addEventListener('click', () => {
        const startPosition = textarea.selectionStart;
        const endPosition = textarea.selectionEnd;
        const codeBlock = "n```'code here'´´´n";

        console.log(startPosition);

        const newValue = textarea.value.substring(0, startPosition) +
                    codeBlock + textarea.value.substring(endPosition);
        textarea.value = newValue;

        // Optional:  Place the cursor after the inserted code block
        textarea.selectionStart = startPosition + codeBlock.length;
        textarea.selectionEnd = startPosition + codeBlock.length;
        textarea.focus();
    });

I’m getting error in the line 6 this code (const startPosition = textarea.selectionStart;)

I’m new in front-end so if someone can give a help on that I appreciate.
Thanks in advance.

Change Video on Scroll

I have taken the source code from this codepen page. It changes the image on scroll, but instead of image, I want to change the video on scroll.

Here is the code I have so far. The first video plays but it won’t change on scroll. I added id=”mainImage” in the Video Source tag, so it should work?
What am I doing wrong?

CSS

<style>
.mouse_scroll {
  overflow: visible;
  position: relative;
}
.side_sec {
  display: flex;
}
.img-links div.text-sec {
  text-align: left;
  padding: 10px 20px;
  border-radius: 10px;
  margin: 500px 0px;
  background: transparent;
}
.img-links div.active {
  border: none;
  background: var(--box-bg-color);
}
.featured-img::before {
  content: "";
  position: absolute;
  background-size: 60%;
  background-repeat: no-repeat;
  background-position: left;
  left: 15%;
  right: 0px;
  top: -50px;
  width: 100%;
  height: 100%;
  z-index: 1;
  margin-right: 0px;
}
.featured-img {
  position: sticky;
  top: 140px;
  height: 700px;
}
.featured-img img {
  position: absolute;
  left: 75px;
  top: 35px;
  width: 55%;
  height: auto;
  border-radius: 30px;
}
.side-text {
  display: flex;
}
.side-text h3{
  color:orange;
}
.side-text p{
  color:#000;
}
</style>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"  crossorigin="anonymous"></script>
<section id="videoonscroll">
  <div class="container">
    <div class="row">
      <div class="side-text">
        <div class="col-xl-7 col-lg-7 col-md-12 col-sm-12 col-12">
          <div class="img-links">
            <div data-src="video1.mp4" class="text-sec active">
              <h3 class="margin-10px-bottom">Better writing,<br>better results</h3>
              <p class="text-16 width-90">Be perfectly professional, clear, and convincing in a few clicks, not a few hours.</p>
            </div>
            <div class="text-sec" data-src="video2.mp4">
              <h3 class="margin-10px-bottom">The right text<br>for the context</h3>
              <p class="text-16 width-90">Get personalized suggestions based on what you’re writing and who will read it.</p>
            </div>
            <div class="text-sec" data-src="video3.mp4">
              <h3 class="margin-10px-bottom">Works where<br>you work</h3>
              <p class="text-16 width-90">Grammarly works across all the apps and sites you use. No copying, no pasting, no context switching.</p>
            </div>
            <div class="text-sec" data-src="video4.mp4">
              <h3 class="text-25 font-weight-700 margin-10px-bottom">Never go out of style</h3>
              <p class="text-16 width-90">Grammarly understands both your personal style and your brand style guide to help you find your voice.</p>
            </div>
            <div class="text-sec" data-src="video5.mp4">
              <h3 class="text-25 font-weight-700 margin-10px-bottom">This is responsible AI</h3>
              <p class="text-16 width-90">Don’t compromise on security. We never sell your data, provide it for advertising purposes, or allow third parties to use it to train their models.</p>
            </div>
            <!-- Add more divs as needed -->
          </div>
        </div>
        <div class="col-xl-5 col-lg-5 col-md-12 col-sm-12 col-12 sm-display-none">
          <div class="featured-img">
            <video class="video-player_video__8GR5s" autoplay="" loop="" muted="" playsinline="" poster="video1.png">
              <source src="video1.mp4" type="video/mp4" data-component-name="default-render-source" id="mainImage" />
            </video>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js'></script>

Javascript

<script id="rendered-js" >

  $(document).ready(function () {
    // Function to handle mouse wheel events
    function handleMouseWheel(ele) {
      // Your logic here
      console.log("Mouse wheel event detected!");
      $('.img-links div.text-sec').removeClass('active');
      $(ele).addClass('active');
      // Update the main image based on the active div
      var newImageSrc = $(ele).data('src');
      $('#mainImage').attr('src', newImageSrc);
    }
    const options = { threshold: 0.4 };
    // Set up Intersection Observer
    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // Add scroll event listener when target element is visible
          $(window).on('wheel', () => handleMouseWheel(entry.target));
        } else {
          // Remove scroll event listener when target element is not visible
          $(window).off('wheel', () => handleMouseWheel(entry.target));
        }
      });
    }, options);
    // Define the target elements you want to observe
    const targetElements = $('.img-links div.text-sec');
    // Start observing each target element
    targetElements.each(function () {observer.observe(this);});
  });
</script>

How to I properly spread a spritesheet in JavaScript

I’m trying to create a mini RPG game and have been following instructions, I got my spritesheet from my gaia profile and images 4-8 are looped with leg movement under the avatar with out feet and the default avatar. The idle one is with shoes, index 5. I’m new to this but I’ve tried adjusting the dimensions of practically everything. Here’s a link to my spritesheet https://ibb.co/HDVVPcYT

<script>
  const canvas = document.getElementById('avatar-canvas');
  const ctx = canvas.getContext('2d');

  const sprite = new Image();
  sprite.src = 'my_avi.png';

  const spriteWidth = 60;
  const spriteHeight = 160;

  const directions = {
    down: 0,
    left: 1,
    right: 2,
    up: 3
  };

  let legFrames = [4, 5, 6, 7]; // footstep animation frames
  let legFrame = 0;

  let frame = 1; // Default idle frame is middle
  let direction = directions.down;

  let x = canvas.width / 2 - spriteWidth / 2;
  let y = canvas.height - spriteHeight - 20;


  let tick = 0;
  let moving = false;
  const keys = {};

  // Draw avatar
  function drawAvatar() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    const col = moving ? legFrames[legFrame % legFrames.length] : frame;
    const row = direction;

    ctx.drawImage(
      sprite,
      col * spriteWidth, row * spriteHeight,
      spriteWidth, spriteHeight,
      x, y,
      spriteWidth, spriteHeight
    );
  }

  // Animation loop
  function update() {
    tick++;

    if (moving && tick % 10 === 0) {
      legFrame++;
    }

    handleMovement();
    drawAvatar();
    requestAnimationFrame(update);
  }

  // Key listeners
  window.addEventListener('keydown', e => {
    keys[e.key.toLowerCase()] = true;
    moving = true;
  });

  window.addEventListener('keyup', e => {
    keys[e.key.toLowerCase()] = false;

    if (!keys['arrowup'] && !keys['arrowdown'] && !keys['arrowleft'] && !keys['arrowright']
        && !keys['w'] && !keys['a'] && !keys['s'] && !keys['d']) {
      moving = false;
      legFrame = 0;
      frame = 4; // Reset to idle frame
    }
  });

// Define world and TV boundary limits
const tvBlockHeight = canvas.height * 0.4; // Approx. top 40% blocked
const padding = 10; // Optional padding from edge

function handleMovement() {
  let nextX = x;
  let nextY = y;

  if (keys['w'] || keys['arrowup']) {
    nextY -= 2;
    direction = directions.up;
  } else if (keys['s'] || keys['arrowdown']) {
    nextY += 2;
    direction = directions.down;
  }

  if (keys['a'] || keys['arrowleft']) {
    nextX -= 2;
    direction = directions.left;
  } else if (keys['d'] || keys['arrowright']) {
    nextX += 2;
    direction = directions.right;
  }

  // World boundary collisions
  const maxX = canvas.width - spriteWidth - padding;
  const maxY = canvas.height - spriteHeight - padding;

  if (nextX >= padding && nextX <= maxX) {
    x = nextX;
  }
  if (nextY >= padding && nextY <= maxY) {
    // TV collision rule
    if (nextY > tvBlockHeight) {
      y = nextY;
    }
  }
}


</script>
<script>
let animationStarted = false;

sprite.onload = () => {
  drawAvatar();
  if (!animationStarted) {
    update();
    animationStarted = true;
  }
};
</script>
<body>
  <div id="container">
    <div id="chat-panel">
      <div id="chat-messages"></div>
      <div class="chat-input">
        <div class="user-icon" style="background-image: url('user-ico/icon_1.jpg');"></div>
        <input type="text" id="chat-input" placeholder="Type a message..." />
      </div>
    </div>

    <div id="canvas-area">
      <canvas id="avatar-canvas" width="800" height="600" style="position:absolute; z-index:100;"></canvas>
      <img id="lounge-canvas" src="video-L.png" alt="Lounge Background" />
      <iframe width="560" height="315" src="https://www.youtube.com/embed/xolgs4-mymQ?si=KgabkxW6n-67L0oM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
    </div>

    <div id="playlist-panel">
      <div class="playlist-item" onclick="loadVideo('43gm3CJePn0')">MCR - Welcome to the Black Parade</div>
      <div class="playlist-item" onclick="loadVideo('zP2rjMx9stw')">Pierce The Veil - King For A Day</div>
      <div class="playlist-item" onclick="loadVideo('K0ibBPhiaG0')">BMTH - Can You Feel My Heart</div>
    </div>
  </div>