How to Annotate an Architectural Diagram of a Fort with Geo-Coordinates for Mapping Purposes?

I have an image that represents the architectural diagram of a fort. I want to annotate this diagram with geo-coordinates so I can pinpoint my current location over the image.

architecture diagram of a fort

Considerations:

  1. It’s uncertain if this diagram is true to size. This means it is only a visual representation of the actual fort, and dimensions in this map cannot be scaled to match the dimensions of the actual fort.
  2. The diagram is tilted at a certain angle and does not represent an exact topographic view (view from above) of the fort. Therefore, I’m not sure how to georeference this image and use it as a layer on mapping services like Google Maps, OpenStreetMap, or Leaflet.

As a coder, I am thinking about placing coordinates (x, y) over certain pixels of this image. This would create a virtual grid over the image, allowing me to cross-reference my location in the grid.

Could anyone suggest any other solutions or systems I might be unaware of?

Puppeteer, how to select button which has no id

Very new to puppeteer & javascript, and I have no JSON experience at all. I’m trying to select the following button:

<button style=SomeColorInfo class=LongListOfClassNames type="button" role="button">
    <span>visible text</span>
</button>

(I have indications that the site already knows it’s being scraped, so I’d rather not publicise the URL or specific details of the code unless necessary.)

I can select the button using (a very long) xpath, but this seems risky as it ties my code to the particular structure of the DOM. Since the page doesn’t have many buttons, and the text in the <span> is extremely unlikely to ever change, I’d prefer that the code look for a button with that text.

There’s no scrolling or next-page issues to take care of; this page is finite. I’m just banging my head against a brick wall trying to get the button selected!

I have tried each of the following in turn:

const theB = await page.locator('button');
const theB = await page
    .locator('button')
    .filter(button => button.innerText === 'visible text');
const theB = await page
    .locator('button')
    .filter(button => button.innerText == 'visible text');
const theB = await page
    .locator('button')
    .filter(button => button.innerText = 'visible text');
const theB = await page
    .locator('button')
    .filter(button => button.innerText.startsWith('visible'));
const theB = await page
    .locator('button')
    .filter(button => button.innerText.contains('visible'));
const theB = await page
    .locator('button')
    .filter(button => button.innerHTML.contains('visible'));
const theB = await page.locator('::-p-aria([role="button"])');

Most of these have been inspired by threads in here. None are working. Each time I get undefined from console.log(theB.innerHTML);

(In case it makes a difference, the end goal is to click this button. Since I wasn’t managing to achieve that I backed off slightly, to just get it selected and then read out its html.)

Can someone please point me in the right direction of solving this seemingly basic problem? Many thanks!

Why isn’t validate function on a NestJs strategy class not being called?

I’m trying to properly activate JwtStategy in a NestJs/Angular project of mine. The problem is that no matter what I do, stategy’s validate() function is not called. Users should be validated in a Microsoft Active Directory server.
Here’s my code:

NestJs Backend:

Authentication Module:

import { Module } from '@nestjs/common';

import { AuthenticationService } from './authentication.service';
import { ConfigService } from '@nestjs/config';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

import { MyProjectStrategy } from './genius.strategy';
import { AuthenticationController } from './authentication.controller';
import { UsersService } from 'src/users/users.service';

const jwtFactory = {
    useFactory: async (configService: ConfigService) => {
        return {
            privateKey: configService.get<string>('JWT_PRIVATE_KEY_EC512', ''),
            publicKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
            signOptions: {
                expiresIn: configService.get('JWT_EXP_H'),
            },
        };
    },
    inject: [ConfigService],
};


@Module({
    imports: [
        JwtModule.registerAsync(jwtFactory),
        PassportModule
    ],
    controllers: [AuthenticationController],
    providers: [
        MyProjectStrategy, 
        ConfigService,      
        AuthenticationService,
        UsersService
    ],
    exports: [
        AuthenticationService,
        JwtModule, 
        MyProjectStrategy, 
        PassportModule
    ],
})
export class AuthenticationModule {}

Authenticaton Controller:

import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';

import { AuthenticationService } from './authentication.service';

@Controller('authentication')
export class AuthenticationController {

    constructor(
        private readonly authenticationService: AuthenticationService
    ) { }

    @Post('/authenticate/:username')
    async postAutenticar(@Param('username') username: string, @Body() body: any): Promise<any> {
        if (!body.password) 
            return { status: 500, msg: "Can't login - password or organization not informed", error: {}};

        return await this.authenticationService.authenticate(username, body.password);
    }
}

Autentication Service:

import { Injectable } from '@nestjs/common';
import 'dotenv/config';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';

import { User } from '../common/models/user.model';
import { UsersService } from 'src/users/users.service';

export class AuthenticationError extends Error { }

@Injectable()
export class AuthenticationService {

    constructor(
        private usersService: UsersService,
        private jwtService: JwtService,
        private configService: ConfigService
    ) {
    }

    async authenticate(username: string, password: string): Promise<{ accessToken: string}> {
        let loggedUser: boolean | User = (await this.usersService.logUser(username, password)).result;
        if (!(loggedUser instanceof User))
            throw new AuthenticationError("Invalid user");

        const accessToken: string = this.jwtService.sign(loggedUser.toPlainObj(), {
            secret: this.configService.get('JWT_PRIVATE_KEY_EC512', ''),
            algorithm: 'ES512',
            expiresIn: 10000
        });

        if (loggedUser instanceof User)
            console.log('User ', loggedUser.name, ' logged.');
        return { accessToken };
    }

    async refreshToken(
        username: string, password: string,
    ): Promise<{ accessToken: string }> {

        let loggedUser = this.authenticate(username, password);
        if (loggedUser instanceof User)
            console.log('User ', loggedUser.name, ' updated token.');
        return loggedUser;
    };

}

Stategy:

import { Injectable, UnauthorizedException, Request } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { User } from 'src/common/models/user.model';
import { UsersService } from 'src/users/users.service';

@Injectable()
export class MyProjectStrategy extends PassportStrategy(Strategy, 'myproject-jwt') {
    constructor(
        private usersService: UsersService,
        private configService: ConfigService
    ) {
        super({
            secretOrKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            algorithms: ['ES512'],
        });
    }

    async validate(user: any): Promise<boolean> {
        console.log('validate');
        return !!(await this.usersService.logUser(user.username, user.password)).result;
    }
}

Guard:

import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'
import { JwtService } from '@nestjs/jwt';
import { ExtractJwt, Strategy } from 'passport-jwt';

