jsPDf not generating pdf in correct format [closed]

I am trying to implement converting my html to pdf i have html/css and .js files.

here is my code i tried in stackblitz

app.js

window.jsPDF = window.jspdf.jsPDF;

// Convert HTML content to PDF
function Convert_HTML_To_PDF() {
    var doc = new jsPDF();
    
    // Source HTMLElement or a string containing HTML.
    var elementHTML = document.querySelector("#contentToPrint");

    doc.html(elementHTML, {
        callback: function(doc) {
            // Save the PDF
            doc.save('document.pdf');
        },
        margin: [10, 10, 10, 10],
        autoPaging: 'text',
        x: 0,
        y: 0,
        width: 190, //target width in the PDF document
        windowWidth: 675, //window width in CSS pixels
    });
}

there is one button which converts html to pdf using javascript and it downloads the pdf (it may take time to download as pdf is large in size).
whereas when i open the index.html in browser it display’s properly.

issue is: downloaded pdf is not correctly formatted/aligned with html.

what is the issue ? can someone help me to find where i am making mistake ?

Thanks in advance!

Problem when holding down “ShiftLeft” and “ShiftRight”

I have this code that is used to detect the keys held on the keyboard, but by holding down the “ShiftLeft” and “ShiftRight” keys, and then releasing them, both are not removed, only one is removed.
I really don’t understand why it happens. I would appreciate if someone could explain to me.
I also realized that the “SHIFT” that doesn’t come off is the one you release first, I don’t know if this info helps.

And I attach the code below so you can run it and replicate the problem.

let keysPressed = []

const keyActions = new Map([
  ["KeyA", () => "Executing the A key action"],
  ["KeyS", () => "Executing the S key action"],
  ["KeyD", () => "Executing the D key action"],
  ["KeyW", () => "Executing the W key action"],
  ["ShiftLeft", () => "Executing the ShiftLeft key action"],
  ["ShiftRight", () => "Executing the ShiftRight key action"],
])

document.addEventListener('keydown', (event) => {
  if (!keysPressed.includes(event.code)) {
    keysPressed.push(event.code)
  }
})

document.addEventListener('keyup', (event) => {
  const keyIndex = keysPressed.indexOf(event.code)
  if (keyIndex !== -1) {
    keysPressed.splice(keyIndex, 1)
  }
})

function update() {
  if (keysPressed.length > 0) {
    document.getElementById('status').innerHTML = 
      `Pressed keys: ${keysPressed.join(', ')}
      <br>Last pressed key: ${keysPressed[keysPressed.length - 1]} 
      <br>Action: ${keyActions.get(keysPressed[keysPressed.length - 1])}`
  } else {
    document.getElementById('status').textContent = 'No keys pressed'
  }
  requestAnimationFrame(update)
}

requestAnimationFrame(update)
body { 
  font-family: Arial, sans-serif; 
}

.status { 
  font-size: 1.5em; 
  color: green; 
}
<body>
  <h2>Hold a key pressed</h2>
  <p class="status" id="status">No key pressed</p>
</body>

deckgl with mapbox: map covering screen only partially

I am experimenting with deckgl and mapbox.

My index.html is:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DeckGL + Cesium Ion Tiles Viewer</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        #app {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <script type="module" src="index.js"></script>
</body>
</html>

And index.js:

// Create a Deck.gl TileLayer to display the tileset
const tileLayer = new Tile3DLayer({
  id: 'tile-3d-layer',
  data: '....',
  loader: CesiumIonLoader,
  loadOptions: {
    'cesium-ion': {accessToken: '....'}
  },
  pointSize: 0.5
});

// Create the Mapbox GL JS map
const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json';

const map = new mapboxgl.Map({
  container: 'app',
  style: 'mapbox://styles/mapbox/light-v9',
  accessToken: MAPBOX_TOKEN,
  center: [73.851210,
    18.516729],
  zoom: 11
});

map.once('load', () => {
  const deckOverlay = new MapboxOverlay({
    interleaved: true,
    layers: [
      tileLayer
    ]
  });

  map.addControl(deckOverlay);
});

With this set-up, I see that map is covering screen only partially:

enter image description here

