Emirates ID Reader Incompatibility After Angular Upgrade [closed]

Background
We have a software application originally developed using Angular version 7.2.5. This application integrates with a hardware device that reads data from Emirates Identity Cards. The integration relies on a JavaScript program available with Emirates ID SDK toolkit through the Emirates ID Reader Service.

Version Upgrade Impact
As part of our ongoing maintenance and modernization efforts, we upgraded the application to Angular version 17.3. After this upgrade, the Emirates ID reading functionality stopped working. No error messages or warnings were displayed in the console or logs.

Despite updating the JavaScript version received from Emirates ID toolkit SDK, the issue persisted. A detailed investigation revealed that Angular 17 enforces stricter programming and scoping rules compared to earlier versions. The existing JavaScript program was not fully compatible with these new constraints, especially in terms of variable scope management and context handling.

Root Cause
JavaScript scope variables used by the Emirates ID Reader Service were not compliant with Angular 17’s stricter encapsulation and execution context.

The legacy JavaScript code was not designed with modern Angular integration practices, resulting in runtime failures or unrecognized references.

Resolution
We performed a complete review of the JavaScript integration layer.

All scope definitions and variable usages were refactored to comply with Angular 17’s requirements.

After implementing these modifications, the Emirates ID reading functionality was successfully restored and is now operating as expected.

Conclusion
Upgrading to Angular 17 requires careful validation of all external JavaScript integrations. Developers should ensure that such integrations respect Angular’s strict scoping rules and best practices for external script execution.

How to Resize a Rectangle Using Hover Dots in Three.js While Keeping One Edge Fixed?

I am working on a Three.js project where
I need to resize a rectangle interactively using hover dots.

The rectangle has two hover dots, one on the left edge and one on the right edge. like this in the screenshot
screenshot

Current behavior:
The rectangle’s width increases, when I drag the dots, but it expands in both directions instead of keeping one edge fixed.
gif

The expected behavior is as follows:

Right Dot Dragged Right:

  • The rectangle’s width increases.
  • The rectangle’s left edge remains fixed.
  • The right hover dot moves outward.

Right Dot Dragged Left:

  • The rectangle’s width decreases.
  • The rectangle’s left edge remains fixed.
  • The right hover dot moves inwards

Left Dot Dragged Left:

  • The rectangle’s width increases.
  • The rectangle’s right edge remains fixed.
  • The left hover dot moves outward.

Left Dot Dragged Right (Crossing the Center):

  • The rectangle’s width decreases.
  • The rectangle’s right edge remains fixed.
  • The left hover dot moves inwards.

Here is the code for the handleDotDrag method:

handleDotDrag(mousePosition, camera) {
  if (!this.isResizing || !this.activeDot) return;

  // Convert mouse position to world coordinates
  const vector = new THREE.Vector3(mousePosition.x, mousePosition.y, 0.5);
  vector.unproject(camera);

  const centerX = this.rectangle.position.x;

  if (this.activeDot.name === "leftDot") {
    // Calculate new width based on the left dot's position
    const newWidth = Math.abs(centerX - vector.x) * 2;

    if (newWidth > 0) {
      this.resize(newWidth);

      // Update the rectangle's position to keep the right edge fixed
      const newCenterX = (this.rectangle.position.x + vector.x) / 2;
      this.rectangle.position.x = newCenterX;
    }
  } else if (this.activeDot.name === "rightDot") {
    // Calculate new width based on the right dot's position
    const newWidth = Math.abs(vector.x - centerX) * 2;

    if (newWidth > 0) {
      this.resize(newWidth);

      // Update the rectangle's position to keep the left edge fixed
      const newCenterX = (this.rectangle.position.x + vector.x) / 2;
      this.rectangle.position.x = newCenterX;
    }
  }
}

Questions:

  • How can I ensure that the rectangle’s left or right edge remains fixed while resizing?
  • Is there a better way to implement this functionality in Three.js?

Any help or suggestions would be greatly appreciated! Thank you!

Javascript API call doesnt display the actual HTML just inserts into element [duplicate]

