Update content in setDragImage when I drag to specific content

Right now I am learning with drag and drop event. And there is a case that I need to update the text in drag image when I drag to specific div

export const DndPage = () => {
  const handleOnDrag = useCallback((event) => {
    const ghost = document.getElementById('ghost');
    event.dataTransfer.effectAllowed = 'copy';
    event.dataTransfer.setDragImage(ghost, 0, 0);
    event.dataTransfer.setData('text', 'text');
  }, []);

  const [text, setText] = useState('');
  const [dragText, setDragText] = useState('');

  const renderItemToDrag = useCallback(
    (text: string) => {
      return (
        <>
          <div
            id='drag-zone'
            draggable
            onDragStart={handleOnDrag}
            style={{
              border: '1px solid black',
              padding: '10px',
              width: '50%',
              marginBottom: '10px',
            }}
          >
            <span>{text}</span>
          </div>
          <div
            id='ghost'
            style={{
              position: 'absolute',
              top: '-1000px',
              width: '100px',
              height: '30px',
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
              textAlign: 'center',
            }}
          >
            Copy to {dragText}
          </div>
        </>
      );
    },
    [dragText]
  );

  return (
    <>
      {renderItemToDrag('this is a draggable')}

      <div
        id='drop-zone'
        onDrop={(e) => {
          const data = e.dataTransfer.getData('text');
          setText(data);
        }}
        onDragOver={(e) => {
          e.dataTransfer.dropEffect = 'none';
          if ((e.target as HTMLElement).className === 'infomation') {
            e.dataTransfer.dropEffect = 'copy';
            setDragText('infomation');
          }
          e.preventDefault();
        }}
        style={{
          display: 'flex',
          flexDirection: 'column',
          gap: '10px',
          width: '400px',
        }}
      >
        <h3 className='infomation'>Drop area</h3>
        <span
          style={{ border: '1px solid black', padding: '10px', width: '200px' }}
        >
          {text}
        </span>
      </div>
    </>
  );
};

As you can see, my function onDragOver get innerText of element and setDragText(value) to display on my ghost drag image (id="ghost").
But it does not update? Can somebody explain why? I try to use ref but it does not work neither

TypeError: Cannot read properties of undefined (reading ‘Link’)

I am working on a piece of code on Nextjs that create a unique id if a user has their profile info in their browser localstorage and redirect them to a link. I have been getting this error message for a while now and i have tried everything, i even asked chatgpt but got nothing. Can anyone help me out?

This is the page.tsx code from create-profile folder:

"use client";

import Setup from "@/components/Setup";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import createUniqueLinkId from "@/libs/createlink";

const Page = () => {
  const router = useRouter();

  interface Profile {
    userName: string;
    avatar: number;
  }

  useEffect(() => {
    const profileFromLocalStorage = localStorage.getItem("profile");
    const profile: Profile | null = profileFromLocalStorage
      ? (JSON.parse(profileFromLocalStorage) as Profile)
      : null;

    const redirectOrSetup = async () => {
      if (profile) {
        try {
          const chatRoomLink = await createUniqueLinkId();
          router.push(`/chatroom/${chatRoomLink}`);
        } catch (error) {
          console.error("Failed to create chat room link", error);
        }
      }
    };

    redirectOrSetup();
  }, []);


  return (
    <div className="lg:mt-32 mt-8">
      <Setup />
    </div>
  );
};

export default Page;

Here is the createlink.ts code:

import { v4 as uuidv4 } from "uuid";
import Link from "@/model/link";
import mongoose from "mongoose";
import { connectToDB } from "@/utils/database"; 

export const createUniqueLinkId = async (): Promise<string> => {
  let linkId = "";
  let isUnique = false;

  try {
    await connectToDB();
    console.log(Link)

    while (!isUnique) {
      linkId = uuidv4();
      
      const existingLink = await Link.findOne({ linkId });

      if (!existingLink) {
        isUnique = true;
      }
    }

    const newLink = new Link({ linkId });
    await newLink.save();

    return linkId;
  } catch (error) {
    console.error("Error creating unique link ID:", error);
    throw new Error("Failed to create a unique link ID");
  }
};

export default createUniqueLinkId;

And finally , here is the link model code:

import mongoose, { Document, Model, Schema } from 'mongoose';

export interface ILink extends Document {
  linkId: string,
  participants: string[];
  createdAt: Date;
}

