Hwo to proprely simulate electric field lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="700" height="500"></canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

function vec3_add(a, b) {
   return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
}

function vec3_sub(a, b) {
   return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
}

function vec3_scale(v, s) {
   return [v[0] * s, v[1] * s, v[2] * s];
}

function vec3_magnitude(v) {
   return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

function vec3_normalize(v) {
   const mag = vec3_magnitude(v);
   if (mag === 0) return [0, 0, 0];
   return [v[0] / mag, v[1] / mag, v[2] / mag];
}
const charges = [{
       pos: [250, 250, 0],
       id: "negative"
   },
   {
       pos: [450, 250, 0],
       id: "positive"
   }
];
const lines = 16;
const step_size = 4;
const max_steps = 1000;

function calculate_field_at_point(point, charges) {
   let field = [0, 0, 0];
   for (let charge of charges) {
       let dir = vec3_sub(charge.pos, point);
       let dist = vec3_magnitude(dir);
       if (dist < 5) continue;
       let strength = charge.id.includes("negative") ? 1 : -1;
       strength /= (dist * dist);
       field = vec3_add(field, vec3_scale(dir, strength));
   }
   return vec3_normalize(field);
}

function rk4_step(point, charges) {
   let k1 = calculate_field_at_point(point, charges);
   let temp = vec3_add(point, vec3_scale(k1, step_size * 0.5));
   let k2 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k2, step_size * 0.5));
   let k3 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k3, step_size));
   let k4 = calculate_field_at_point(temp, charges);
   return vec3_scale(
       vec3_add(
           vec3_add(
               vec3_scale(k1, 1 / 6),
               vec3_scale(k2, 1 / 3)
           ),
           vec3_add(
               vec3_scale(k3, 1 / 3),
               vec3_scale(k4, 1 / 6)
           )
       ),
       step_size
   );
}

function generate_circle_points(center, radius, num_points) {
   let points = [];
   for (let i = 0; i < num_points; i++) {
       let angle = (i / num_points) * Math.PI * 2.0;
       let x = center[0] + radius * Math.cos(angle);
       let y = center[1] + radius * Math.sin(angle);
       points.push([x, y, 0]);
   }
   return points;
}

function integrate_field_line(start_point, charges) {
   let points = [start_point];
   let current_point = [...start_point];
   for (let i = 0; i < max_steps; i++) {
       let step = rk4_step(current_point, charges);
       if (vec3_magnitude(step) < 0.1) break;
       current_point = vec3_add(current_point, step);
       points.push([...current_point]);
       let too_close = charges.some(charge => vec3_magnitude(vec3_sub(current_point, charge.pos)) < 15);
       if (too_close) break;
       if (current_point[0] < 0 || current_point[0] > canvas.width ||
           current_point[1] < 0 || current_point[1] > canvas.height) break;
   }
   return points;
}
const positive_charges = charges.filter(c => c.id.includes("positive"));
for (let charge of positive_charges) {
   let start_points = generate_circle_points(charge.pos, 35, lines);
   for (let start_point of start_points) {
       let points = integrate_field_line(start_point, charges);
       if (points.length > 5) {
           ctx.beginPath();
           ctx.moveTo(points[0][0], points[0][1]);
           for (let i = 1; i < points.length; i++) {
               ctx.lineTo(points[i][0], points[i][1]);
           }
           ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
           ctx.lineWidth = 2;
           ctx.stroke();
       }
   }
}
ctx.beginPath();
ctx.arc(charges[0].pos[0], charges[0].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[0].pos[0] - 15, charges[0].pos[1]);
ctx.lineTo(charges[0].pos[0] + 15, charges[0].pos[1]);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.arc(charges[1].pos[0], charges[1].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[1].pos[0] - 15, charges[1].pos[1]);
ctx.lineTo(charges[1].pos[0] + 15, charges[1].pos[1]);
ctx.moveTo(charges[1].pos[0], charges[1].pos[1] - 15);
ctx.lineTo(charges[1].pos[0], charges[1].pos[1] + 15);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
</script>
</body>
</html>

I am trying to simulate an electric field between two charged particles.
To do I sample points around the positive charge, take a series of small steps and approximate the direction of the electric field each time using Runge–Kutta.

However in my snippet the field lines cannot reach behind the negative charge on the left.

I tried generating lines from negative to positive charges but lines start colliding and it looks ugly.

Basically this is what I have:
enter image description here

And what I want:
enter image description here

Vite React Django5 allauth jwt google login

My google login code seems to be working but does redirect back to the home page. the access and refresh token are in the browser application but it still wont redirect back to the home page, it keeps going to the login page. but if i enter the home page url directly it shows authenticated. Google is sending the code and token and this is being swapped into jwt tokens

import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import Cookies from 'js-cookie'; // Import js-cookie

const GoogleCallback = () => {
    const navigate = useNavigate();

    useEffect(() => {
        const handleCallback = async () => {
            try {
                await axios.get(
                    "http://localhost:8000/api/google/callback/" + window.location.search,
                    { withCredentials: true } // Ensure cookies are sent & received
                );

                // Check for both tokens using js-cookie with domain and path
                const accessToken = Cookies.get('access_token', { domain: '127.0.0.1', path: '/' });
                const refreshToken = Cookies.get('refresh_token', { domain: '127.0.0.1', path: '/' });

                if (accessToken && refreshToken) {
                    navigate("/");
                } else {
                    console.warn("Access token or refresh token not found in cookies under 127.0.0.1:5173");
                    navigate("/login");
                }


            } catch (error) {
                console.error("Google login failed", error);
                navigate("/login");
            }
        };

        handleCallback();
    }, [navigate]);

    return <h2>Logging in...</h2>;
};

export default GoogleCallback;
from django.shortcuts import redirect
from django.http import JsonResponse
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from allauth.socialaccount.models import SocialAccount, SocialToken
from rest_framework_simplejwt.tokens import RefreshToken
from datetime import timedelta
from django.utils.timezone import now
from django.core.exceptions import ObjectDoesNotExist

User = get_user_model()

def google_login(request):
    """Redirects the user to Google's OAuth 2.0 authentication page."""
    google_oauth_url = (
        "https://accounts.google.com/o/oauth2/auth"
        "?response_type=code"
        f"&client_id={settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID}"
        f"&redirect_uri={settings.SOCIAL_AUTH_GOOGLE_REDIRECT_URI}"  
        "&scope=email%20profile%20openid"
        "&access_type=offline"
        "&prompt=consent"
    )
    return redirect(google_oauth_url)

