Why is docx library generating invalid doc file when creating a different copy of the node?

I am trying to build a library using docx. And I wanted to implement plugin architecture. So, the data provided by plugin should work well with the eco-system.

But strangely, when I compare the data with JSON.stringify, the same data when created directly using docx library instance in the app works but the same data geenrated in some other library using the docx library does not work.

Initially I had no clue. But I just tested out this

// this is the simplified code
new Documnet({
   sections: [
    children: {
       Object.assign({}, new Paragraph("some text"));
    }
  ]
})

If you don’t use Object.assign, it works well. But it created corrupted document when I used Object.assign.

When I compared the working and non-working files, I noted that the corrupt file had <rootKey>w:r</rootKey> in document.xml in place of <w:r><w:t xml:space="preserve">some text here</w:t></w:r>.

Also check out this issue – https://github.com/dolanmiu/docx/issues/2988

I tried going through the codebase but didn’t find any obvious reason. Can anyone help with this please.

Firefox&vue seems to catch my variable.value late and shows null at the beginning

    const getSubscription = async () => {
        console.log(user.value) // this part is null
        const profile = user.value?.profile
        if (!profile) return null

        const {data: subscriptionData } = await supabase
            .from("organisation_subscriptions")
            .select("current_subscription, state, agb_version, current_period_start, current_period_end")
            .eq("organisation_id", profile.organisation_id)
            .limit(1)
            .single()
            .throwOnError()

        return subscriptionData
    }

Lately, I’ve encountered a bug in Firefox (it works fine in Chrome) where user.value is initially null. When I log the whole user value to the console, it also shows null, but when I expand it, it refreshes to the correct state and displays the actual value. I tried using a timeout as a workaround, but every time I access user.value, it starts as null. I couldn’t find anything about this on the internet. I hope my desperate soul will find a solution here.

Vue version: 3.4.29
Firefox version: latest

EDIT:

Additional images

Before I expand the consoled variable:

enter image description here

After I expand the consoled variable:

enter image description here

Getting Qualtrics multiple choice options to dynamically populate from javascript array

I’m running a conjoint experiment using javascript in qualtrics, which involves respondents having to choose between two political candidates.

I want to reference the names of the profiles in the multiple choice response options, so ‘who do you prefer: X or Y’.

Right now I have this code to save the profile names:

    setTimeout(function () {
        document.querySelectorAll(".QuestionText").forEach(q => {
            q.innerHTML = q.innerHTML
                .replace(/[PROFILE1]/g, profile1Name)
                .replace(/[PROFILE2]/g, profile2Name);
        });
    }, 500);

When I insert [PROFILE1] and [PROFILE1] into the question text for a multiple choice question in qualtrics, they populate the profile names correctly, e.g. “John” and “Michael”. However, when I put these tags into the actual response options themselves, they’re blank.

Does anyone know how to solve this?

Here is my full code:

Qualtrics.SurveyEngine.addOnload(function() {

    // Arrays containing all attribute levels:
    var EducationArray = ["High school diploma", "Bachelor's degree"];  
    var GenderArray = ["Male","Female"]; // Gender is still randomized normally
    var RaceArray = ["White", "Black", "Hispanic"];
    
    // Political Experience Array
    var PoliticalExperienceArray = ["None", "State legislator", "Congress"]; 

    // Name arrays
    var MaleNames = ["James", "Michael", "William", "John", "David"];
    var FemaleNames = ["Susan", "Jennifer", "Emily", "Olivia", "Barbara"];

    // Fisher-Yates shuffle function
    function shuffle(array) {
        for (var i = array.length - 1; i > 0; i--) { 
            var j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]]; // Swap elements
        }
        return array;
    }

    // Function to shuffle and return a single element
    function shuffle_one(theArray) { 
        return shuffle(theArray.slice())[0]; // Avoid modifying original array
    }

    // Function to select two different names from respective lists
    function getRandomNames(gender1, gender2) {
        let name1, name2;
        if (gender1 === "Male") {
            let shuffledMales = shuffle(MaleNames);
            name1 = shuffledMales[0];
            name2 = gender2 === "Male" ? shuffledMales[1] : shuffle_one(FemaleNames);
        } else {
            let shuffledFemales = shuffle(FemaleNames);
            name1 = shuffledFemales[0];
            name2 = gender2 === "Female" ? shuffledFemales[1] : shuffle_one(MaleNames);
        }
        return [name1, name2];
    }

    // Generate profile pairs
    function genprof() {
        var gender1 = shuffle_one(GenderArray);
        var gender2 = shuffle_one(GenderArray);
        var [name1, name2] = getRandomNames(gender1, gender2);

        var attributes = [
            { label: "Education", value1: shuffle_one(EducationArray), value2: shuffle_one(EducationArray) },
            { label: "Race", value1: shuffle_one(RaceArray), value2: shuffle_one(RaceArray) },
    
            { label: "Political Experience", value1: shuffle_one(PoliticalExperienceArray), value2: shuffle_one(PoliticalExperienceArray) },
            { label: "Age", value1: "44", value2: "44" } // Now included in the randomization
        ];

        // Ensure "Congress" is gendered appropriately
        attributes.forEach(attr => {
            if (attr.label === "Political Experience") {
                if (attr.value1 === "Congress") {
                    attr.value1 = gender1 === "Male" ? "Congressman" : "Congresswoman";
                }
                if (attr.value2 === "Congress") {
                    attr.value2 = gender2 === "Male" ? "Congressman" : "Congresswoman";
                }
            }
        });

        // Retrieve attribute order if already set, otherwise shuffle and set it
let storedAttributeOrder = Qualtrics.SurveyEngine.getEmbeddedData("attribute_order");

if (storedAttributeOrder) {
    // Use the stored order (convert from JSON string back to array)
    let orderedLabels = JSON.parse(storedAttributeOrder);

    // Reorder attributes based on stored order
    attributes.sort((a, b) => orderedLabels.indexOf(a.label) - orderedLabels.indexOf(b.label));
} else {
    // First repetition: Shuffle attributes and save order
    shuffle(attributes);
    let attributeOrderLabels = attributes.map(a => a.label);
    
    // Store in Qualtrics Embedded Data
    Qualtrics.SurveyEngine.setEmbeddedData("attribute_order", JSON.stringify(attributeOrderLabels));
}

        return { profiles: [[name1, gender1], [name2, gender2]], attributes };
    }

    // Get current round from embedded data, default to 1 if not set
    let currentRound = parseInt(Qualtrics.SurveyEngine.getEmbeddedData("current_round")) || 1;
    console.log("Current Round:", currentRound); // Debugging
    
    // Generate profiles
    let { profiles, attributes } = genprof();

    // Format the names with "(First candidate)" and "(Second candidate)"
    let profile1Name = profiles[0][0] + " (First candidate)";
    let profile2Name = profiles[1][0] + " (Second candidate)";
    
    // Store data for this round with unique keys
    Qualtrics.SurveyEngine.setEmbeddedData("profile1_round" + currentRound, profile1Name);
    Qualtrics.SurveyEngine.setEmbeddedData("profile2_round" + currentRound, profile2Name);
    
    Qualtrics.SurveyEngine.setEmbeddedData("attribute_order" + currentRound, JSON.stringify(attributes.map(a => a.label)));

    // Store attribute data for profiles