const linkSchema = new Schema<ILink>({
  linkId:{
    type: String,
    required: true,
    unique: true,
  },
  participants: {
    type: [String],
    default: [],
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
}, { timestamps: true });

const Link: Model<ILink> = mongoose.models.Link || mongoose.model<ILink>('Link', linkSchema);

export default Link;

I would really love to hear from everyone.

I tried to adjust the mongodb connection code, although when i send other api requests, it works perfectly but im confused now, here is the code, if it might help:

"use server"
import mongoose from "mongoose";

let isConnected: boolean = false;

// Connect to MongoDB
export const connectToDB = async (): Promise<void> => {
     
    if (isConnected) {
        console.log('MongoDB is already connected');
        return;  
    }

    try {
        await mongoose.connect(process.env.MONGODB_URI as string, {
            dbName: "anonymous_project"
        });

        isConnected = true;
        console.log('MongoDB connection is successful');
    } catch (error) {
        if (error instanceof Error) {
            throw new Error(`MongoDB connection failed: ${error.message}`);
        } else {
            throw new Error('MongoDB connection failed with an unknown error');
        }
    }
};

Material-UI DateCalendar Closes When Interacting with Year/Month Dropdowns in React

I am working on a React component that uses Material-UI’s DateCalendar. The component includes an input field that, when clicked, opens a date picker. The date picker should remain open while the user interacts with it, and when user click somewhere outside the datepicker, the datepicker should be closed.

My current solution is from How can I prevent onBlur event from firing?. It works fine in most cases. When user interacts with most of the area of datepicker, the datepicker remains open, when user click outside of the datepicker, the datepicker closed.

However, I am encountering an issue where the date picker closes when the user interacts with the year or month dropdown menus.

My code:

import { DateCalendar, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { useState } from "react";

function App() {
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    setOpen(true);
  };

  const handleBlur = () => {
    setOpen(false);
  };

  const handleMouseDown = (event) => {
    event.preventDefault();
  };

  return (
    <>
      <input id="dateInput" onClick={handleClick} onBlur={handleBlur} />
      {open ? (
        <LocalizationProvider dateAdapter={AdapterDayjs}>
          <DateCalendar onMouseDown={handleMouseDown} />
        </LocalizationProvider>
      ) : null}
    </>
  );
}

export default App;

What I expected:

The date picker should remain open while the user interacts with it, and when user click somewhere outside the datepicker, the datepicker should be closed.

Apply style to separate span elements in Rich Text Editor

I recently decided to make my own Rich Text Editor, I was generally satisfied with the version via execCommand, but I wanted to take on my own implementation because of the work with.

Here is the code of my currently working version:

import React, { useRef } from "react";
import "./TextEditor_v2.css";

const ModernTextEditor = () => {
  const editorRef = useRef();

  const applyStyle = (style, value) => {
    const selection = window.getSelection();
    if (!selection.rangeCount) return;

    const range = selection.getRangeAt(0);
    if (range.collapsed) return;

    const selectedContent = range.cloneContents();
    const fragment = document.createDocumentFragment();

    const mergeStyles = (existingStyles, newStyles) => {
      const tempSpan = document.createElement("span");
      tempSpan.style.cssText = existingStyles;

      if (style === "bold")
        tempSpan.style.fontWeight = value ? "bold" : "normal";
      if (style === "italic")
        tempSpan.style.fontStyle = value ? "italic" : "normal";
      if (style === "color") tempSpan.style.color = value;
      if (style === "fontSize") tempSpan.style.fontSize = value + "px";
      if (style === "fontFamily") tempSpan.style.fontFamily = value;

      return tempSpan.style.cssText;
    };

    const processNode = (node, parentStyles = "") => {
      if (node.nodeType === Node.TEXT_NODE) {
        const span = document.createElement("span");
        span.style.cssText = mergeStyles(parentStyles, style);
        span.textContent = node.textContent;
        return span;
      } else if (node.nodeType === Node.ELEMENT_NODE) {
        const combinedStyles = mergeStyles(node.style.cssText, style);
        const span = document.createElement("span");
        span.style.cssText = combinedStyles;

        Array.from(node.childNodes).forEach((child) => {
          span.appendChild(processNode(child, combinedStyles));
        });

        return span;
      }

      return node;
    };

    Array.from(selectedContent.childNodes).forEach((node) => {
      fragment.appendChild(processNode(node));
    });

    range.deleteContents();
    range.insertNode(fragment);

    selection.removeAllRanges();
  };

  const handleCommand = (command, value = null) => {
    applyStyle(command, value);
  };

  return (
    <div className="text-editor">
      <div className="toolbar">
        <button onClick={() => handleCommand("bold", true)}>Bold</button>
        <button onClick={() => handleCommand("italic", true)}>Italic</button>
        <input
          type="color"
          onChange={(e) => handleCommand("color", e.target.value)}
        />
        <select
          onChange={(e) => handleCommand("fontSize", e.target.value)}
          defaultValue="16"
        >
          {[12, 14, 16, 18, 20, 24, 28].map((size) => (
            <option key={size} value={size}>
              {size}px
            </option>
          ))}
        </select>
        <select
          onChange={(e) => handleCommand("fontFamily", e.target.value)}
          defaultValue="Arial"
        >
          {["Arial", "Georgia", "Courier New", "Verdana"].map((font) => (
            <option key={font} value={font}>
              {font}
            </option>
          ))}
        </select>
      </div>
      <div
        ref={editorRef}
        className="editor"
        contentEditable={true}
        suppressContentEditableWarning={true}
      ></div>
    </div>
  );
};

export default ModernTextEditor;

And styles:

`.text-editor {
  border: 1px solid #ccc;
  border-radius: 5px;
  width: 80%;
  margin: 20px auto;
  padding: 10px;
}

.toolbar {
  display: flex;
  gap: 10px;
  margin-bottom: 10px;
}

.editor {
  min-height: 200px;
  border: 1px solid #eee;
  padding: 10px;
  font-family: Arial, sans-serif;
  font-size: 16px;
  outline: none;
  cursor: text;
}`

At the moment, everything works through a div with contentEditable that can be filled with text, and when I apply some styles, the text is wrapped in a span with the necessary styles. I really didn’t like the fact that in the end I came up with a solution in which applying a new style creates a new span in which the old one is wrapped. I tried to do something about it and wrote another version of the method:

  const applyStyle = (style, value) => {
    const selection = window.getSelection();
    if (!selection.rangeCount) return;

    const range = selection.getRangeAt(0);
    if (range.collapsed) return; // No text selected

    const selectedText = range.extractContents();
    const parentNode = range.startContainer.parentNode;

    const processNode = (node) => {
      if (node.nodeType === Node.TEXT_NODE) {
        // Create a span to wrap this text node with the new style
        const span = document.createElement("span");

        // Preserve existing styles
        if (parentNode.nodeName === "SPAN") {
          span.style.cssText = parentNode.style.cssText;
        }

        // Apply the new style
        if (style === "bold") span.style.fontWeight = value ? "bold" : "normal";
        if (style === "italic")
          span.style.fontStyle = value ? "italic" : "normal";
        if (style === "color") span.style.color = value;
        if (style === "fontSize") span.style.fontSize = value + "px";
        if (style === "fontFamily") span.style.fontFamily = value;

        span.textContent = node.textContent;
        return span;
      } else if (node.nodeName === "SPAN") {
        // If the node is a span, recursively process its children
        const newSpan = document.createElement("span");
        newSpan.style.cssText = node.style.cssText;

        // Apply the new style to this span
        if (style === "bold")
          newSpan.style.fontWeight = value ? "bold" : "normal";
        if (style === "italic")
          newSpan.style.fontStyle = value ? "italic" : "normal";
        if (style === "color") newSpan.style.color = value;
        if (style === "fontSize") newSpan.style.fontSize = value + "px";
        if (style === "fontFamily") newSpan.style.fontFamily = value;

        Array.from(node.childNodes).forEach((child) => {
          newSpan.appendChild(processNode(child));
        });
        return newSpan;
      }
      return node;
    };

    const newNodes = [];
    Array.from(selectedText.childNodes).forEach((node) => {
      newNodes.push(processNode(node));
    });

    // Split existing span into 3 parts if selection is in the middle
    const startOffset = range.startOffset;
    const endOffset = range.endOffset;

    if (parentNode.nodeName === "SPAN") {
      const textContent = parentNode.textContent;

      const beforeText = textContent.slice(0, startOffset);
      const afterText = textContent.slice(endOffset);

      if (beforeText) {
        const beforeSpan = document.createElement("span");
        beforeSpan.style.cssText = parentNode.style.cssText;
        beforeSpan.textContent = beforeText;
        parentNode.parentNode.insertBefore(beforeSpan, parentNode);
      }

      if (afterText) {
        const afterSpan = document.createElement("span");
        afterSpan.style.cssText = parentNode.style.cssText;
        afterSpan.textContent = afterText;
        parentNode.parentNode.insertBefore(afterSpan, parentNode.nextSibling);
      }

      parentNode.parentNode.removeChild(parentNode);
    }

    // Insert the new styled nodes
    newNodes.forEach((node) => {
      range.insertNode(node);
    });

    // Clean up selection
    selection.removeAllRanges();
  };

This version of the method provides many cases and is able to split span elements into separate ones if I wanted to apply a style only to part of the text, but there is a problem that I cannot make a mechanism for applying styles to several selected span elements at the same time, for example, write 2 separate words and give each of them styles and then try to change the color of both of them by selecting them at the same time, it just turns into a mess.

Bootstrap 5 Dropdown Arrow displays incorrectly when the page is refreshed or accessed for the first time

I have a Bootstrap 5 dropdown menu using Vue.js and Nuxt.js :


<div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
              <ul class="navbar-nav mb-2 mb-lg-0">
                <li class="nav-item">
                  <a class="nav-link" aria-current="page" href="/">Home</a>
                </li>

                <!--Dropdown-->
                <li class="nav-item dropdown">
                  <a
                    class="nav-link "
                    href="#"
                    role="button"
                    data-bs-toggle="dropdown"
                    aria-expanded="false"
                  >
                    Dropdown1
                    <font-awesome-icon :icon="['fas', 'sort-down']" size="xs" class="move-up" />

                  </a>
                  <ul class="dropdown-menu">                
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                    <li><hr class="dropdown-divider" /></li>
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                    <li><hr class="dropdown-divider" /></li>
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                  </ul>
                </li>
                <!--Dropdown-->

                <!--Dropdown-->
                <li class="nav-item dropdown">
                  <a
                    class="nav-link "
                    href="#"
                    role="button"
                    data-bs-toggle="dropdown"
                    aria-expanded="false"
                  >
                    Dropdown2
                    <font-awesome-icon :icon="['fas', 'sort-down']" size="xs" class="move-up" />

                  </a>
                  <ul class="dropdown-menu">                
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                    <li><hr class="dropdown-divider" /></li>
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                    <li><hr class="dropdown-divider" /></li>
                    <li><a class="dropdown-item" href="#">Dropdown-items</a></li>
                  </ul>
                </li>
                <!--Dropdown-->
                          

              </ul>

            </div>

For the Bootstrap Nav Bar dropdown menu, it will display a downward arrow when the page is refreshed or accessed for the first time. After a while, the arrow will be gone.

This arrow will also cause my dropdown-toggle to shift upwards when the arrows displayed.

Navbar capture 1:

refreshed or accessed for the first time

Navbar capture 2:

after a while

I tried to hide the arrow using CSS like this, but it did not work:

.navbar .dropdown-toggle::after { 
    display: none; 
}

I found that if I add “<ClientOnly>” for the nav bar, it does not have this problem.
However, it is not a good solution to render content exclusively on the client side, as it makes the nav bar display slower than the content on the pages.

Thanks for help.

React bundle size increased by more than the file that it included

require(`./path/to/large_file`)

Without this file being required, the React app build bundle resulted in 36 MB.

With this file included, it resulted in 80 MB.

However, the size of the file itself, ./path/to/large_file, is only 21 MB, via this linux command:

du -sh ./path/to/large_file

21 + 36 = 57

80 – 57 = 23

Can someone explain what accounts for this additional 23 MB in size to the bundle?

TypeScript error when using registerRichText with Lexical Editor: “Excessive stack depth comparing types ‘LexicalEditor’ and ‘LexicalEditor'”

I’m working on implementing a rich text editor using the Lexical library with vanilla JavaScript and TypeScript. My understanding is that the editor should be attached to a contenteditable div, and the registerRichText function should enable rich text features like bold, italic, and underline.

I’m encapsulating the editor setup in a class. Here’s the code:

import { registerRichText } from '@lexical/rich-text';
import { createEditor } from 'lexical';

export class CustomLexicalEditor {
  private config = {
    namespace: 'MyEditor',
    theme: {
      text: {
        bold: 'text-bold',
        italic: 'text-italic',
        underline: 'text-underline',
      },
    },
    onError: console.error,
  };

  initialize(
    editorContainer: HTMLElement,
    toolbarContainer: HTMLElement,
    placeholder?: string,
  ): void {
    const editorPlaceholder = placeholder || 'Start typing...';

    const editor: LexicalEditor = createEditor(this.config);
    editor.setRootElement(editorContainer);

    registerRichText(editor);
  }
}

I can successfully create the editor and set the root element, but when I call registerRichText with the editor instance, I get the following TypeScript error:

Excessive stack depth comparing types 'LexicalEditor' and 'LexicalEditor'.ts(2321)

Here’s the relevant HTML for the editorContainer:

<div class="custom-text-editor__box" contenteditable></div>

I’ve been stuck on this issue for quite some time. Does anyone know why this type mismatch occurs and how to resolve it? Any help would be greatly appreciated!

It seems like the LexicalEditor instance I created doesn’t match the type expected by registerRichText. I tried casting the editor to LexicalEditor, but the problem persists.

How to Implement Server-Sided Seeking in Shaka Player for Seamless Playback

I’m developing a custom video player interface using Shaka Player for adaptive streaming. The unique requirement is to handle server-sided seeking, where any seek action by the user triggers an API call to the server. The server processes this request and ensures that Shaka Player always requests segment 0 upon seeking. The server maps segment 0 to the desired playback position internally.

The server will set the video to playback at the given timestamp sucessfully when requested but the player must start with segment 0 just like how the video player first loads the video.

Objectives:

  1. Trigger API on Seek: When a user seeks to a new timestamp, an API call is made to inform the server of the desired position.
  2. Reload Video at Segment 0: After the server processes the seek, Shaka Player should reload the video starting from segment 0.
  3. Maintain Full Duration Display: The player’s seek bar should reflect the full duration of the video, not just the buffered segments.
  4. Seamless User Experience: The seek operation should feel smooth, without noticeable delays or playback interruptions.
  5. Update Seek Bar Position: After seeking, the seek bar should accurately represent the new playback position.

Issues encountered:

  1. When seeking, it makes the api request then followed by successfully getting segment 0 for video and audio but the problem is Shaka makes the request of segment something-hundred which fails as it doesn’t exist.
  2. I tried other ways and another issue I come across is the seek bar resets to the start and the duration of the video is the remainder of the video.

How can I effectively implement server-sided seeking in Shaka Player with a custom UI, ensuring that:

Any seek action triggers an API call to the server.
Shaka Player reloads the video starting from segment 0.
The seek bar displays the full duration and accurately reflects the new playback position.
The entire process is seamless, without noticeable delays (excluding the small time for get requests) or playback interruptions.
class DashSegmentHelper {
    constructor(manifestXml) {
        this.manifestXml = manifestXml;
        this.manifest = this.parseManifest(manifestXml);
        this.segmentInfo = this.extractSegmentInfo();
        this.duration = this.parseDuration(this.manifest.mediaPresentationDuration);
        this.baseUrl = this.extractBaseUrl();
        
        if (DashSegmentHelper.initialTotalDuration === undefined) {
            DashSegmentHelper.initialTotalDuration = this.duration;
        }
        this.totalDuration = DashSegmentHelper.initialTotalDuration;
    }

    extractBaseUrl() {
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(this.manifestXml, "text/xml");
        const baseUrl = xmlDoc.querySelector('BaseURL');
        return baseUrl ? baseUrl.textContent : '';
    }

    parseManifest(manifestXml) {
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(manifestXml, "text/xml");
        
        const mpd = xmlDoc.getElementsByTagName('MPD')[0];
        if (!mpd) throw new Error('No MPD element found');
        
        const period = xmlDoc.getElementsByTagName('Period')[0];
        if (!period) throw new Error('No Period element found');
        
        return {
            type: mpd.getAttribute('type'),
            minBufferTime: mpd.getAttribute('minBufferTime'),
            mediaPresentationDuration: mpd.getAttribute('mediaPresentationDuration'),
            periodStart: period.getAttribute('start'),
            periodDuration: period.getAttribute('duration')
        };
    }

    extractSegmentInfo() {
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(this.manifestXml, "text/xml");
        const segmentTemplates = xmlDoc.getElementsByTagName('SegmentTemplate');
        const videoTemplate = this.findVideoSegmentTemplate(segmentTemplates);
        
        if (!videoTemplate) throw new Error('No valid segment template found');

        return {
            timescale: parseInt(videoTemplate.getAttribute('timescale')),
            duration: parseInt(videoTemplate.getAttribute('duration')),
            startNumber: parseInt(videoTemplate.getAttribute('startNumber')) || 1,
            initializationTemplate: videoTemplate.getAttribute('initialization'),
            mediaTemplate: videoTemplate.getAttribute('media')
        };
    }

    findVideoSegmentTemplate(templates) {
        for (const template of templates) {
            const parent = template.parentElement;
            if (parent?.getAttribute('mimeType')?.includes('video')) {
                return template;
            }
        }
        return templates[0] || null;
    }

    parseDuration(isoDuration) {
        const regex = /PT(?:(d+)H)?(?:(d+)M)?(?:(d+)S)?/;
        const matches = isoDuration.match(regex);
        if (!matches) return 0;
        
        const [_, hours = 0, minutes = 0, seconds = 0] = matches;
        return parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds);
    }

    getBufferConfig() {
        return {
            minBufferTime: this.parseDuration(this.manifest.minBufferTime),
            segmentDuration: this.segmentInfo.duration / this.segmentInfo.timescale,
            totalDuration: this.totalDuration
        };
    }
}