I have the following function in my website…

    function updateFilteredHtml(data) {
        $('#items').html("");
        var htmlItems = "";
         data.map(kc => {

                htmlItems += '<div class="col-md-4"><div class="card mb-4 box-shadow"><img class="card-img-top" src="' + kc.imgUrl + '" alt="Image"><div class="card-body"><p class="card-text"><strong>' + kc.player + '</strong><br>' + kc.cardType + '<br><small style="font-size: 8px; color:white">item #: ' + kc.docId + '</small></p><div class="d-flex justify-content-between align-items-center"><small class="text-muted">' + kc.sport + '</small><small class="text-muted">$10.00</small></div><div style="text-align: center; margin-top:5px;"><form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"><input type="hidden" name="cmd" value="_s-xclick" /><input type="hidden" name="hosted_button_id" value="' + kc.paypalId + '" /><input type="hidden" name="currency_code" value="USD" />  <input type="image" src="#" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Add to Cart" /></form></div></div></div></div>';
            })

            $('#items').html(htmlItems);
    }

However, when I view the source of the website, I dont see the actual HTML, I just see the code I wrote. When I write websites in PHP, I would see the actual HTML and not the PHP code, which makes sense since PHP is a backend code, Javascript lives on the browser itself.

My question is, is there a way to see the actual HTML when I view the source of the code? My goal is to benefit from SEO.

Nonce not working in browser even though it is set in policy and script

The nonce doesn’t seem to work anywhere. Currently we cut out everything that could make problems, since of course it should be generated, but now it is just static for testing purposes.

Our content policy is defined in our filter:

@Component
public class CSPFilter extends OncePerRequestFilter {

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {

        HttpServletResponse httpResponse = (HttpServletResponse) response;

        SecureRandom random = new SecureRandom();

        byte[] nonceBytes = new byte[16];

        random.nextBytes(nonceBytes);

        String nonce = Base64.getEncoder().encodeToString(nonceBytes);
        nonce = "static";

        String policy = "default-src 'self'; script-src 'self' 'nonce-" + nonce + "'  img-src 'self'; object-src 'none';";

        var oldHeader = httpResponse.getHeader("Content-Security-Policy");
        if (oldHeader!=null) {
            oldHeader = oldHeader + " " + policy;
            httpResponse.setHeader("Content-Security-Policy", oldHeader);
        }
        else
            httpResponse.setHeader("Content-Security-Policy", policy);

        request.setAttribute("nonce", nonce);

        filterChain.doFilter(request, response);
    }
}

and the html looks like this:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<!DOCTYPE html>
<head>
    <title>Login to ToDo Roo</title>
    <link th:href="@{/main.css}" rel="stylesheet"/>
</head>
<body  nonce="static" class="todoroo-body" onload="test()3">
<script language="text/javascript" nonce="static">function test3(){console.log("this should work")}</script>
<script>function test(){console.log("this shouldn't work")}</script>
<h2 th:nonce="${nonce}" th:text="${nonce}" align="center"></h2>

</body>
</html>

yet all browsers state something like this:

Content-Security-Policy: The page’s settings blocked an event handler
(script-src-attr) from being executed because it violates the
following directive: “script-src ‘self’ ‘nonce-static’ http://img-src
‘self’” Source: test()3

Content-Security-Policy: The page’s settings blocked an inline script
(script-src-elem) from being executed because it violates the
following directive: “script-src ‘self’ ‘nonce-static’ http://img-src
‘self’”

We tried multiple solutions, even putting the content policy into a meta tag and loading the site by drag and drop into the browser. We tried multiple policy options like “unsafe-hashes” or the like to no avail.

chrome.windows.update Does not Focus minimized Chrome Window After Clicking The Notification in Ubuntu Notification Center?

I am developing a Google Chrome Extension where Push Notification send to Ubuntu OS shows it in the floating window and after that it goes to Notification Center.

The idea is to focus the current window and show a extension popup when User clicks on that system notification.

It works when User clicks on the Floating Notification, but it doesn’t work if User opens the Notification Center and clicks the Notification there.

The Chrome window doesn’t get focused (bring to the front) and Inspecting the Extensions reveals the following Error in console:

"Error opening popup. Could not find an active browser window"

Here is the Code I used to handle Notification Click:

chrome.notifications.onClicked.addListener(async function (notificationId) {
  const windows = await chrome.windows.getAll();
  await chrome.windows.update(windows[0].id, {
          focused: true
        });
  await chrome.action.setPopup({popup: 'index.html'});
  await chrome.action.openPopup();
});

How do I build Vue+Vite project that contains web worker and WASM?