attributes.forEach((attr, index) => {
    let attrKey = attr.label.replace(/s+/g, "_").toLowerCase(); // Convert to a safe format
    
    Qualtrics.SurveyEngine.setEmbeddedData("profile1_" + attrKey + "_round" + currentRound, attr.value1);
    Qualtrics.SurveyEngine.setEmbeddedData("profile2_" + attrKey + "_round" + currentRound, attr.value2);
});
    
    // Increment round number for next iteration
    let nextRound = currentRound + 1;
    Qualtrics.SurveyEngine.setEmbeddedData("current_round", nextRound);

    console.log("Next Round Set to:", nextRound); // Debugging
    
    // Update table headers with names
    document.getElementById("profile1_name").textContent = profiles[0][0]; 
    document.getElementById("profile2_name").textContent = profiles[1][0];

    // Clear and repopulate the table body dynamically
    let tableBody = document.querySelector("#conjointTable tbody");
    tableBody.innerHTML = ""; // Clear previous rows

    attributes.forEach(attr => {
        let row = document.createElement("tr");

        let labelCell = document.createElement("td");
        labelCell.textContent = attr.label;

        let profile1Cell = document.createElement("td");
        profile1Cell.textContent = attr.value1;

        let profile2Cell = document.createElement("td");
        profile2Cell.textContent = attr.value2;

        row.appendChild(labelCell);
        row.appendChild(profile1Cell);
        row.appendChild(profile2Cell);
        tableBody.appendChild(row);
    });

    // Increment and save the round number for the next task
    Qualtrics.SurveyEngine.setEmbeddedData("current_round", currentRound + 1);

    // **Dynamically update question text in the same block**
    setTimeout(function () {
        document.querySelectorAll(".QuestionText").forEach(q => {
            q.innerHTML = q.innerHTML
                .replace(/[PROFILE1]/g, profile1Name)
                .replace(/[PROFILE2]/g, profile2Name);
        });
    }, 500);
});

I’m relatively new to javascript, so would really appreciate any help, thanks.

Integrating google translate widget in Next JS application

I am trying to integrate google translate dropdown widget in my Next Js application, I integrated that the widget also but when I try to redirect from one page to another where the widget will not be available, the page gets blank and throws error like NotFoundError: Failed to execute ‘removeChild’ on ‘Node’: The node to be removed is not a child of this node.

I tried to not clean up the component for persisting the script but it also doesn’t seem to be working.

"use client";
import { useEffect } from "react";

const GoogleTranslate = () => {
useEffect(() => {
// Load the Google Translate script
const script = document.createElement("script");
script.src =
"https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit";
script.async = true;
document.body.appendChild(script);

// Initialize the Google Translate element
window.googleTranslateElementInit = () => {
new window.google.translate.TranslateElement(
{ pageLanguage: "en" },
"google_translate_element"
);
};
}, []);

return <div id="google_translate_element"></div>;
};

export default GoogleTranslate;

This is my google translate component code and I am using it in the header of the application like this,

<AppBox>
<GoogleTranslate />
</AppBox>

Note: I am using App Router

GramJS Api.messages.Report() return 400 OPTION_INVALID

I have a method reportMessage:

async reportMessage(channelId: string, messageId: number, reason: ReportReason, comment?: string) {
    let reasonApi: Api.TypeReportReason;
    
    switch (reason) {
      case ReportReason.SPAM:
        reasonApi = new Api.InputReportReasonSpam();
        break;
      case ReportReason.VIOLENCE:
        reasonApi = new Api.InputReportReasonViolence();
        break;
      case ReportReason.PORNOGRAPHY:
        reasonApi = new Api.InputReportReasonPornography();
        break;
      case ReportReason.COPYRIGHT:
        reasonApi = new Api.InputReportReasonCopyright();
        break;
      case ReportReason.CHILD_ABUSE:
        reasonApi = new Api.InputReportReasonChildAbuse();
        break;
      case ReportReason.FAKE:
        reasonApi = new Api.InputReportReasonFake();
        break;
      case ReportReason.OTHER:
      default:
        reasonApi = new Api.InputReportReasonOther();
        break;
    }

    return this.handleCall(
      (client) =>
        client.invoke(
          new Api.messages.Report({
            peer: channelId,
            id: [messageId],
            option: reasonApi.getBytes(),
            message: comment || "",
          })
        ),
      { name: "reportMessage" }
    );
  }

