How to retrieve automatic values from the Y-axis in the recharts library?

The domain parameter is responsible for generating values on the Y-axis. If expenses are added, the axis generates 5 points (tickCount={5}) from 0 to the "auto" value, which returns an automatically generated value from the library.

Here is my question: how can I retrieve the highest value among the generated values on the Y-axis?

<YAxis
   dataKey="totalExpenses"
   orientation="right"
   type="number"
   tickCount={5}
   fontSize={10}
   fontWeight={500}
   width={40}
   domain={noExpenses || noData ? [0, 120] : [0, "auto"]}
/>

Example Chart

What I need to achieve is, for example, as shown in the sample image, I need to retrieve the value 600, which is the highest value from the Y-axis that was automatically generated in the YAxis component.

Review box disappears when slider to the left is clicked

I am very new to JavaScript.

My review slider works on the button to the right but when left is clicked and try to get from the first to the left last slide the review box disappears

const slides = document.querySelectorAll('.slide')
const leftBtn = document.getElementById('left')
const rightBtn = document.getElementById('right')

let activeSlide = 0

rightBtn.addEventListener('click', () => {
  activeSlide++

  if (activeSlide > slides.length - 1) {
    activeSlide = 0
  }

  setActiveSlide()
})

leftBtn.addEventListener('click', () => {
  activeSlide--

  if (activeSlide > slides.length - 1) {
    activeSlide = slides.length - 1
  }

  setActiveSlide()
})

function setActiveSlide() {
  slides.forEach(slide => {
    slide.classList.remove('active')
    slide.classList.add('hide')
  })

  slides [activeSlide].classList.add('active')
  slides [activeSlide].classList.remove('hide')
}
<section class="section-recensies">
    <div class="recensies container">
        <h2>Recensies</h2>
        <div class="slider-container">

            <div class="recensie-box slide active">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <div class="recensie-box slide hide">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <div class="recensie-box slide hide">
                <p class="recensie-tekst">Recensie</p>
                <img src="{{ asset('images/logo/grace-business-group-lijn-met-kroon.png') }}"
                     alt="Logo Grace Business Group met lijn">
                <p>
                    <span>Naam</span>
                    <span>Bedrijf</span>
                </p>
            </div>

            <button class="recensie-button recensie-button-links" id="left"><</button>
            <button class="recensie-button recensie-button-rechts" id="right">></button>

        </div>
    </div>
</section>

I can’t see why it is not working because it does go to the left. When I click the right button the review box appears again.

Angular Jasmine tests that continue upon previous tests

I have a service called items here is the spec file:

import { TestBed } from '@angular/core/testing';

import { ItemsService } from './items.service';

describe('ItemsListService', () => {
  let service: ItemsService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(ItemsService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should insert new item', () => {
    const item = {
      id: '123',
      name: 'item',
      baseUnit: 'unit',
      brand: 'brand',
      price: 100,
      quantity: 10,
      supplier: 'supplier',
      cost: 50,
      imageUrl: 'url',
    };
    service.insertItem(item);
    const items = service.getItems();
    expect(items.length).toBeGreaterThan(0);
    expect(items).toContain(item);
  });
});

And here is another service called orders that depends on items service to create orders from items stored.

import { TestBed } from '@angular/core/testing';

import { OrdersService } from './orders.service';

describe('OrdersService', () => {
  let service: OrdersService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(OrdersService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should insert new order', async () => {
    const newOrder = await service.insertOrder(itemsService.getItems()[0]);
  });
});

So in order for orders service to create a new order it needs to access the item stored inside items service here service.insertOrder(itemsService.getItems()[0]).

How do I access the same instance of items service created in the spec file?

Syncing two folders

I created custom script to sync data from 2 folders in Google Drive.
To be blunt this is due to me emulating gameboy/gameboy advance on both android device and PC.
The emulator on PC works fine for both GB/GBA games and it stores save files in specified location (does not diffrentiate ROM type, stores and load all .sav files from set location)
But on android I have two emulators (one for GB one for GBA) and each of them have option of syncing with google cloud, but no custom selecting target folder (these are by default named after emulators)
I of course selected one of these folder as save file location for PC emmulator, but it lacks saves from other.
I created google apps script that triggers based on timer (for testing set once every 5 minutes)