In my project, I have a web worker at ./src/test.worker.js that looks like this:

import("mywasm").then(wasm => {
  self.onmessage = event => {
    const { x, y } = event.data
    const z = wasm.func(x, y)
    self.postMessage({ z })
  }
})

I import it in a Vue component this way:

<script setup>
import TestWorker from '@/test.worker.js?worker'
...
const worker = new TestWorker()
worker.onmessage = event => {
  console.log(event.data)
}
...
worker.postMessage({x: 25, y: 36})
...
</script>

If I run this project while developing with npm run dev (in package.json I have scripts.dev: vite and scripts.build: vite build), everything works file. But if I build with NODE_ENV=production npm run build, I get the error:

error during build:
[vite:worker] The "path" argument must be of type string. Received undefined
file: /home/myuser/Development/myproj/src/test.worker.js?worker

This makes me think that on the build stage Vite forgets that ?worker is not a part of the file name, so it can’t find the exact name test.worker.js?worker because instead test.worker.js exists. How do I fix it?. Or is there a way to build the project exactly in the way as vite does on the development server mode? Or maybe I forgot something in vite-plugin-wasm configuration?

My vite.config.js:

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import wasm from "vite-plugin-wasm"
import topLevelAwait from "vite-plugin-top-level-await"

// https://vite.dev/config/
export default defineConfig({
  build: {
    target: 'esnext',
  },
  plugins: [
    vue(),
    vueDevTools(),
    wasm(),
    topLevelAwait(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
  worker: {
    plugins: [
      wasm(),
      topLevelAwait(),
    ],
  },
})

And my package.json:

{
  "name": "myproj",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "description": "",
  "author": "",
  "license": "MIT",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "mywasm": "file:../mywasm/mywasm-pkg",
    "vite-plugin-top-level-await": "^1.5.0",
    "vite-plugin-wasm": "^3.4.1",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.0",
    "vite": "^5.4.11",
    "vite-plugin-vue-devtools": "^7.7.2"
  }
}

How to prevent an embedded video from exiting fullscreen-mode due to a reloading component?

I’m working on an Angular application where I display embedded videos using . The videos are part of a list of topics, and each video is shown when a topic is expanded (using *ngIf). Here’s the issue:

Problem: When I click the fullscreen button on the video player (YouTube, for example), the video exits fullscreen immediately or never enters it. It seems like the Angular component reloads or destroys and re-renders the DOM element, causing the fullscreen to close.


<div class="course-container mt-3" *ngIf="topics.length > 0">   <div
*ngFor="let topic of topics; let i = index" class="topic-section">
    <div id="accordion-{{topic.tid}}" class="row">
      <div class="card my-4">
        <div class="topic-card d-flex justify-content-between align-items-center">
          <div class="topic-header row"
            [ngClass]="{ 'disabled-topic': topic.isPaid === 2 && topic.paymentStatus === 0 }">
            <div class="col-md-10">
              <span class="topic-title">Topic {{ i + 1 }} : {{ topic.tname }}</span>
            </div>
            <div class="col-md-1 d-flex">
              <span class="toggle-icon-container col-md-6 d-flex"
                (click)="toggleVideo(topic.tid, topic.isPaid, topic.paymentStatus)"
                [ngClass]="{ rotate: expandedTopicId === topic.tid }">
                <svg width="28" height="28" viewBox="0 0 24 24" fill="none">
                  <circle cx="12" cy="12" r="10" fill="#dd9fff" />
                  <path d="M8 10l4 4 4-4" stroke="black" stroke-width="2" stroke-linecap="round"
                    stroke-linejoin="round" />
                </svg>
              </span>
            </div>

          </div>
          <div class="col-md-2 d-flex justify-content-center align-items-center">
            <span class="paidStatusLabel" [ngClass]="{
            freePlan: topic.isPaid === 1,
            paidPlanAvailable: topic.isPaid === 2 && topic.paymentStatus === 1,
            paidPlanEnquire: topic.isPaid === 2 && topic.paymentStatus === 0
          }">
              <ng-container *ngIf="topic.isPaid === 2 && topic.paymentStatus === 0; else normalBadge">
                <!-- If Paid but needs Enquiry, show button -->
                <button class="badge badge-warning border-0" (click)="enquire(topic)">
                  Paid – Click Here to Enquire
                </button>
              </ng-container>
              <ng-template #normalBadge>
                <!-- Otherwise, show simple badge -->
                <span class="badge" [ngClass]="{
              'badge-light': topic.isPaid === 1,
              'badge-success': topic.isPaid === 2 && topic.paymentStatus === 1,
            }">
                  {{
                  topic.isPaid === 1
                  ? 'Free'
                  : 'Paid and Available'
                  }}

                </span>
              </ng-template>
            </span>
          </div>
        </div>

        <!-- Videos Section -->
        <div class="videos-container" *ngIf="expandedTopicId === topic.tid">
          <ul class="list-group" style="padding-top: 2rem;padding-bottom: 2rem;">
            <li class="list-group-item" *ngFor="let video of topicVideos[topic.tid]">
              <div *ngIf="video.englishUrl" class="text-center">
                <iframe [src]="sanitizeUrl(video.englishUrl)" 
                  width="40%" height="300" frameborder="0" 
                  allow="fullscreen; accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                   allowfullscreen>
                </iframe>
                
                  <a [href]="video.englishUrl" target="_blank" rel="noopener">
                    <i class="bi bi-arrows-fullscreen ml-2" title="Open Fullscreen in New Tab"></i>
                  </a>
              
              </div>
            </li>
          </ul>

          <!--  Topic Rating Section  -->
          <div class="rating-container my-3 text-center">
            <p>Rate this Topic</p>
            <div>
              <div class="my-2" style="padding: 1.25rem;">
                <div *ngIf="selectedRatings[topic.topicId] !== undefined">
                  <div *ngIf="emojiKey" [@slideUp]>
                  <span style="font-size: 4rem;">    
                    {{ getEmoji(selectedRatings[topic.topicId]) }}
                  </span>
                  </div>
                </div>
              </div>
              <span *ngFor="let star of [].constructor(starCount); let starIndex = index"
                (click)="rateVideo(topic.topicId, starIndex + 1)"
                style="cursor: pointer; font-size: 3rem; color: gold;">
                {{ showIcon(starIndex, topic.topicId) === 'star' ? '★' : '☆' }}
              </span>
            </div>
          </div>

        </div>

        <div *ngIf="topics.length === 0 && selectedCourseId">
          <p>No topics available for this course.</p>
        </div>
      </div>
    </div>   </div> </div>