When I try to call the method, telegram returns me an error: OPTION_INVALID

I’m not sure if I’m passing the correct value in the option field, but I can’t find any information on what it should look like.

Based on the documentation I tried to provide reason field instead of option:

async reportMessage(channelId: string, messageId: number, reason: ReportReason, comment?: string) {
    let reasonApi: Api.TypeReportReason;
    
    switch (reason) {
      case ReportReason.SPAM:
        reasonApi = new Api.InputReportReasonSpam();
        break;
      case ReportReason.VIOLENCE:
        reasonApi = new Api.InputReportReasonViolence();
        break;
      case ReportReason.PORNOGRAPHY:
        reasonApi = new Api.InputReportReasonPornography();
        break;
      case ReportReason.COPYRIGHT:
        reasonApi = new Api.InputReportReasonCopyright();
        break;
      case ReportReason.CHILD_ABUSE:
        reasonApi = new Api.InputReportReasonChildAbuse();
        break;
      case ReportReason.FAKE:
        reasonApi = new Api.InputReportReasonFake();
        break;
      case ReportReason.OTHER:
      default:
        reasonApi = new Api.InputReportReasonOther();
        break;
    }

    return this.handleCall(
      (client) =>
        client.invoke(
          new Api.messages.Report({
            peer: channelId,
            id: [messageId],
            reason: reasonApi,
            message: comment || "",
          })
        ),
      { name: "reportMessage" }
    );
  }

But in this case I have an error:

I also tried to create Buffer manually but failed again

How to make prev and next date buttons only move one date

I built a date picker that shows a list of dates, with three previous dates, the current date in the middle, and three next dates. On either side, there are buttons:

The Previous button moves the list backward.
The Next button moves the list forward.

The idea is that this should work in a circular way so when I reach the last date in the list and click Next, it should loop back to the first few dates, and the same goes in reverse when clicking Previous.

For example, if my current date list looks like this:

< 01/22 02/22 03/22 04/22 (Current) 05/22 06/22 07/22 >

Clicking Next should ideally wrap around and continue like this:

< 02/22 03/22 04/22 05/22 (Current) 06/22 07/22 08/22 >

But instead, my implementation just goes to the next available dates after 07/22 and the list is 08/22 and onwards.

Here is my code:

import { DatePicker, Button } from 'antd';
import './ButtonedAntDDatePicker.css';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import { useState, useEffect } from 'react';

dayjs.extend(customParseFormat);