DashSegmentHelper.initialTotalDuration = undefined;

class EnhancedVideoPlayer {
    constructor() {
        this.video = document.getElementById('videoPlayer');
        this.loadingIndicator = document.getElementById('loadingIndicator');
        this.loadingText = document.getElementById('loadingText');
        this.bufferInfo = document.getElementById('bufferInfo');
        this.debugInfo = document.getElementById('debugInfo');
        this.seekPreview = document.getElementById('seekPreview');
        
        this.player = null;
        this.segmentHelper = null;
        
        // Seeking and buffering state
        this.seekOperationInProgress = false;
        this.isSeekInProgress = false;
        this.lastSeekTime = 0;
        this.currentSegmentNumber = 0;
        this.isInitialSegmentLoaded = false;
        this.segmentsToBuffer = new Set();
        
        // Buffer monitoring
        this.lastBufferUpdate = 0;
        this.bufferingInProgress = false;
        this.currentBufferGoal = 30;
        this.bufferInterval = null;
        
        this.initialize();
    }

    async initialize() {
        try {
            shaka.polyfill.installAll();
            this.player = new shaka.Player(this.video);
            
            await this.setupNetworkFilters();
            this.configurePlayer();
            this.setupEventListeners();
            await this.loadVideo();
            this.startBufferMonitoring();
            
        } catch (error) {
            console.error('Player initialization error:', error);
            this.updateBufferInfo('Error: ' + error.message);
        }
    }