def google_login_callback(request):
    """Handles OAuth callback, exchanges code for Google tokens, and generates JWTs."""
    code = request.GET.get('code')

    if not code:
        return JsonResponse({'error': 'Missing authorization code'}, status=400)

    # Exchange code for access & refresh tokens
    token_url = "https://oauth2.googleapis.com/token"
    data = {
        'code': code,
        'client_id': settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID,
        'client_secret': settings.SOCIAL_AUTH_GOOGLE_SECRET,
        'redirect_uri': settings.SOCIAL_AUTH_GOOGLE_REDIRECT_URI,
        'grant_type': 'authorization_code',
    }
    response = requests.post(token_url, data=data)
    token_data = response.json()

    if 'error' in token_data:
        return JsonResponse({'error': 'Failed to fetch tokens', 'details': token_data}, status=400)

    access_token = token_data.get('access_token')
    refresh_token = token_data.get('refresh_token')
    id_token = token_data.get('id_token')

    # Fetch user info from Google
    user_info_url = "https://www.googleapis.com/oauth2/v2/userinfo"
    headers = {'Authorization': f'Bearer {access_token}'}
    user_info_response = requests.get(user_info_url, headers=headers)
    user_info = user_info_response.json()

    if 'email' not in user_info:
        return JsonResponse({'error': 'Google authentication failed'}, status=400)

    email = user_info['email']

    # Check if user exists, create if not
    user, created = User.objects.get_or_create(username=email, defaults={'email': email})

    # Store Google tokens in SocialToken model
    try:
        social_account = SocialAccount.objects.get(user=user, provider='google')
    except ObjectDoesNotExist:
        social_account = SocialAccount.objects.create(user=user, provider='google', extra_data=user_info)

    social_token, _ = SocialToken.objects.update_or_create(
        account=social_account,
        defaults={'token': access_token, 'token_secret': refresh_token, 'expires_at': now() + timedelta(seconds=token_data.get("expires_in", 3600))}
    )

    # Generate JWT tokens
    refresh = RefreshToken.for_user(user)
    access = refresh.access_token

    # Set JWT tokens as secure HTTP-only cookies
    response = JsonResponse({'message': 'Login successful'})
    response.set_cookie('access_token', str(access), httponly=True, secure=True, samesite='Lax')
    response.set_cookie('refresh_token', str(refresh), httponly=True, secure=True, samesite='Lax')

    return response

def refresh_google_token(user):
    """Refreshes the user's Google OAuth token if expired."""
    try:
        social_account = SocialAccount.objects.get(user=user, provider='google')
        social_token = SocialToken.objects.get(account=social_account)

        if social_token.expires_at and social_token.expires_at < now():
            token_url = "https://oauth2.googleapis.com/token"
            data = {
                'client_id': settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID,
                'client_secret': settings.SOCIAL_AUTH_GOOGLE_SECRET,
                'refresh_token': social_token.token_secret,
                'grant_type': 'refresh_token',
            }
            response = requests.post(token_url, data=data)
            new_token_data = response.json()

            if 'access_token' in new_token_data:
                social_token.token = new_token_data['access_token']
                social_token.expires_at = now() + timedelta(seconds=new_token_data.get("expires_in", 3600))
                social_token.save()
                return new_token_data['access_token']
            else:
                return None
        return social_token.token
    except ObjectDoesNotExist:
        return None
from rest_framework_simplejwt.tokens import AccessToken

def verify_user(request):
    """Verifies the user's authentication using JWT stored in cookies."""
    access_token = request.COOKIES.get("access_token")

    if not access_token:
        return JsonResponse({'authenticated': False, 'error': 'No access token'}, status=401)

    try:
        payload = AccessToken(access_token)
        user = User.objects.get(id=payload['user_id'])
        return JsonResponse({'authenticated': True, 'email': user.email})
    except Exception:
        return JsonResponse({'authenticated': False, 'error': 'Invalid or expired token'}, status=401)
import { useEffect, useState } from "react";
import { verifyUser, refreshGoogleToken } from "./api";
import { useNavigate } from "react-router-dom";

const Home = () => {
    const [user, setUser] = useState(null);
    const navigate = useNavigate();

    useEffect(() => {
        const checkUser = async () => {
            const data = await verifyUser();
            if (data.authenticated) {
                setUser(data);
            } else {
                console.warn("User verification failed:", data.error);
                navigate("/login"); // Redirect immediately if not authenticated - Removed setTimeout delay
            }
        };

        checkUser();
    }, [navigate]);

    return (
        <div>
            {user ? (
                <div>
                    <h2>Welcome, {user.email}</h2>
                </div>
            ) : (
                <h2>Loading...</h2>
            )}
        </div>
    );
};

export default Home;

Browser Cookie

Jumps and lags when switching between sections (ScrollTrigger)

I am new to frontend and have started using gsap for animations. I use gsap mainly for switching between sections. On screens larger than 576px I have a problem: when switching between sections there are jumps and they disappear after a while if you scroll up and down the page. Maybe someone has encountered this ?
I don’t seem to have a lot of animations so far, but already so laggy.

Here’s what I tried to do:

  1. Remove all possible negative margins.

  2. Add to all elements to which the transition is made, properties:

    transform: translate3d(0,0,0,);
    will-change: transform;
    backface-visibility: hidden;

  3. Set property ease – sine, power1, power2.

All this had no effect.

In the mobile version I have a lot of animation that works perfectly with gsap.

    
        <iframe height="300" style="width: 100%;" scrolling="no" title="Biorytm" src="https://codepen.io/bcnngjlt-the-scripter/embed/MYWvPvJ?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
          See the Pen <a href="https://codepen.io/bcnngjlt-the-scripter/pen/MYWvPvJ">
          Biorytm</a> by Яша Проценко (<a href="https://codepen.io/bcnngjlt-the-scripter">@bcnngjlt-the-scripter</a>)
          on <a href="https://codepen.io">CodePen</a>.
        </iframe>

react.js make component persist on specific route

I am rendering a component like this in Approuter.js on element document.getElementById("side-bar"); , element is in wordpress homepage

When I click and route to /deals , the component renders but disappears right away in 1 sec . How do I modify it so the component stays/persist as long as user is on /deals

  import React, { useEffect, useState } from "react";
import { Routes, Route, useLocation } from "react-router-dom";
import { createPortal } from "react-dom";  
...

export const AppRouter = () => {
    const location = useLocation();
    const [categories, setCategories] = useState({});
    const [sidebarContainer, setSidebarContainer] = useState(null);

    useEffect(() => {
        // Fetch categories once when the app loads
        fetchCategories().then((data) => {
            if (typeof data === "object" && data !== null) {
                setCategories(data);
            } else {
                console.error("Invalid category format:", data);
                setCategories({});
            }
        });
    }, []);

    useEffect(() => {

        // Find the sidebar container from the WordPress PHP homepage

        const container = document.getElementById("side-bar");
        setSidebarContainer(container);
    }, []); // Run once on mount

    return (
        <div className="app-wrapper">
            <Header />

            <div className="content-wrapper">
                <div className="page-container">
                    <main className="main-content">
                        <Routes>
                            <Route
                                path="/"
                                element={
                                    <>
                                    ...
                                    </>
                                }
                            />
                            ..
                            <Route path="/deals" element={<DealsList />} />
                        </Routes>
                    </main>
                </div>

                {/*  Inject Sidebar into WordPress' existing #side-bar */}
                {location.pathname === "/deals" && sidebarContainer
                    ? createPortal(<Sidebar categories={categories} />, sidebarContainer)
                    : null}
            </div>

            <Footer />
        </div>
    );
};

Remove Zoom Controls from Google Streetview

I have a Google Map implemented with basic Javascript, where a toggle between Streetview and the normal View. I am NOT talking about remove StreetView Toggles/Controls in normal views.