function syncFolders() {
  const gbcFolderId = <link to GB folder>; 
  const gbaFolderId = <link to GBA folder>; 

  const gbcFolder = DriveApp.getFolderById(gbcFolderId);
  const gbaFolder = DriveApp.getFolderById(gbaFolderId);

  syncFolderFiles(gbcFolder, gbaFolder); // Sync from GBC to GBA
  syncFolderFiles(gbaFolder, gbcFolder); // Sync from GBA to GBC
}

function syncFolderFiles(sourceFolder, targetFolder) {
  const sourceFiles = sourceFolder.getFiles();
  const targetFilesMap = createFileMap(targetFolder);

  while (sourceFiles.hasNext()) {
    const sourceFile = sourceFiles.next();
    const sourceName = sourceFile.getName();
    const sourceDate = sourceFile.getLastUpdated();

    if (
      targetFilesMap[sourceName] &&
      targetFilesMap[sourceName].date >= sourceDate
    ) {
      continue; // Skip if target file is newer or the same
    }

    // Copy or update the file in the target folder
    if (targetFilesMap[sourceName]) {
      targetFilesMap[sourceName].file.setTrashed(true); // Trash old version
    }
    sourceFile.makeCopy(sourceName, targetFolder);
  }
}

function createFileMap(folder) {
  const fileMap = {};
  const files = folder.getFiles();

  while (files.hasNext()) {
    const file = files.next();
    fileMap[file.getName()] = {
      file: file,
      date: file.getLastUpdated(),
    };
  }

  return fileMap;
}

The issue is that even though this script indeed syncs folders with each other allowing me to have all actual saves in both folders. but some of them are appended with incremented number (1) in the name, file copy style. This also lead to having save file for one of games in literally 5000 copies overnight.

And since old files are set to be trashed many of saves were unusable before renaming and removing index from the name.

Any idea how to fix the script to not append name/ trim it after copying or any other way to make the script work as intended?

How to fix “paths[0]” argument must be of type string. Received an instance of Array” error when introducing Mustache partials with ExpressJS?

index.js logging the view paths to ensure partials are visable

import express from 'express';
import bodyParser from 'body-parser';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import mustacheExpress from 'mustache-express';
import dotenv from 'dotenv';
import homeRouter from './home/router.js';
import jobsRouter from './jobs/router.js';
import errors from './errors/errors.js';

dotenv.config();

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const app = express();

// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, '..', 'public')));

// Configure mustache
app.set('views', [
  path.join(__dirname, 'home'), // Home templates
  path.join(__dirname, 'jobs'), // Jobs templates
  path.join(__dirname, 'errors'), // Error templates
  path.join(__dirname, 'site/partial-views'),
]);
console.log('Views set to:', app.get('views'));

app.set('view engine', 'mustache');
app.engine('mustache', mustacheExpress());

// Routes
app.use('/', homeRouter);
app.use('/jobs', jobsRouter);

// Error handling middleware
app.use(errors.notFound);
app.use(errors.internalServerError);
app.use(errors.emailError);

export default app;

terminal with views log

[nodemon] starting `node ./app/server.js`
Views set to: [
  'C:\Users\Keegan\source\repos\my-website\app\home',
  'C:\Users\Keegan\source\repos\my-website\app\jobs',
  'C:\Users\Keegan\source\repos\my-website\app\errors',
  'C:\Users\Keegan\source\repos\my-website\app\site\partial-views'
]
Server running on port 3000
Connected to MySQL database

terminal with error

node:internal/errors:540
      throw error;
      ^

TypeError [ERR_INVALID_ARG_TYPE]: The "paths[0]" argument must be of type string. Received an instance of Array
    at Object.resolve (node:path:198:9)
    at C:UsersKeegansourcereposmy-websitenode_modulesmustache-expressmustache-express.js:99:24
    at C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:247:13
    at eachOfArrayLike (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:507:13)
    at eachOf (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:627:16)
    at awaitable (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:212:32)
    at _asyncMap (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:245:16)
    at Object.map (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:750:16)
    at Object.awaitable [as map] (C:UsersKeegansourcereposmy-websitenode_modulesasyncdistasync.js:212:32)
    at loadAllPartials (C:UsersKeegansourcereposmy-websitenode_modulesmustache-expressmustache-express.js:94:8) {
  code: 'ERR_INVALID_ARG_TYPE'
}