import { AuthenticationService } from './authentication.service';
import { Request } from 'express';
import 'dotenv/config';
import { User } from 'src/common/models/user.model';
import { TokenExpiredException } from './token-expired.exception';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class MyProjectGuard extends AuthGuard('myproject-jwt') {

    constructor(
        private readonly authenticationService: AuthenticationService,
        private jwtService: JwtService,
        private configService: ConfigService
    ) {
        super({
          jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
          secretOrKey: configService.get<string>('JWT_PUBLIC_KEY_EC512', ''),
        });
    }

    
    async canActivate(context: ExecutionContext): Promise<boolean> {
        console.log('canActivate');
        const request: Request = context.switchToHttp().getRequest();

        const token = request.header('Authorization');
        if (!token) 
            throw new HttpException('Header Bearer <token> missing', HttpStatus.UNAUTHORIZED);

        const parts = token.split(' ');
        if (parts.length !== 2 || parts[0] !== 'Bearer') 
            throw new HttpException('Header Bearer <token> invalid', HttpStatus.UNAUTHORIZED);

        let user = new User(this.jwtService.decode(parts[1]));

        if (user.administrator)
            return true;
        else
            throw new HttpException('Usuário não autorizado', HttpStatus.UNAUTHORIZED);
    }
    
    handleRequest(err, user, info) {
        console.log('handleRequest')
        if (err || !user) {
            if (info ? info.message.indexOf('expired') >= 0 : false)
                throw new TokenExpiredException();
            else 
                throw err || new UnauthorizedException();
        }
        return user;
    }   
}

Users controller:

import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';

import { UsersService } from './users.service';
import { MyProjectGuard } from '../authentication/genius.guard';

@Controller('users')
export class UsersController {

    constructor(
        private readonly usersService: UsersService
    ) { }

    @UseGuards(MyProjectGuard)
    @Get('/read/:username')
    async getObter(@Param('username') username: string): Promise<any> {
        console.log('OK');
        return this.usersService.readUser(username);
    }
    
    @Post('/log/:username')
    async postLogar(@Param('username') username: string, @Body() body: any): Promise<any> {
        if (!body.password) 
            return { status: 500, msg: "Can't login - password or organization not informed", error: {}};

        return await this.usersService.logUser(username, body.password);
    }
}

Users service:

import { Injectable } from '@nestjs/common';
import { Client, Attribute, Change } from 'ldapts';
import fs from 'fs';
import 'dotenv/config';

import { User } from 'src/common/models/user.model';
import { BeResult } from 'src/common/models/beResult.model';

@Injectable()
export class UsersService {

    client: Client;
    searchDN: string;
    baseDN: string;
    bindDN: string = process.env.LDAP_BIND_DN;
    admPwd: string = process.env.LDAP_BIND_PWD;

    constructor() {
        this.searchDN = process.env.LDAP_USERS_BASE_DN;
        this.baseDN = process.env.LDAP_USERS_BASE_DN;
        let certCA = fs.readFileSync(process.env.LDAP_CERT_CA_PATH);
        this.client = new Client({
            url: process.env.LDAP_URL,
            timeout: 3000,
            connectTimeout: 5000,
            tlsOptions: {
                ca: [certCA],
            }
        });

        this.client.bind(this.bindDN, this.admPwd);
    }

    (...)       

    async _readUser(username) {
        const { searchEntries } = await this.client.search(this.searchDN, {
            scope: 'sub',
            filter: `&(objectClass=person)(sAMAccountName=${this.susername(username)})`,
            attributes: ['objectClass', 'sn', 'name', 'description', 'sAMAccountName', 'mail', 'telephoneNumber', 'street', 'title', 'unicodePwd'],
        });
        let result = searchEntries.length ? this.convertUserLdapToUser(searchEntries[0]) : {}; 
        return result;
        
    } 

    async readUser(username: string) {
        return { status: 200, msg: 'OK', result: await this._readUser(username) }
    }
    
    async logUser(username: string, password: string) : Promise<BeResult> {
        let res: boolean = false;
        let error: any;
        let user: User | {};
        try {
            await this.client.unbind();
            await this.client.bind(`mydomain\${this.susername(username)}`, password);
            res = true; 
        } catch(e: any) {
            if (e.name != 'InvalidCredentialsError')
                error = e;
        } finally {
            if (res) {
                user = await this._readUser(username);
            }
            await this.client.unbind();
            await this.client.bind(this.bindDN, this.admPwd);
            return erro ? { status: 500, msg: "Can't log", erro } :
                { status: 200, msg: res ? "Logged user" : "Invalid Credential", result: res ? user : false };
        }
    }

    (...)

}

Frontend:

Users service:

import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
import { User } from '../model/user.model';
import { BehaviorSubject, catchError, map, Observable, tap } from 'rxjs';
import { BeResult } from '../model/beResult.model';
import { HttpClient } from '@angular/common/http';
import { ProcessHTTPMsgService } from './process-httpmsg.service';
import { jwtDecode } from "jwt-decode";

@Injectable({
    providedIn: 'root'
})
export class UsersService {

    token: string = '';

    constructor(
        private http: HttpClient,
        private processHTTPMsgService: ProcessHTTPMsgService,
    ) {
    }

    private currentUserSource = new BehaviorSubject<User>(new User({.name: 'My Name (test)', organizacao: 'My Organization (test)' }));
    currentUser = this.currentUserSource.asObservable();

    getHeaders() {
        return { headers: { 'Authorization': `Bearer ${this.token}` } };
    }

    setCurrentUser(user: User) {
        this.currentUserSource.next(user);
    }

    (...)    

    log(user: User): Observable<boolean | User | {}> {
        let body: any = user;
        body['headers'] = this.getHeaders();

        return this.http
            .post<BeResult>(`${environment.apiUrl}/authentication/authenticate/${user.username}`, body)
            .pipe(map(r => this.convertResultado(r)), catchError(this.processHTTPMsgService.handleError));
    }

    public getUserByUsername(user: User): Observable<User> {
        
        return this.http
            .get<BeResult>(`${environment.apiUrl}/users/read/${user.username}`, this.getHeaders())
            .pipe(tap(console.log), map(r => r.result), catchError(this.processHTTPMsgService.handleError));
    }

}

Login component:

import { Component } from '@angular/core';
import { InputTextModule } from 'primeng/inputtext';
import { CardModule } from 'primeng/card';
import { ButtonModule } from 'primeng/button';
import { Router, RouterLink } from '@angular/router';
import { FormsModule } from '@angular/forms';