I want to hide the zoomControls when the StreetView is active. But the config is not working for the zoomControls. It looks like, only some of the options are working (like enableCloseButton).

It’s working for the normal view, but not explicit within the StreetView.

marked plus and minus controls on the streetview

Jsfiddle:

https://jsfiddle.net/Maybach/3j75f2w9/7/
(its just cloned from their official documentation and i added the options object)

HTML:

<!doctype html>
<!--
 @license
 Copyright 2019 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->
<html>
  <head>
    <title>Disabling the Default UI</title>

    <!-- jsFiddle will insert css and js -->
  </head>
  <body>
    <div id="map"></div>

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>

Javascript:

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
let panorama

function initMap() {
  const astorPlace = { lat: 40.729884, lng: -73.990988 }
  // Set up the map
  const map = new google.maps.Map(document.getElementById("map"), {
    center: astorPlace,
    zoom: 18,
    streetViewControl: false,
  })

  document.getElementById("toggle").addEventListener("click", toggleStreetView)


  // We get the map's default panorama and set up some defaults.
  // Note that we don't yet set it visible.
  panorama = map.getStreetView() // TODO fix type
  panorama.setPosition(astorPlace)
  panorama.setPov(
    /** @type {google.maps.StreetViewPov} */ {
      heading: 265,
      pitch: 0,
    },
  )
}

function toggleStreetView() {
  const toggle = panorama.getVisible()
  panorama.setOptions({
    disableDefaultUI: true,
    enableCloseButton: true,
    linksControl: false,
    panControl: false,
    zoomControl: false,
    scrollwheel: false,
    fullscreenControl: false,
    motionTracking: false,
    motionTrackingControl: false,
    addressControl: false,
    clickToGo: false,
    showRoadLabels: false,
  })
  if (toggle == false) {
    panorama.setVisible(true)
  } else {
    panorama.setVisible(false)
  }
}

window.initMap = initMap

CSS:

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#floating-panel {
  position: absolute;
  top: 10px;
  left: 25%;
  z-index: 5;
  background-color: #fff;
  padding: 5px;
  border: 1px solid #999;
  text-align: center;
  font-family: "Roboto", "sans-serif";
  line-height: 30px;
  padding-left: 10px;
}

#floating-panel {
  margin-left: -100px;
}

  1. I’ve read the official documentation about the StreetView options
  2. I added the options object to remove the zoomControls: false as its working within the normal view.

I was expecting the zoomControls in the bottom right corner (the + & -) will be hidden, when the streetView is activated.

Split a record in an object [closed]

I have this object:

{
Name:    "Jim"
Cars:    "BMW|NISSAN"
Numbers: "1|3"
},
{
Name:    "Mary"
Cars:    "DODGE|BMW"
Numbers: "2|3"
}

So Jim has 1 BMW and 3 Nissan. Mary has 2 Dodge and 3 BMW.
I need some simple “functionality” to transform this object to this:

{
Name:    "Jim"
Cars:    "BMW"
Numbers: "1"
},
{
Name:    "Jim"
Cars:    "NISSAN"
Numbers: "3"
},
{
Name:    "Mary"
Cars:    "DODGE"
Numbers: "2"
}
{
Name:    "Mary"
Cars:    "BMW"
Numbers: "3"
}

I don’t have the option to create the initial object in a different way. I have to deal with what I receive from the database and solve it in the FrontEnd (javascript).

Convert TXT to JSON in javascript

The application should:

  • Read data from a .txt file (e.g., weather:blabla:sunny).

  • Convert it to .json.

Problem:
During the .txt → .json conversion, the last segment (e.g., “sunny”) is being cut off.

.txt format:
Key before the first :, value after the second :, everything in between is irrelevant.

Conditions:

  • No external software, sensitive data.

Link to the application:
Google Drive
Run: In VS Code → terminal → npx electron.

Expected .txt format:
weather:blabla:sunny or (weather:blabla;blabla:sunny).

QUESTION How to make it so that after the second ‘:’ it reads the data into JSON, while the rest of the TXT file is being read correctly?
CODE:

const { ipcRenderer } = require("electron");

// Function to process data from a TXT file into a JSON object
const fs = require('fs');

// Function to convert TXT file content to JSON
function convertToJson(text) {
    console.log("Input data:", text); // For debugging
    
    const lines = text.split('n').filter(line => line.trim());
    const result = {};
    
    for (const line of lines) {
      console.log("Processed line:", line); // For debugging
      
      // Skip empty lines
      if (!line.trim()) continue;
      
      // Find the position of the first colon
      const firstColonIndex = line.indexOf(':');
      
      // If there’s no colon, skip to the next line
      if (firstColonIndex === -1) continue;
      
      // Extract the key (before the first colon)
      const key = line.substring(0, firstColonIndex).trim();
      
      // Extract the full value after the first colon
      const fullValue = line.substring(firstColonIndex + 1);
      
      console.log(`Found key: "${key}", value: "${fullValue}"`); // For debugging
      
      // Save to the result object
      result[key] = fullValue.trim();
    }
    
    console.log("JSON result:", result); // For debugging
    return result;
  }
  
  // Test
  const testData = "weather:sunny:flower";
  const result = convertToJson(testData);
  console.log(JSON.stringify(result, null, 2));
    
  
// "Select TXT File" button
document.getElementById("selectFile").addEventListener("click", async () => {
    console.log("Clicked 'Select TXT File'");
    const fileContent = await ipcRenderer.invoke("open-file");
    if (fileContent) {
        console.log("File content:", fileContent);
        document.getElementById("fileContent").innerText = JSON.stringify(fileContent, null, 2);
    }
});

// "Save as DOCX" button
document.getElementById("convertToDocx").addEventListener("click", async () => {
    console.log("Clicked 'Save as DOCX'");

    // Transform the content into a JSON object
    const fileContent = document.getElementById("fileContent").innerText;
    const data = JSON.parse(fileContent);  // Convert text to a JSON object
    
    await ipcRenderer.invoke("save-docx", data);  // Send the data object
    alert("DOCX file has been saved!");
});

dialog with role=’dialog’ causing screen reader to announce all content despite setting focus on specific element

I’m implementing an accessible profile menu in Vue 3 using v-menu with role=”dialog”. When the menu opens, I’m setting focus on a specific element, but the screen reader (specify which one: NVDA, JAWS, VoiceOver, etc.) is announcing all content in the dialog instead of just the focused element.