Node.js v22.12.0
[nodemon] app crashed - waiting for file changes before starting...

directory

my-website/
----app/
--------index.js
--------server.js
--------db.js
--------config.js
--------jobs/router.js, jobs.mustache
--------home/router.js, home.mustache
--------errors/errors.js, erorrs.mustache
--------site/partial-views/head.mustache
----public/

home.mustache

<html lang="en" data-bs-theme="auto">
{{> head}}
</html>

home/router.js

import express from 'express';
import connection from '../db.js';

const router = express.Router();

router.get('/', (req, res, next) => {
  const query = 'SELECT id, title, location, salary, posted FROM jobs';

  connection.query(query, (err, results) => {
    if (err) {
      return next(err);
    }
    res.render('home', { jobs: results });
  });
});

export default router;

home.mustache is currently the only template attempting to use the partial. Removing the 1 line {{> head}} in home.mustache and manually copying the contents of head.mustache in its place removes the error, and thewebsite renders the ‘/’ root endpoint correctly.

Thank you for your time and feedback !

Auth.js/NextAuth – Application redirect not working after successful sign in

I am using Auth.js Google Oauth to login the user, then checking if that email exists in my database. If yes, then I route the user to “/dashboard/inventory”. Once sign in is successful, my app is not auto-routing the user to “/dashboard/inventory”. It’s stuck on “/”. I am able to manually go to “/dashboard/inventory” and verify the authentication is successful.

If the user signs out, then the app properly redirects to “/” as I have setup, but that’s probably because I also have a middleware.js in place which is set to redirect to “/” if no user session is found.

app/auth.js

import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import { supabase } from "@/lib/supabase"

export const { handlers, auth, signIn, signOut } = NextAuth({
    providers: [Google],
    callbacks: {
        async signIn({ user }) {
            const { data, error } = await supabase
                .from('stores')
                .select()
                .eq('email', user.email)
                .single()

            if (error || !data) {
                return '/error'
            }

            return true
        },
    },
    pages: {
        error: '/error'
    }
})

app/utils/auth-utils.js

"use server"
import { signIn, signOut } from '@/auth'

const SignInWithGoogle = async () => {
    await signIn('google', { callbackUrl: "/dashboard/inventory" })
}

const SignOut = async () => {
    await signOut({ callbackUrl: "/" })
}

export { SignInWithGoogle, SignOut }

app/components/AuthComponent.js

"use client"
import { useSearchParams } from "next/navigation"
import { Button } from "@/components/ui/button"
import { SignInWithGoogle, SignOut } from "@/lib/auth-utils"

const AuthComponent = () => {
    const searchParams = useSearchParams()
    const error = searchParams?.get('error')

    return (
        <div className="flex flex-col w-full h-full justify-center items-center">{error && (
            <p className="text-red-500 mb-4">
                {error}
            </p>
        )}
            <form action={SignInWithGoogle}>
                <Button className="gap-2 font-semibold m-4" variant="secondary">
                    sign in with google
                </Button>
            </form>
            <form action={SignOut}>
            <Button type="submit" variant="secondary">sign out</Button>
            </form>
            </div>
    )
}

export default AuthComponent

app/page.js

import AuthComponent from "@/components/app/AuthComponent"

const HomePage = () => {
    

    return (
        <div className="flex w-full justify-center items-center overflow-hidden flex-col h-dvh p-4">
            <AuthComponent/>
        </div>
    )
}

export default HomePage

string length issue when sent to Laravel server