    setupNetworkFilters() {
        const networkingEngine = this.player.getNetworkingEngine();
        if (!networkingEngine) return;

        networkingEngine.registerRequestFilter((type, request) => {
            if (type === shaka.net.NetworkingEngine.RequestType.SEGMENT) {
                const url = new URL(request.uris[0]);
                request.originalUri = request.uris[0];
                
                const segmentMatch = url.toString().match(/segment[_-](d+)/i);
                if (segmentMatch && this.isSeekInProgress) {
                    const segmentNumber = parseInt(segmentMatch[1]);
                    
                    if (!this.isInitialSegmentLoaded) {
                        // First segment after seek - use segment-0
                        const newUrl = url.toString().replace(
                            /segment[_-]d+/i,
                            'segment-0'
                        );
                        url.href = newUrl;
                        url.searchParams.set('t', Math.floor(this.lastSeekTime));
                        this.isInitialSegmentLoaded = true;
                        this.currentSegmentNumber = 1;
                    } else {
                        // Subsequent segments - use incrementing numbers
                        const newUrl = url.toString().replace(
                            /segment[_-]d+/i,
                            `segment-${this.currentSegmentNumber}`
                        );
                        url.href = newUrl;
                        this.currentSegmentNumber++;
                    }
                    
                    request.uris[0] = url.toString();
                    console.log('Segment request:', {
                        original: request.originalUri,
                        modified: request.uris[0],
                        isInitial: !this.isInitialSegmentLoaded
                    });
                }
            }
        });

        networkingEngine.registerResponseFilter((type, response) => {
            if (type === shaka.net.NetworkingEngine.RequestType.SEGMENT) {
                if (response.request?.uris[0]) {
                    const segmentMatch = response.request.uris[0].match(/segment[_-](d+)/i);
                    if (segmentMatch) {
                        const segmentNumber = parseInt(segmentMatch[1]);
                        this.segmentsToBuffer.delete(segmentNumber);
                        
                        // End seek state when all required segments are buffered
                        if (this.isSeekInProgress && this.segmentsToBuffer.size === 0) {
                            this.isSeekInProgress = false;
                            this.isInitialSegmentLoaded = false;
                            console.log('All required segments buffered, ending seek state');
                        }
                    }
                }
            }
        });
    }