<div class="d-sm-flex d-md-flex justify-end align-center menu-items">
                <v-menu v-model="menu" :close-on-content-click="false" location="bottom"
                  class="profile-card" @update:modelValue="handleMenuToggle">
                  <template v-slot:activator="{ props }">
                    <v-btn color="indigo" v-bind="props" class="profile-btn" role="button" id="profile-menu-button"
                      @click="openProfileMenu" aria-labelledby="profileLabel userName userRoleText"
                      :aria-expanded="menu" aria-haspopup="dialog" ref="profileButton">
                      <img src="/images/user.svg" alt="" aria-hidden="true" />
                      <div class="d-none d-sm-flex flex-column align-start">
                        <span id="profileLabel" class="sr-only">{{ t('profile') }}</span>
                        <span class="font-16 font-700 color-white" id="userName">{{ store?.user?.name }}</span>
                        <span class="font-14 font-400 color-white" id="userRoleText">{{ userRole }}</span>
                      </div>
                    </v-btn>
                  </template>
                  <v-card min-width="380" role="dialog" aria-modal="true" aria-label="Profile Mega Menu" ref="menuCard"
                    @keydown.escape="closeProfileMenu">
                    <div class="header text-right pa-6 pr-8 py-0">
                      <button id="close-profile-menu" @click="closeProfileMenu" :aria-label="ariaLabelData.closeProfile"
                        class="cursor-pointer close-icon" ref="closeButton">
                        <img src="/images/close-icon.svg" alt="" aria-hidden="true" />
                      </button>
                    </div>
                    <div class="uesrname" aria-labelledby="profileLabelInner userNameInner userRoleTextInner"
                      tabindex="0">
                      <div class="user-profile">
                        <span id="profileLabelInner" class="sr-only">{{ t('profile') }}</span>
                        <p id="userNameInner" class="font-16 font-600 name">{{ store?.user?.fullName }}</p>
                        <p id="userRoleTextInner" class="font-12 font-400 role">{{ userRole }}</p>
                      </div>
                    </div>
                    <div class="profileMenu">
                      <div 
                       
                        @click="closeMenu" 
                        :aria-label="ariaLabelData.myProfile" 
                        tabindex="0"
                        id="my-profile-button" 
                        ref="myProfileButton"
                        class="profileMenu__item">
                        <img src="/public/images/user-blue.svg" class="mr-4" alt="" aria-hidden="true" />
                        <span aria-hidden="true">{{ t('myProfile') }}</span>
                      </div>
                    
                      <div
                        v-if="store?.user?.roles.includes('PARTNER_ADMIN') || store?.user?.roles.includes('ADMIN') || store?.user?.roles.includes('SUB_ADMIN')"
                        @click="redirectToDashboard" 
                        class="profileMenu__item profileMenu__item--dashboard" 
                        :aria-label="ariaLabelData.dashboard"
                        tabindex="0">
                        <div class="profileMenu__item-wrapper">
                          <div class="profileMenu__item-primary">
                            <img src="/public/images/dashboard.svg" class="mr-4" alt="" aria-hidden="true" />
                            <span aria-hidden="true">{{ t('dashboard') }}</span>
                          </div>
                          <div class="profileMenu__item-secondary">
                            <img src="/public/images/action-icon.svg" alt="" aria-hidden="true" />
                          </div>
                        </div>
                      </div>
                    
                      <div
                        v-if="(store?.user?.roles.includes('PARTNER_ADMIN') || store?.user?.roles.includes('ADMIN')) && store?.user?.profileConfiguration?.enabled"
                        @click="redirectToProfile" 
                        class="profileMenu__item profileMenu__item--profile" 
                        :aria-label="ariaLabelData.profilePage"
                        tabindex="0">
                        <div class="profileMenu__item-wrapper">
                          <div class="profileMenu__item-primary">
                            <img src="/public/images/corporate-icon.svg" class="mr-4" alt="" aria-hidden="true" />
                            <span aria-hidden="true">{{ t('profilePage') }}</span>
                          </div>
                          <div class="profileMenu__item-secondary">
                            <img src="/public/images/action-icon.svg" alt="" aria-hidden="true" />
                          </div>
                        </div>
                      </div>
                    
                      <div
                        @click="closeMenu" 
                        class="profileMenu__item" 
                        :aria-label="ariaLabelData.mfasettings"
                        tabindex="0">
                        <img src="/public/images/mfa-setting.svg" class="mr-4" alt="" aria-hidden="true" />
                        <span aria-hidden="true">{{ t('mfasettings') }}</span>
                      </div>
                    </div>
                    <v-card-actions>
                      <v-btn variant="text" @click="signout" :aria-label="ariaLabelData.signout" role="button"
                        tabindex="0">
                        {{ t('signout') }}
                        <img src="/public/images/arrow-blue-right.svg" class="ml-2" alt="" aria-hidden="true" />
                      </v-btn>
                    </v-card-actions>
                  </v-card>
                </v-menu>
              </div>

Expected behavior: When the dialog opens and focus is set on the my profile button, only that button should be announced by the screen reader.

Actual behavior: The screen reader announces all focusable elements within the dialog, even though focus is properly trapped and set on the my profile button.

What I’ve tried:

  • Setting aria-modal=”true”
  • Using trapFocus() to contain focus within the dialog
  • Setting explicit focus on the my profile button with a delay

Is there something specific about Vue’s rendering or the way screen readers interact with dynamically created dialogs that I’m missing?

Organizational chart using d3 in Angular

Angular version 16; D3 version 7.9.0; D3-org-chart version 3.1.1… I created a chart of organization. But I would want to provide the organizational chart a zoom-in and zoom-out function.

I added the code for zoom-in and zoom-out feature but when zoom-in or out the chart disappeared.

I don’t know how to set the zoom-in ,zoom-out limitations for organizational chart.

HTML code

<div id="orgchart" class="chart-container"></div>


SCSS code

#orgchart {

    .node .label {
        fill: green !important;
    }

    .node .title {
        fill: red !important;
    }

    .node rect {
        fill: lightblue !important;
    }

    svg {
        background-color: rgb(213, 219, 220);
    }

    ::ng-deep.node-button-div {
        width: 134% !important;
    }


    // ::ng-deep svg.svg-chart-container {
    //     height: 200vh !important;
    // }

    ::ng-deep svg.svg-chart-container {
        height: auto !important;
        width: auto !important;

    }

}




TS code

import {
    OnChanges,
    Component,
    OnInit,
    Input,
    ViewChild,
    ElementRef,
} from '@angular/core';
import * as d3 from 'd3';
import { HierarchyNode } from 'd3';
import { OrgChart, State } from 'd3-org-chart';



@Component({
    selector: 'app-org-chart',
    templateUrl: './org-chart.component.html',
    styleUrls: ['./org-chart.component.scss']
})
export class OrgChartComponent implements OnInit, OnChanges {
    @ViewChild("chartContainer") chartContainer: ElementRef | undefined;
    @Input() data: any[] | undefined;
    chart: any;
    datas: any[] = [];
    constructor() { }