>
  constructor(
    private axiosService: AxiosService,
    private toastr: ToastrService,
    private sanitizer: DomSanitizer
  ) { }

  ngOnInit(): void {
    this.axiosService.userId$.subscribe((id) => {
      this.userId = id ? Number(id) : null;
      // this.fetchLastChoice();
    });

    this.getLastViewChoice();


    // for (let index = 0; index < this.starCount; index++) {
    //   this.ratingArr.push(index);
    // }
  }

  sanitizeUrl(url: string): SafeResourceUrl {
    const trustedUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
    console.log('Sanitized URL:', trustedUrl);
    return trustedUrl;
  }

Angular iframe embedded video exits fullscreen immediately due to component reload

I’m working on an Angular application where I display embedded videos using . The videos are part of a list of topics, and each video is shown when a topic is expanded (using *ngIf). Here’s the issue:

Problem: When I click the fullscreen button on the video player (YouTube, for example), the video exits fullscreen immediately or never enters it. It seems like the Angular component reloads or destroys and re-renders the DOM element, causing the fullscreen to close.


<div class="course-container mt-3" *ngIf="topics.length > 0">   <div
*ngFor="let topic of topics; let i = index" class="topic-section">
    <div id="accordion-{{topic.tid}}" class="row">
      <div class="card my-4">
        <div class="topic-card d-flex justify-content-between align-items-center">
          <div class="topic-header row"
            [ngClass]="{ 'disabled-topic': topic.isPaid === 2 && topic.paymentStatus === 0 }">
            <div class="col-md-10">
              <span class="topic-title">Topic {{ i + 1 }} : {{ topic.tname }}</span>
            </div>
            <div class="col-md-1 d-flex">
              <span class="toggle-icon-container col-md-6 d-flex"
                (click)="toggleVideo(topic.tid, topic.isPaid, topic.paymentStatus)"
                [ngClass]="{ rotate: expandedTopicId === topic.tid }">
                <svg width="28" height="28" viewBox="0 0 24 24" fill="none">
                  <circle cx="12" cy="12" r="10" fill="#dd9fff" />
                  <path d="M8 10l4 4 4-4" stroke="black" stroke-width="2" stroke-linecap="round"
                    stroke-linejoin="round" />
                </svg>
              </span>
            </div>

          </div>
          <div class="col-md-2 d-flex justify-content-center align-items-center">
            <span class="paidStatusLabel" [ngClass]="{
            freePlan: topic.isPaid === 1,
            paidPlanAvailable: topic.isPaid === 2 && topic.paymentStatus === 1,
            paidPlanEnquire: topic.isPaid === 2 && topic.paymentStatus === 0
          }">
              <ng-container *ngIf="topic.isPaid === 2 && topic.paymentStatus === 0; else normalBadge">
                <!-- If Paid but needs Enquiry, show button -->
                <button class="badge badge-warning border-0" (click)="enquire(topic)">
                  Paid – Click Here to Enquire
                </button>
              </ng-container>
              <ng-template #normalBadge>
                <!-- Otherwise, show simple badge -->
                <span class="badge" [ngClass]="{
              'badge-light': topic.isPaid === 1,
              'badge-success': topic.isPaid === 2 && topic.paymentStatus === 1,
            }">
                  {{
                  topic.isPaid === 1
                  ? 'Free'
                  : 'Paid and Available'
                  }}

                </span>
              </ng-template>
            </span>
          </div>
        </div>

        <!-- Videos Section -->
        <div class="videos-container" *ngIf="expandedTopicId === topic.tid">
          <ul class="list-group" style="padding-top: 2rem;padding-bottom: 2rem;">
            <li class="list-group-item" *ngFor="let video of topicVideos[topic.tid]">
              <div *ngIf="video.englishUrl" class="text-center">
                <iframe [src]="sanitizeUrl(video.englishUrl)" 
                  width="40%" height="300" frameborder="0" 
                  allow="fullscreen; accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                   allowfullscreen>
                </iframe>
                
                  <a [href]="video.englishUrl" target="_blank" rel="noopener">
                    <i class="bi bi-arrows-fullscreen ml-2" title="Open Fullscreen in New Tab"></i>
                  </a>
              
              </div>
            </li>
          </ul>

          <!--  Topic Rating Section  -->
          <div class="rating-container my-3 text-center">
            <p>Rate this Topic</p>
            <div>
              <div class="my-2" style="padding: 1.25rem;">
                <div *ngIf="selectedRatings[topic.topicId] !== undefined">
                  <div *ngIf="emojiKey" [@slideUp]>
                  <span style="font-size: 4rem;">    
                    {{ getEmoji(selectedRatings[topic.topicId]) }}
                  </span>
                  </div>
                </div>
              </div>
              <span *ngFor="let star of [].constructor(starCount); let starIndex = index"
                (click)="rateVideo(topic.topicId, starIndex + 1)"
                style="cursor: pointer; font-size: 3rem; color: gold;">
                {{ showIcon(starIndex, topic.topicId) === 'star' ? '★' : '☆' }}
              </span>
            </div>
          </div>

        </div>

        <div *ngIf="topics.length === 0 && selectedCourseId">
          <p>No topics available for this course.</p>
        </div>
      </div>
    </div>   </div> </div>