    configurePlayer() {
        this.player.configure({
            streaming: {
                bufferingGoal: this.currentBufferGoal,
                rebufferingGoal: 15,
                bufferBehind: 30,
                stallEnabled: false,
                stallSkip: 0.1,
                
                retryParameters: {
                    maxAttempts: 2,
                    baseDelay: 500,
                    backoffFactor: 1.5,
                    fuzzFactor: 0.5
                }
            },
            abr: {
                enabled: true,
                defaultBandwidthEstimate: 50000000,
                switchInterval: 8
            }
        });
    }

    setupEventListeners() {
        this.player.addEventListener('error', this.handlePlayerError.bind(this));
        this.player.addEventListener('buffering', this.handleBuffering.bind(this));
        this.player.addEventListener('variantchanged', () => this.updateDebugInfo());
        this.player.addEventListener('trackschanged', () => this.updateDebugInfo());

        this.video.addEventListener('seeking', this.handleSeeking.bind(this));
        this.video.addEventListener('seeked', this.handleSeeked.bind(this));
        this.video.addEventListener('waiting', this.handleWaiting.bind(this));
        this.video.addEventListener('playing', this.handlePlaying.bind(this));
        
        window.addEventListener('beforeunload', () => {
            if (this.segmentsToBuffer.size > 0) {
                this.segmentsToBuffer.clear();
                if (this.player.getNetworkingEngine()) {
                    this.player.getNetworkingEngine().clear();
                }
            }
        });
    }