import { UsersService } from '../../common/services/users.service';
import { User } from '../../common/model/user.model';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [InputTextModule, CardModule, ButtonModule, RouterLink, FormsModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.scss'
})
export class LoginComponent {
    
    constructor(
        private usersService: UsersService,
        private router: Router
    ) { }

    
    username: string = '';
    password: string = '';  

    log() {
        let user = new User();
        user.username = this.username;
        user.password = this.password;
        this.usersService.log(user).subscribe((user: boolean | User | {}) => {
            if (user instanceof User && user.administrator) {
                this.usersService.setCurrentUser(user);
                this.router.navigate(['one-user']);
            }
        })
    }
    
}

Component that tried to access a protected route:

import { Component } from '@angular/core';
(...)
import { UsersService } from '../../common/services/users.service';
import { User } from '../../common/model/user.model';

@Component({
  selector: 'app-one-user',
  standalone: true,
  imports: [InputTextModule, InputMaskModule, PasswordModule, DividerModule, ButtonModule],
  templateUrl: './one-user.component.html',
  styleUrl: './one-user.component.scss'
})
export class UmUserComponent {
    
    constructor(
        private usersService: UsersService
    ) {
        
    }
    
    gravar() {
        let user = new User({ username: 'myusername'});
        
        this.usersService.getUserByUsername(user).subscribe((user: User) => {
            console.log('user', user)
        })
    }
}

I’ve seen several questions regarding this subject and can’t see anyone that is doing nothing very different of what I’m doing. What’s the problem here ?

In one of the questions I saw, says that validate() is only called when the payload is correct. I’m reazonably certain the payload is correct as it apears correctly when I debug it.
Any ideas ?

I had to translate several parts of the code to english and revised it 3x. Please apologize me if I missed something.

Use wrapKey to encrypt one public key with another public key: OperationError

My application calls for encrypting one public key with another public key. I can generate both keys, but calling wrapKey throws an OperationError with no details regarding why it is failing.