    ngOnInit() {





        const res = {
            data: [
                {
                    id: 1,
                    pos_name: 'KITTING PROCESS',
                    size: '',
                    parentId: 0,
                    member: 'Mani.N  (027)',
                    imgname: null,
                    desig_id: 1,
                    desig_name: 'Managing Director',
                    file_path: null,
                    // status: 'inprogress',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },
                {
                    id: 2,
                    pos_name: 'ASSEMBLY PROCESS',
                    size: '',
                    parentId: 0,
                    // member: 'Soumen Deb  (142)',
                    imgname: null,
                    desig_id: 2,
                    // desig_name: 'Test27_desig',
                    file_path: null,
                    // status: 'pending',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },
                {
                    id: 3,
                    pos_name: 'TESTING PROCESS',
                    size: '',
                    parentId: 0,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 3,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'completed',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },
                {
                    id: 4,
                    pos_name: 'Dry Process',
                    size: '',
                    parentId: 1,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 4,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'completed',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },
                {
                    id: 5,
                    pos_name: 'Wet Process',
                    size: '',
                    parentId: 1,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 4,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'pending',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },

                {
                    id: 6,
                    pos_name: 'Radix Lab to Fab Products',
                    size: '',
                    parentId: 1,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 6,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'inprogress',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },

                {
                    id: 7,
                    pos_name: "Strategic Technology Platforms",
                    size: "",
                    parentId: 1,
                    member: "Sanjay.G  (476)",
                    imgname: null,
                    desig_id: 73,
                    desig_name: "Senior Officer - Costing",
                    file_path: null,
                    // status: 'completed',
                    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
                },
                {
                    id: 8,
                    pos_name: 'Dry Process',
                    size: '',
                    parentId: 2,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 4,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'completed',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },
                {
                    id: 9,
                    pos_name: 'Wet Process',
                    size: '',
                    parentId: 2,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 4,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'pending',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },

                {
                    id: 10,
                    pos_name: 'Radix Lab to Fab Products',
                    size: '',
                    parentId: 2,
                    // member: 'Tanmoy Ghosh  (341)',
                    imgname: null,
                    desig_id: 6,
                    // desig_name: 'Manager-Laser Customer Relations',
                    file_path: null,
                    // status: 'inprogress',
                    imageUrl:
                        'http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png',
                },

                {
                    id: 11,
                    pos_name: "Strategic Technology Platforms",
                    size: "",
                    parentId: 2,
                    member: "Sanjay.G  (476)",
                    imgname: null,
                    desig_id: 73,
                    desig_name: "Senior Officer - Costing",
                    file_path: null,
                    // status: 'completed',
                    imageUrl: "http://103.154.184.122:8080/kpi_api_dev/authenticate/profileimage/user.png"
                },
             
                {
                    id: 0,
                    pos_name: "YES",
                    size: "",
                    parentId: "",
                    member: "Organizational Chart",

                    desig_id: 73,
                    desig_name: "",

                }
            ]
        }



        console.log('Org Chart Data:', res.data);
        console.log('ID type:', typeof res.data[0].id, 'ParentID type:', typeof res.data[0].parentId);
        console.log('IDs:', res.data.map(item => item.id));
        res.data.forEach(item => {
            if (item.id === null || item.id === undefined || item.parentId === null || item.parentId === undefined) {
                console.log('Null or undefined found:', item);
            }
        });

        if (res) {
            const chartContainer = d3.select('.chart-container');
            chartContainer.append('defs')
                .append('clipPath')
                .attr('id', 'clip')
                .append('rect')
                .attr('width', '100%')
                .attr('height', '100%');
            this.chart = new OrgChart()
                .container('.chart-container')
                .layout('left')
                .data(res.data)
                .nodeWidth((d: any) => 300)
                .initialZoom(0.8)
                .nodeHeight((d: any) => 175)


                .buttonContent(
                    ({
                        node,
                        state,
                    }: {
                        node: HierarchyNode<any>;
                        state: State<any>;
                    }) => {
                        return `<div style="color:#fff;    border-radius: 5px;
    padding: 3px 9px 3px 8px;font-size:10px;margin:auto auto;background-color:#007672;border: 1px solid #007672">
    <span style="font-size:14px">
    ${node.data._directSubordinates}
        ${node.children
                                ? `<i class="fa fa-angle-up"></i>`
                                : `<i class="fa fa-angle-down"></i>`
                            }
    </span>

    </div>`;
                    }
                )
                .linkUpdate((d: any, i: any, arr: any) => {
                    const selection = d3.select('.chart-container');
                    const strokeColor = '#007672';
                    const strokeWidth = d.data._upToTheRootHighlighted ? 15 : 1;
                    selection.style('stroke', strokeColor);
                    selection.attr('stroke-width', strokeWidth);


                    if (d.data._upToTheRootHighlighted) {
                        selection.raise();
                    }
                })
                .childrenMargin((d: any) => 50)
                .compactMarginBetween((d: any) => 35)
                .compactMarginPair((d: any) => 90)
                .nodeContent(function (
                    d: HierarchyNode<any>,
                    i: number,
                    arr: HierarchyNode<any>[],
                    state: State<any>
                ) {
                    const nodeWithWidth = d as HierarchyNode<any> & { width: number };
                    const imageUrl = d.data.imageUrl ? d.data.imageUrl : 'assets/masters/user.jpg';

                    let borderColor = 'lightgray'; // Default border color

                    if (d.data.status === 'pending') {
                        borderColor = 'orange';
                    } else if (d.data.status === 'inprogress') {
                        borderColor = 'blue';
                    } else if (d.data.status === 'completed') {
                        borderColor = 'green';
                    }
                    return `

    

    <div style="padding-top:0px;background-color:none;    border-radius: 20px; margin-left:1px;height:${d.height}px;border-radius:2px;overflow:visible">
    <div style="height:inherit;padding-top:0px;background-color:white;border:5px solid ${borderColor};    border-radius: 20px;">
    <div
    class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-0"
    style="
      background: #ffffff 0% 0% no-repeat padding-box;
      box-shadow: 0px 3px 6px #00000029;
      height: inherit;
      width: inherit;
  border-radius: 20px;
    "
    >
    
    <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 p-3">
      <div
        class="col-xs-12 col-sm-12 col-md-12 col-lg-12 row m-0 d-flex align-items-center p-0"
      >
        <div class="col-xs-3 col-sm-3 col-md-3 col-lg-3 p-0">
          <img src="assets/card/profile1.png" style="width: 70px; height: 70px;border-radius: 50%;" />
        </div>
        <div class="col-xs-9 col-sm-9 col-md-9 col-lg-9 p-0">
          <div
            class="col-xs-12 col-sm-12 col-md-12 col-lg-12 d-flex flex-column ms-4"
          >
            <div
              class="col-xs-12 col-sm-12 col-md-12 col-lg-12 pt-2 d-flex justify-content-start fw-bolder"
            >
            ${d.data.pos_name}
            </div>

              <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light " style="color: #007672;">
            ${d.data.desig_name}
            </div>

            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 py-1 fw-light "  style="color: #868585;">
            ${d.data.member}
            </div>
          
          </div>
        </div>
      </div>

    </div>`;
                })

            const zoom: any = d3
                .zoom<SVGSVGElement, any>()
                .scaleExtent([1, 10])
                .on('zoom', (event: any) => {
                    chartContainer.attr('transform', event.transform);
                });
            chartContainer.call(zoom);

            console.log("Chart after expandAll:", this.chart);
            console.log("Chart after render:", this.chart);
            this.chart.expandAll();

            this.chart.render();






        }

    }


    ngAfterViewInit() {
        if (!this.chart) {

        }
        this.updateChart();
    }



    ngOnChanges() {
        this.updateChart();
    }
    updateChart() {
        if (!this.data) {
            return;
        }
        if (!this.chart) {
            return;
        }
        this.chart
            .container('.chart-container')
            .data(this.data)
            .svgWidth(500)

            .onNodeClick((d: any) => console.log(d + ' node clicked'))
            .render();
    }


}

How can I implement Markdown in Javascript for Bold and italic

I’m trying to implement this for bold and italic markdown styles, upon selecting individual text or entire sentence the style should be applied and toggled but with my current code its not working as expected.

If i write a sentence and select each word with bold or italic and hit send its giving correct output, but when i select all and click bold or italic the previous styles are overidden and everything changes to selected style.

I want the output to be proper irrespective of any testcase that user performs on these buttongs.

Markdown syntax link

I have the below code, and trying to add this markdown feature to a chat window.. hence a mock code.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Text Editor with Toggle Bold/Italic</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="editor-container">
        <div class="toolbar">
            <button onclick="toggleStyle('bold')">Bold</button>
            <button onclick="toggleStyle('italic')">Italic</button>
        </div>
        <div contenteditable="true" id="textbox" class="textbox"></div>
        <button onclick="convertToMarkdown()">Send</button>
    </div>
    <div class="output" id="output"></div>
 
    <script src="script.js"></script>
</body>
</html>

body {
    font-family: Arial, sans-serif;
    text-align: center;
    margin-top: 50px;
}
 
.editor-container {
    display: inline-block;
    text-align: left;
    border: 1px solid #ccc;
    padding: 10px;
    width: 300px;
}
 
.toolbar {
    margin-bottom: 5px;
}
 
.toolbar button {
    margin-right: 5px;
    padding: 5px 10px;
    cursor: pointer;
}
 
.textbox {
    width: 100%;
    min-height: 100px;
    border: 1px solid #ccc;
    padding: 5px;
    outline: none;
}
 
.output {
    margin-top: 10px;
    font-weight: bold;
}
 
.bold {
    font-weight: bold;
}
 
.italic {
    font-style: italic;
}

function toggleStyle(style) {
    const selection = window.getSelection();
    if (!selection.rangeCount) return;
 
    const range = selection.getRangeAt(0);
    const selectedText = range.toString().trim();
    if (!selectedText) return;
 
    let parentSpan = range.commonAncestorContainer.parentElement;
    let shouldRemoveStyle = false;
 
    if (parentSpan.tagName === "SPAN") {
        if (parentSpan.classList.contains(style)) {
            // Remove style if it's already applied
            parentSpan.classList.remove(style);
            shouldRemoveStyle = true;
        }
    }
 
    if (!shouldRemoveStyle) {
        // Apply new style only to selected text
        const span = document.createElement("span");
        span.classList.add(style);
        span.textContent = selectedText;
        range.deleteContents();
        range.insertNode(span);
    }
 
    // Remove empty spans
    document.querySelectorAll(".textbox span").forEach(el => {
        if (el.classList.length === 0) {
            el.replaceWith(el.textContent);
        }
    });
 
    selection.removeAllRanges();
}
 
function convertToMarkdown() {
    const textbox = document.getElementById("textbox");
    let rawHTML = textbox.innerHTML;
 
    // Step 1: Handle bold + italic first
    rawHTML = rawHTML.replace(/<span class="bold italic">(.*?)</span>/g, "***$1***");
 
    // Step 2: Handle only bold (after bold+italic is processed)
    rawHTML = rawHTML.replace(/<span class="bold">(.*?)</span>/g, "**$1**");
 
    // Step 3: Handle only italic (after bold+italic is processed)
    rawHTML = rawHTML.replace(/<span class="italic">(.*?)</span>/g, "_$1_");
 
    // Step 4: Ensure no redundant markdown (**_text_** → ***text***)
    rawHTML = rawHTML.replace(/**_([^_*]+)_**/g, "***$1***");
    rawHTML = rawHTML.replace(/_**([^_*]+)**_/g, "***$1***");
 
    // Step 5: Ensure no extra symbols (fixes issues like __text****)
    rawHTML = rawHTML.replace(/_{2,}([^_]+)_{2,}/g, "_$1_"); // Fixes __text__ → _text_
    rawHTML = rawHTML.replace(/*{4,}([^*]+)*{4,}/g, "***$1***"); // Fixes ****text**** → ***text***
 
    document.getElementById("output").innerText = "Markdown Output: " + rawHTML;
}

expecting the correct output for user’s action on bold and italic styles providing me the right markdown string

check box Javascript Function

I have this view:

@model Progetto2.Modell.CreateUtentecs

@{
    ViewBag.Title = "GrigliaFormUtente";
}
<script type="text/javascript">
    function validazioneCampi() {
        const cognome = document.getElementById('Cognome').value.trim();
        const nome = document.getElementById('Nome').value.trim();
        const comuneId = document.getElementById('ComuneId').value;
        const sesso = document.getElementById('Sesso').value.trim();
        const professioniID = document.getElementById('ProfessioniID').value;
        const error = document.getElementById('error');

        let messages = "";

        if (cognome === "") messages = "Inserire un Cognome";
        if (nome === "") messages = "Inserire un Nome";
        if (sesso === "") messages = "Inserire il Sesso";

        if (messages !== "") {
            error.innerHTML = "<span style='color:red;'>" + messages + "</span>";
            return;
        }

        const proprietaIds = [];
        document.querySelectorAll('input[name="ProprietaIds"]:checked').forEach(function (checkbox) {
            proprietaIds.push(checkbox.value);
        });

        $.ajax({
            type: 'POST',
            url: '/Utentes/CreaUtente',
            data: {
                Cognome: cognome,
                Nome: nome,
                ComuneId: comuneId,
                Sesso: sesso,
                ProfessioniID: professioniID,
                ProprietaIds: proprietaIds
            },
            success: function (response) {
                $('#result').html(response);
                error.innerHTML = "";
            },
            error: function () {
                error.innerHTML = "<span style='color:red;'>Errore nell'inserimento</span>";
            }
        });
    }
    </script>

<h2>Aggiungi un Nuovo Utente</h2>
<hr />

<div class="row">
    <div class="col-md-4">
        <form id="form_1" action="@Url.Action("CreaUtente1", "Utentes")" method="post">
            <div class="form-group">
                <label>Cognome</label>
                @Html.TextBoxFor(m => m.Cognome, new { @class = "form-control", id = "Cognome" })
            </div>
            <br />

            <div class="form-group">
                <label>Nome</label>
                @Html.TextBoxFor(m => m.Nome, new { @class = "form-control", id = "Nome" })
            </div>
            <br />

            <div class="form-group">
                <label>Comune</label>
                @Html.DropDownListFor(m => m.ComuneId,
                              Model.ListaComuni.Select(m => new SelectListItem
                              {
                                  Value = m.Id.ToString(),
                                  Text = m.NomeComune
                              }), new { @class = "form-control", id = "ComuneId" })
            </div>
            <br />

            <div class="form-group">
                <label>Sesso</label>
                @Html.TextBoxFor(m => m.Sesso, new { @class = "form-control", id = "Sesso" })
            </div>
            <br />

            <div class="form-group">
                <label>Professione</label>
                @Html.DropDownListFor(m => m.ProfessioneId,
                              Model.ListaProfessioni.Select(m => new SelectListItem
                              {
                                  Value = m.ID.ToString(),
                                  Text = m.NomeProfessione
                              }), new { @class = "form-control", id = "ProfessioniID" })
            </div>
            <br />

            <div class="form-group">
                <label>Proprietà</label>
                <div>
                    @foreach (var prop in Model.listProp)
                    {
                        <div class="checkbox">
                            <label>
                                <input type="checkbox" name="ProprietaIds" value="@prop.Id" /> @prop.Oggetto_Di_Proprieta
                            </label>
                        </div>
                    }
                </div>
            </div>
            <br />

            <div class="form-group">
                <input type="button" onclick="validazioneCampi()" value="Aggiungi" class="btn btn-primary" />
            </div>
        </form>

        <div id="error"></div>
        <br />
        <div id="result"></div>
    </div>
</div>

and this other view:

@model Progetto2.Modell.CreateUtentecs

@{
    ViewBag.Title = "GrigliaFormUtente";
}
<script type="text/javascript">
    function validazioneCampi() {
        const cognome = document.getElementById('Cognome').value.trim();
        const nome = document.getElementById('Nome').value.trim();
        const comuneId = document.getElementById('ComuneId').value;
        const sesso = document.getElementById('Sesso').value.trim();
        const professioniID = document.getElementById('ProfessioniID').value;
        const error = document.getElementById('error');

        let messages = "";

        if (cognome === "") messages = "Inserire un Cognome";
        if (nome === "") messages = "Inserire un Nome";
        if (sesso === "") messages = "Inserire il Sesso";

        if (messages !== "") {
            error.innerHTML = "<span style='color:red;'>" + messages + "</span>";
            return;
        }

        const proprietaIds = [];
        document.querySelectorAll('input[name="ProprietaIds"]:checked').forEach(function (checkbox) {
            proprietaIds.push(checkbox.value);
        });

        $.ajax({
            type: 'POST',
            url: '/Utentes/CreaUtente',
            data: {
                Cognome: cognome,
                Nome: nome,
                ComuneId: comuneId,
                Sesso: sesso,
                ProfessioniID: professioniID,
                ProprietaIds: proprietaIds
            },
            success: function (response) {
                $('#result').html(response);
                error.innerHTML = "";
            },
            error: function () {
                error.innerHTML = "<span style='color:red;'>Errore nell'inserimento</span>";
            }
        });
    }
    </script>

<h2>Aggiungi un Nuovo Utente</h2>
<hr />

<div class="row">
    <div class="col-md-4">
        <form id="form_1" action="@Url.Action("CreaUtente1", "Utentes")" method="post">
            <div class="form-group">
                <label>Cognome</label>
                @Html.TextBoxFor(m => m.Cognome, new { @class = "form-control", id = "Cognome" })
            </div>
            <br />

            <div class="form-group">
                <label>Nome</label>
                @Html.TextBoxFor(m => m.Nome, new { @class = "form-control", id = "Nome" })
            </div>
            <br />

            <div class="form-group">
                <label>Comune</label>
                @Html.DropDownListFor(m => m.ComuneId,
                              Model.ListaComuni.Select(m => new SelectListItem
                              {
                                  Value = m.Id.ToString(),
                                  Text = m.NomeComune
                              }), new { @class = "form-control", id = "ComuneId" })
            </div>
            <br />

            <div class="form-group">
                <label>Sesso</label>
                @Html.TextBoxFor(m => m.Sesso, new { @class = "form-control", id = "Sesso" })
            </div>
            <br />

            <div class="form-group">
                <label>Professione</label>
                @Html.DropDownListFor(m => m.ProfessioneId,
                              Model.ListaProfessioni.Select(m => new SelectListItem
                              {
                                  Value = m.ID.ToString(),
                                  Text = m.NomeProfessione
                              }), new { @class = "form-control", id = "ProfessioniID" })
            </div>
            <br />

            <div class="form-group">
                <label>Proprietà</label>
                <div>
                    @foreach (var prop in Model.listProp)
                    {
                        <div class="checkbox">
                            <label>
                                <input type="checkbox" name="ProprietaIds" value="@prop.Id" /> @prop.Oggetto_Di_Proprieta
                            </label>
                        </div>
                    }
                </div>
            </div>
            <br />

            <div class="form-group">
                <input type="button" onclick="validazioneCampi()" value="Aggiungi" class="btn btn-primary" />
            </div>
        </form>

        <div id="error"></div>
        <br />
        <div id="result"></div>
    </div>
</div>

and this are the main methods of my service:

public List<Utente> GetUtentiConProprieta()
{
    List<Utente> listaUtenti = new List<Utente>();

    using (SqlConnection conn = new SqlConnection(_connString))
    {
        try
        {
            conn.Open();

            // Query con STUFF per concatenare le proprietà
            string query = @"
                      SELECT a.Id, a.Cognome, a.Nome, c.NomeComune, a.Sesso, p.Professione,
                      STUFF((
                      SELECT ', ' +prv.proprieta
                      FROM uv_anagraficaproprietavendute prv
                      WHERE prv.anagraficaid = a.Id
                      FOR XML PATH ('') 
                      ), 1, 2, '') AS ProprietaVendute,
                      STUFF((
                      SELECT ', ' +pra.proprieta
                      FROM uv_anagraficaproprietaacquistate pra
                      WHERE pra.anagraficaid = a.Id
                      FOR XML PATH ('') 
                      ), 1, 2, '') AS ProprietaAcquistate,
                      STUFF((
                      SELECT ', ' +pr.proprieta
                      FROM uv_anagraficaproprieta pr
                      WHERE pr.anagraficaid = a.Id
                      FOR XML PATH ('') 
                      ), 1, 2, '') AS ProprietaAssegnate
                      FROM uv_anagrafica a
                      LEFT JOIN Comuni c ON a.ComuneId = c.id 
                      LEFT JOIN professioni p ON a.ProfessioniID = p.id;";

            SqlCommand cmd = new SqlCommand(query, conn);
            SqlDataReader reader = cmd.ExecuteReader();

            while (reader.Read())
            {
                listaUtenti.Add(new Utente
                {
                    Id = Convert.ToInt32(reader["Id"]),
                    Cognome = reader["Cognome"].ToString(),
                    Nome = reader["Nome"].ToString(),
                    NomeComune = reader["NomeComune"].ToString(),
                    Sesso = reader["Sesso"].ToString(),
                    Professione = reader["professione"].ToString(),
                    ProprietaVendute = reader["ProprietaVendute"] != DBNull.Value ? reader["ProprietaVendute"].ToString() : "Nessuna",
                    ProprietaAcquistate = reader["ProprietaAcquistate"] != DBNull.Value ? reader["ProprietaAcquistate"].ToString() : "Nessuna",
                    Proprieta = reader["ProprietaAssegnate"] != DBNull.Value ? reader["ProprietaAssegnate"].ToString() : "Nessuna"
                });
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Errore: " + ex.Message);
        }
    }

    return listaUtenti;
}
public List<Utente> AggiornaListaUtenti()
{
    List<Utente> lista = new List<Utente>();
    string connString = "Server=svrsql2016;Database=esercizoanagrafica;User Id=usertest;Password=usertest;TrustServerCertificate=true;";

    using (SqlConnection conn = new SqlConnection(connString))
    {
        try
        {
            conn.Open();
            string query = "SELECT * FROM uv_anagrafica";
            SqlCommand cmd = new SqlCommand(query, conn);
            SqlDataReader reader = cmd.ExecuteReader();

            while (reader.Read())
            {
                lista.Add(new Utente
                {
                    Id = Convert.ToInt32(reader["Id"]),
                    Cognome = reader["Cognome"].ToString(),
                    Nome = reader["Nome"].ToString(),
                    NomeComune = reader["NomeComune"].ToString(),
                    Sesso = reader["Sesso"].ToString(),
                    Professione = reader["Professione"].ToString()
                });
            }
            conn.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Errore: " + ex.Message);
        }
    }
    return lista;
}


public void AggiornaUtente(int id, string Cognome, string Nome, int NomeComuneId, int ProfessioneId, string Sesso, List<int> ProprietaId)
{
    using (SqlConnection conn = new SqlConnection(_connString))
    {
        conn.Open();
        using (var transaction = conn.BeginTransaction()) 
        {
            try
            {
                // Aggiorna dati utente
                string query = @"UPDATE Anagrafica 
                         SET Cognome = @Cognome, Nome = @Nome, ComuneId = @ComuneId, 
                             Sesso = @Sesso, ProfessioniID = @ProfessioneId 
                         WHERE id = @id";

                using (SqlCommand cmd = new SqlCommand(query, conn, transaction))
                {
                    cmd.Parameters.AddWithValue("@id", id);
                    cmd.Parameters.AddWithValue("@Cognome", Cognome);
                    cmd.Parameters.AddWithValue("@Nome", Nome);
                    cmd.Parameters.AddWithValue("@ComuneId", NomeComuneId);
                    cmd.Parameters.AddWithValue("@Sesso", Sesso);
                    cmd.Parameters.AddWithValue("@ProfessioneId", ProfessioneId);
                    cmd.ExecuteNonQuery();
                }

                // Ottieni proprietà attuali
                List<int> proprietaAttuali = new List<int>();
                string selectQuery = "SELECT proprietaId FROM anagraficaProprieta WHERE anagraficaId = @id";

                using (SqlCommand selectCmd = new SqlCommand(selectQuery, conn, transaction))
                {
                    selectCmd.Parameters.AddWithValue("@id", id);
                    using (SqlDataReader reader = selectCmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            proprietaAttuali.Add(reader.GetInt32(0));
                        }
                    }
                }

                // Trova proprietà da rimuovere 
                var proprietaDaRimuovere = proprietaAttuali.Where(p => !ProprietaId.Contains(p)).ToList();
                if (proprietaDaRimuovere.Any())
                {
                    string deleteQuery = "DELETE FROM anagraficaProprieta WHERE anagraficaId = @id AND proprietaId = @propId";
                    string insertSoldQuery = "INSERT INTO anagraficaproprietavendute (anagraficaid, proprietaid) VALUES (@id, @propId)";
                    foreach (int propId in proprietaDaRimuovere)
                    {
                        using (SqlCommand deleteCmd = new SqlCommand(deleteQuery, conn, transaction))
                        {
                            deleteCmd.Parameters.AddWithValue("@id", id);
                            deleteCmd.Parameters.AddWithValue("@propId", propId);
                            deleteCmd.ExecuteNonQuery();
                        }
                        using (SqlCommand insertCmd = new SqlCommand(insertSoldQuery, conn, transaction))
                        {
                            insertCmd.Parameters.AddWithValue("@id", id);
                            insertCmd.Parameters.AddWithValue("@propId", propId);
                            insertCmd.ExecuteNonQuery();
                        }
                    }
                }

                // Trova nuove proprietà da aggiungere
                var proprietaDaAggiungere = ProprietaId.Where(p => !proprietaAttuali.Contains(p)).ToList();
                if (proprietaDaAggiungere.Any())
                {
                    string insertQuery = "INSERT INTO anagraficaProprieta (anagraficaId, proprietaId) VALUES (@id, @propId)";
                    string insertPurchaseQuery = "INSERT INTO anagraficaproprietaacquistate (anagraficaId, proprietaId) VALUES (@id, @propId)";
                    foreach (int propId in proprietaDaAggiungere)
                    {
                        using (SqlCommand insertCmd = new SqlCommand(insertQuery, conn, transaction))
                        {
                            insertCmd.Parameters.AddWithValue("@id", id);
                            insertCmd.Parameters.AddWithValue("@propId", propId);
                            insertCmd.ExecuteNonQuery();
                        }
                        using (SqlCommand insertCmd = new SqlCommand(insertPurchaseQuery, conn, transaction))
                        {
                            insertCmd.Parameters.AddWithValue("@id", id);
                            insertCmd.Parameters.AddWithValue("@propId", propId);
                            insertCmd.ExecuteNonQuery();
                        }
                    }
                }

                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                Console.WriteLine("Errore AggiornaUtente: " + ex.Message);
            }
        }
    }
}

public int CreaUtente(string Cognome, string Nome, int ComuneId, int ProfessioniID, string Sesso, List<int> ProprietaIds)
{
    int nuovoId = -1;

    using (SqlConnection conn = new SqlConnection(_connString))
    {
        try
        {
            conn.Open();
            string query = @"
                INSERT INTO Anagrafica (Cognome, Nome, ComuneId, Sesso, ProfessioniID) 
                OUTPUT INSERTED.Id
                VALUES (@Cognome, @Nome, @ComuneId, @Sesso, @ProfessioniID)";

            SqlCommand cmd = new SqlCommand(query, conn);
            cmd.Parameters.AddWithValue("@Cognome", Cognome);
            cmd.Parameters.AddWithValue("@Nome", Nome);
            cmd.Parameters.AddWithValue("@ComuneId", ComuneId);
            cmd.Parameters.AddWithValue("@Sesso", Sesso);
            cmd.Parameters.AddWithValue("@ProfessioniID", ProfessioniID);

            nuovoId = (int)cmd.ExecuteScalar();

            // Se il nuovo utente è stato creato, assegniamo le proprietà
            if (nuovoId > 0 && ProprietaIds != null && ProprietaIds.Count > 0)
            {
                foreach (int propId in ProprietaIds)
                {
                    _proprietaService.AssegnaProprieta(nuovoId, propId);
                }
            }

            // Dopo aver assegnato le proprietà, aggiorniamo la tabella aggregata
            _proprietaService.AggiornaUvAnagraficaProprieta();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Errore CreaUtente: " + ex.Message);
        }
    }
    return nuovoId;
}

i want to create a js function that on che click on checkbox it insert property on uv_anagrafica and, when i update a user, it post the checked property on uv_anagraficaproprietaacquistate. If when i update an user, i uncheck some properties, function have to post them in uv_anagraficaproprietavendute. if i don’t chek any property, proprieta values in uv_anagrafica, uv_anagraficaproprietavendute e uv_anagraficaproprietaacquistate, must be “Nessuna”.

I read javascript documentation, but i can’t solve my problem. Please help me!

How to make a specific column readonly in tabulator?

I am using tabulator grid (https://tabulator.info/) version no 6.3. I have integrated that it into my project and now i want to make student id column as readonly so that user cant edit it.

How to make a specific column such as studentId or any other readonly in tabulator grid only so that user cant edit it ?

I tried to search for the solution in tabulator documentation but i did not find any way.