> ```




  constructor(
    private axiosService: AxiosService,
    private toastr: ToastrService,
    private sanitizer: DomSanitizer
  ) { }

  ngOnInit(): void {
    this.axiosService.userId$.subscribe((id) => {
      this.userId = id ? Number(id) : null;
      // this.fetchLastChoice();
    });

    this.getLastViewChoice();


    // for (let index = 0; index < this.starCount; index++) {
    //   this.ratingArr.push(index);
    // }
  }

  sanitizeUrl(url: string): SafeResourceUrl {
    const trustedUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
    console.log('Sanitized URL:', trustedUrl);
    return trustedUrl;
  }

How to implement support for multiple dynamic model responses in deep-chat Angular?

enter image description hereI am working with the deep-chat library and am trying to support multiple models in my setup. Specifically, I want to handle multiple models inside the onMessage event by making manual fetch requests to dynamically selected models.

I’m using the handler function and getting the response from multiple models properly. However, the sender button of the message is still showing as “loading” even after the response is received. You can check the image as well.

I’ve attached a screenshot and included the relevant code I’m using to handle the multiple models:

this.chatRequest = {
"url": this.appService.basePath + "api/providers/cloudlyte/v1/chat/completions",
"method": "POST",
"stream": { simulation: 20 }
};

    setTimeout(() => {
        if (this.chatElementRef) {
            const chatEl = this.chatElementRef.nativeElement;
            chatEl.connect = {
                handler: async (body, signals) => {
                    const modelsToCall = [...this.selectedModelList];
                    await Promise.all(modelsToCall.map(async (model) => {
                        const requestBody = {
                                model,
                                messages: [
                                    {
                                        role: 'user',
                                        content: body.messages[0].text
                                    }
                                ]
                            };
                        const response = await fetch(this.appService.basePath + "api/providers/cloudlyte/v1/chat/completions", {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'Authorization': `Bearer ${this.keyId}`
                            },
                        
                            body: JSON.stringify(requestBody)
                        },)
                        const result = await response.json();
                        if (response.status === 401) {
                                chatEl.addMessage({
                                    text: `${model}: ❌ Unauthorized – check API key.`,
                                    role: 'ai'
                                });
                            } else {
                                const reply = result?.choices?.[0]?.message?.content || 'No reply received';
                                chatEl.addMessage({
                                    text: `${model}: ${reply}`,
                                    role: 'ai'
                                });
                            }
                    }))
                }
            }
       }
    });

<deep-chat
#elementRef
[introMessage]="introMessage"
[textInput]="textInput"
[responseInterceptor]="responseInterceptor"
[demo]="false"
[demoMessage]="null"
[submitButtonStyles]="submitButtonStyles"
[messageStyles]="messageStyles"
[avatars]="avatars"
[connect]="chatRequest"
stream='{"simulation": 6}'
style="
border-radius: 10px;
width: 100%;
height: calc(100vh - 100px);
max-height: 55%;
padding-top: 10px;
font-family: 'Plus Jakarta Sans';
font-size: 0.9rem;
background: #faf8f8;

                  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
                ">
              </deep-chat>

Hide “build” in the URL

After building my Laravel/Vue app in local, the URL looks like http://crm.local/**build**/login.

I’m using Vite as a frontend server and there is the content of vite.config.js:

import { fileURLToPath } from 'node:url'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import laravel from 'laravel-vite-plugin'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import vuetify from 'vite-plugin-vuetify'
import svgLoader from 'vite-svg-loader'

// https://vitejs.dev/config/
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        api: 'modern',
        quietDeps: true,
        quiet: true,
      },
    },
  },
  plugins: [vue({
    template: {
      transformAssetUrls: {
        base: null,
        includeAbsolute: false,
      },
    },
  }), vueJsx(), laravel({
    input: ['resources/js/main.js'],
    refresh: true,
  }), // Docs: https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin
  vuetify({
    styles: {
      configFile: 'resources/styles/variables/_vuetify.scss',
    },
  }), Components({
    dirs: ['resources/js/@core/components', 'resources/js/components'],
    dts: true,
  }), // Docs: https://github.com/antfu/unplugin-auto-import#unplugin-auto-import
  AutoImport({
    imports: ['vue', 'vue-router', '@vueuse/core', '@vueuse/math', 'pinia'],
    vueTemplate: true,

    // ℹ️ Disabled to avoid confusion & accidental usage
    ignore: ['useCookies', 'useStorage'],
    eslintrc: {
      enabled: true,
      filepath: './.eslintrc-auto-import.json',
    },
  }), svgLoader()],
  define: { 'process.env': {} },
  resolve: {
    alias: {
      '@core-scss': fileURLToPath(new URL('./resources/styles/@core', import.meta.url)),
      '@': fileURLToPath(new URL('./resources/js', import.meta.url)),
      '@core': fileURLToPath(new URL('./resources/js/@core', import.meta.url)),
      '@layouts': fileURLToPath(new URL('./resources/js/@layouts', import.meta.url)),
      '@images': fileURLToPath(new URL('./resources/images/', import.meta.url)),
      '@styles': fileURLToPath(new URL('./resources/styles/', import.meta.url)),
      '@configured-variables': fileURLToPath(new URL('./resources/styles/variables/_template.scss', import.meta.url)),
    },
  },
  build: {
    chunkSizeWarningLimit: 5000,
  },
  optimizeDeps: {
    exclude: ['vuetify'],
    entries: [
      './resources/js/**/*.vue',
    ],
    include: ['fast-deep-equal'],
  },
})

And there is my vhost:

<VirtualHost *:80>
    ServerName crm.local
    ServerAlias www.crm.local
    DocumentRoot "D:/crm/public"
    ErrorLog "logs/crm.local-error.log"
    <Directory "D:/crm/public">
    AllowOverride All
        Require all granted    
    </Directory>
</VirtualHost>

When I open the app on the browser, after running vite build, I can see the webpage, but I want to remove the build term in the URL.

I tried to add base: '/' in the vite.config.js file to remove it, but it damages all styles.

The build folder is located at D:/crm/public/.

Any help?

How can I use Playwright to interact with Chrome’s Context Menu in E2E tests?

I am working on a Chrome Extension that creates a custom Context Menu option that is available when the user highlights text and right clicks it on any background page.

Chrome’s default context menu options + my custom ‘Add Word’ option

All of this functionality works perfectly when I am manually testing the application but I would like to add E2E tests that test clicking this option. I am using Playwright to write my E2E tests and my custom ‘Add Word’ option does show up while in debug mode.

Playwright Debug Screenshot of my custom ‘Add Word’ option

My only question is how can I interact with Chrome’s Context Menu options through Playwright?

I have tried using Keyboard Shortcuts like ‘Shift+F10’ to open the context menu and using ‘ArrowUp’ and ‘ArrowDown’ commands to traverse through the menu. I have tried to find the ‘Add Word’ menu by using the ‘page.getByText(‘Add Word’)’ and ‘getByRole(‘button’, { name: ‘Add Word’ })’ selectors to no avail. I have tried using right click options. I also tried clicking at specific coordinates using this ‘page.mouse.click’ API at the coordinates of the context menu to no avail: https://playwright.dev/docs/api/class-mouse#mouse-move. Playwright can’t find this menu option. This is because this context menu is not apart of the page, understandably. I need some way to query that option.

Here is my Playwright test code. I am going to an arbitrary background page “https://www.google.com”. The implementation details of ‘loginWith’ are irrelevant. This custom menu option shows up in the test browser as I showed in the screenshots previously:

import { test, expect } from "../fixtures";
import { goto, loginWith, testUser, VITE_API_DOMAIN } from "../helpers";

test.describe("Context menus", () => {
  test.describe("when the user is logged in", () => {
    test.beforeEach(async ({ page, context, request, extensionId }) => {
      await context.clearCookies();
      await request.delete(`${VITE_API_DOMAIN}/testing/reset`);
      await request.post(`${VITE_API_DOMAIN}/users`, {
        data: testUser,
      });
      await goto(page, extensionId);
    });

    test("it shows the context menu", async ({
      page,
    }) => {
      await loginWith(page, testUser.email, testUser.password);

      await page.goto("https://www.google.com");

      const privacyButton = page.getByText("Privacy");
      await privacyButton.selectText();
      await privacyButton.click({ button: "right" });

      const addWordButton = page.getByRole("button", { name: "Add Word" });
      await expect(addWordButton).toBeVisible();
    });

  });
});

Test output:

Running 1 test using 1 worker
  1) [chromium] › tests/e2e/ContextMenu/context_menu.spec.ts:15:5 › Context menus › when the user is logged in › it shows the context menu when the user is logged in 

    Error: Timed out 5000ms waiting for expect(locator).toBeVisible()

    Locator: getByRole('button', { name: 'Add Word' })
    Expected: visible
    Received: <element(s) not found>
    Call log:
      - expect.toBeVisible with timeout 5000ms
      - waiting for getByRole('button', { name: 'Add Word' })


      25 |
      26 |       const addWordButton = page.getByRole("button", { name: "Add Word" });
    > 27 |       await expect(addWordButton).toBeVisible();
         |                                   ^
      28 |     });

This other post mentions wanting to interact with the browser’s default context menu but this user did not add a custom menu option so it is not applicable to my situation: Right mouse click and selecting the appropriate item using Playwright.

This particular post mentions interacting with in-application custom context menus which I do not have. My context menu is apart of the browser’s context menu: How to Identify that the Right Click Menu Is Available and You Can Open in New Tab Without Screenshots.

If you want something close to a very simple similar repro, setup a basic playwright project as shown here to try and click any of the default Chrome browser context menu options like “Reload”, “Inspect”, etc: https://playwright.dev/docs/intro. If one is able to select these default Chrome context menu options, the same should be applicable for my custom ‘Add Word’ context menu option.

Intl.DateTimeFormat formatted value difference between client and server

I’m currently trying to format a hour value to 12 hour format using Intl.DateTimeFormat and I noticed that if I format it on the client or on the server I get different values. If I format 12:30 (noon), on the client I get the correct value 12:30PM, but on the server I get a wrong value 0:30PM. Both formatting are done with the same function and I’m passing the same values to both.

const a = new Intl.DateTimeFormat('en-GB', {
            hour: 'numeric',
            minute: 'numeric',
            hour12: true
        })

a.format(new Date('01 Jan 1970 12:30:00'))

//on server I get 0:30PM
//on client I get 12:30PM

How do I use Local Storage to save the state of a toggled class?

I’m trying use to use Local storage to save whether or not my class="crt" has been set or not for my comic’s website resulting it in it either showing or not showing.

I only have a basic W3Schools JavaScript code thus far for the actual toggling of the class and it works, but I’d like to have its value saved and extended across the whole website.

function myFunction() {
   var element = document.getElementById("BodyId");
   element.classList.toggle("crt");

This is the HTML that will be affected

<body class="crt" id="BodyId">

I have looked briefly into how the functionality works for using Local Storage for dark/light mode toggling however because I’m not switching between two classes and being new to JavaScript I’ve found myself a little lost with where to go.

In my mind I believe it’d be something to do with making sure to declare the class’s status as either null or positive so when the local storage checks the status it’ll have two categories to choose from and save for the rest of the site.

Increase or decrease notes according to music scale

When I type the music note do in the textarea and click the up button, it goes to do# and the “notes_Sharps” scale works perfectly, whether it is in uppercase or lowercase (Do or do).

What I want is:

  1. If I write reb and click the up button, to follow the “notes_Flats” scale. ['do','reb','re','mib','mi','fa','solb','sol','lab','la','sib','si']
    Now, it reads the “notes_Sharps” scale.

  2. In the same way, I want exactly the same thing if I write ρε# or ρεb, to follow the corresponding scales,
    “notes_Sharps_Gr” and “notes_Flats_Gr”

How do I program it? It’s been a week and I’m still at this result.. Thanks in advance

const
textareaId = document.querySelector('#textarea'), 
notes_Sharps = ['do','do#','re','re#','mi','fa','fa#','sol','sol#','la','la#','si'], 

notes_Flats =  ['do','reb','re','mib','mi','fa','solb','sol','lab','la','sib','si'], 
notes_Sharps_Gr =  ['ντο','ντο#','ρε','ρε#','μι','φα','φα#','σολ','σολ#','λα','λα#','σι'], 
notes_Flats_Gr =  ['ντο','ρεb','ρε','μιb','μι','φα','σολb','σολ','λαb','λα','σιb','σι'], 

notesExchg = {
    reb:'do#',
    mib:'re#',
    solb:'fa#',
    lab:'sol#',
    sib:'la#'
},

    
changeNotes = val => notesExchg[val.toLowerCase()] || val;

function stepOn(stepNotes = +1) {
let textarea_regex = textareaId.value.split(/([A-Za-zÉé]+#?)/);

textareaId.value = textarea_regex.reduce((acc, Nx) => {
if (!Boolean(Nx))                      
    return acc;

let flatToSharp = changeNotes(Nx), 
isUpC = Nx[0].toUpperCase() === Nx[0], 
noteSearch = notes_Sharps.findIndex(x=>flatToSharp.toLowerCase()===x);

if (noteSearch===-1) 
acc += Nx;

else {
let NotesStep = notes_Sharps[(noteSearch + stepNotes + notes_Sharps.length) % notes_Sharps.length];
acc += isUpC ? NotesStep.toUpperCase() : NotesStep;
}
return acc;  
}, '')
}
<button onclick="stepOn(+1 )" class="stepUp_Button">Up</button>
<button onclick="stepOn(-1 )" class="stepDown_Button">Down</button>


<textarea id="textarea" rows="5" cols="30">
</textarea>
</div>