function ButtonedAntDDatePicker({
    selectedDate,
    disabledDate,
    isDatePickerDisabled,
    enabledDates,
    setSelectedDate
}) {
    const [isAnimating, setIsAnimating] = useState(false);
    const [prevButtonsStyle, setPrevButtonsStyle] = useState({});
    const [nextButtonsStyle, setNextButtonsStyle] = useState({});
    const [datePickerInput, setDatePickerInput] = useState(null);

    // handle date picker input field
    useEffect(() => {
        const input = document.querySelector('.date-picker-container .ant-picker-input input');
        if (input) {
            setDatePickerInput(input);

            // If selectedDate is already set (from URL), update the input field
            if (selectedDate) {
                input.value = selectedDate.format('DD/MM/YYYY');
            }

            const handleInputEvent = (e) => {
                const inputValue = e.target.value;
                const formats = ['DD/MM/YYYY', 'D/M/YYYY', 'DD/M/YYYY', 'D/MM/YYYY'];

                for (const format of formats) {
                    const parsedDate = dayjs(inputValue, format, true);

                    if (parsedDate.isValid()) {
                        setSelectedDate(parsedDate);
                        e.target.value = parsedDate.format('DD/MM/YYYY');
                        break;
                    }
                }
            };

            input.addEventListener('blur', handleInputEvent);
            input.addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    handleInputEvent(e);
                }
            });

            return () => {
                input.removeEventListener('blur', handleInputEvent);
                input.removeEventListener('keypress', handleInputEvent);
            };
        }
    }, [setSelectedDate, selectedDate]);

    const findSurroundingDates = (currentDate, count) => {
        if (enabledDates.length === 0 || !currentDate) return { prevDates: [], nextDates: [] };

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const currentIndex = sortedDates.findIndex(date =>
            dayjs(date).isSame(currentDate, 'day')
        );

        const effectiveIndex = currentIndex === -1
            ? sortedDates.findIndex(date => dayjs(date).isAfter(currentDate))
            : currentIndex;

        const prevDates = sortedDates
            .slice(Math.max(0, effectiveIndex - count), effectiveIndex)
            .map(date => dayjs(date));

        const nextDates = sortedDates
            .slice(effectiveIndex + 1, effectiveIndex + count + 1)
            .map(date => dayjs(date));

        let hasFirstDate = false;
        let firstDateIndex = -1;

        const isNearEnd = effectiveIndex >= sortedDates.length - count;

        if (nextDates.length < count) {
            const remainingDates = count - nextDates.length;
            const firstDates = sortedDates.slice(0, remainingDates).map(date => dayjs(date));
            if (firstDates.length > 0) {
                hasFirstDate = true;
                firstDateIndex = nextDates.length;
            }
            nextDates.push(...firstDates);
        }

        //if it is first date in the list prev dates should be last dates in the list 
        if (prevDates.length < count) {
            const remainingDates = count - prevDates.length;
            const lastDates = sortedDates.slice(-remainingDates).map(date => dayjs(date));
            prevDates.unshift(...lastDates);
        }

        return { prevDates, nextDates, hasFirstDate, firstDateIndex, isNearEnd };
    };

    const { prevDates, nextDates, hasFirstDate, firstDateIndex, isNearEnd } = findSurroundingDates(selectedDate, 4);

    const handleDatePickerChange = (date) => {
        if (!date) return;
        setSelectedDate(date);
    };

    const handleDateButtonClick = (date) => {
        if (isAnimating || !date) return;
        setIsAnimating(true);

        const isGoingLeft = date.isAfter(selectedDate);

        if (isGoingLeft) {
            setPrevButtonsStyle({ animation: 'moveLeftOut 0.15s cubic-bezier(0.33, 1.0, 0.68, 1.0) forwards' });
            setNextButtonsStyle({ animation: 'moveLeftIn 0.15s cubic-bezier(0.22, 1.0, 0.36, 1.0) forwards' });
        } else {
            setPrevButtonsStyle({ animation: 'moveRightIn 0.15s cubic-bezier(0.22, 1.0, 0.36, 1.0) forwards' });
            setNextButtonsStyle({ animation: 'moveRightOut 0.15s cubic-bezier(0.33, 1.0, 0.68, 1.0) forwards' });
        }

        setTimeout(() => {
            setSelectedDate(date);
            setIsAnimating(false);

            setPrevButtonsStyle({});
            setNextButtonsStyle({});
        }, 200);
    };

    const formatButtonDate = (date) => {
        return date.format('DD/MM/YYYY');
    };

    // checks if it is first date in the sorted dates list
    const isFirstDate = (date) => {
        if (!date || enabledDates.length === 0) return false;

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const firstDate = dayjs(sortedDates[0]);
        return date.isSame(firstDate, 'day');
    };

    // checks if it is the last date in the sorted dates list
    const isLastDate = (date) => {
        if (!date || enabledDates.length === 0) return false;

        const sortedDates = [...enabledDates].sort((a, b) => dayjs(a).diff(dayjs(b)));
        const lastDate = dayjs(sortedDates[sortedDates.length - 1]);
        return date.isSame(lastDate, 'day');
    };

    const renderPrevButtons = () => {
        if (!prevDates.length) return null;

        return prevDates.map((date, index) => {
            const isLastDateIndicator = isLastDate(date);
            return (
                <div
                    key={`prev-${index}`}
                    style={{ position: 'relative' }}
                >
                    <Button
                        onClick={() => handleDateButtonClick(date)}
                        className={`date-button prev-date ${index === 0 ? 'first-button' : ''}`}
                    >
                        {index === 0 ? <i className="fa-solid fa-caret-left"></i> : formatButtonDate(date)}
                    </Button>
                </div>
            );
        });
    };

    const renderNextButtons = () => {
        if (!nextDates.length) return null;

        return nextDates.map((date, index) => {
            return (
                <div
                    key={`next-${index}`}
                    style={{ position: 'relative' }}
                >
                    <Button
                        onClick={() => handleDateButtonClick(date)}
                        className={`date-button next-date ${index === nextDates.length - 1 ? 'last-button' : ''}`}
                    >
                        {index === nextDates.length - 1 ? <i className="fa-solid fa-caret-right"></i> : formatButtonDate(date)}
                    </Button>
                </div>
            );
        });
    };

    if (isDatePickerDisabled) {
        return null;
    }

    return (
        <div className="date-picker-container">
            <div
                className="date-buttons-container prev-dates"
                style={prevButtonsStyle}
            >
                {renderPrevButtons()}
            </div>

            <DatePicker
                value={selectedDate}
                style={{ zIndex: 9999, margin: '0px' }}
                format={'DD/MM/YYYY'}
                onChange={handleDatePickerChange}
                disabledDate={disabledDate}
                allowClear={true}
                disabled={isDatePickerDisabled}
                inputReadOnly={false}
            />

            <div
                className="date-buttons-container next-dates"
                style={nextButtonsStyle}
            >
                {renderNextButtons()}
            </div>
        </div>
    );
}

export default ButtonedAntDDatePicker;
<div className='datepickers'>
     <ButtonedAntDDatePicker
       selectedDate={startDate}
       disabledDate={disabledDate}
       isDatePickerDisabled={isDatePickerDisabled}
       enabledDates={enabledDates}
       setSelectedDate={setStartDate}
     />
</div>

I expect that when I click on next/prev button it will only move to the prev date in the list not move whole thing. I’ve tried shifting the dates but it didn’t quite work. Anyone has any idea on how to approach this issue?

Why did Var a did not change the value to 1000? [closed]

var a = 50

function x() {
  var a = 100
  console.log(a, "x----")

  function y() {
    console.log(a, "y----")
  }
  return y
}
console.log(a, "---outside")
var z = x()
console.log(x, "---why")
z()
The output is:
50 ---outside
100 x----
[Function: x] ---why
100 y----

The value of last a inside Y function should be 1000 ?? right what I am wrong

When I click on the cart button, it’s not sliding like a drawer as I expected

I am trying to make an E Commerce website. When I press the cart icon in the top bar, the drawer element created in the getData function should slide from right to left. The div should slide from right to left, with z-index. I have added the event listener to handle the click on the cart, but when I click nothing happens to the drawer.