    async handleSeeking() {
        if (this.seekOperationInProgress) return;
        
        this.seekOperationInProgress = true;
        this.isSeekInProgress = true;
        this.isInitialSegmentLoaded = false;
        this.lastSeekTime = this.video.currentTime;
        this.currentSegmentNumber = 0;
        
        // Reset segment tracking
        this.segmentsToBuffer.clear();
        
        // Calculate required segments for initial buffer
        const segmentDuration = this.segmentHelper?.getBufferConfig().segmentDuration || 2;
        const initialBufferSeconds = 30;
        const segmentsNeeded = Math.ceil(initialBufferSeconds / segmentDuration);
        
        for (let i = 0; i < segmentsNeeded; i++) {
            this.segmentsToBuffer.add(i);
        }

        this.loadingIndicator.style.display = 'block';
        this.loadingText.textContent = 'Seeking to ' + this.formatTime(this.lastSeekTime);

        try {
            let mpdUrl = new URL(window.DASH_MPD_LINK);
            mpdUrl.searchParams.set('t', Math.floor(this.lastSeekTime));

            await this.player.unload();
            
            const response = await fetch(mpdUrl.toString());
            const manifestXml = await response.text();
            this.segmentHelper = new DashSegmentHelper(manifestXml);

            await this.player.load(mpdUrl.toString(), this.lastSeekTime);
            
        } catch (error) {
            console.error('Seek error:', error);
            this.updateBufferInfo('Seek error: ' + error.message);
            this.seekOperationInProgress = false;
            this.isSeekInProgress = false;
        }
    }

    handleSeeked() {
        this.seekOperationInProgress = false;
        this.loadingIndicator.style.display = 'none';
        this.updateDebugInfo();
    }

    handlePlayerError(event) {
        console.error('Player error:', event.detail);
        this.updateBufferInfo('Error: ' + event.detail.message);
    }

    handleBuffering(event) {
        this.bufferingInProgress = event.buffering;
        this.loadingIndicator.style.display = event.buffering ? 'block' : 'none';
        if (event.buffering) {
            this.loadingText.textContent = 'Buffering...';
        }
    }

    handleWaiting() {
        if (!this.bufferingInProgress) {
            this.loadingIndicator.style.display = 'block';
            this.loadingText.textContent = 'Loading...';
        }
    }

    handlePlaying() {
        this.loadingIndicator.style.display = 'none';
    }

    async loadVideo(seekTime = 0) {
        try {
            let mpdUrl = new URL(window.DASH_MPD_LINK);
            if (seekTime > 0) {
                mpdUrl.searchParams.set('t', Math.floor(seekTime));
            }

            const response = await fetch(mpdUrl.toString());
            const manifestXml = await response.text();
            this.segmentHelper = new DashSegmentHelper(manifestXml);

            await this.player.load(mpdUrl.toString(), seekTime);

        } catch (error) {
            console.error('Error loading video:', error);
            this.updateBufferInfo('Failed to load video: ' + error.message);
            throw error;
        }
    }

    startBufferMonitoring() {
        this.bufferInterval = setInterval(() => {
            if (!this.seekOperationInProgress && !this.video.paused) {
                this.updateBufferInfo();
                this.updateDebugInfo();
            }
        }, 1000);

        this.video.addEventListener('ended', () => {
            if (this.bufferInterval) {
                clearInterval(this.bufferInterval);
            }
        });
    }

    updateBufferInfo() {
        if (!this.segmentHelper) return;

        const now = Date.now();
        if (now - this.lastBufferUpdate < 1000) return;
        this.lastBufferUpdate = now;

        const buffered = this.video.buffered;
        let bufferStatus = 'Buffered ranges:n';

        for (let i = 0; i < buffered.length; i++) {
            const start = Math.floor(buffered.start(i));
            const end = Math.floor(buffered.end(i));
            bufferStatus += `[${this.formatTime(start)} - ${this.formatTime(end)}] `;
        }

        const stats = this.player.getStats();
        bufferStatus += 'nBandwidth: ' + Math.round(stats.estimatedBandwidth / 1000000) + ' Mbps';
        bufferStatus += 'nBuffer Ahead: ' + this.getBufferAhead() + ' s';
        bufferStatus += 'nTotal Duration: ' + this.formatTime(this.segmentHelper.totalDuration);
        
        if (this.isSeekInProgress) {
            bufferStatus += 'nBuffering segments: ' + Array.from(this.segmentsToBuffer).join(', ');
        }

        this.bufferInfo.textContent = bufferStatus;
    }