I want map to cover entire screen.

What am I missing in my code?

PS: This worked:

html, body {
     margin: 0;
     padding: 0;
     height: 100%;
}

Better way of handling logout on tab close but not on refresh?

I have a js code that logs out only on tab close, using sessionStorage, but it was not as smooth as I would like

if (sessionStorage.getItem('reloaded') != null) {
console.log('page was reloaded');
} else {
console.log("page was closed");
fetch('logout.php', {
    method: 'POST', // Use POST for sensitive actions like logout
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded', // Content type
    },
  
})
.then(response => {
    if (response.ok) {
        console.log('Logged out successfully');
        window.location.replace("http://localhost/foodblog/index.php");
    } else {
        console.log('Logout failed');
    }
})
.catch(error => {
    console.error('Error during logout:', error);
});


}


sessionStorage.setItem('reloaded', 'yes');

this would show a redirect on page load, I think it looks kinda bad, also I tried using
the “unload” event listener:

window.addEventListener('unload', function () {

navigator.sendBeacon('logout.php');
});

this works perfectly fine, but unload is deprecated, any idea how to improve this?

logout.php:

<?php
session_start();

if(isset($_SESSION["user_id"])){
  unset($_SESSION["user_id"]);
}

if(isset($_SESSION["username"])){
   unset($_SESSION["username"]);
}
session_destroy();
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 3600, '/');
}
header("Location: index.php");
exit();
?>

Angular 17 does not post and i get an TypeError in dev console

I’m following a tutorial for image upload in Angular, but I get this error in my Chrome dev console whenever I post the form

TypeError: Argument 2 (‘blobValue’) to FormData.append must be an instance of Blob

here is my code for the routing to the database(Mongo Atlas):

const express = require("express");
const router = express.Router();
const Post = require("../models/post");
const multer = require("multer");

const MIME_TYPE_MAP = {
  "image/png": "png",
  "image/jpeg": "jpg",
  "image/jpg": "jpg",
};

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "Api/upload");
  },
  filename: (req, file, cb) => {
    const name = file.originalname.toLowerCase().split(" ").join("-");
    const extension = MIME_TYPE_MAP[file.mimetype];
    cb(null, name + "-" + Date.now() + "." + extension);
  },
});

/* Here i post to the Mongo database  */
router.post(
  "",
  multer({ storage: storage }).single("image"),
  (req, res, next) => {
    const post = new Post({
      title: req.body.title,
      content: req.body.content,
    });
    post.save().then((createdPost) => {
      //console.log(result);
      res.status(201).json({
        message: "Post added succesfully to MongoDB!",
        postId: createdPost._id,
      });
    });
    //console.log(post);
  }
);

/* Here i fetch from the Mongo database  */
router.get("", (req, res, next) => {
  Post.find().then((documents) => {
    res
      .status(200)
      .json({ message: "Post fetched Successfully!", posts: documents });
  });
});

/* Here i delete from the Mongo database */
router.delete("/:id", (req, res, next) => {
  /* console.log(req.params.id);
  res.status(200).json({ message: "Post has een deleted!" }); */
  Post.deleteOne({ _id: req.params.id }).then((result) => {
    console.log(result);
    res.status(200).json({ message: "Post has een deleted!" });
  });
});

router.put("/:id", (req, res, next) => {
  const post = new Post({
    _id: req.body.id,
    title: req.body.title,
    content: req.body.content,
  });
  Post.updateOne({ _id: req.params.id }, post).then((result) => {
    // console.log(result);
    res.status(200).json({ message: "Update Post Successful!" });
  });
});

router.get("/:id", (req, res, next) => {
  Post.findById(req.params.id).then((post) => {
    if (post) {
      res.status(200).json(post);
    } else {
      res.status(404).json({ message: "Post not Found!" });
    }
  });
});

module.exports = router;

and this is the code for the post-service