let ismenuopen = true;
const ul = document.querySelector('ul');
const li = document.querySelectorAll('ul li');
const menu = document.querySelector('i');
let cart = document.querySelector('.fa-shopping-cart');
let number = document.querySelector('.number');
let btn = document.querySelector('.addToCart');
let num = 0;

menu.addEventListener('click', () => {
  li.forEach((element) => {
    if (ismenuopen) {
      ul.style.display = 'flex';
      element.style.display = 'list-item'
    } else {
      ul.style.display = 'none';
      element.style.display = 'none';
    }
  })
  ismenuopen = !ismenuopen;
})

async function getData() {
  let response = await fetch(`https://fakestoreapi.com/products`);
  let result = await response.json();
  console.log(result);
  let result1 = result.slice(0, 18);
  result1.forEach((val) => {
    let container = document.querySelector('.container1');
    let div = document.createElement('div');
    let title = document.createElement('span');
    title.className = 'title';
    title.textContent = val.title.split(' ').slice(0, 3).join(' ');
    div.className = 'product';
    container.appendChild(div);
    div.appendChild(title);
    let img = document.createElement('img');
    img.src = val.image;
    div.appendChild(img);
    let price = document.createElement('p');
    price.textContent = `Price: ${val.price}`;
    price.className = 'price';
    div.appendChild(price);
    let btn = document.createElement('button');
    btn.className = 'addToCart';
    btn.textContent = 'Add to cart ';
    div.appendChild(btn);
    btn.addEventListener('click', () => {
      number.textContent = ++num;
    });
  })
  let drawer = document.createElement('div');
  drawer.className = 'drawer';
  document.body.appendChild(drawer);
  let drawerIsOpen = false;
  cart.addEventListener('click', () => {
    if (!drawerIsOpen) {
      drawer.style.transform = 'translateX(0%)';
      drawer.style.opacity = '1';
    } else {
      drawer.style.opacity = '0';
      drawer.style.transform = 'translateX(-100%)';
    }
    drawerIsOpen = !drawerIsOpen;
  })
  console.log(result1);
}
getData();
* {
  margin: 0px;
  padding: 0px;
}

nav ul {
  display: flex;
  gap: 50px;
  justify-content: center;
  list-style-type: none;
  font-size: 20px;
  color: white;
  background-image: linear-gradient(to right, black, grey);
}

nav .fa-bars {
  display: none;
}

nav .fa-shopping-cart {
  margin-top: 2px;
}

.number {
  padding-left: 3px;
}

.container {
  background-image: url(images/desktop background image.png);
  background-size: cover;
  background-repeat: no-repeat;
  width: 100%;
  height: 100vh;
}

.product {
  height: 180px;
  width: 147px;
  border: solid 4px gainsboro;
  padding: 30px;
  border-radius: 6px;
  margin-top: 4px;
  margin-left: 4px;
}

.product img {
  height: 100px;
  width: 100px;
  padding-top: 13px;
  padding-left: 10px;
}

.price {
  padding-left: 24px;
  padding-top: 10px;
}

.container1 {
  display: flex;
  flex-wrap: wrap;
  padding-top: 4%;
  padding-left: 8%;
  background: linear-gradient(to right, white, grey);
}

.addToCart {
  margin-top: 12px;
  margin-left: 24px;
  border-radius: 6px;
  border-style: none;
  width: 73px;
  height: 16px;
}

.drawer {
  height: 100vh;
  width: 250px;
  background-color: gainsboro;
  transform: translateX(100%);
  /* visibility: hidden;  */
  opacity: 0;
  transition: transform 2s ease-in-out;
  position: fixed;
}