    updateDebugInfo() {
        if (!this.segmentHelper) return;

        const stats = this.player.getStats();
        const track = this.player.getVariantTracks().find(t => t.active);
        const bufferConfig = this.segmentHelper.getBufferConfig();

        const debugHTML = [
            ['Current Quality', track ? `${track.width}x${track.height}` : 'N/A'],
            ['Buffered Ahead', `${this.getBufferAhead()} s`],
            ['Estimated Bandwidth', `${Math.round(stats.estimatedBandwidth / 1000000)} Mbps`],
            ['Buffer Health', `${Math.round(stats.bufferingHealth * 100)}%`],
            ['Dropped Frames', stats.droppedFrames],
            ['Segment Duration', `${bufferConfig.segmentDuration.toFixed(2)} s`],
            ['Total Duration', this.formatTime(this.segmentHelper.totalDuration)],
            ['Seek State', this.isSeekInProgress ? 'Seeking' : 'Normal'],
            ['Current Segment', this.currentSegmentNumber],
            ['Segments to Buffer', this.segmentsToBuffer.size]
        ].map(([key, value]) => `<div><strong>${key}:</strong> ${value}</div>`);

        this.debugInfo.innerHTML = debugHTML.join('');
    }

    getBufferAhead() {
        const currentTime = this.video.currentTime;
        const buffered = this.video.buffered;

        for (let i = 0; i < buffered.length; i++) {
            if (buffered.start(i) <= currentTime && currentTime <= buffered.end(i)) {
                return Math.floor(buffered.end(i) - currentTime);
            }
        }
        return 0;
    }

    formatTime(seconds) {
        const h = Math.floor(seconds / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);
        return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
    }
}

// Initialize player when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
    const player = new EnhancedVideoPlayer();

    // Add keyboard controls
    document.addEventListener('keydown', (event) => {
        const seekStep = 300; // 5 minutes in seconds
        if (event.ctrlKey) {
            if (event.key === 'ArrowRight') {
                const newTime = Math.min(
                    player.video.currentTime + seekStep,
                    player.video.duration
                );
                player.video.currentTime = newTime;
            } else if (event.key === 'ArrowLeft') {
                const newTime = Math.max(
                    player.video.currentTime - seekStep,
                    0
                );
                player.video.currentTime = newTime;
            }
        }
    });

    // Add progress bar interactions
    const progressBar = document.querySelector('.shaka-progress-container');
    if (progressBar) {
        progressBar.addEventListener('mousemove', (event) => {
            const rect = progressBar.getBoundingClientRect();
            const pos = (event.clientX - rect.left) / rect.width;
            const timeInSeconds = player.segmentHelper?.totalDuration * pos || 0;
            
            const preview = document.getElementById('seekPreview');
            if (preview) {
                preview.style.display = 'block';
                preview.style.left = `${event.clientX}px`;
                preview.textContent = player.formatTime(timeInSeconds);
            }
        });

        progressBar.addEventListener('mouseleave', () => {
            const preview = document.getElementById('seekPreview');
            if (preview) {
                preview.style.display = 'none';
            }
        });

        progressBar.addEventListener('click', (event) => {
            const rect = progressBar.getBoundingClientRect();
            const pos = (event.clientX - rect.left) / rect.width;
            const seekTime = player.segmentHelper?.totalDuration * pos || 0;
            player.video.currentTime = seekTime;
        });
    }
});

not able to reduce React app build bundle size

This line of code is expensive to run:

require(`./path/to/large_file`)

Without it, the React app build bundle resulted in 36 MB.

With it, it resulted in 92 MB.

The above line of code is run during initiation.

I tried to lazy load it by instead return this:

function lazy_loader() {
  return () => require(`./path/to/large_file`)
}

And during initiation, I the instead only runs:

lazy_loader();

lazy_loader() only returns a function, which will require the large_file if run.

However, this actually made no difference at all.

The build bundle size is still 92 MB.

Can someone explain why this didn’t help?

And if possible what I can do to fix this?

Multer in node.js not returning a readable upload into backend folders/files

I am currently using multer to submit a profile picture to the folder “pfps” for a school assignment, which should create a file that is the same as the front-end. However, it creates a file of random text characters with no .png or .jpg ending tag and cannot be read.

My code in the backend javascript currently is

const express = require("express");
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database(".database/user.db")
var fs = require('fs');
const cors = require("cors");
var bodyParser = require('body-parser');

const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'myPWA/pfps')
    },
    filenane: (req, file, cb) => {
        console.log(file);
        cb(null, Date.now() + path.extname(file.originalname))
    }
})

const upload = multer({ dest: "/pfps" });

const app = express();
app.use(express.static(path.join(__dirname, "myPWA")));

app.get("/", function (req, res) {
    res.sendFile(path.join(__dirname, "myPWA/index.html"));
});

app.use(cors());
app.use(express.json());

app.use(
    bodyParser.urlencoded({
    extended: true
    }),
    express.static(path.join(__dirname, "myPWA"))
)

app.post('/profile', upload.single('avatar'), function (req, res) {
    console.log(req.body);
    console.log(req.files);
})

and the html for the form is

<div class="page-content">
    <form action="/profile" method="post" enctype="multipart/form-data">
        <input type="file" name="avatar" id="avatar"/>
        <button type="submit">upload</button>
    </form>
</div>

Which should return some file in the backend files like
this
However, it returns a file like this.

Also, when I tried to log the file,

    console.log(req.body);
    console.log(req.files);

it returns

[Object: null prototype] {}
undefined

however I have looked online and it returns something like this

Does anybody have a solution??
Thanks in advance.

How do I check if the width of the image is > 80% and if it is, set width to 80% in JavaScript?

My HTML is this:

<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<title>Aerocello</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="stylesheet.css">
<body>
    <div class="gallery" *ngFor="let image of images; let i = index;">
        <img src="logo2.png" alt="logo" class="centre">
        <script>
            // if (what goes here?) {
                // and here
            // }
        </script>
    </div>
    <div class="idk">
        <p class="roboto">hi</p>
    </div>
</body>
</html>

and my CSS is this:

body {
    background-color: #AEAEAE;
    margin: 0;
    padding: 0;
    
    height: 100vh;
}

.gallery {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    position: relative;
    height: 100%;
}

.centre {
    width: auto;
    max-width: auto;
    height: 80%;
    max-height: 80%;
    display: block;
    margin-left: auto;
    margin-right: auto;
    
   overflow: auto;
   margin: auto;
   position: absolute;
   top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.roboto {
    font-family: roboto;
}

So, My image’s height is 80% and the width is set proportional to that but I want to check if width is over 80% and if it is set it to 80% and set the height to be proportional to the new width. How do I do that in JavaScript?

I didn’t try anything really, I didn’t know where to start?

Odoo CORS Access Issue

I’m having trouble executing a AJAX call to a controller that belongs to Odoo.sh, I’m testing from my localhost.

try {
const response = await fetch(`${BASE_URL}/check_available_venues`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        from_date: start,
                        to_date: end,
                        booking_individuals_total
                    })
                });
            // Log the full response for debugging
            console.log('Response status:', response.status);
            console.log('Response headers:', Object.fromEntries(response.headers.entries()));

            const responseText = await response.text(); // Get text before potentially parsing as JSON
            console.log('Response body:', responseText);

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}, body: ${responseText}`);
            }

            const data = JSON.parse(responseText);

            // Rest of your existing code...
        } catch (error) {
            console.error('Venue check error details:', {
                message: error.message,
                stack: error.stack
            });
            this._toastMessage(`Failed to check available venues: ${error.message}`, 'error');
            noNextStep = true;
        }

This is the controller I’m trying to access:

@http.route("/check_available_venues", type='json', auth="none", methods=['POST', 'GET'], website=True, csrf=False,
            cors='*')
def check_available_venues(self, from_date, to_date, booking_individuals_total):

While this is the error I’m getting:

enter image description here

Javascript / AJAX code for AI quiz generator

I am trying to see the Javascript / AJAX which gets triggered

In the web page =>

https://www.heuristi.ca/tools/free-ai-quiz-generator

when we select Tab Website, paste a link and press the button =>

Generate Quiz from Input

Is it possible to customize this website to our own needs?

Which function script should I call when user presses the button ( Generate Quiz from Input ?

Can you please help?

I have already tried Chrome debugging, but did not reach anywhere

Thanks

BookFans

Can any one assist with the following errors please

Launching libmain.dart on SM A336E in debug mode…

√ Built buildappoutputsflutter-apkapp-debug.apk

E/AndroidRuntime(12756): java.lang.RuntimeException: Unable to instantiate application com.wetoucart.delivery.MyApplication package com.wetoucart.delivery: java.lang.ClassNotFoundException: Didn’t find class “com.wetoucart.delivery.MyApplication” on path: DexPathList[[zip file “/data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/base.apk”],nativeLibraryDirectories=[/data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/lib/arm64, /data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/base.apk!/lib/arm64-v8a, /system/lib64]]

E/AndroidRuntime(12756): at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1555)

E/AndroidRuntime(12756): at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1484)

E/AndroidRuntime(12756): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7651)

E/AndroidRuntime(12756): at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)

E/AndroidRuntime(12756): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2478)

E/AndroidRuntime(12756): at android.os.Handler.dispatchMessage(Handler.java:106)

E/AndroidRuntime(12756): at android.os.Looper.loopOnce(Looper.java:230)

E/AndroidRuntime(12756): at android.os.Looper.loop(Looper.java:319)

E/AndroidRuntime(12756): at android.app.ActivityThread.main(ActivityThread.java:8919)

E/AndroidRuntime(12756): at java.lang.reflect.Method.invoke(Native Method)

E/AndroidRuntime(12756): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)

E/AndroidRuntime(12756): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

E/AndroidRuntime(12756): Caused by: java.lang.ClassNotFoundException: Didn’t find class “com.wetoucart.delivery.MyApplication” on path: DexPathList[[zip file “/data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/base.apk”],nativeLibraryDirectories=[/data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/lib/arm64, /data/app/~~8ekj92xZwfE14e1IvtLRkw==/com.wetoucart.delivery–W_TChSPocTlP0hGtbLbqQ==/base.apk!/lib/arm64-v8a, /system/lib64]]

E/AndroidRuntime(12756): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259)

E/AndroidRuntime(12756): at java.lang.ClassLoader.loadClass(ClassLoader.java:637)

E/AndroidRuntime(12756): at java.lang.ClassLoader.loadClass(ClassLoader.java:573)

E/AndroidRuntime(12756): at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)

E/AndroidRuntime(12756): at androidx.core.app.CoreComponentFactory.instantiateApplication(CoreComponentFactory.java:51)

E/AndroidRuntime(12756): at android.app.Instrumentation.newApplication(Instrumentation.java:1282)

E/AndroidRuntime(12756): at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1547)

E/AndroidRuntime(12756): … 11 more

I was getting this errors while assemble debugging please help.

Read and upload large file (> 2GB) in chunks using JavaScript, without loading entire file into RAM

What JavaScript API should I look for if I want to read a File (e.g. from an <input type="file" ...> or a drop area) in chunks and upload these chunks sequentially – but without loading the entire file (e.g. multiple gigabytes) into memory? I want only the current chunk (e.g. 1 MB) to reside in memory.

I see example codes use Blob.slice. But doesn’t that involve loading the entire file into memory and then accessing it by increasing offset and fixed length sequentially?

As my files probably will be 2 GB or even larger, I want to avoid crashing the browsers with “out of memory” errors.

My question is only about the client side. Server side is no problem.