Leap Year mismatch

I have covered Leap Year 1403 for calendar but and it is now leap year, but the problem is that the date picker still shows 29 days for last month of that year.
And the other problem is dates are not adjusted with days, for example: if months starts with Sunday it shows the first date in Monday (only for that 1403 year) the 1404 is correct (it is first date of 1404 year and it is Friday 21 March 2025).

/* Persian calendar for jQuery v2.1.0.
   Written by Keith Wood, updated to fix leap year calculation and date conversion.
   Available under the MIT license. */

(function ($) {
    'use strict';

    function PersianCalendar(language) {
        this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
    }

    PersianCalendar.prototype = new $.calendars.baseCalendar();

    $.extend(PersianCalendar.prototype, {
        name: 'Persian',
        jdEpoch: 1948320.5,
        daysPerMonth: [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30],
        hasYearZero: false,
        minMonth: 1,
        firstMonth: 1,
        minDay: 1,

        regionalOptions: {
            '': {
                name: 'Persian',
                epochs: ['BP', 'AP'],
                monthNames: ['حمل', 'ثور', 'جوزا', 'سرطان', 'اسد', 'سنبله', 'میزان', 'عقرب', 'قوس', 'جدی', 'دلو', 'حوت'],
                monthNamesShort: ['حمل', 'ثور', 'جوزا', 'سرطان', 'اسد', 'سنبله', 'میزان', 'عقرب', 'قوس', 'جدی', 'دلو', 'حوت'],
                dayNames: ['یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
                dayNamesShort: ['یک', 'دو', 'سه', 'چهار', 'پنج', 'جمعه', 'شنبه'],
                dayNamesMin: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
                digits: null,
                dateFormat: 'yyyy-mm-dd',
                firstDay: 6,
                isRTL: false
            }
        },


        leapYear: function (year) {
            var date = this._validate(year, this.minMonth, this.minDay, $.calendars.local.invalidYear);
            var y = date.year();
            var modYear = (y + 38) % 2820;
            var leapYears = [0, 4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45, 49, 53, 57, 61, 66, 70, 74, 78, 82, 86, 90, 95, 99, 103, 107, 111, 115, 119, 124, 128];
            return leapYears.includes(modYear % 128);
        },
        daysInMonth: function (year, month) {
            var date = this._validate(year, month, this.minDay, $.calendars.local.invalidMonth);
            var days = this.daysPerMonth[date.month() - 1];
            if (date.month() === 12 && this.leapYear(date.year())) {
                days = 30; // Esfand has 30 days in leap years
            }
            return days;
        },


        toJD: function (year, month, day) {
            var date = this._validate(year, month, day, $.calendars.local.invalidDate);
            year = date.year();
            month = date.month();
            day = date.day();

            var epBase = year - (year >= 0 ? 474 : 473);
            var epYear = 474 + (epBase % 2820);
            return day + (month <= 7 ? (month - 1) * 31 : (month - 1) * 30 + 6) +
                Math.floor((epYear * 682 - 110) / 2816) + (epYear - 1) * 365 +
                Math.floor(epBase / 2820) * 1029983 + this.jdEpoch;
        },
        fromJD: function (jd) {
            jd = Math.floor(jd) + 0.5;
            var depoch = jd - this.toJD(475, 1, 1);
            var cycle = Math.floor(depoch / 1029983);
            var cyear = depoch % 1029983;
            var ycycle = 2820;

            if (cyear !== 1029982) {
                var aux1 = Math.floor(cyear / 366);
                var aux2 = cyear % 366;
                ycycle = Math.floor(((2134 * aux1) + (2816 * aux2) + 2815) / 1028522) + aux1 + 1;
            }

            var year = ycycle + (2820 * cycle) + 474;
            if (year <= 0) {
                year--;
            }

            var yday = jd - this.toJD(year, 1, 1) + 1;
            var month = yday <= 186 ? Math.ceil(yday / 31) : Math.ceil((yday - 6) / 30);
            var day = jd - this.toJD(year, month, 1) + 1;
            return this.newDate(year, month, day);
        },
     


        fromJSDate: function (jsd) {
            var utcDate = new Date(Date.UTC(
                jsd.getFullYear(),
                jsd.getMonth(),
                jsd.getDate()
            ));
            var gregCal = $.calendars.instance('gregorian');
            var jd = gregCal.toJD(
                utcDate.getUTCFullYear(),
                utcDate.getUTCMonth() + 1,
                utcDate.getUTCDate()
            );
            return this.fromJD(jd);
        },
        weekOfYear: function (year, month, day) {
            var checkDate = this.newDate(year, month, day);
            checkDate.add(-((checkDate.dayOfWeek() + 1) % 7), 'd');
            return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
        },

        weekDay: function (year, month, day) {
            return this.dayOfWeek(year, month, day) !== 5;
        },

        dayOfWeek: function (year, month, day) {
            var date = this._validate(year, month, day, $.calendars.local.invalidDate);
            return (Math.floor(date.toJD()) + 2) % 7; // Adjusted to +2
        },
    });

    function mod(a, b) {
        return a - (b * Math.floor(a / b));
    }

    $.calendars.calendars.persian = PersianCalendar;
    $.calendars.calendars.jalali = PersianCalendar;
})(jQuery);

This is my code for that

About Angular Material Expansion Table

Github Page: https://windjim.github.io/Angular-Material-Table/expansion-table

Source code: https://github.com/windjim/Angular-Material-Table/tree/main/src/app/expansion-table

When I click to expand a row, the functionality works as expected. However, when I sort the data by clicking on the table header, the expanded state becomes inconsistent or incorrect.

I expect the expanded state to remain unchanged when sorting the data by clicking on the table header.

code:

ts

import {
  Component,
  EventEmitter,
  Injectable,
  Input,
  OnInit,
  Output,
  ViewChild,
} from '@angular/core';
import {
  MatPaginator,
  MatPaginatorIntl,
  PageEvent,
} from '@angular/material/paginator';
import { Router } from '@angular/router';
import { MatSort, Sort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';

// 改變分頁器文字
@Injectable()
class CustomMatPaginatorIntl extends MatPaginatorIntl {
  itemsPerPageLabel = '筆數/頁';
}



@Component({
  selector: 'app-expansion-table',
  templateUrl: './expansion-table.component.html',
  styleUrls: ['./expansion-table.component.scss'],
  providers: [{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl }],
})
export class ExpansionTableComponent implements OnInit {
  // 外部引入
  @Input() elementData: any[] = []; // 資料
  @Input() jsonFieldName: string[] = []; // JSON對應欄位
  @Input() displayColName: string[] = []; // 顯示用表頭


  @Input() CaseDatas: any[] = []; // 案件總筆數
  @Input() collapseBtn: boolean = true; 
  @Input() size: number = 5; // 每頁顯示幾筆
  @Input() headerColumns: string[] = [
    'caseNo',
    "applyTime",
  ]; // 上排欄位定義
  @Input() detailsColumns: string[] = [
    "idNo",
    'applyOption'
  ]; // 下排欄位定義
  @Input() routerLinks: string = ''; //url

  @Output() btnClick = new EventEmitter();

  // 內部變數
  isCollapsed: boolean = false; // 表格收合控制
  paginatedItems: any[] = []; // 分頁顯示資料集合
  searchBtn: string = 'A'; // 查詢按鈕樣式

  maxPage: number = 0; // 最大頁數
  pageInput: number = 1; // 輸入第幾頁
  paginator!: MatPaginator; // 分頁控制器

  @ViewChild('paginator') set paginatorSetter(paginator: MatPaginator) {
    if (paginator) {
      this.paginator = paginator;
      paginator.page.subscribe((event: PageEvent) => this.onPageChange(event));
    }
  }

  @ViewChild(MatSort) sort!: MatSort;

  constructor(private router: Router) {}

  ngOnInit(): void {
    this.setPaginatedItems(0, this.size); //初始化顯示資料筆數
    this.maxPage = Math.ceil(this.CaseDatas.length / this.size); // 計算最大頁數
  }

  // 詳情頁面
  clk(event: MouseEvent, data: any) {
    event.stopPropagation();
    this.btnClick.emit(data);
  }

  // 顯示多少筆數
  setPaginatedItems(startIndex: number, endIndex: number) {
    this.paginatedItems = [
      {
          "applyTime": "2025/03/11",
          "caseNo": "2025031100004",
          "idNo": "A184385581",
          "applyOption": "FO",
          
      },
      {
          "applyTime": "2025/01/15",
          "caseNo": "2025011400002",
          "idNo": "A127944600",
          "applyOption": "FO",

      },
      {
          "applyTime": "2025/01/14",
          "caseNo": "2025011400001",
          "idNo": "A163479793",
          "applyOption": "F",

      }
  ];
  }

  // 頁數有變化時
  onPageChange(event: PageEvent) {
    this.pageInput = event.pageIndex + 1;
    const startIndex = event.pageIndex * event.pageSize;
    const endIndex = startIndex + event.pageSize;
    this.setPaginatedItems(startIndex, endIndex);
  }

  // 輸入頁數後
  jumpToPage() {
    if (this.pageInput < 1 || this.pageInput > this.maxPage) {
      alert(`請輸入 1 到 ${this.maxPage} 之間的頁碼`);
      return;
    }

    // 將頁碼轉換為索引
    const pageIndex = this.pageInput - 1;
    this.paginator.pageIndex = pageIndex;
  }
  sortData(sort: Sort) {
    const data = this.paginatedItems;

    if (!sort.active || sort.direction === '') {
      this.paginatedItems = data;
      return;
    }

    this.paginatedItems = data.sort((a: any, b: any) => {
      const isAsc = sort.direction === 'asc';
      const key = sort.active as keyof typeof a;

      return this.compare(a[key], b[key], isAsc);
    });

    console.log('this.paginatedItems',data)
  }

  private compare(a: string , b: string, isAsc: boolean): number {
    if (typeof a === 'string' && typeof b === 'string') {
      return (a.toLowerCase() < b.toLowerCase() ? -1 : 1) * (isAsc ? 1 : -1);
    }
    return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
  }
}

html:

<div class="my-3">
  <!-- 左上角的收合按鈕 -->
  <div *ngIf="collapseBtn" class="collapseBtn"> 
      <button mat-icon-button (click)="isCollapsed = !isCollapsed" class="toggle-btn" style="border: 1px solid grey;">
        {{ isCollapsed ? 'Collapse' : 'Expand' }}
      </button>
  </div>
  <div *ngFor="let item of paginatedItems" class="mb-1">
    <mat-expansion-panel [expanded]="isCollapsed" hideToggle>
      <mat-expansion-panel-header>
        <mat-panel-title>
          <table class="table-container" matSort (matSortChange)="sortData($event)">
            <thead>
              {{isCollapsed ? 'Now is the expansion':'Now is the Collapse'}}
              <tr>
                <th *ngFor="let column of headerColumns;let i = index"
                    mat-sort-header="{{item[column]}}"
                    [disabled]="column === 'function'">
                  {{ column }}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td *ngFor="let property of headerColumns">{{ item[property] }}</td>
              </tr>
            </tbody>
          </table>
        </mat-panel-title>
      </mat-expansion-panel-header>

      <div class="d-flex">
        <table class="table-content">
          <tr>
            <th *ngFor="let column of detailsColumns">{{ column }}</th>
          </tr>
          <tr>
            <td *ngFor="let property of detailsColumns">{{item[property]}}</td>
          </tr>
        </table>
      </div>
    </mat-expansion-panel>
  </div>
</div>

Hamburger menu not animating when clicking on menu item

I hope this doesn’t sound too confusing but when I click on an menu item I want my hamburger to show its closed state, currently when I click on an menu item it stays open. I have added some pictures below to show what is happening.

Menu opened

What the hamburger looks after looking on a link

Here is my code

export const Navbar = () => {
const [isOpen, setOpen] = useState(false);


return (
<>
<nav className="bg-pink-300 shadow-lg flex items-center 
justify-around py-3 px-32 sticky top-0 left-0 w-full">
  <Link to={"/"}>
  <span className='font-semibold text-lg flex items-center
  gap-3 text-black hover:text-sky-300 transition 
  duration-300'>
    <GiAbstract037 ></GiAbstract037>
    <span>xFigure</span>
  </span>
  </Link>

  <div className='hidden md:flex items-center gap-5 text-black'>
  <Link to={"/"} className='py-1 px-3 text-lg font-light text-white 
  hover:text-sky-300 rounded-2x1 hover:bgslate-700 transition 
  duration-300'>
    Home
  </Link>

  <Link to={"/new"} className='py-1 px-3 text-lg font-light text-white 
  hover:text-sky-300 rounded-2x1 hover:bgslate-700 transition 
  duration-300'>
    New
  </Link>

  <Link to={"/popular"} className='py-1 px-3 text-lg font-light text-white 
  hover:text-sky-300 rounded-2x1 hover:bgslate-700 transition 
  duration-300 border-r'>
    Popular
  </Link>

  <Link to={"/login"} className='py-1 px-3 text-lg font-light text-white 
  hover:text-sky-300 rounded-2x1 hover:bgslate-700 transition 
  duration-300'>
     <span className='font-semibold text-lg flex items-center
  gap-3 text-black hover:text-sky-300 transition 
  duration-300'></span>
  <CgProfile />
  </Link>
  </div>

  <div className="md:hidden" onClick={() => setOpen(!isOpen)}>
  <Hamburger size={18} ></Hamburger>
  </div>

  <div className={`absolute md:hidden top-15 left-0
    w-full bg-white flex flex-col items-center gap-7 
    font-semibold text-lg transform-transition 
    ${isOpen ? "opacity-100" : "hidden"}`}
    style={{transition: "transform 0.3s ease, opacity 0.3s ease"}}>
      <Link to={"/"}className="list-none w-full text-center p-4 
      hover:bg-sky-50 hover:text-white transition-all
      cursor-pointer" onClick={() => setOpen(false) }>Home</Link>

      <Link to={"/new"}className="list-none w-full text-center p-4 
      hover:bg-sky-50 hover:text-white transition-all
      cursor-pointer" onClick={() => setOpen(false)}>New</Link>

      <Link to={"/popular"}className="list-none w-full text-center p-4 
      hover:bg-sky-50 hover:text-white transition-all
      cursor-pointer" onClick={() => setOpen(false)}>Popular</Link>

      <Link to={"/login"}className="list-none w-full text-center p-4 
      hover:bg-sky-50 hover:text-white transition-all
      cursor-pointer" onClick={() => setOpen(false) }>Account</Link>
    </div>

</nav>
</>

)
}

Bind object instance to the “this” keyword in addEventListener function

I have a class for an editor that attaches menus and events when enabled. I want to capture keyboard events related to this editor, which trigger functions within the object instance when pressed. The following works as intended, making the ' key toggle an editor element:

var edit = null;

class editor {
    constructor() {
        this.element = document.createElement("div");

        addEventListener("keydown", function(event) {
            if(event.key == "`") {
                edit.toggle();
            }
        });
    }

    toggle() {
        if(root.contains(this.element)) {
            document.body.removeChild(this.element);
        } else {
            document.body.appendChild(this.element);
        }
    }
}

edit = new editor();

The problem is I can’t make addEventListener call the this.toggle function of the object: I tried using bind but nothing I did works, I can’t make the event listener carry the object reference when using the this keyword. As such I store my object in a global var and have the object’s internal functions call itself through that var, which feels like a messy and wrong way to do it, especially if I decide to use multiple instances in which case the reference would need to be unique. Here’s a version that gets close to what I’m trying to achieve but won’t work:

class editor {
    constructor() {
        this.element = document.createElement("div");

        addEventListener("keydown", function(event) {
            if(event.key == "`") {
                this.toggle();
            }
        }).bind(this);
    }

    toggle() {
        if(root.contains(this.element)) {
            document.body.removeChild(this.element);
        } else {
            document.body.appendChild(this.element);
        }
    }
}

new editor();

How to trace why an awaited file write is not working in Node 22?

I have the following code in my Node app. It should write a JSON file and then run a function.

import { run } from '../main.js';
import { writeFile } from 'node:fs/promises';
import { Config, CONFIG_PATH } from '../config.js';

// This is a test config that keeps everything self-contained
const config: Config = {
    guid: '2030ba7c74aee8ed3c831468b1a09b56',
    type: 'http',
    url_base: 'https://books.toscrape.com',
    report_crawl_url: 'http://localhost:8081/register/crawl',
    report_logs_url: 'http://localhost:8081/register/logs',
    urls: ['/'],
    recursive: true,
    crawlerAutoStart: false,
    listenerTimeout: 10000,
};
await writeFile(
    CONFIG_PATH,
    JSON.stringify(config, null, 2),
    { encoding: 'utf8' }
);

await run(true);

I need the writeFile() to succeed before the run() is called, but the file does not exist, despite the await on the file operation. The app is being run in Docker.

I transpile this TypeScript, also in Docker, like so:

./node_modules/typescript/bin/tsc

I run the code using the transpiled output:

node dist/entrypoint/test.js

I don’t fully understand TS configuration, but I wonder if it might be relevant:

{
  // https://www.typescriptlang.org/tsconfig#compilerOptions
  "compilerOptions": {
    "esModuleInterop": true,
    "lib": ["es2020"],
    "module": "es2022",
    "preserveConstEnums": true,
    "moduleResolution": "node",
    "strict": true,
    "sourceMap": true,
    "target": "es2022",
    "types": ["node"],
    "outDir": "dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

If I comment out the run() and transpile again then the program writes the JSON file and exits cleanly. If I leave it in then I get an error when something wants the file that should have written:

1e714cd97a20:/project# node dist/entrypoint/test.js
node:internal/fs/promises:638
  return new FileHandle(await PromisePrototypeThen(
                        ^

Error: ENOENT: no such file or directory, open '/crawl-config.json'
    at async open (node:internal/fs/promises:638:25)
    at async Object.readFile (node:internal/fs/promises:1242:14)
    at async readConfigFile (file:///project/dist/config.js:11:18)
    at async file:///project/dist/crawler.js:7:16 {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: '/crawl-config.json'
}

Node.js v22.14.0

What can I do to investigate why the write operation is failing to write immediately?

RGraph Charts cannot be scaled when container is resizing

Is it possible to scale charts?
I tried:

<script>
                        
    function graf(){
        new RGraph.SVG.Bar({...}).wave();
    }
    
    const resizeObserver = new ResizeObserver((object) => {
    
        console.dir('...' );//info if action is catch
        object[0].target.innerHTML = '';//...optional no matter
        graf();
    });

    resizeObserver.observe(document.getElementById('chart-container' ) );
</script>

Each time graf() is called again, the graph and its continer of SVG tag disappear from the HTML document. It doesn’t matter whether innerHTML cleanup is used. Console.dir(‘…’) is called but with no chart, it disappears.
With listeners do the same. It doesn’t matter if it’s onmouseover etc.

Misaligned frames and images

I’m trying to create a photobooth web application. I’m encountering problems when using custom frames I created.

My problem is that the images and my custom frames are misaligned. I tried to adjust the frames but it also adjust the images so it’s still misaligned. It works perfectly when just choosing colored frames.

image of using my custom frame with the photo strip

Please help me align the frames and my photostrip.

const photoStrip = document.getElementById('photoStrip');
    const photos = JSON.parse(localStorage.getItem('photos')) || [];

    photos.forEach(photo => {
        const img = document.createElement('img');
        img.src = photo;
        photoStrip.appendChild(img);
    });

    // Clear photos from localStorage after displaying
    localStorage.removeItem('photos');

    document.getElementById('downloadBtn').addEventListener('click', () => {
        html2canvas(photoStrip, {
            onrendered: function(canvas) {
                const link = document.createElement('a');
                link.href = canvas.toDataURL('image/png');
                link.download = 'photo_strip.png';
                link.click();
            }
        });
    });

    document.getElementById('retakeBtn').addEventListener('click', () => {
        window.location.href = 'camera.html';
    });

    document.querySelectorAll('.frame, .frame1').forEach(frame => {
        frame.addEventListener('click', () => {
            // Remove selected class from all frames
            document.querySelectorAll('.frame, .frame1').forEach(f => f.classList.remove('selected'));
            // Add selected class to clicked frame
            frame.classList.add('selected');

            const color = frame.getAttribute('data-color');
            const frameSrc = frame.getAttribute('data-frame');

            // Remove existing frame overlay if any
            const existingOverlay = photoStrip.querySelector('.frame-overlay');
            if (existingOverlay) {
                existingOverlay.remove();
            }

            if (frameSrc) {
                photoStrip.style.backgroundColor = 'transparent';
                const overlay = document.createElement('img');
                overlay.src = frameSrc;
                overlay.className = 'frame-overlay';
                photoStrip.appendChild(overlay);

                // Ensure the overlay gets the correct size
                overlay.style.width = "220px";
                overlay.style.height = "740px";

                // Shift photos and photo-strip 10px lower to match custom color positioning
                photoStrip.querySelectorAll('img').forEach(img => {
                    img.style.marginTop = '10px';
                });
                photoStrip.style.marginTop = '-10px';
            } else {
                photoStrip.style.backgroundColor = color;

                // Shift photos and photo-strip 10px lower for custom color
                photoStrip.querySelectorAll('img').forEach(img => {
                    img.style.marginTop = '10px';
                });
                photoStrip.style.marginTop = '10px';
            }
        });
    });

    // Add color picker event listener
    document.getElementById('colorPicker').addEventListener('input', function(e) {
        // Remove selected class from all frames
        document.querySelectorAll('.frame, .frame1').forEach(f => f.classList.remove('selected'));

        // Remove existing frame overlay if any
        const existingOverlay = photoStrip.querySelector('.frame-overlay');
        if (existingOverlay) {
            existingOverlay.remove();
        }

        // Apply the selected color
        photoStrip.style.backgroundColor = e.target.value;

        // Shift photos and photo-strip 10px lower for custom color
        photoStrip.querySelectorAll('img').forEach(img => {
            img.style.marginTop = '10px';
        });
        photoStrip.style.marginTop = '10px';
    });
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
.banner {
    width: 100%;
    height: 100vh; /* Ensure it occupies the full viewport height */
    background-image: url(bg3.png);
    background-size: cover;
    background-position: center;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    margin: 0; /* Remove any default margin */
    padding: 0; /* Remove any default padding */
    box-sizing: border-box; /* Include padding and border in height calculation */
}
body {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    margin: 0; /* Remove default margin */
    padding: 0; /* Remove default padding */
    font-family: 'Cascadia Code', sans-serif;
    overflow: hidden; /* Prevent scrolling if content overflows */
}
.photo-strip {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 !important; /* Remove padding to align the frame properly */
    background-color: #91A6F7;
    margin-right: 20px;
    position: relative;
    width: 220px;
    height: 740px; /* Ensure it matches the frame overlay */
    box-sizing: border-box; /* Include padding and border in height calculation */
    margin-top: 0 !important; /* Ensure it starts at the top */
    z-index: 5; /* Ensure photos are below the frame overlay */
}
.photo-strip img {
    margin: 10px 0;
    width: 200px;
    height: 150px;
    border-radius: 5px;
    z-index: 5; /* Ensure photos are below the frame overlay */
}
#downloadBtn, #retakeBtn {
    font-family: 'Cascadia Code';
    padding: 10px 20px;
    background-color: #91A6F7;
    color: black;
    font-size: 20px;
    width: 175px;
    height: 60px;
    border: none;
    border-radius: 30px;
    cursor: pointer;
    margin: 10px 5px;
}
#retakeBtn {
    background-color: #91A6F7;
}
.frame-options {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    margin-bottom: 20px;
    justify-content: center;
}
.frame, .frame1 {
    top: 0;
    width: 60px;
    height: 60px;
    border-radius: 5px;
    cursor: pointer;
    background-size: cover;
    background-position: center;
}
.color-picker-container {
    margin: 10px 0;
    display: flex;
    align-items: center;
    gap: 10px;
}
.color-picker-label {
    font-size: 16px;
    color: black;
}
#colorPicker {
    width: 60px;
    height: 60px;
    padding: 0;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}
.controls {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 20px; /* Add spacing below the photo strip */
}
.button-row {
    display: flex;
    justify-content: center;
    width: 100%;
}
.frame-overlay {
    position: absolute;
    top: 0; /* Move the frame overlay to the top */
    left: 0;
    width: 220px;
    height: 740px;
    z-index: 10; /* Ensure it's above the photos */
    pointer-events: none; /* Prevent interaction with the frame overlay */
}
<div class="banner">
    <div class="photo-strip" id="photoStrip">
        <!-- Photos will be appended here -->
    </div>
    <div class="controls">
        <div class="frame-options">
            <div class="frame1" style="background-image: url('frames/frame1.png')" data-frame="frames/frame1.png"></div>
        </div>
        <div class="color-picker-container">
            <span class="color-picker-label">Custom Color:</span>
            <input type="color" id="colorPicker" value="#91A6F7">
        </div>
        <button id="downloadBtn">Download</button>
        <button id="retakeBtn">Retake</button>
    </div>
</div>

How can i get customers details when they visit my website, using node js and other npm modules? [closed]

Question:
I have a website, and I want to collect some details about visitors, such as their email, phone number, or company information. My goal is to identify potential customers and improve lead generation.

I have considered the following approaches:

Google Analytics – Tracks user behavior but doesn’t provide personal details.

Lead Capture Forms – Asking users to fill out a form, but many visitors leave without submitting.

Live Chat & Chatbots – Can collect emails, but I want a more passive method.

IP Tracking & Lookup Services – Tools like Clearbit and Leadfeeder claim to identify companies, but I’m unsure how effective they are.

Cookies & Local Storage – Can store data if a user logs in, but new visitors remain anonymous.

Code Attempts:
I tried using express-ip to get the visitor’s IP address:

const express = require("express");
const requestIp = require("request-ip");

const app = express();
app.use(requestIp.mw());

app.get("/", (req, res) => {
    const ip = req.clientIp;
    res.send(`Visitor IP: ${ip}`);
});

app.listen(3000, () => console.log("Server running on port 3000"));

Then, I attempted to fetch company details using Clearbit:

const fetch = require("node-fetch");

async function getCompanyInfo(ip) {
    const response = await fetch(`https://company.clearbit.com/v2/companies/find?ip=${ip}`, {
        headers: {
            Authorization: `Bearer YOUR_CLEARBIT_API_KEY`
        }
    });
    const data = await response.json();
    console.log(data);
}

getCompanyInfo("8.8.8.8"); // Example IP

Questions:
Are there any reliable methods to passively gather visitor details while complying with privacy laws (GDPR, CCPA)?

How can I integrate a service like Clearbit or HubSpot to fetch company details from an IP address?

Is there a way to encourage users to provide their contact details without disrupting their experience?

I’m using React.js for the frontend and Node.js for the backend. Any API recommendations or code examples would be appreciated.

How can i get customers details when they visit my website?

Question:
I have a website, and I want to collect some details about visitors, such as their email, phone number, or company information. My goal is to identify potential customers and improve lead generation.

I have considered the following approaches:

Google Analytics – Tracks user behavior but doesn’t provide personal details.

Lead Capture Forms – Asking users to fill out a form, but many visitors leave without submitting.

Live Chat & Chatbots – Can collect emails, but I want a more passive method.

IP Tracking & Lookup Services – Tools like Clearbit and Leadfeeder claim to identify companies, but I’m unsure how effective they are.

Cookies & Local Storage – Can store data if a user logs in, but new visitors remain anonymous.

Code Attempts:
I tried using express-ip to get the visitor’s IP address:

const express = require("express");
const requestIp = require("request-ip");

const app = express();
app.use(requestIp.mw());

app.get("/", (req, res) => {
    const ip = req.clientIp;
    res.send(`Visitor IP: ${ip}`);
});

app.listen(3000, () => console.log("Server running on port 3000"));

Then, I attempted to fetch company details using Clearbit:

const fetch = require("node-fetch");

async function getCompanyInfo(ip) {
    const response = await fetch(`https://company.clearbit.com/v2/companies/find?ip=${ip}`, {
        headers: {
            Authorization: `Bearer YOUR_CLEARBIT_API_KEY`
        }
    });
    const data = await response.json();
    console.log(data);
}

getCompanyInfo("8.8.8.8"); // Example IP

Questions:
Are there any reliable methods to passively gather visitor details while complying with privacy laws (GDPR, CCPA)?

How can I integrate a service like Clearbit or HubSpot to fetch company details from an IP address?

Is there a way to encourage users to provide their contact details without disrupting their experience?

I’m using React.js for the frontend and Node.js for the backend. Any API recommendations or code examples would be appreciated.

How to make a gallery of images take all available space?

I’m making a project to emulate a pinterest page and the setup is to have the images sorted in 5 columns but the rows should take all the available space depending on the size of each image. I am using js to create all the components and scss to style it.

I have created a div container(.gallery)

.gallery{
height: 100%;
max-height: 1000px;
width: 100%;
display: grid;
grid-template-columns:repeat(5, 1fr);
grid-template-rows: auto;
}.

Inside I have another div for each ‘card’ with another div ‘photo that has the image (from Unsplash) and some buttons.

.card{
@include flex (start, center, column, 5px);
width: 100%;
height: auto;
margin-top: 10px;}

then each image.

.mainImg{
object-fit: cover;
object-position: center;
max-width: 275px;
max-height: 450px;
height: auto;
border-radius: 15px;
};

As it is, the images take 5 columns but the height of each row is the same for all ‘cards’ instead of each taking the space needed based on their actual size. I’ve played around with templaenter code herete-rows, auto-rows but cannot get it to work. Thanks for your help 🙂

myPage

Newbie JS scope question: How do I pass an object’s key/value pair to the outside of an event handler function? [duplicate]

When I console.log(newObject) inside the function as shown, I see newObject and its key/value pair. However, when I console.log outside the function, I see an empty newObject. How do I pass newObject‘s key/value pair to the outside of the function?

let newObject = {}
document.getElementById("submitButton").addEventListener("click", function () {
  newObject = {firstName: 'John', lastName: 'Doe'}
  console.log(newObject)
})

Are 2 load scripts normal in Livewire?

I have a question about Livewire, im bundling livewire manualy and im seeing 2 load scripts (in the head and at the bottom of the body). im not sure if this is always like this but im not sure. The load scripts are identical so im a bit confused on why this is happening. here is the load script that is being injected twice:

window.addEventListener('load', () => window.setTimeout(() => {
   const makeLink = (asset) => {
       const link = document.createElement('link')

       Object.keys(asset).forEach((attribute) => {
           link.setAttribute(attribute, asset[attribute])
       })

       return link
   }

   const fragment = new DocumentFragment;
   [].forEach((asset) => fragment.append(makeLink(asset)))
   document.head.append(fragment)
}))

Im importing Livewire like this inside my bootstrap.js:

import { Livewire, Alpine } from '../../../../vendor/livewire/livewire/dist/livewire.esm.js';

window.Livewire = Livewire;
window.Alpine = Alpine;

And inside my vite.config.js im resolving Livewire:

resolve: {
    alias: {
        livewire: path.resolve(__dirname, 'vendor/livewire/livewire/dist/livewire.esm.js')
    }
},

And chunking Livewire into the core chunk:

manualChunks: {
   "core": ["axios", "livewire"],
   "vendor": ["bootstrap"],
   "charts": ["d3"]
},

Does enyone know why this happens and if its a problem?

If this is a problem how can i fix it, thanks in advance for any insight.

Not able to access POST params in PHP when called from JS [closed]

I have created a very simple PHP file as displayed below.

If I call the API using postman, it works fine and prints the value of “a”.
But when I call through my javascript code, it only returns “……..>>>>>>>” but not the value of “a”.

header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: content-type,x-requested-with");
echo "........>>>>>>>";
$name = $_POST['a'];
echo $name;

Javascript code

fetch(url, {
method: "POST",
body: JSON.stringify(attrObj),
headers: {
"Content-type": "application/json",
},
})
.then((response) => response.json())
.then((json) => callback.call(obj, json));

Not able to access POST params in PHP when called from JS

I have created a very simple PHP file as displayed below.

If I call the API using postman, it works fine and prints the value of “a”.
But when I call through my javascript code, it only returns “……..>>>>>>>” but not the value of “a”.

header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: content-type,x-requested-with");
echo "........>>>>>>>";
$name = $_POST['a'];
echo $name;

Javascript code

fetch(url, {
method: "POST",
body: JSON.stringify(attrObj),
headers: {
"Content-type": "application/json",
},
})
.then((response) => response.json())
.then((json) => callback.call(obj, json));