@media only screen and (max-width: 600px) {
  body {
    overflow-x: hidden;
  }

  nav ul {
    display: none;
    gap: 15px;
    flex-direction: column;
    margin-top: 10px;
  }

  nav {
    background-image: linear-gradient(to right, black, grey);
    overflow-x: hidden;
  }

  nav .fa {
    display: inline;
    color: azure;
  }

  .container {
    background-image: url(images/background image for mobile.jpg);
    height: 28vh;
  }

  .container1 {
    padding-left: 20%;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  <link rel="stylesheet" href="style.css">
  <script defer src='script.js'></script>
</head>

<body>
  <nav>
    <i class= "fa fa-bars"></i>
    <ul>
      <li>Home</li>
      <li>Shop</li>
      <li>Product</li>
      <li>Details</li>
      <li>Cart</li>
      <li>Checkout</li>
      <li>Contact</li>
      <i class="fa fa-shopping-cart"><span class = 'number'></span></i>
    </ul>
  </nav>
  <div class="container">
  </div>
  <div class="container1">
  </div>
</body>

</html>

Make tooltip enterable/interactive

I have a tooltip component that uses Javascript to attach mouseover,mouseout event listeners to an element and on mouseover instantiates a tooltip component. I would like to make my tooltips interactive/enterable, so that if the user moves the mouse toward/into the tooltip itself when showing, the tooltip stays visible and allows mouse interaction.

The trouble is that moving the mouse toward the tooltip triggers the mouseout on the parent element, closing the tooltip. How does one prevent this? Is there a way to expand the element’s boundaries invisibly to include the tooltip, so that mouseout is only fired if the mouse is moved away from both parent element and the tooltip?

Here’s a simplified version of my code (I’ m using Svelte as my framework, but the question should be valid independent of framework):

<!-- component that wants to use a tooltip -->

  <span
      use:attachTooltip={{
        component: EntityCardTooltip      
      }}
  >My visible text</span>
  export const attachTooltip = (node,options) => {
    if (options) {

      let _component

      node.addEventListener('mouseover',addTooltip)
      node.addEventListener('mouseout',removeTooltip)

      function addTooltip(e) {
        const {component, ...otherOpts} = options

        _component = new component({
          target: node,
          props: {
            mouseEvent: e,
            ...otherOpts
          }
        })
      }

      function removeTooltip(e) {
        _component.$destroy()
      }

      return {
        destroy() {
          node.removeEventListener('mouseover',addTooltip)
          node.removeEventListener('mouseout',removeTooltip)
        }
      }
    }
  }

<!-- EntityCardTooltip --->

  <Tooltip {mouseEvent}>
    <div class="tipBody" slot="Content">
      <!--- tooltip content here --->
    </div>
  </Tooltip>

<!-- Tooltip --->

  <div class="tip" use:adjustTooltipOnMouseOver >
    {#if $$slots.Content}
      <slot name="Content" />
    {:else if innerHTML}
      {@html innerHTML}
    {/if}
  </div>

<style>
  @keyframes fadeInFromNone {
    0% {
        display: block;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
  }

  .tip, .tip:before {
    pointer-events: none;
    box-sizing: border-box;
    display: block;
    opacity: 1;
  }

  .tip:before {
    content: "";
    position: absolute;
    width: 2rem;
    height: 2rem;
    z-index: 13;
  }

  .tip {
    animation: fadeInFromNone 0.05s linear 0.05s;
    animation-fill-mode: both;
    position: fixed;
    color: black;
    min-width: 3rem;
    white-space: nowrap;
    display: block;
    text-overflow: ellipsis;
    white-space: pre;
    z-index: 12;
  }
</style>

There is more CSS not shown here, used to conditionally position by transforms using CSS vars set by adjustTooltipOnMouseOver (determines direction, prevents overflow, etc.).

How should i create a uppdating svg map

Hello me and my friend is creating a project for school where we maped out the school as an svg a rough of how it would look, the idea is to fill in the rooms that we have had, will have, or are having lessons in we have the times of each lesson and what room it is in but the problem is we are not sure how we should go about making this function. We have had some ideas like creating one svg for each room and then just making them vissibl when that lesson is activated but there should be a better way of doing this if someone has any ideas that would be aperciated.

We get the times and rooms in javascript and are not using any frameworks like next

Testing a function that calls another function

I have a function getIssuesWithWorklogs that returns an array of objects. Each object has a field worklogs, which is an array.
After getting the results of invokeWithRateLimit(“getIssues”), the array is iterated over, and if a particular object has worklogs.length > 20, then a second function (fetchAllWorklogs) is called to get additional worklogs.

I need to check if the nested function fetchAllWorklogs is called with the passed parameter of the third fetch result.

I am getting the response of invokeWithRateLimit, where the second object (results[2]) has more than 20 worklogs, and I want to check that fetchAdditionalWorklogs was called with the id of the third object.

But I am having problems with this, because when calling getIssuesWithWorklogs, the fetchAdditionalWorklogs function is not called, but the real one. Maybe I should rewrite the test differently, or is there a way to fix this?


export const getIssuesWithWorklogs = async (jqlQuery, fields, reportName, changelog, progressCallback) => {
  try {
    const totalNumberOfIssues = await invokeWithRateLimit("getTotalNumberIssues", {
      jqlQuery: jqlQuery,
      fields: fields,
      reportName: reportName,
      changelog
    });
    if (totalNumberOfIssues === 0) return [];
    await progressCallback(0, totalNumberOfIssues);
    const point = 100;
    const numberOfApiCalls = Math.ceil(totalNumberOfIssues / point);
    const rerenderInterval = Math.ceil(totalNumberOfIssues / MAX_PROGRESSBAR_UPDATES);
    let issuesLoaded = 0;
    let issuesLoadedByResponses = 0;
    const apiCallPromises = [];
    for (let i = 0; i < numberOfApiCalls; i++) {
      const startAtIssues = i * point;
      const promise = invokeWithRateLimit("getIssues", {
        jqlQuery: jqlQuery,
        fields: fields,
        reportName: reportName ? reportName : "",
        startAtIssues: startAtIssues,
        maxResults: 100,
        totalNumber: totalNumberOfIssues,
        changelog
      })
        .then(async (issues) => {
          for (const issue of issues) {
            if (issue.fields.worklog && issue.fields.worklog.total > 20) {
              const additionalWorklogs = await fetchAllWorklogs(issue.id);

              console.log("Fetching ended", additionalWorklogs);

              issue.fields.worklog.worklogs = [...issue.fields.worklog.worklogs, ...additionalWorklogs];
            }
          }

          issuesLoadedByResponses += issues.length;
          progressCallback(Math.min(issuesLoadedByResponses, totalNumberOfIssues), totalNumberOfIssues);
          return issues;
        })
        .catch((error) => {
          console.error("Error in getIssues:", error);

          throw error;
        });
      apiCallPromises.push(promise);
      await delay(30);
      issuesLoaded += point;
    }
    const responses = await Promise.all(apiCallPromises);
    let response = [];
    responses.forEach((issues) => {
      response = response.concat(issues);
    });
    return response;
  } catch (error) {
    console.error("Error in getIssues:", error, jqlQuery, fields);
    throw error;
  }
};



export const fetchAllWorklogs = async (issueId) => {
  const worklogs = [];
  let startAt = 20;

  try {
    const response = await invokeWithRateLimit("getIssueWorklogs", {
      issueId: issueId,
      startAt: startAt
    });

    worklogs.push(...response.worklogs);
  } catch (error) {
    console.error(`Error fetching worklogs for issue ${issueId}:`, error);
  }

  return worklogs;
};



import invokeWithRateLimit from "utils/invokeWithRateLImit";

import { mockParametersForReports, mockReportsResults, mockWorklogsForTimesheet } from "../__mocks__/mockData";
import * as JiraUtils from "../src/services/JiraUtils";
import { getIssuesWithWorklogs } from "../src/services/JiraUtils";

jest.mock("../src/services/JiraUtils", () => {
  const actual = jest.requireActual("../src/services/JiraUtils");
  return {
    ...jest.genMockFromModule("../src/services/JiraUtils"), // Generate mocks for all functions
    getIssuesWithWorklogs: actual.getIssuesWithWorklogs // Keep this function real
  };
});


jest.mock("@forge/bridge", () => ({
  invoke: jest.fn()
}));

jest.mock("../src/utils/invokeWithRateLImit.js", () => jest.fn());
jest.mock("constants", () => require("../src/constants"));

describe("getIssuesWithWorklogs", () => {
  let mockProgressCallback;

  beforeEach(() => {
    jest.clearAllMocks();
    mockProgressCallback = jest.fn();
  });

  test("Should return issues with worklogs when totalNumberOfIssues = 3", async () => {
    const mockedParameters = mockParametersForReports["Timesheet"];

    invokeWithRateLimit.mockResolvedValueOnce(3).mockResolvedValueOnce(mockReportsResults["Timesheet"]);
    invokeWithRateLimit.mockResolvedValueOnce({
      worklogs: [{ id: "18920", issueId: "13202", timeSpent: "1h" }]
    });

    JiraUtils.fetchAllWorklogs.mockResolvedValue(mockWorklogsForTimesheet);

    const result = await getIssuesWithWorklogs(
      mockedParameters.jqlQuery,
      mockedParameters.fields,
      mockedParameters.reportName,
      mockedParameters.changelog,
      mockProgressCallback
    );
    expect(result).toHaveLength(3);
    expect(result[2].fields.worklog.worklogs).toContainEqual(mockWorklogsForTimesheet[0]);

    expect(mockProgressCallback).toHaveBeenCalledTimes(2);
    expect(mockProgressCallback).toHaveBeenCalledWith(0, 3);
    expect(mockProgressCallback).toHaveBeenCalledWith(3, 3);

    expect(invokeWithRateLimit).toHaveBeenNthCalledWith(1, "getTotalNumberIssues", {
      jqlQuery: mockedParameters.jqlQuery,
      fields: mockedParameters.fields,
      reportName: mockedParameters.reportName,
      changelog: mockedParameters.changelog
    });
    expect(invokeWithRateLimit).toHaveBeenNthCalledWith(2, "getIssues", {
      jqlQuery: mockedParameters.jqlQuery,
      fields: mockedParameters.fields,
      reportName: mockedParameters.reportName,
      startAtIssues: 0,
      maxResults: 100,
      totalNumber: 3,
      changelog: mockedParameters.changelog
    });
    console.log(result[2].id);
    console.log("Mock calls:", JiraUtils.fetchAllWorklogs.mock.calls);
    await new Promise((resolve) => setTimeout(resolve, 100));
    await expect(JiraUtils.fetchAllWorklogs).toHaveBeenCalledWith(result[2].id);

    expect(invokeWithRateLimit).toHaveBeenCalledTimes(2);
  });



Here are my logs and error message:
console.log
Fetching ended [ { id: ‘18920’, issueId: ‘13202’, timeSpent: ‘1h’ } ]

  at log (src/services/JiraUtils.js:427:23)

console.log
13202

  at Object.log (__tests__/jiraUtils.test.js:176:13)

console.log
Mock calls: []

  at Object.log (__tests__/jiraUtils.test.js:177:13)

FAIL tests/jiraUtils.test.js
getIssuesWithWorklogs
× Should return issues with worklogs when totalNumberOfIssues = 3 (167 ms)

● getIssuesWithWorklogs › Should return issues with worklogs when totalNumberOfIssues = 3

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: "13202"

Number of calls: 0

  177 |     console.log("Mock calls:", JiraUtils.fetchAllWorklogs.mock.calls);
  178 |     await new Promise((resolve) => setTimeout(resolve, 100));
> 179 |     await expect(JiraUtils.fetchAllWorklogs).toHaveBeenCalledWith(result[2].id);
      |                                              ^
  180 |
  181 |     expect(invokeWithRateLimit).toHaveBeenCalledTimes(2);
  182 |   });

  at Object.toHaveBeenCalledWith (__tests__/jiraUtils.test.js:179:46) 

I’ve also tried spy function

Making box using html and css [closed]

[![enter image description here][1]][1]

Kindly made 2 rows shown in picture it will be helpful
I wanna add Activity in 2nd row enter code here
[1]: https://i.sstatic.net/M6sayc7p.jpg

enter code here

Today’s Activity -10-07-2024 (Attendance Hours: 8hrs)

Activity |
Count |
Status |
Start Time |
End Time |
Total Time |
Comments |
Edit |
Delete

Drug curation update

–>

<div class="main-container">
    <div class="header-section">
        <div>Today's Activity - 10/12/2025 <i>arwegthjgm</i></div>  
        <div>
            Toatl- activity : 0 
           
        </div>

        <div>
            Toatl- Time : 00:00:00
        </div>
        <div>
            <button>Submit</button>
        </div>

    </div>

    <div>
        Recent
    </div>

    <div class="activity">
        <div class="activity-details">
            <div class="activity-heading">Activity Name</div>
            <div>Drug Curation</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Count</div>
            <div>20</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Status</div>
            <div> New</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Start time</div>
            <div> ---</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">End time</div>
            <div> ---</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Total Time</div>
            <div> ---</div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Comments</div>
            <div><i class="fa fa-address-book" style="font-size:24px"></i></div>
        </div>

        <div class="activity-details">
            <div class="activity-heading">Edit</div>
         <div>  <i class="fa fa-eyedropper" style="font: size 40px; color:black"></i>
         </div></div>

         <div class="activity-details">
            <div class="activity-heading">Delete </div>
            <div> <i class="fa fa-paper-plane" style="font-size:20px;color:black"></i></div>
         </div>  
         
         <div class="activity-details">
            <div class="activity-heading">Start time</div>
            <div> <i class="fa fa-toggle-on" style="font: size 40px;color:green"></i>   <i class="fa fa-toggle-on" style="font: size 40px;color:grey ; margin-left: 10px;"></i></div>
        </div>
         <br>

        <div class="activity-2">
            <div class="activity-details-2">
                <div class="activity-heading">Activity Name</div>
                <div>Drug Curation</div>
            </div>
       </div>





    </div>

How do I change this html code so it doesn’t need javascript (onmouseover) [closed]

I have existing html that displays different images using javascript based on the text the mouse is “over”.

<table id="AutoNumber1" cellSpacing="1" width="100%" border="0">
  <tr>
    <td vAlign="top" width="38%">
    <p style="MARGIN-RIGHT: 10px" class="Standard"><font face="Arial" size="2">The example on the 
    right shows the resulting image from a stack of 1, 2, 4, 16 and 32 images.<br>
    <br>
    No calibration was done and some hot pixels are visible in some cases (no 
    dark and bias subtraction, no flat division).<br>
    <br>
    Mouse over the text to see the result of the stack for&nbsp; <br>
    <b>&nbsp;&nbsp;
    <A onmouseover="document['STACKRESULT01'].src='../images/Theory/Stack_1.jpg';;document.getElementById('LEGEND01').innerHTML='One Light Frame';" href="javascript:void(0);">
    1 image</A></b><br>
    <b>&nbsp;&nbsp;
    <A onmouseover="document['STACKRESULT01'].src='../images/Theory/Stack_2.jpg';document.getElementById('LEGEND01').innerHTML='Stack of 2 light frames';" href="javascript:void(0);">
    2 images</A></b><br>
    <b>&nbsp;&nbsp;
    <A onmouseover="document['STACKRESULT01'].src='../images/Theory/Stack_4.jpg';;document.getElementById('LEGEND01').innerHTML='Stack of 4 light frames';" href="javascript:void(0);">
    4 images</A></b><br>
    <b>&nbsp;&nbsp;
    <A onmouseover="document['STACKRESULT01'].src='../images/Theory/Stack_16.jpg';;document.getElementById('LEGEND01').innerHTML='Stack of 16 light frames';" href="javascript:void(0);">
    16 images</A></b><br>
    <b>&nbsp;&nbsp;
    <A onmouseover="document['STACKRESULT01'].src='../images/Theory/Stack_32.jpg';;document.getElementById('LEGEND01').innerHTML='Stack of 32 light frames';" href="javascript:void(0);">
    32 images</A></b></font></p>
    <p class="Standard"><font face="Arial" size="2">You can see that the resulting image is not 
    lighter or more colorful when the number of stacked light frames is 
    increasing but is much smoother.</font></p>
    <p>&nbsp;</p></td>
    <td width="62%">
    <p align="center">
    <IMG id=STACKRESULT01 border=0 name=STACKRESULT01 src="../images/Theory/Stack_2.jpg" width=591 height=591><br>
    <font face="Arial" size="2"><b id="LEGEND01" name="LEGEND01">
    One Light Frame</b></font></p></td>
  </tr>
</table>

I want to achieve the same behaviour in an environment that doesn’t support javascript (it uses litehtml).

I’m not an html expert, so if there’s a way to do this without javascript, I would be grateful to know how to do it. It may involve the use of the CSS:hover pseudo class, however I don’t know CSS/html well enough to put it all together. A working example that does the same as the above would help enormously.

Many thanks, David

Livewire dispatched alert isn’t shown

Since it isn’t possible in Livewire to show an alert multiple times with like session()->flash() I tried looking for an alternative. I found a pretty good alternative, which is by dispatching an event and then rendering an alert in JavaScript. But, somehow, nothing renders. The event goes through and works, it gets console logged correctly, but somehow, I can’t get anything to render in the HTML.

I’ve made it a very simple script:

document.addEventListener("DOMContentLoaded", function () {
    Livewire.on("error", (bericht) => {
        console.log("Error event received:", bericht);
        alert();
    });

    Livewire.on("succes", (bericht) => {
        console.log("Success event received:", bericht);
        alert();
    });

    function alert()
    {
        const alert_container = document.getElementById("alert-container");

        alert_container.innerHTML = "test";
    }
});

And the alert container is in the HTML itself:

<div>
    <form wire:submit.prevent="inloggen">
        @csrf

        @if (session()->has('error'))
            <x-alert.alert :bericht="session('error')" :bericht_type="'error'" />
        @endif

        @if (session()->has('succes'))
            <x-alert.alert :bericht="session('succes')" :bericht_type="'succes'" />
        @endif

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

And the event is dispatched here:

        } else {
            $this->dispatch('error', 'Ongeldig e-mailadres of wachtwoord.');
            return;
        }

And at last it logs:

Error event received: ['Ongeldig e-mailadres of wachtwoord.']

So it does receive it, but doesn’t edit the HTML.
Does anyone know a solution?