When a string is appended to a FormData object and sent to Laravel server, who decides the encoding that will be used? How can we make sure that the string length reported on the client-side matches the one that we get on the server, and not changed due to control characters, especially new line (rn vs ‘n`)?

Detail

I’m POSTing my model object that includes, among other things, its description that is a string (max: 1000 chars), using axios from my SPA (Vue 3 + Vuetify + TS) to the backend Laravel server’s API endpoint. Validations are in place on both client and server sides. On that frontend, this description is being displayed in a v-textarea that reports that the content is exactly 1000 characters, including 8 newline characters. This description, along with other data (including images), is then POSTed to the server using a FormData object.

On the server-side, this string is received as 1008 characters long instead of 1000, which causes the validation rule to fail. Upon inspecting the client- and server-side versions of the string, the only difference is that those newlines have been converted from n to rn at some point. I’m just looking for how to avoid this conversion or at least make the reported length match on both client and server sides.

Using igGrid to generate a horizontal grid based on columns

I am trying to get igGrid to generate a grid which would look something like this:

 |       | 2005/06 | 2006/07 | 2007/08 | 2008/09 | 2009/10 | 2010/11 | 2011/12 | 2023/13
 |ID     | 169530  | 169531  | 169532  | 169533  | 169534  | 169535  | 169536  | 169537
 |Land   | 0       | 1       | 2       | 3       | 4       | 5       | 6       | 7
 |Total  | 100     | 101     | 102     | 103     | 104     | 105     | 106     | 107

I have 2 arrays, columns and countyPivot which contain the data needed to generate the igGrid.

The array called columns has this data:

 columns[0] = {dataType: "string", headerText: "2005/06", hidden: false, key: "2005/06", width: "75px"}
 columns[1] = {dataType: "string", headerText: "2006/07", hidden: false, key: "2006/07", width: "75px"}
 columns[2] = {dataType: "string", headerText: "2007/08", hidden: false, key: "2007/08", width: "75px"}
 columns[3] = {dataType: "string", headerText: "2008/09", hidden: false, key: "2008/09", width: "75px"}
 columns[4] = {dataType: "string", headerText: "2009/10", hidden: false, key: "2009/10", width: "75px"}
 columns[5] = {dataType: "string", headerText: "2010/11", hidden: false, key: "2010/11", width: "75px"}
 columns[6] = {dataType: "string", headerText: "2011/12", hidden: false, key: "2011/12", width: "75px"}
 columns[7] = {dataType: "string", headerText: "2012/13", hidden: false, key: "2012/13", width: "75px"}

And the array called countyPivot has this data:

 countyPivot[0] = {{Key: "id", Value: 169530}, {Key: "TaxYear", Value: "2005/06"}, {Key: "Land", Value: 0}, {Key: "Total", Value: 100}}
 countyPivot[1] = {{Key: "id", Value: 169531}, {Key: "TaxYear", Value: "2006/07"}, {Key: "Land", Value: 1}, {Key: "Total", Value: 101}}
 countyPivot[2] = {{Key: "id", Value: 169532}, {Key: "TaxYear", Value: "2007/08"}, {Key: "Land", Value: 2}, {Key: "Total", Value: 102}}
 countyPivot[3] = {{Key: "id", Value: 169533}, {Key: "TaxYear", Value: "2008/09"}, {Key: "Land", Value: 3}, {Key: "Total", Value: 103}}
 countyPivot[4] = {{Key: "id", Value: 169534}, {Key: "TaxYear", Value: "2009/10"}, {Key: "Land", Value: 4}, {Key: "Total", Value: 104}}
 countyPivot[5] = {{Key: "id", Value: 169535}, {Key: "TaxYear", Value: "2010/11"}, {Key: "Land", Value: 5}, {Key: "Total", Value: 105}}
 countyPivot[6] = {{Key: "id", Value: 169536}, {Key: "TaxYear", Value: "2011/12"}, {Key: "Land", Value: 6}, {Key: "Total", Value: 106}}
 countyPivot[7] = {{Key: "id", Value: 169537}, {Key: "TaxYear", Value: "2012/13"}, {Key: "Land", Value: 7}, {Key: "Total", Value: 107}}

This is the definition of my igGrid:

 $("#grdCountyAssessment").igGrid({
      width: "100%",
      height: "500px",  // Adjust height as needed
      columns: columns,
      dataSource: countyPivot,   
      autoGenerateColumns: false, // We're manually defining the columns
      features: [
      {
           name: "Sorting"
      },
      {
           name: "Paging",       // Enable paging
           pageSize: 10          // Set page size
      }
      ]
 });

And this is what ends up getting generated:

enter image description here

How do I adjust the data source so the igGrid can display the data correctly?

Any assistance is greatly appreciated.

Javascript IF multiple arguments [duplicate]

I’m debugging an external js library code and I found this syntax:

if(argument1, argument2) { ... }

It is the first time I face more than one argument in an if block, what is its meaning?

I tried to understand how it works with some tests:

if(false,false,true) alert("TEST"); //this alert is fired!

and

if(true,false,false) alert("TEST"); //this alert is not fired!

And it seems only the last argument is evaluated.

Webauthn – Browser fails to recognize autocomplete attribute

I am using an input for users to log in using email or phone. However, recently added an initial support for passkeys. I want to use the Conditional UI so users are not bothered if no passkeys are stored for this site. To use that, I need an input with autocomplete attribute set to webauthn – as required for the browserautofill property to use conditional UI.

However, the browser -I believe- does not recognize the webauthn autocomplete value having 3 autocomplete values in total

Like this:

<input type="text" autocomplete="email tel-national webauthn" />

It works with autocomplete="email webauthn" or autocomplete="tel-national webauthn".

It only happens for my desktop browsers (Arc and Google Chrome)

Arc
Chromium Engine Version 131.0.6778.140


However, it works on Chrome mobile, the browser recognize the input and presents my passkey options

Tried with only 2 values.

I want to keep the email tel-national attributes as there are more users enrolled with them than passkey.

I would rather display the password selection modal than the dropdown of user autofill, but only the later supports Conditional UI and, thus, I use the ONLY input I have on this page.

The desired behavior would be to still let the users be suggested with emails and phones and only be suggested with passkey options if existing credentials (Conditional UI). Not sure if I remove email or tel-national I could still suggest both for users without passkeys.

how convert to array properly with special character in string [duplicate]

i want to split string to array,but if the string contain é,the split method not work

var string = "JonBenét";
var arrays = string.split("");

why arrays like this ?

Array(9) [ “J”, “o”, “n”, “B”, “e”, “n”, “e”, “́”, “t” ]

i want convert like this,any suggestion thanks very much

Array(8) [ “J”, “o”, “n”, “B”, “e”, “n”, “é”, “t” ]

enter image description here

i try Array.string(string),but it still convert
Array(9) [ “J”, “o”, “n”, “B”, “e”, “n”, “e”, “́”, “t” ]
enter image description here

Order of await execution makes no sense in node

I wanted to understand at which point an async js function was appended to the event loop and when it was being immediately executed:

async function test2(){
    console.log('3 test2 is being executed');
}
async function test(){
    console.log('2 test is being executed');

    await test2();
    console.log('4 test 2 was awaited');
}

console.log('1 test is about to be called');
test();
console.log('5 test was called');

Originally, I assumed that no matter if a function had any actual async functions happneing (setTimeout, fetch, loadFile, etc), the function would always be appended to the event loop if it was declared async.

So by that logic expected the order of console logs like this:

1 test is about to be called
5 test was called
2 test is being executed
3 test2 is being executed
4 test 2 was awaited

So I assumed, async function that don’t have any actual real async functions in them always get executed immediately, leading to this order:

1 test is about to be called
2 test is being executed
3 test2 is being executed
4 test 2 was awaited
5 test was called

But instead I get this:

1 test is about to be called
2 test is being executed
3 test2 is being executed
5 test was called
4 test 2 was awaited

This means the order of execution is:

test is called
test is being immediately executed (despite being marked async)
test2 is being immediately executed (same as test)
after test2 is being run, control is returned to line 14 (which means the event loop must have done something after all)
finally, test reports that test2 was executed.

Can somebody explain this to me? How can it be that async functions sometimes get thrown onto the event loop and sometimes not? I this predictable and when?

Also if you remove the await, the order is 1 2 3 4 5.

I’d just like to understand what’s the logic here.

How to intercept text before sending it to ChatGPT in a Chrome Extension and filter sensitive data using a backend API?

I’m developing a Chrome extension that intercepts the text a user sends to ChatGPT before it’s sent to the model. My goal is to verify whether the text contains any sensitive data by sending it to a backend API. If the data is sensitive, I want the backend to filter it and return the masked version, which will then be sent to ChatGPT.

However, I’m facing an issue where the data is being sent directly to ChatGPT without being intercepted, and I can’t block the direct request to the API.

How can I properly intercept the text before it reaches ChatGPT and ensure it is filtered by my backend API before it is sent out?

Content.js


document.addEventListener("DOMContentLoaded", () => {
  const sendButton = document.querySelector('[aria-label="Send prompt"]');
  const inputField = document.querySelector("textarea");
  console.log(sendButton);
  if (sendButton && inputField) {
    console.log("Send button and input field found.");

    // Intercept the message when the send button is clicked
    sendButton.addEventListener("click", (event) => {
      event.preventDefault(); // Prevent sending the message immediately

      const userMessage = inputField.value;
      console.log("Intercepted message:", userMessage);

      // Modify the message (for testing purposes)
      const modifiedMessage = `${userMessage} (modified by extension)`;

      // Show the modified message in a dialog
      if (
        confirm(
          `Modified Message:nn${modifiedMessage}nnDo you want to send this message?`
        )
      ) {
        // Trigger API call to simulate data leak prevention action
        chrome.runtime.sendMessage({ action: "triggerApiCall" }, (response) => {
          if (response && response.success) {
            console.log("API Call Successful, sending message to ChatGPT...");
            inputField.value = modifiedMessage; // Set the modified message back to the input
            sendButton.click(); // Trigger the send button click again to send the modified message
          } else {
            console.error(
              "API Call Failed:",
              response ? response.error : "No response"
            );
            alert("Error preventing data leak.");
          }
        });
      }
    });
  } else {
    console.log("Send button or input field not found.");
  }
});

manifest.json

{
    "manifest_version": 3,
    "name": "ChatGPT Message Interceptor",
    "version": "1.0",
    "description": "Intercept and modify messages sent to ChatGPT",
    "permissions": [
      "activeTab",
      "storage"
    ],
    "content_scripts": [
      {
        "matches": ["https://chatgpt.com/*", "https://*.chat.openai.com/*"],
        "js": ["content.js"]
      }
    ],
    "background": {
      "service_worker": "background.js"
    },
    "action": {
      "default_popup": "popup.html",
      "default_icon": {
        "16": "images/icon-16.png",
        "48": "images/icon-48.png",
        "128": "images/icon-128.png"
      }
    }
  }

popup.html

<!DOCTYPE html>
<html>

<head>
    <title>ChatGPT Message Interceptor</title>
    <style>
        body {
            width: 200px;
            padding: 10px;
        }

        button {
            width: 100%;
            padding: 10px;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <h3>Activate Interceptor</h3>
    <button id="activateButton">Activate</button>
    <script src="popup.js"></script>
</body>

</html>```

appendChild does not work while modifying innerHTML does

Ok I know the title looks like I’ll be asking “why does parent.appendChild("some text") not work”. It is not the case though.

I have a function of signature string => SVGElement that outputs a rather complex (but valid) SVG node. I also have an empty <div id="app"></div> that covers the whole screen.

I tried two things to render my newly create element:

  1. document.getElementById("app")!.appendChild(svgElement)
  2. document.getElementById("app")!.innerHTML += svgElement.outerHTML

If I open the inspector, the output HTML tree is the same for both methods. However, my SVG does not appear with the first method and does appear with the second one. I know the context is not complete, but I’d like to have insights to know what could cause this difference.

Thanks in advance for answering and have a nice day!

Paste URL to custom input programmatically and invoke JS action associated with it on Google My Maps

I want to automate the way to add points to Google My Maps. They don’t provide any API for that, so I thought I would use a custom user script to interact with the page.

I haven’t written it yet, for now, I’m only checking in the dev tools console if everything is accessible and if it will be possible to achieve.

Here is the problem I encountered: it’s possible to create a new point, edit the name, etc., but I’m stuck at adding a photo to the point. There are a lot of custom properties and solutions made by Google in that area and I’m looking for a solution that will work with that.

There is an input field where I can paste or type url to the photo. If I do this, the value is written to the attribute data-initial-value and then the photo appears below that input field, and the save button is activated.

<input type="text" class="whsOnd zHQkBf" jsname="YPqjbf" autocomplete="off" tabindex="0" aria-label="Wklej adres URL obrazu..." aria-disabled="false" autofocus="" data-initial-value="" badinput="false">

When I programmatically set the photo URL to either value or data-initial-value attributes, then nothing really happens, their custom JS isn’t activated, so the photo can’t be added that way.

input = document.querySelector(".whsOnd.zHQkBf");
input.setAttribute("data-initial-value", "https://picsum.photos/200");
input.setAttribute("value", "https://picsum.photos/200");
input.value = "https://picsum.photos/200";

You can see in this video how it behaves when I paste the link and then later when it’s set programmatically.

So, the question is if there is any way I can simulate paste or type action to that field, so their JS will detect it? Or is there any other solution how to make it work?

Steps to get this input on Google My Maps:

  1. Create and open map on https://www.google.com/maps/d/
  2. Click the pin icon in the toolbar to create a new point
  3. Click the photo icon in the edit dialog box
  4. Click the URL tab

Here is full HTML of this area with input:

<div
  class="V6Fpqd-nUpftc"
  jscontroller="qo18Ab"
  jsaction="f9NQ3d:jSxSE;iUYTcf:MphkWb;E6LM8:eAlKye;GljaIf:qZ14Je;arml9e:LvSimf;TcIE3e:VZij4d;tJHP7e:ulAvwc"
  jsname="oLnkS"
  role="tabpanel"
>
  <div jsname="V8jame"></div>
  <div class="kPOohe-wZVHld-nUpftc-haAclf" jsname="StCdjf">
    <div
      class="kPOohe-wZVHld-nUpftc"
      jsaction="rcuQ6b:rcuQ6b"
      jscontroller="JKlM1b"
    >
      <div class="eXh7Ud" jscontroller="cDvIdc" jsaction="rcuQ6b:rcuQ6b;">
        <div class="mPVwKb hCTQCe" jsname="L8jiB">
          <div
            class="ndlgBc"
            jscontroller="KIcJpf"
            jsaction="JIbuQc:LYGBsb(gQ2Xie),mm65pe(Slj9he);O22p3e:fpfTEe(cB3E7e);rcuQ6b:rcuQ6b;YqO5N:XNc1Le(cB3E7e); keydown:Hq2uPe(cB3E7e)"
          >
            <div jsname="ODTp8" class="hSF15e">
              <div jsname="oK1IRd">
                <div class="YHtwxf">
                  <div
                    class="rFrNMe qr4X2e zKHdkd"
                    jscontroller="pxq3x"
                    jsaction="clickonly:KjsqPd; focus:Jt1EX; blur:fpfTEe; input:Lg5SV"
                    jsshadow=""
                    jsname="cB3E7e"
                  >
                    <div class="aCsJod oJeWuf">
                      <div class="aXBtI I0VJ4d Wic03c">
                        <div class="Xb9hP">
                          <input
                            type="text"
                            class="whsOnd zHQkBf"
                            jsname="YPqjbf"
                            autocomplete="off"
                            tabindex="0"
                            aria-label="Wklej adres URL obrazu..."
                            aria-disabled="false"
                            autofocus=""
                            data-initial-value=""
                          />
                          <div
                            jsname="LwH6nd"
                            class="ndJi5d snByac"
                            aria-hidden="true"
                          >
                            Wklej adres URL obrazu...
                          </div>
                        </div>
                        <span jsslot="" class="A37UZe sxyYjd MQL3Ob"
                          ><span data-is-tooltip-wrapper="true"
                            ><button
                              class="pYTkkf-Bz112c-LgbsSe pYTkkf-Bz112c-LgbsSe-OWXEXe-SfQLQb-suEOdc DmnNTb"
                              jscontroller="PIVayb"
                              jsaction="click:h5M12e; clickmod:h5M12e;pointerdown:FEiYhc;pointerup:mF5Elf;pointerenter:EX0mI;pointerleave:vpvbp;pointercancel:xyn4sd;contextmenu:xexox;focus:h06R8; blur:zjh6rb;mlnRJb:fLiPzd;"
                              data-idom-class="DmnNTb"
                              data-use-native-focus-logic="true"
                              jsname="gQ2Xie"
                              disabled=""
                              aria-label="Wyczyść podgląd adresu URL"
                              data-tooltip-enabled="true"
                              data-tooltip-id="tt-c4"
                            >
                              <span
                                class="OiePBf-zPjgPe pYTkkf-Bz112c-UHGRz"
                              ></span
                              ><span
                                class="RBHQF-ksKsZd"
                                jscontroller="LBaJxb"
                                jsname="m9ZlFb"
                                soy-skip=""
                                ssk="6:RWVI5c"
                              ></span
                              ><span
                                class="pYTkkf-Bz112c-kBDsod-Rtc0Jf"
                                jsname="S5tZuc"
                                aria-hidden="true"
                                ><svg
                                  class="Q6yead QJZfhe"
                                  height="24"
                                  viewBox="0 -960 960 960"
                                  width="24"
                                >
                                  <path
                                    d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"
                                  ></path></svg
                              ></span>
                              <div class="pYTkkf-Bz112c-RLmnJb"></div>
                            </button>
                            <div
                              class="ne2Ple-oshW8e-V67aGc"
                              id="tt-c4"
                              role="tooltip"
                              aria-hidden="true"
                            >
                              Wyczyść podgląd adresu URL
                            </div></span
                          ></span
                        >
                        <div class="i9lrp mIZh1c"></div>
                        <div jsname="XmnwAc" class="OabDMe cXrdqd"></div>
                      </div>
                    </div>
                    <div class="LXRPh">
                      <div jsname="ty6ygf" class="ovnfwe Is7Fhb"></div>
                    </div>
                  </div>
                  <div class="aRx7Te nbtmO" jsname="aZ2wEe">
                    <div
                      class="EmVfjc qs41qe"
                      data-loadingmessage="Ładuję"
                      jscontroller="qAKInc"
                      jsaction="animationend:kWijWc;dyRcpb:dyRcpb"
                      data-active="true"
                      jsname="aZ2wEe"
                    >
                      <div class="Cg7hO" aria-live="assertive" jsname="vyyg5">
                        Ładuję
                      </div>
                      <div jsname="Hxlbvc" class="xu46lf">
                        <div class="ir3uv uWlRce co39ub">
                          <div class="xq3j6 ERcjC">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="HBnAAc">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="xq3j6 dj3yTd">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                        </div>
                        <div class="ir3uv GFoASc Cn087">
                          <div class="xq3j6 ERcjC">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="HBnAAc">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="xq3j6 dj3yTd">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                        </div>
                        <div class="ir3uv WpeOqd hfsr6b">
                          <div class="xq3j6 ERcjC">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="HBnAAc">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="xq3j6 dj3yTd">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                        </div>
                        <div class="ir3uv rHV3jf EjXFBf">
                          <div class="xq3j6 ERcjC">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="HBnAAc">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                          <div class="xq3j6 dj3yTd">
                            <div class="X6jHbb GOJTSe"></div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="CpRZQe" role="alert">
                    Możesz użyć tylko takich obrazów, do których posiadasz
                    stosowne prawa.
                  </div>
                  <div
                    class="Gkzg6e nbtmO"
                    jsname="VEOBbf"
                    role="status"
                    aria-live="assertive"
                  >
                    <div class="Gkzg6e nbtmO" jsname="VEOBbf"></div>
                    <div class="nbtmO">
                      Nie możemy znaleźć obrazu lub uzyskać do niego dostępu pod
                      tym adresem URL. Sprawdź, czy adres został wpisany
                      poprawnie.
                    </div>
                    <div class="nbtmO">
                      Ten adres URL jest nieprawidłowy. Upewnij się, że zaczyna
                      się od http:// lub https://.
                    </div>
                  </div>
                </div>
              </div>
              <div jsname="qlI0xc" class="nbtmO">
                <div class="PDztrf"><img jsname="dGKeKb" class="LLY0Xe" /></div>
              </div>
              <div jsname="Zpe2Q" class="APdXXd">
                <div
                  role="button"
                  class="U26fgb O0WRkf oG5Srb HQ8yf C0oVfc fuRKz M9Bg4d RDPZE"
                  jscontroller="VXdfxd"
                  jsaction="click:cOuCgd; mousedown:UX7yZ; mouseup:lbsD7e; mouseenter:tfO1Yc; mouseleave:JywGue; focus:AHmuwe; blur:O22p3e; contextmenu:mg9Pef;touchstart:p6p2H; touchmove:FwuNnf; touchend:yfqBxc(preventDefault=true); touchcancel:JMtRjd;"
                  jsshadow=""
                  jsname="Slj9he"
                  aria-label="wstaw obraz"
                  aria-disabled="true"
                  tabindex="-1"
                >
                  <div class="Vwe4Vb MbhUzd" jsname="ksKsZd"></div>
                  <div class="ZFr60d CeoRYc"></div>
                  <span jsslot="" class="CwaK9"
                    ><span class="RveJvd snByac">wstaw obraz</span></span
                  >
                </div>
              </div>
            </div>
            <div jsname="nlOOSe" class="TMnCxb nbtmO"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>