import { Injectable } from '@angular/core';
import { Post } from './postModel';
import { Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class PostServiceService {
  private posts: Post[] = [];
  private postsUpdated = new Subject<Post[]>();

  constructor(private http: HttpClient) {}

  /*  getPost(): Observable<Post[]> {
    const posts = of(this.posts);
    return posts;
  } */

  /* Here I get posts route */
  getPost() {
    //return [...this.posts];
    this.http
      .get<{ message: string; posts: any }>('http://localhost:4000/api/posts')
      .pipe(
        map((postData) => {
          return postData.posts.map((post: any) => {
            return {
              title: post.title,
              content: post.content,
              id: post._id,
            };
          });
        })
      )
      .subscribe((transformedtData) => {
        this.posts = transformedtData;
        this.postsUpdated.next([...this.posts]);
      });
  }

  getPostUpdatedListener() {
    return this.postsUpdated.asObservable();
  }

  getPosts(id: any) {
    return this.http.get<{ _id: string; title: string; content: string }>(
      'http://localhost:4000/api/posts/' + id
    );
  }

  addPost(title: string, content: string, image: File) {
    /*     const post: Post = {
      id: id,
      title: title,
      content: content,
    }; */
    const postData = new FormData();
    postData.append('title', title);
    postData.append('content', content);
    postData.append('image', image, title);

    this.http
      .post<{ message: string; postId: string }>(
        'http://localhost:4000/api/posts',
        postData
      )
      .subscribe((responseData) => {
        const post: Post = {
          id: responseData.postId,
          title: title,
          content: content,
        };
        /*   const id = responseData.postId;
        post.id = id; */
        //console.log(responseData.message);
        this.posts.push(post);
        this.postsUpdated.next([...this.posts]);
      });
  }

  updatePost(id: string, title: string, content: string) {
    const post: Post = { id: id, title: title, content: content };
    this.http
      .put('http://localhost:4000/api/posts/' + id, post)
      .subscribe((response) => /*console.log(response)*/ {
        const updatedPosts = [...this.posts];
        const oldPostsIndex = updatedPosts.findIndex((p) => p.id === post.id);
        updatedPosts[oldPostsIndex] = post;
        this.posts = updatedPosts;
        this.postsUpdated.next([...this.posts]);
      });
  }

  deletePost(postId: string) {
    this.http
      .delete('http://localhost:4000/api/posts/' + postId)
      .subscribe(() => {
        const updatedPosts = this.posts.filter((post) => post.id !== postId);
        this.posts = updatedPosts;
        this.postsUpdated.next([...this.posts]);
      });
  }
}```

How to redirect a page if URL parameters are not in URL on page load

I have a URL that I need to redirect away from if the page visitor does not have a specific parameter in the URL when they visit the page. This is the code I have, but it seems to only work one time in Firefox and not at all when Chrome or Edge.

The goal is that if the visitor has the gcl_customer_id parameter in the URL, they stay on the page. If there is no parameter in the URL when they visit the page, they need to be redirected away instantly.

I can only do this on the client side, in browser on page load. I have no ability to handle this server side.

How can I fix this code?

  <script>
    function checkForXYZId() {
      const urlParams = new URLSearchParams(window.location.search);
      if (!urlParams.has('xyz_id')) {
        window.location.href = 'https://facebook.com'; 
      }
    }
    window.onload = checkForXYZId;
  </script>

Trigger form onChange on input click in React

I’m creating my custom React component with some extra features. I would like to keep it as close to the default browser behavior as possible. Whenever I change something in the form’s input I would like the change event to bubble up to the form. Unfortunately, I can’t make it work.

I’ve read that the “change” event is apparently some special even in React’s synthetic event system. None of the proposed solutions worked for me as they were for older React versions.

To simplify my problem I created this demo and what I would like to do is to trigger the form.onChange from within input’s onPointerDown or whatever other event handler. Of course I can’t call the form.onChange directly as I’m not in the control of this part of the code. Everything should be done through events bubbling. Is it even possible in React? I’ve tested it in some of the popular UI libraries and so far none of them does that for non-native select components.

export function Demo() {
  return (
    <form
      onChange={(event) => {
        const form = event.currentTarget;
        const formData = new FormData(form);
        console.log(JSON.stringify(Array.from(formData)));
      }}
    >
      <input
        name="name"
        onPointerDown={(event) => {
          const changeEvent = new Event("change", { bubbles: true });
          event.currentTarget.dispatchEvent(changeEvent);
        }}
      />
    </form>
  );
}

How to prevent Native Javascript from redirecting a ?

I want to modify a button on a website that is accessed with webview, because the button on the website opens a page with a new tab / window.

<div>
  <button>
    <span>Button Name</span>
  </button>
</div>

*As a note, the <button> does not have a, href, data-url inline

I don’t want that, I want the button to open the link in the same window / tab, for performance reasons.

I have tried implementing customJS to MainActivity.java, it successfully redirects to a custom URL as a test.

function setupButtonObserver(targetNode) {
  const buttonActions = {
    "/link1": ["1LanguageA", "1LanguageB"],
    "/link2": ["2LanguageA", "2LanguageB"],
  };

  document.addEventListener('click', (event) => {
    const button = event.target.closest('button');
    if (button) {
      for (const [url, texts] of Object.entries(buttonActions)) {
        if (texts.some(text => button.textContent.includes(text))) {
          event.preventDefault();
          window.open(url, '_self');
          break;
        }
      }
    }
  });

  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.addedNodes.length) {
        mutation.addedNodes.forEach((node) => {
          if (node.nodeType === 1) {
          }
        });
      }
    });
  });
  observer.observe(targetNode, { childList: true, subtree: true });
}
setupButtonObserver(document.body);

But it still opens a New Tab / Window. When I press “Back”, then the Custom URL test appears which opens in the same Tab / Window as the webview.

So the current condition is, when pressing 1 button, 2 URLs open on different Tabs/Window. (1 opens in the same tab/window, and 1 more opens in a new tab/window).

Then I have also tried to add this:

function disableTargetBlank() {
  const links = document.querySelectorAll('a[target="_blank"]');

  links.forEach(link => {
    link.removeAttribute('target');
  });
}

window.onload = function() {
  disableTargetBlank();
};

const lihatblank = new MutationObserver(() => {
  disableTargetBlank();
});

lihatblank.observe(document.body, { childList: true, subtree: true });

But it seems to be due to a conflict with the JavaScript function that already exists on the website.
In addition there is a function:

("a", {
className: "underline",
href: "/link",
target: "_blank",
rel: "noopener noreferrer",
children: "here"
})

There is also a native function that manages:

onClick: () => {
  window.open("/link")
}

Both are in different Javascript files.

So is there a way to prevent Native Javascript from redirecting <button>?

Wix checkbox doesn’t update CMS field value on change due to callback error because index cannot be found

I am receiving the error:

UserError: An error occurred in one of undefined callbacks
Caused by cannot update field values: index not found

I’m newish to coding and cannot figure out why my index can’t be found in my current code. The goal of the code is to update the value of the resultField based on whether or not the fieldName (boolean) is true or false. The checkbox is attached to the fieldName and I want the resultField to be updated on the page when the checkbox is changed.

Any suggested fixes?

Here is my current test code:

$w.onReady(() => {
    $w("#dynamicDataset").onReady(() => {
        try {
            const dataset = $w("#dynamicDataset");
            const currentItem = dataset.getCurrentItem();

            if (!currentItem) {
                console.warn("No current item found in dataset.");
                return;
            }

            // Log current item for debugging
            console.log("Current Item from CMS:", currentItem);

            // Define the array that maps dataset fields to their corresponding checkbox IDs
            const checkboxMappings = [
                { fieldName: "inspiration",                         checkboxID: "#inspirationcheckbox"},
                { fieldName: "strengthSavingThrowProficiency",      checkboxID: "#strengthcheckbox" ,       resultField: "strengthSavingThrow",     modifier: "strength1"};

            console.log("Dataset is ready.");

            checkboxMappings.forEach(mapping => {
                const { fieldName, checkboxID } = mapping;
                const checkbox = $w(checkboxID);

                if (!checkbox) {
                    console.warn(`Checkbox with ID ${checkboxID} not found.`);
                    return;
                }

                const value = currentItem[fieldName] || false; // Default to false
                checkbox.checked = !!value;

                // Debugging logs
                console.log(`Checkbox ${checkboxID} set to ${!!value} for ${fieldName}`);

                checkbox.onChange(() => handleCheckboxChange(dataset, mapping));
            });
        } catch (error) {
            console.error("Error initializing checkboxes:", error);
        }
    });
});

function handleCheckboxChange(dataset, mapping) {
    const { fieldName, checkboxID, resultField, modifier } = mapping;

    if (!resultField) {
        console.error("Mapping is missing resultField:", mapping);
        return;
    }

    // const checkbox = $w(checkboxID);
    // if (!checkbox) {
    //     console.error(`Checkbox ${checkboxID} not found.`);
    //     return;
    // }

    // const isChecked = checkbox.checked;

    dataset.onReady(() => {
        const currentItem = dataset.getCurrentItem();

        if (!currentItem) {
            console.error("No current item found in dataset. Retrying after dataset refresh...");
            dataset.refresh()
                .then(() => handleCheckboxChange(dataset, mapping)) // Retry after refresh
                .catch(err => {
                    console.error("Failed to refresh dataset:", err);
                });
            return;
        }
        const isChecked = currentItem[fieldName] || false;
        const baseValue = currentItem[modifier] || 0;
        const proficiencyBonus = currentItem.proficiencyBonus;
        const updatedValue = isChecked ? baseValue + proficiencyBonus : baseValue;

        dataset.setFieldValue(resultField, updatedValue)
            .then(() => {
                console.log(`${resultField} successfully updated to ${updatedValue}`);
            })
            .catch(err => {
                console.error(`Error updating field ${resultField}:`, err);
            });
    });
}

When I check the checkbox I receive the following error:

UserError: An error occurred in one of undefined callbacks
Caused by cannot update field values: index not found
Caused by: cannot update field values: index not found

When I uncheck the checkbox I receive the following error:
No current item found in dataset. Retrying after dataset refresh…

UserError: An error occurred in one of undefined callbacks
Caused by TypeError: Cannot read properties of undefined (reading ‘then’)
Caused by: TypeError: Cannot read properties of undefined (reading ‘then’)

How to fix the “Binding multisampled flag (1) doesn’t match the layout’s multisampled flag (0)” error in WebGPU?

I`m trying to add MSAA to a model with multiple textures (normal, metallicRoughness, baseColor, emission), but get the following error:

Binding multisampled flag (1) doesn't match the layout's multisampled flag (0)
 - While validating that the entry-point's declaration for @group(2) @binding(1) matches [BindGroupLayout "materialBindGroupLayout"]
 - While validating the entry-point's compatibility for group 2 with [BindGroupLayout "materialBindGroupLayout"]
 - While validating fragment stage ([ShaderModule (unlabeled)], entryPoint: "fragment_main").
 - While validating fragment state.
 - While calling [Device].CreateRenderPipeline([RenderPipelineDescriptor]).

The problem is i don’t even understand what’s that supposed to mean.

All the textures are multisampled, and are collected in the same bind group. What layout is in question? Bind group, buffer or pipeline? Is there any written explanation for what this error means? I sure could not find one, so any nudge in right direction would be appreciated.

Sequence of rejected promise with Promise.reject()

I am learning microtask queue and i have a simple question.

With the following code:

Promise.resolve().then(()=>console.log("Resolved1 Promise"));
Promise.reject().then(()=>console.log("Resolved2 Promise")).catch(()=>console.log('Rejected2 Promise'));
Promise.resolve().then(()=>console.log("Resolved3 Promise"));
Promise.resolve().then(()=>console.log("Resolved4 Promise"));
Promise.resolve().then(()=>console.log("Resolved5 Promise"));

I expected output to be in the sequence:

Resolved1 Promise
Rejected2 Promise
Resolved3 Promise
Resolved4 Promise
Resolved5 Promise

But as you can try above, rejected callback is triggered in the last. Can anyone explain it to me?

Why does leaflet fit bounds do this?

When I use map.fitbounds to center a map it messes up everything the problem is attached below. When it should look like formated and all. Could anyone please help thank you?? The problem, if the images don’t work is that the sidebar is missing and the header is moved to the side. It has been tested problem comes from the fitbounds rule.

The code for the leaflet verison is

 var map = L.map('map-canvas').setView([<%= correctAns.location.lat %>, <%= correctAns.location.longg %>], 3);
// Add the tile layer
// Initialize the map with the original center and zoom level
var map = L.map('map-canvas', {
    minZoom: 2,
    maxZoom: 6
}).setView([<%= correctAns.location.lat %>, <%= correctAns.location.longg %>], 6);
// Add the tile layer
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
@@ -169,53 +176,70 @@ bounds.extend(correctMarker.getLatLng());
// Function to create colored icon (unchanged)
function createColoredIcon(color) {
  const svgTemplate = `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="marker">
      <path fill-opacity=".25" d="M16 32s1.427-9.585 3.761-12.025c4.595-4.805 8.685-.99 8.685-.99s4.044 3.964-.526 8.743C25.514 30.245 16 32 16 32z"/>
      <path fill="#${color}" stroke="#fff" d="M15.938 32S6 17.938 6 11.938C6 .125 15.938 0 15.938 0S26 .125 26 11.875C26 18.062 15.938 32 15.938 32zM16 6a4 4 0 100 8 4 4 0 000-8z"/>
    </svg>`;
  return L.icon({
    iconUrl: 'data:image/svg+xml;base64,' + btoa(svgTemplate),
    iconSize: [32, 32],
    iconAnchor: [16, 32],
    popupAnchor: [0, -32]
  });
    const svgTemplate = `
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="marker">
            <path fill-opacity=".25" d="M16 32s1.427-9.585 3.761-12.025c4.595-4.805 8.685-.99 8.685-.99s4.044 3.964-.526 8.743C25.514 30.245 16 32 16 32z"/>
            <path fill="#${color}" stroke="#fff" d="M15.938 32S6 17.938 6 11.938C6 .125 15.938 0 15.938 0S26 .125 26 11.875C26 18.062 15.938 32 15.938 32zM16 6a4 4 0 100 8 4 4 0 000-8z"/>
        </svg>`;
    return L.icon({
        iconUrl: 'data:image/svg+xml;base64,' + btoa(svgTemplate),
        iconSize: [32, 32],
        iconAnchor: [16, 32],
        popupAnchor: [0, -32]
    });
}
// Add user answer markers and extend bounds
<% allUsers.forEach(function(user) { %>
  <% if (user.answer && user.answer.loc) { %>
    var userColor = '<%= user.color %>'.replace('#', ''); // Remove '#' if present
    var userIcon = createColoredIcon(userColor);
    var userMarker = L.marker([<%= user.answer.loc.lat %>, <%= user.answer.loc.longg %>], {
      icon: userIcon
    }).addTo(map)
      .bindPopup("<%= user.name %>'s Answer");
    
    bounds.extend(userMarker.getLatLng());
  <% } %>
    <% if (user.answer && user.answer.loc) { %>
        var userColor = '<%= user.color %>'.replace('#', '');
        var userIcon = createColoredIcon(userColor);
        var userMarker = L.marker([<%= user.answer.loc.lat %>, <%= user.answer.loc.longg %>], {
            icon: userIcon
        }).addTo(map)
            .bindPopup("<%= user.name %>'s Answer");
        
        bounds.extend(userMarker.getLatLng());
    <% } %>
<% }); %>
// Adjust the view to fit all markers
// Function to check if all markers are visible
function areAllMarkersVisible() {
    var mapBounds = map.getBounds();
    return bounds.toBBoxString() === mapBounds.toBBoxString() || bounds.within(mapBounds);
}
// Function to find appropriate zoom level
function findAppropriateZoom() {
    var currentZoom = 6; // Start at max zoom
    map.setView(bounds.getCenter(), currentZoom);
    
    while (currentZoom > 2 && !areAllMarkersVisible()) {
        currentZoom--;
        map.setZoom(currentZoom);
    }
    
    return currentZoom;
}
// Adjust the view to fit all markers with adaptive zoom
map.fitBounds(bounds, {
    padding: [50, 50], // Add 50 pixels of padding
    maxZoom: 15, // Limit the max zoom level
    animate: false // Disable animation for instant change
    padding: [50, 50],
    maxZoom: 6,
    minZoom: 2,
    animate: false
});
 <% allUsers.forEach(function(user) { %>
  <% if (user.answer && user.answer.loc) { %>
    var userColor = '<%= user.color %>'.replace('#', ''); // Remove '#' if present
    var userIcon = createColoredIcon(userColor);
// Find and set appropriate zoom level
var appropriateZoom = findAppropriateZoom();
map.setView(bounds.getCenter(), appropriateZoom, { animate: false });
    var marker2 = L.marker([<%= user.answer.loc.lat %>, <%= user.answer.loc.longg %>], {
      icon: userIcon
    }).addTo(map)
      .bindPopup("<%= user.name %>'s Answer");
  <% } %>
<% }); %>
// Ensure we're not too zoomed out
if (map.getZoom() < 2) {
    map.setZoom(2);
}
 
        window.onload = function(){
            <% if (auto!='yes'){ %>
@@ -239,4 +263,4 @@ map.fitBounds(bounds, {


</body>
</html>
</html>

Vitest snapshot: unsorted html attributes

I am running a vitest to test if a vue component match an snapshot.

The vue component use javascript flatpickr component to generate a datepicker.
The snapshot is generated with another environment and it generates the html attributes in a different order:

Here you are the original html generated:

<input data-v-7f26d67d="" id="datepicker" class="form-control flatpickr-input" type="text" readonly="readonly">

And the html generated right now:

<input data-v-7f26d67d="" id="datepicker" type="text" class="form-control flatpickr-input" readonly="readonly">

As you can see it only change the position of the type attribute. Html attributes by definition are unsorted, so this is an equivalent html code.

I want my code pass this kind of tests for any environment.
How do people achieve robust snapshot test?

I am using this dependency:

  "devDependencies": {
    "vitest": "^2.0.5"
  },
  "dependencies": {
    "vue-flatpickr-component": "^11"
  }

And my test is the next:

      it("match snapshot", async () => {
          const minDate = new Date(2024, 1, 1)
          const date = new Date(2024, 1, 1)
          const component = await mountSuspended(MyDayPicker, {
              props: {
                  modelValue: date, minDate: minDate
              }
          });
          expect(component.html()).toMatchSnapshot();
      });

Simple Javascript to make something in range

I am trying to simply move the the cursor so that when it is between 200=x and 300=x the canvas background goes salmon and outside of that range it goes blue.

Here is my full attempt.

<!DOCTYPE html>
<html>
<head>
<style>
body{ margin:10px; background:#CCC; }
#my_canvas{ background:#FFF; border:#000 1px solid; }
</style>
<script>
function initCanvas(){
    var ctx = document.getElementById('my_canvas').getContext('2d');
    ctx.canvas.addEventListener('mousemove', function(event){
        var mouseX = event.clientX - ctx.canvas.offsetLeft;
        var mouseY = event.clientY - ctx.canvas.offsetTop;
        var status = document.getElementById('status');
        status.innerHTML = mouseX+" | "+mouseY;
    });
    ctx.canvas.addEventListener('click', function(event){
        var mouseX = event.clientX - ctx.canvas.offsetLeft;
        var mouseY = event.clientY - ctx.canvas.offsetTop;
        // alert(mouseX+" | "+mouseY);
        ctx.fillStyle = "orange";
        ctx.fillRect(mouseX-15, mouseY-15, 30, 30);
    });
}
window.addEventListener('load', function(event) {
    initCanvas();
});
</script>
</head>
<body>
<canvas id="my_canvas" width="500" height="300">
    <script>
    const ctx = my_canvas.getContext("2d");
    ctx.fillStyle = "salmon";
        // Create a Canvas:
        //const canvas = document.getElementById("myCanvas");
        // Define a new path
        ctx.beginPath();
        // Set a start-point
        ctx.moveTo(200,150);
        // Set an end-point
        ctx.lineTo(200, 500);
        // The other vertical line
        ctx.moveTo(300, 150);
        ctx.lineTo(300, 500);
        ctx.stroke();
        if ((mouseX > 200 && mouseX < 300)) {
            ctx.fillStyle = "blue";
        }
        ctx.stroke();
        </script>
</canvas>
<h2 id="status">0 | 0</h2>
</body>  
</html

Any help would be greatly appreciated.

Thanks,

Shane