const {publicKey: wrappingKey} = await window.crypto.subtle.generateKey(
  {name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256'},
  true, ['decrypt', 'wrapKey', 'unwrapKey']);
const {publicKey: myKey} = await window.crypto.subtle.generateKey(
  {name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256'},
  true, ['encrypt', 'decrypt']);
try {
  const output = await window.crypto.subtle.wrapKey('jwk', myKey, wrappingKey, {name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256'});
  console.log(output);
} catch (e) {
  console.log(e);
}

What about this operation is causing a problem? Running in Chrome.

The empty error looks like this:

{
stack: "OperationError"
code: 0
message: ""
name: "OperationError"
}

universalTransition with grouped data

I have this code and it works perfectly. In it I show how I would like to make the transition, with the dots moving perfectly:

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
  <style>
    .chart {
      width: 700px;
      height: 430px;
      margin: 0 auto;
    }
  </style>
</head>

<button class='barBttn'>
Bar Chart
</button>

<button class='scatterBttn'>
Scatter Chart
</button>

  <div class="chart"></div>

  <script>
    
    function chartSystem () {
    
      return {
        'maleData': [
            ['x', 'y'],
            [150, 45], 
            [160, 50], 
            [170, 55], 
            [180, 60]
        ],
        'femaleData': [
            ['x', 'y'],
            [160, 65], 
            [170, 70], 
            [180, 75], 
            [190, 80]
        ],
        'barData': [
          ['title', 'values', 'itemGroupId'],
          ['Male', 56, 'male'],
          ['Female', 88, 'female']
        ]
      }
    
    }
    
    //
    let pullDataset = null;
    let pullData = null;
    
    function chartSend () {
      //
      const dfInit = chartSystem();
      //
      pullDataset = [
        {
          source: dfInit.maleData,
          sourceHeader: true
        },
        {
          source: dfInit.femaleData,
          sourceHeader: true
        },
        {
          source: dfInit.barData,
          sourceHeader: true
        }
      ]
      //
      pullData = [
        {
          data: dfInit.barData.slice(1).map(item => ({
            name: item[0],
            value: item[1],
            itemGrmoupId: item[2]
          }))
        }
      ]
    }
    
    chartSend();
    
    //
    const chartUse = echarts.init(document.getElementsByClassName("chart")[0]);
    
    // Configuração do scatter plot
function chartFrameSwitch0() {
  const xAxis0 = {
    scale: true
  };
  const yAxis0 = {
    scale: true
  };

  const series0 = {
    type: 'scatter',
    id: 'male',
    datasetIndex: 0,
    encode: {
      x: 0,
      y: 1
    },
    xAxisIndex: 0,
    yAxisIndex: 0,
    dataGroupId: 'male', // Consistência com o gráfico de barras
    universalTransition: {
      enabled: true,
      delay: function (idx, count) {
        return Math.random() * 400;
      }
    },
    itemStyle: { color: '#1e90ff' } // Cor para "Male"
  };

  const series1 = {
    type: 'scatter',
    id: 'female', // identifica a serie (serieKey)
    dataGroupId: 'female', // agrupa a serie
    datasetIndex: 1,
    encode: {
      x: 0,
      y: 1
    },
    xAxisIndex: 0,
    yAxisIndex: 0,
    universalTransition: {
      enabled: true,
      delay: function (idx, count) {
        return Math.random() * 400;
      }
    },
    itemStyle: { color: '#ff69b4' } // Cor para "Female"
  };

  const option = {
    dataset: [pullDataset[0], pullDataset[1]],
    xAxis: [xAxis0],
    yAxis: [yAxis0],
    series: [series0, series1]
  };

  chartUse.setOption(option, { replaceMerge: ['xAxis', 'yAxis', 'series'] });
}

function chartFrameSwitch1() {

  const xAxis0 = {
    type: 'category',
    scale: false
  };
  const yAxis0 = {
    type: 'value',
    scale: false
  };

  const series0 = {
    type: 'bar',
    datasetIndex: 0,
    encode: {
      x: 0,
      y: 1,
      itemGroupId: 2
    },
    itemStyle: {
      color: (params) => {
        const color = ['#1e90ff', '#ff69b4'];
        return color[params.dataIndex];
      }
    },
    universalTransition: {
      enabled: true,
      divideShape: 'split',
      seriesKey: ['male', 'female'], // Consistência com o scatter
      delay: function (idx, count) {
        return Math.random() * 400;
      }
    }
  };

  const option = {
    dataset: [pullDataset[2]],
    xAxis: [xAxis0],
    yAxis: [yAxis0],
    series: [series0]
  };

  chartUse.setOption(option, { replaceMerge: ['xAxis', 'yAxis', 'series'] });
}

chartFrameSwitch0();

    const scatterClick = document.getElementsByClassName('scatterBttn')[0];
    const barClick = document.getElementsByClassName('barBttn')[0];
    
    scatterClick.addEventListener('click', chartFrameSwitch0);
    barClick.addEventListener('click', chartFrameSwitch1);
    
  </script>

But instead of doing series.type: 'scatter' twice, I’d like to separate them with a group divider, as shown in chartSystem.genderData. I would like to pass the id and dataGroupId for each of the groups, without having to do two series type: 'scatter' for this. Is this possible? See that the transition I tried didn’t work, it doesn’t occur with the smoothed effect, as we saw in the first example provided.

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
  <style>
    .chart {
      width: 700px;
      height: 430px;
      margin: 0 auto;
    }
  </style>
</head>

<button class='barBttn'>Bar Chart</button>
<button class='scatterBttn'>Scatter Chart</button>

<div class="chart"></div>

<script>
  function chartSystem() {
    return {
      genderData: [
        ['x', 'y', 'dataGroupId'],
        [150, 45, 'male'],
        [160, 50, 'male'],
        [170, 55, 'male'],
        [180, 60, 'male'],
        [160, 65, 'female'],
        [170, 70, 'female'],
        [180, 75, 'female'],
        [190, 80, 'female']
      ],
      barData: [
        ['Title', 'values', 'itemGroupId'],
        ['Male', 56, 'male'],
        ['Female', 88, 'female']
      ]
    };
  }

  let pullDataset = null;
  let pullData = null;

  function chartSend() {
    const dfInit = chartSystem();

    // Definindo dataset consolidado
    pullDataset = [
      {
        source: dfInit.genderData,
        sourceHeader: true
      },
      {
        source: dfInit.barData,
        sourceHeader: true
      }
    ];

    // Preparando os dados para o gráfico de barras
    pullData = [
      {
        data: dfInit.barData.slice(1).map(item => ({
          name: item[0],
          value: item[1]
        }))
      }
    ];
  }

  chartSend();

  const chartUse = echarts.init(document.getElementsByClassName("chart")[0]);

  // Configuração do scatter plot com `groupId`
 // Configuração do scatter plot com `dataGroupId`
function chartFrameSwitch0() {
  const xAxis0 = {
    scale: true
  };
  const yAxis0 = {
    scale: true
  };

  const series0 = {
    type: 'scatter',
    datasetIndex: 0,
    encode: {
      x: 0, // Coluna 'x' no dataset
      y: 1, // Coluna 'y' no dataset
      id: 2,
      dataGroupId: 2 // Modificado para `dataGroupId`
    },
    universalTransition: {
      enabled: true,
      delay: function (idx, count) {
        return Math.random() * 400;
      }
    },
    itemStyle: {
      color: function (params) {
        // Cor dinâmica baseada no dataGroupId
        return params.data[2] === 'male' ? '#1e90ff' : '#ff69b4';
      }
    }
  };

  const option = {
    dataset: [pullDataset[0]], // Dataset consolidado
    xAxis: [xAxis0],
    yAxis: [yAxis0],
    series: [series0]
  };

  chartUse.setOption(option, { replaceMerge: ['xAxis', 'yAxis', 'series'] });
}

  // Configuração do gráfico de barras
  function chartFrameSwitch1() {
    const xAxis0 = {
      type: 'category'
    };
    const yAxis0 = {
      type: 'value'
    };

    const series0 = {
      type: 'bar',
      datasetIndex: 0,
      encode: {
        x: 0,
        y: 1,
        itemGroupId: 2
      },
      itemStyle: {
        color: function (params) {
          const colors = ['#1e90ff', '#ff69b4'];
          return colors[params.dataIndex];
        }
      },
      universalTransition: {
        enabled: true,
        divideShape: 'split',
        seriesKey: ['male', 'female'],
        delay: function (idx, count) {
          return Math.random() * 400;
        }
      }
    };

    const option = {
      dataset: [pullDataset[1]],
      xAxis: [xAxis0],
      yAxis: [yAxis0],
      series: [series0]
    };

    chartUse.setOption(option, { replaceMerge: ['xAxis', 'yAxis', 'series'] });
  }

  // Inicializando o scatter plot
  chartFrameSwitch0();

  // Botões para alternar entre gráficos
  const scatterClick = document.getElementsByClassName('scatterBttn')[0];
  const barClick = document.getElementsByClassName('barBttn')[0];

  scatterClick.addEventListener('click', chartFrameSwitch0);
  barClick.addEventListener('click', chartFrameSwitch1);
</script>

How to make this adjustment?

How to pass key to React functional component inside .map()?

I have faced a typical problem of React must be having ‘key prop for each child’ inside .map() method.

I have tried to make a key inside a functional component, like…

export default function FunctionalComponent({ title, parentDashedTitle }) {
    const dashedTitle = title.split(' ').join('-');

    return <div
    key={`${dashedTitle}-in-${parentDashedTitle}`}
    >
        {title}
    </div>
}

But it didn’t work. I have found that React requires the ‘key’ prop to be pointed inside the .map() method’s function itself, but any time I try to put ‘key’ prop inside functional component, like…

//Parent.js
export default function ParentComponent({ bigTitle, titles }) {
    const parentDashedTitle = bigTitle.split(' ').join('-');

    return <>
        {
            titles.map(title => {
                const dashedTitle = title.split(' ').join('-');

                return <FunctionalComponent
                key={`${dashedTitle}-in-${parentDashedTitle}`}
                title={title}
                parentDashedTitle={parentDashedTitle}
                />
            })
        }
    </>
}

//Child.js
export default function FunctionalComponent({ title, key }) {
    return <div key={key}>
        {title}
    </div>
}

but it doesn’t work either and returns an error of “key is not a prop and will return undefined if it’s called”. Is there any solution to this problem? Do you have any suggestions?
It doesn’t affect work or appearance of my website, but I want this error to be gone. If it helps, I test it in Safari.

Failed to execute ‘requestFullscreen’ on ‘Element’ – The issue is only on Android (Chrome); it works fine on iOS

I created a solution in React for entering and exiting fullscreen mode on a Vimeo player, depending on the view mode (landscape or portrait). When the user changes the phone’s orientation from portrait to landscape, the Vimeo video switches to fullscreen. It exits fullscreen when the user returns to portrait mode.

This works perfectly on every iPhone (iOS – Safari, Chrome). However, on Android (Chrome), I get the message: “Failed to execute ‘requestFullscreen’ on ‘Element’: API can only be initiated by a user gesture.”

Fullscreen is enabled only after I click the play button on the video and then change the phone’s orientation.

import Vimeo, { EndEvent, PauseEvent, PlayEvent } from "@u-wave/react-vimeo";
import Player from "@vimeo/player";

const CoursePlayer = () => {
    const vimeoPlayerRef = useRef<any>(null);
    const isMobile = useMediaQuery(theme.breakpoints.down("md"));

    useEffect(() => {
          if (isMobile) {
             window.addEventListener("resize", (event) => {
                if (window.innerHeight > window.innerWidth) {
                   console.log("Orientation changed: Portrait");
                   changeViewMod(true);
                } else {
                   console.log("Orientation changed: Landscape");
                   changeViewMod(false);
                }
             });
    
             return () => {
                window.removeEventListener("resize", () => {});
             };
          }
       }, []);
    
    const changeViewMod = (currentViewport: boolean) => {
          if (!vimeoPlayerRef.current) {
             console.warn("Vimeo player ref is not ready");
             return;
          }
    
          const playerElement = vimeoPlayerRef.current.player?.element;
          if (playerElement) {
             const player = new Player(playerElement);
             if (!currentViewport) {
                player.requestFullscreen().then(function() {
                   console.log('enter the fullscreen');
                }).catch(function(e) {
                   console.error(e);
                });
             } else {
                player.exitFullscreen().then(function() {
                   console.log('exit the fullscreen');
                }).catch(function(e) {
                   console.error(e);
                });
             }
          }

return (
    <>
<Vimeo
    showTitle={false}
    video={currentLesson.video?.ID}
    responsive={true}
    playsInline={true}
    autoplay={false}
    ref={vimeoPlayerRef}
 />
</>
)
};

Why sometmes mouseup does not work properly when mousemove is fired after mousedown on an targeted element

I am trying to create a draggable input field. On click of ‘Create input’ button, an input is created and a mousedown event is registered on the created input. And dragStart is fired, which further register mouseup and mousemove, which handles the drag functionality.

Most of the time, the dragging functionality works properly, but if I move the mouse very quickly, the mouseup event sometimes fails to register. I am unsure what might be causing this issue.

Thank you in advance for your insights!

const inputs = [];
let selectedInput = null;
let bufferX = null;
let bufferY = null;
let isDragging = false;

const workAreaLeftMargin = parseInt(
  getComputedStyle(document.querySelector("#work-area")).marginLeft
);

const dragStart = (e) => {
  // Prevent unwanted propagation
  e.stopPropagation();

  // Initialize dragging state
  selectedInput = e.target;
  if (!selectedInput) return;

  const rect = selectedInput.getBoundingClientRect();
  bufferX = e.clientX - rect.left;
  bufferY = e.clientY - rect.top;

  isDragging = true;

  // Add listeners for drag lifecycle
  document.addEventListener("mousemove", dragging);
  document.addEventListener("mouseup", dragEnd);

  console.log("Drag started");
};

const dragging = (e) => {
  if (!isDragging || !selectedInput) return;

  // Update position
  const newTop = e.clientY - bufferY;
  const newLeft = e.clientX - bufferX - workAreaLeftMargin;

  selectedInput.style.top = `${newTop}px`;
  selectedInput.style.left = `${newLeft}px`;
};

const dragEnd = (e) => {
  if (!isDragging) return;

  // Clean up event listeners
  document.removeEventListener("mousemove", dragging);
  document.removeEventListener("mouseup", dragEnd);

  // Reset state
  selectedInput = null;
  isDragging = false;

  console.log("Drag ended");
};

function createText() {
  const workArea = document.querySelector("#work-area");

  // Create the draggable wrapper
  const inputWrapper = createElement("div");
  inputWrapper.index = inputs.length++;
  inputWrapper.className = `p-1 absolute group cursor-move border flex items-center justify-center`;
  inputWrapper.style.top = `${inputWrapper.index * 5}px`;
  inputWrapper.style.left = `${inputWrapper.index * 5}px`;
  inputWrapper.innerHTML = `
    <div class="relative flex items-center justify-center pointer-events-none">
      <div
        class="border p-2 rounded-[6px] bg-white text-[10px] absolute h-full group-hover:opacity-50 opacity-0 duration-200 select-none pointer-events-none"
      >
        <p class="pointer-events-none">X : 0</p>
        <p class="pointer-events-none">Y : 0</p>
      </div>
      <input
        class="placeholder-white text-white caret-white bg-transparent text-center p-[10px] border-[2px] border-white focus:border-white focus:outline-none rounded-[6px] max-w-[48px] w-full min-w-[16px] pointer-events-none"
      />
    </div>
  `;

  // Attach `mousedown` to initiate drag
  inputWrapper.addEventListener("mousedown", dragStart);

  // Append to the work area
  workArea.appendChild(inputWrapper);
}

// Utility to create DOM elements
const createElement = (name) => document.createElement(name);
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Document</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="./index.js" defer></script>
  </head>
  <body>
    <div id="container" class="bg-red-500 w-screen h-screen flex flex-col">
      <!-- screen -->
      <div id="screen" class="flex-1 bg-slate-200 relative">
        <div
          id="work-area"
          class="h-full m-auto max-w-[700px] bg-blue-500 p-2 relative"
        ></div>
      </div>

      <!-- options -->
      <div id="options" class="flex justify-center items-center">
        <button
          id="create-text"
          type="button"
          class="bg-blue-500 p-2 rounded m-2 text-white"
          onclick="createText()"
        >
          Create input
        </button>
        <div id="resize-text-btn" class="flex items-center justify-center">
          <button
            class="bg-green-200 w-[24px] h-[24px] flex items-center justify-center p-2 rounded-l"
          >
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
              <!--!Font Awesome Free 6.7.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
              <path
                d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
              />
            </svg>
          </button>

          <p class="bg-white h-[24px] leading-[22px]">14px</p>

          <button
            class="bg-red-200 w-[24px] h-[24px] flex items-center justify-center p-2 rounded-r"
          >
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
              <!--!Font Awesome Free 6.7.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
              <path
                d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"
              />
            </svg>
          </button>
        </div>
      </div>
    </div>
  </body>
</html>

I

Convert working C# code for signing data using Ed25519 to JavaScript code

I have a working C# code, which is used to sign data using the Ed25519 algorithm. In the C# code I use the BouncyCastle library. Here is the C# version of the code:

bool SignFunction(byte[] inData, byte[] outData)
{
    if ((inData.Length != 32) || (outData.Length != 64))
    {
        return false;
    }
    byte[] SecretBytes =
    {
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
    };

    Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(SecretBytes, 0);
    Ed25519phSigner signer = new Ed25519phSigner(Array.Empty<byte>());
    signer.Init(true, privateKey);
    signer.BlockUpdate(inData, 0, inData.Length);
    byte[] signature = signer.GenerateSignature();
    Array.ConstrainedCopy(signature, 0, outData, 0, signature.Length);
    return true;
}

I call the function like this:

byte[] InData = 
{
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};
byte[] OutData = new byte[0x40];

SignFunction(InData, OutData);

And the result which is stored in OutData is:

OutData: 98 CA B8 60 77 C2 C7 F3 9A E1 81 5D 2F B6 3E 4D EC 24 84 C6 61 12 20 8F 4D 78 01 32 25 3F 95 60 51 71 0A 4C F4 A8 C2 5D F2 69 EA 49 72 5A D0 99 95 F0 EC 89 53 F7 8D 79 CC 62 EF DA 94 8A 41 06


I want to use the same algorithm in JavaScript. I use “https://cdn.jsdelivr.net/npm/[email protected]/nacl.min.js” for the Ed25519 signing algorithm. Here is my JavaScript code:

const nacl = window.nacl;
    
function onClick() 
{
  updateLabel();
}

function toByteArray(x) 
{
    var hexString = x.toString(16);
    if(hexString.length % 2 > 0) hexString = "0" + hexString;
    var byteArray = [];
    for(var i = 0; i < hexString.length; i += 2) 
    {
        byteArray.push(parseInt(hexString.slice(i, i + 2), 16));
    }
    return byteArray;
}

function toPaddedHexString(num, len) 
{
    str = num.toString(16);
    return "0".repeat(len - str.length) + str;
}

function calculateSHA512(byteArray) 
{
    var hashArrayStr = sha512.hex(byteArray);
    const hashArray = toByteArray(hashArrayStr);

    return hashArray;
}

function SignFunction(inData, outData)
{
    if (inData.length !== 32 || outData.length !== 64) {
        return; // Return early if sizes are incorrect
    }
    
    const SecretBytes = new Uint8Array([
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
]);
var InBytes = new Uint8Array(32).fill(1);
for(var i=0; i<32; i++)
    InBytes[i] = inData[i];

// Generate the key pair from the 32-byte seed (private and public keys)
    const keyPair = nacl.sign.keyPair.fromSeed(SecretBytes);

    // Sign the Hash512Bytes using the private key (keyPair.secretKey)
    const signature = nacl.sign.detached(InBytes, keyPair.secretKey);

    // Copy the signature into outData (64 bytes)
    for (let i = 0; i < signature.length; i++) {
        outData[i] = signature[i];
    }
}

function updateLabel() 
{
  var InData = new Uint8Array(32);
  var OutData = new Uint8Array(64);
  
  InData = toByteArray("0101010101010101010101010101010101010101010101010101010101010101");
  SignFunction(InData, OutData);
  
  var InStr =  "";
  for(var i = 0; i < 32; i += 1) 
    {
        InStr = InStr + toPaddedHexString(InData[i], 2) + ' ';
    }
  
  var OutStr =  "";
  for(var i = 0; i < 64; i += 1) 
    {
        OutStr = OutStr + toPaddedHexString(OutData[i], 2) + ' ';
    }
    label.textContent = 'InData: ' + InStr.toUpperCase() + '   -> OutData: ' + OutStr.toUpperCase();
}

var label = document.querySelector('p');
var button = document.querySelector('button');
button.addEventListener('click', onClick);

The result I get is totally different. Can anyone please advise what library I should use in order to make the JavaScript code generate the same result as the C# code?

I tried to use ChatGpt, because I have almost zero experience with JavaScript, but it didn’t help 🙂

How do I set up a “professional” creative coding workstation in VSCode? [closed]

Background

Hello! I’ve actually got a few questions that pertain to the main point of the post. But for some background: Until September of this year, I’ve been coding with JavaScript in the p5.js Web Editor (a popular creative coding library/IDE) since 2021.

In September of this year, and in an attempt to take off the “training wheels” of my favorite pastime, I migrated my workflow to Visual Studio Code and installed extensions like Live Server and p5.vscode to emulate the behavior of the online editor (e.g., easy setup and automatic preview refresh). By copy-and-pasting the p5.js library into my VSC project, I could use Live Server’s local server feature to code anywhere regardless of internet connection, which I found extremely preferable over using a website to code.

In November, I discovered q5.js, a “spiritual successor to the p5.js and Processing graphics libraries.” It was built for the modern web, supporting WebGPU, including many new features, and clocking in at nearly 2% the size of p5.js, so of course I switched to using the former.

Question 1

To keep myself in-line with the “training wheels” idea that I shared above, should I use a dedicated JavaScript runtime environment?

I’ve heard so much about JavaScript runtimes like Node.js, Bun, and Deno. But I’ve done most of my research on Deno 2, as it appears to be analogous to q5.js, being a “spiritual successor” to Node.js. (Isn’t it true that virtually all serious JS developers use a JavaScript runtime? What’s the point? For my purposes, would using a runtime really help?)

Question 2

If using a JS runtime is worth it, how in the world would I actually use q5.js as a Node.js module or Deno dependency in VSCode?

Having tinkered around with Node.js and npm, I could never get the same behavior of copy-and-pasting q5.js into my project and including it in the index.html file.

Question 3

To again “shed the training wheels”, should I start using TypeScript instead of JavaScript for my projects?

Eventually, I’d like to use TypeScript entirely, as again it appears to be analogous to q5.js in being a “spiritual successor” to JavaScript, adding type-checking, enumerations, and more features that I’d find super handy. (Also, q5.js and Deno provide full support for TypeScript.)

Conclusion

I’m all over the place and I apologize, but no amount of searching the internet and consulting ChatGPT could replace the helpfulness of interactions between real people on this platform. If you have any clarifying questions as to what I’m asking, I’d be happy to answer! Any help at all will be greatly appreciated. Thanks in advance!

define a variable like a path for dynamic gallery

I’m doing a simple website, in my website I have two pages, INDEX and IMAGES.
INDEX have a iframe that load IMAGES, I’m using codes that I found online by the way.

In IMAGES I have a some links like this:

<img
  class="gg-box, other-css-clas"
  src="/IMGS/THUMBNAILS/29.webp"
  alt="Image 29"
  loading="lazy"
  data-title="Image 29"
  data-author="Autor 29"
  data-link="#"
  data-img-fullres="IMGS/FULL-RES/29.jpg"
>

IMAGES have a javascript that send data to INDEX javascript to load a modal css.

<script>
    // Selects all images inside the gg-box class
    const images = document.querySelectorAll(".gg-box img");

    // For each image, add a click event listener
    images.forEach((image, index) => {
        image.addEventListener("click", function() {
            // Get the src attribute (URL) and data attributes of the clicked image
            const imageSrc = image.src;
            const title = image.getAttribute("data-title") || "X . X"; 
            const author = image.getAttribute("data-author") || "X . X";
            const link = image.getAttribute("data-link") || "#"; 

            // Sends a message to the parent (INDEX) with image details
            parent.postMessage({
                type: "openImage",
                src: imageSrc,
                index: index,
                images: Array.from(images).map(img => ({
                    src: img.src,
                    title: img.getAttribute("data-title") || "X . X",
                    author: img.getAttribute("data-author") || "X . X",
                    link: img.getAttribute("data-link") || "#"  
                }))
            }, "*");
        });
    });
</script>

In INDEX I have a simple css modal with a img tag in the middle:

<img
   id="modal-image"
   src=""
   alt="comment"
   onclick="event.stopPropagation()"
>

This is the parent code in INDEX:

// DOM Element Declarations

const ggScreen = document.getElementById('gg-screen');
const modalImage = document.getElementById('modal-image');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const closeBtn = document.getElementById('close-btn');
const downloadBtn = document.getElementById('download-full-res-btn'); // Download button

// Variables for Image Control

let images = [];
let currentImgIndex = -1;

function openImageModal(imageSrc) {
    modalImage.src = imageSrc;
    ggScreen.style.display = 'flex';

    // Make the image focusable
    modalImage.setAttribute("tabindex", "0");
    modalImage.focus();

    const currentImage = images[currentImgIndex];

    if (currentImage) {
        // Fill in the title, author, and link information
        const title = currentImage.title || "X . X";
        const author = currentImage.author || "X . X";
        const link = currentImage.link || "#";

        document.getElementById('data-text-title').textContent = title;
        document.getElementById('data-text-author').textContent = author;
        document.getElementById('data-text-link').innerHTML = `<a href="${link}" target="_blank">Open link</a>`;

        // Set up the download button with the image URL
        const fullResImage = currentImage.fullRes || imageSrc;
        configureDownloadButton(fullResImage);
    }
}

// Function to configure the download button

function configureDownloadButton(imageSrc) {
    const fileName = imageSrc.split('/').pop(); // Extract the file name from the URL
    const fullResUrl = `/IMGS/FULL-RES/${fileName}`; // Complete URL for download

    // Set the download URL attribute on the modal image
    modalImage.setAttribute('data-img-fullres', fullResUrl);

    // Update the download button with the URL and file name
    downloadBtn.setAttribute('href', fullResUrl);
    downloadBtn.setAttribute('download', fileName);
}
// Image download handler
function handleDownloadClick(event) {
    event.stopPropagation(); // Prevent the modal from closing when the download button is clicked

    const imageUrl = modalImage.getAttribute('data-img-fullres');
    const fileName = imageUrl.split('/').pop();

    if (imageUrl) {
        console.log("Download button clicked! The download URL is: " + imageUrl);

        // Create a temporary link for downloading
        const a = document.createElement('a');
        a.href = imageUrl;
        a.download = fileName;

        // Trigger the click for automatic download
        document.body.appendChild(a);
        a.click();

        // Remove the temporary link after the click
        document.body.removeChild(a);
    } else {
        console.log("Error: Could not find the download URL.");
    }
}
// Call the initialization function

init();

The code is working, but not properly.

Information like data-title, data-author and data-link works fine, but data-fullres don’t, what I need is set the id="modal-image" to receive the data-img-fullres="IMGS/FULL-RES/29.jpg" from IMAGES.

Right now I don’t find a way to properly assing the data-img-fullres to the src of id="modal-image", the default behavior is open the THUMBNAIL, in some tries I got a undefined error from the browser.

How can I properly address the data-img-fullres?

define a variable like a path for dinamic gallery

I’m doing a simple website, in my website I have two pages, INDEX and IMAGES.
INDEX have a iframe that load IMAGES, I’m using codes that I found online by the way.

In IMAGES I have a some links like this:

<img
  class="gg-box, other-css-clas"
  src="/IMGS/THUMBNAILS/29.webp"
  alt="Image 29"
  loading="lazy"
  data-title="Image 29"
  data-author="Autor 29"
  data-link="#"
  data-img-fullres="IMGS/FULL-RES/29.jpg"
>

IMAGES have a javascript that send data to INDEX javascript to load a modal css.

<script>
    // Selects all images inside the gg-box class
    const images = document.querySelectorAll(".gg-box img");

    // For each image, add a click event listener
    images.forEach((image, index) => {
        image.addEventListener("click", function() {
            // Get the src attribute (URL) and data attributes of the clicked image
            const imageSrc = image.src;
            const title = image.getAttribute("data-title") || "X . X"; 
            const author = image.getAttribute("data-author") || "X . X";
            const link = image.getAttribute("data-link") || "#"; 

            // Sends a message to the parent (INDEX) with image details
            parent.postMessage({
                type: "openImage",
                src: imageSrc,
                index: index,
                images: Array.from(images).map(img => ({
                    src: img.src,
                    title: img.getAttribute("data-title") || "X . X",
                    author: img.getAttribute("data-author") || "X . X",
                    link: img.getAttribute("data-link") || "#"  
                }))
            }, "*");
        });
    });
</script>

In INDEX I have a simple css modal with a img tag in the middle:

<img
   id="modal-image"
   src=""
   alt="comment"
   onclick="event.stopPropagation()"
>

This is the parent code in INDEX:

// DOM Element Declarations

const ggScreen = document.getElementById('gg-screen');
const modalImage = document.getElementById('modal-image');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const closeBtn = document.getElementById('close-btn');
const downloadBtn = document.getElementById('download-full-res-btn'); // Download button

// Variables for Image Control

let images = [];
let currentImgIndex = -1;

function openImageModal(imageSrc) {
    modalImage.src = imageSrc;
    ggScreen.style.display = 'flex';

    // Make the image focusable
    modalImage.setAttribute("tabindex", "0");
    modalImage.focus();

    const currentImage = images[currentImgIndex];

    if (currentImage) {
        // Fill in the title, author, and link information
        const title = currentImage.title || "X . X";
        const author = currentImage.author || "X . X";
        const link = currentImage.link || "#";

        document.getElementById('data-text-title').textContent = title;
        document.getElementById('data-text-author').textContent = author;
        document.getElementById('data-text-link').innerHTML = `<a href="${link}" target="_blank">Open link</a>`;

        // Set up the download button with the image URL
        const fullResImage = currentImage.fullRes || imageSrc;
        configureDownloadButton(fullResImage);
    }
}

// Function to configure the download button

function configureDownloadButton(imageSrc) {
    const fileName = imageSrc.split('/').pop(); // Extract the file name from the URL
    const fullResUrl = `/IMGS/FULL-RES/${fileName}`; // Complete URL for download

    // Set the download URL attribute on the modal image
    modalImage.setAttribute('data-img-fullres', fullResUrl);

    // Update the download button with the URL and file name
    downloadBtn.setAttribute('href', fullResUrl);
    downloadBtn.setAttribute('download', fileName);
}
// Image download handler
function handleDownloadClick(event) {
    event.stopPropagation(); // Prevent the modal from closing when the download button is clicked

    const imageUrl = modalImage.getAttribute('data-img-fullres');
    const fileName = imageUrl.split('/').pop();

    if (imageUrl) {
        console.log("Download button clicked! The download URL is: " + imageUrl);

        // Create a temporary link for downloading
        const a = document.createElement('a');
        a.href = imageUrl;
        a.download = fileName;

        // Trigger the click for automatic download
        document.body.appendChild(a);
        a.click();

        // Remove the temporary link after the click
        document.body.removeChild(a);
    } else {
        console.log("Error: Could not find the download URL.");
    }
}
// Call the initialization function

init();

The code is working, but not properly.

Information like data-title, data-author and data-link works fine, but data-fullres don’t, what I need is set the id="modal-image" to receive the data-img-fullres="IMGS/FULL-RES/29.jpg" from IMAGES.

Right now I don’t find a way to properly assing the data-img-fullres to the src of id="modal-image", the default behavior is open the THUMBNAIL, in some tries I got a undefined error from the browser.

How can I properly address the data-img-fullres?

Javascript fetch api is not resolving in success or error

I am building a react native app, where I am using a fetch api to get the login response. The code snippet looks as below :

 export function login(username, password) {

    const url = `${baseUrl}/login`;
    const usernamePassword = `${username}:${password}`;
    const encodedString = Buffer.from(usernamePassword).toString('base64');
    console.log('encoded : ', encodedString);

    return fetch(url, {
        headers: {
            'Authorization': `Basic ${encodedString}`,
            'Cache-Control': 'no-cache'
        }
    }).then(resp => {
        console.log(resp);
    }).catch(err => {
        console.log(err)
    });
}

I tried a lot of things, but the last logger which gets printed is :

    console.log('encoded : ', encodedString);

No matter what I try its never printing the loggers added in then or catch.

a few observations :

  • if I remove the Authorization header, its start working fine (getting exception)
  • I am using a sprint-boot backend service and CORS is disabled in that
  • I tried hitting the same request with postman and I could get a 401 response code

Can someone please help resolving this.

Chakra UI: Help on scrollable tab content needed

In Chakra UI version 2, I have the following Tabs component whose content overflows instead of being scrollable:

function App() {
  return (
    <ChakraProvider>
      <Box
        width={300}
        height={300}
        borderWidth={2}
        borderRadius="lg"
        margin={4}
        padding={2}
      >
        <Flex direction="column">
          <Box flex="1">
            <Tabs height="full" display="flex" flexDirection="column">
              <TabList>
                <Tab>Tab 1</Tab>
                <Tab>Tab 2</Tab>
              </TabList>
              <TabPanels overflowY="scroll">
                <TabPanel>
                  <Text>Content for Tab 1</Text>
                </TabPanel>
                <TabPanel>
                  <LoremIpsum />
                </TabPanel>
              </TabPanels>
            </Tabs>
          </Box>
        </Flex>
      </Box>
    </ChakraProvider>
  );
}

The result looks like this:

Overflow

The code will actually work if the Flex and the inner Box component surrounding the Tabs component are removed:

function App() {
  return (
    <ChakraProvider>
      <Box
        width={300}
        height={300}
        borderWidth={2}
        borderRadius="lg"
        margin={4}
        padding={2}
      >
        <Tabs height="full" display="flex" flexDirection="column">
          <TabList>
            <Tab>Tab 1</Tab>
            <Tab>Tab 2</Tab>
          </TabList>
          <TabPanels overflowY="scroll">
            <TabPanel>
              <Text>Content for Tab 1</Text>
            </TabPanel>
            <TabPanel>
              <LoremIpsum />
            </TabPanel>
          </TabPanels>
        </Tabs>
      </Box>
    </ChakraProvider>
  );
}

No overflow

However, these two components originate from other parts of the code, and I only added them to create a minimal working example and narrow down the problem. They unfortunately cannot be removed.

What is causing these issues and how do I fix them?

Click here for a working copy (give it some 30 seconds to load).

(Thanks to any answer in advance, this has been bugging me for quite some time now.)

Chrome plugin content script cannot access all window objects?

I’m trying to fetch an object from the current page window via a content script. The object itself is available if I log the window/this object but when I try to access it via the content script it returns undefined?

I’m using a content script for this that injects itself on document_idle and via “MAIN” world so the js objects of the page are shared with my content script, see manifest.json:

{
  "name": "Hello Extensions",
  "description": "Base Level Extension",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "hello.html",
    "default_icon": "hello_extensions.png"
  },
  "content_scripts": [
    {
      "matches": [
        "https://example.com/*"
      ],
      "js": [
        "content.js"
      ],
      "run_at": "document_idle",
      "world": "MAIN"
    }
  ]
}

And here you have content.js:

addEventListener("load", (event) => {
    console.log("loading")
    console.log(window)
    console.log(window.THEOplayer)
});

And the output in console.log:
window output which clearly depicts the object I want to use

and then the output of theoplayer is undefined.
Output showing a return of an undefined theoplayer

I have tried different solutions accessing it via the this object. Without using the addEventListener…

I can’t seem to find a way to access this object. Even though it seem it’s available in the window it returns a undefined whenever I try to access it via the script. It seems that this object is, one way or another blacklisted from being accessed via my script? But can’t seem to find such a feature via the documentation of the chrome plugin content scripts
. can somebody have a explanation for this and a possible workaround?