How to iterate over an object reactively?

I need to iterate over an object. The object is declared with:

const obj = $state({ 
    one: { checked: true }, 
    two: { checked: false }, 
    three: { checked: true } 
});

In my Svelte templating code, I do this:

{#each Object.entries(obj) as [key, value]}
        <div class="mb-8">
            {key}: {value.checked}
            <input type="checkbox" {value} onchange={e => obj[key].checked = e.target.checked}>
        </ion-toggle>
    </div>
{/each}

Checking the checkbox does update the value of one of the items of my object, but does not change the value of {value.checked} in the DOM. My suspicion is that obj itself is reactive, but value within obj is not. How do I fix this?

(Note: I’m actually using Ionic and <ion-toggle> instead of <input type="checkbox"> – I know there is a better way to bind the value (bind:value), but this method works throughout the rest of my app for <ion-toggle>.)

I have also attempted making the obj a SvelteMap<string, ClassVersion>, where ClassVersion is:

class MyObj {
   checked = $state(false);
}

Shopify Update Variants Options

Recently I need to update the graphql api version of my application,I am currently using ApiVersion.April24, and updating the options to variants is done using the respective graphql structure:

 static update_variant_data(variantId,data){
    const variantMutation = `
        mutation updateProductVariantMetafields($input: ProductVariantInput!) {
            productVariantUpdate(input: $input) {
                productVariant {
                    title
                    price
                    compareAtPrice
                    price
                    position
                    weight
                    weightUnit
                    sku
                    barcode
                    id
                    selectedOptions {
                        name
                        optionValue {
                            id
                            name
                        }
                        value
                    }
                    metafields(first: 3) {
                        edges {
                            node {
                                id
                                namespace
                                key
                                value
                            }
                        }
                    }
                }
                userErrors {
                     message
                    field
                }
            }
        }
    `;

    const variantVariable = {
        input: {
            id:"gid://shopify/ProductVariant/"+variantId,
            ...data
        }
    }

    return {query:variantMutation,variables:variantVariable};
}

and the value I send is the following

{
     "variant_id":aaaaaa,
     "options":["Default Title"],
     "price":"122.00",
     "compare_at_price":"100.00",
     "sku":"TD5200BLXXZZ",
     "barcode":"",
     "grams":0,
     "weight":0,
     "weight_unit":""
  }

https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/productvariantupdate

The problem appeared in more recent versions of API where this method is deprecated.So I used the new productVariantBulkUpdate model, I manage to update everything without problems but I am unable to update the options for the variant. (https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/productVariantsBulkUpdate)

static update_variant_data(variantId,prodId,data){
    const variantMutation = `
       mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
            productVariantsBulkUpdate(productId: $productId, variants: $variants) {
              
                productVariants {
                    id
                    compareAtPrice
                    price
                    barcode
                    inventoryItem {
                        sku
                        measurement {
                            weight{
                                unit
                                value
                            }
                        }
                    }
                    optionValues {
                        id
                        name
                    }
                }
                userErrors {
                    field
                    message
                }
            }
        }
    `;

    const variantVariable = {
        productId:"gid://shopify/Product/"+prodId,
        variants:[
            { 
                id:"gid://shopify/ProductVariant/"+variantId,
                price:data.price,
                compareAtPrice:data.promo,
                inventoryItem:{
                    sku:data.sku,
                    measurement:{
                        weight:{
                            unit:data.weightUnit,
                            value:data.weight
                        }
                    }
                },
                optionValues:{
                    id:"gid://shopify/ProductOptionValue/zzzzzz",
                    name:"Default Title"
                }
            }
        ]
    }

    return {query:variantMutation,variables:variantVariable};
}

But it doesn’t work even though I followed what they give in the documentation:

"Error Update Variant DataError: Field 'optionValues' doesn't exist on type 'ProductVariant'"

I searched on the internet but generally the worst I found was the most confusing. If you have any ideas or a different approach, please help me.

I am an artist and I want my artwork tagged but am not able to get the JS together [closed]

Searching for tag mostly leads to html-tag. I have a pen of this on codepen: https://codepen.io/michielschukking/pen/ZEgwWvp

Turning tags on and off works because of this function:

tags = []
currentImages = []

function tagOnOff(obj) {
  obj.classList.toggle("carribean");
  tag = obj.innerHTML;
  console.log("Klik!"); 
  console.log("Aantal tags:"); 
  console.log(tags.length);
  displayedImagesNew = []

  // Als tag er nog niet is 'push' erbij, anders verwijder selectie uit array.
  if(!tags.includes(tag)){          
    tags.push(tag);              
  } else{
    tags.splice(tags.indexOf(tag), 1); 
  }
  // Met de juiste geselecteerde tags.
  displayTagsImages(tags, images);
}

On this html:

  <span onclick="tagOnOff(this)" class="tag random">karakter</span>
  <span onclick="tagOnOff(this)" class="tag random">kind</span>
  <span onclick="tagOnOff(this)" class="tag random">dier</span>
  <span onclick="tagOnOff(this)" class="tag random">plant</span>
  <span onclick="tagOnOff(this)" class="tag random">auto</span>
  <span onclick="tagOnOff(this)" class="tag random">vaartuig</span>
  <span onclick="tagOnOff(this)" class="tag random">vliegtuig</span>
  <span onclick="tagOnOff(this)" class="tag random">landschap</span>
  <span onclick="tagOnOff(this)" class="tag random">gebouw</span>
  <span onclick="tagOnOff(this)" class="tag random">muziekinstument</span>
  <span onclick="tagOnOff(this)" class="tag random">wapen</span>
  <span onclick="tagOnOff(this)" class="tag abstract">licht & donker (stijl)</span>
  <span onclick="tagOnOff(this)" class="tag abstract">klare lijn (stijl)</span>
  <span onclick="tagOnOff(this)" class="tag abstract">spontaan (stijl)</span>
</div>

From these test images

let images = [{ title: "Orchidee",
url: "./img/IMG_0001.JPG", 
tags: ["plant", "spontaan (stijl"]}, { title: "Bergwandelaar",
url: "./img/IMG_0025.JPG", 
tags: ["plant", "karakter"]}, { title: "Kantoor uit/Vrouw aan",
url: "./img/IMG_0007.JPG", 
tags: ["gebouw", "karakter"]}, { title: "Zeilboot in worsteling",
url: "./img/IMG_0007 (1).JPG", 
tags: ["spontaan", "vaartuig"]}, { title: "Een 'haven' in Afrika",
url: "./img/IMG_0342.jpeg", 
tags: ["gebouw", "vaartuig", "potlood"]}, { title: "Een 'haven' in Veere",
url: "./img/IMG_0343.jpeg", 
tags: ["gebouw", "vaartuig", "auto", "potlood"]}, { title: "Grot restaurant",
url: "./img/IMG_1621.jpeg", 
tags: ["gebouw", "karakter"]}, { title: "Omgegooid rood sap",
url: "./img/IMG_1624.jpeg", 
tags: ["karakter", "kind"]}, { title: "Liggend naakt",
url: "./img/IMG_2328.JPG", 
tags: ["karakter", "spontaan"]}, { title: "Mei",
url: "./img/IMG_4222.jpeg", 
tags: ["landschap", "gebouw"]}

]

I got this far.

function displayTagsImages(tags, allImages) {
  console.log("tags:")
  console.log(tags)

  console.log("allImages:")
  console.log(allImages)

  console.log("currentImages:")
  console.log(currentImages)
  console.log(tags.length)

  // current images is bij o of 1e tag all images

  // Eerste tag selectie
  if (tags.length === 0 || tags.length === 1) { 
    currentImages = allImages
  }

  console.log("currentImages!!!:")
  console.log(currentImages)


  // Voor elke tag uit vorige currentImages.
  currentImagesNew = []
  tags.forEach(t => {
    currentImages.forEach(i => {
      if (i.tags.includes(t)) {
        currentImagesNew.push(i);
      } else{
        currentImagesNew.splice(currentImagesNew.indexOf(i), 1);
      } 
    });
  })
  currentImages = currentImagesNew
  // Werkt. Maar nog 1 tag erbij niet. "karakter", maar "gebouw erbij", en 
  // "karakter" nog steeds volledig.

  console.log("currentImages after loop:")
  console.log(currentImages)

  console.log("EINDE")
  console.log("EINDE")
  console.log("EINDE")
  // let text = "";
  // document.getElementById("filtered-sketches").innerHTML = text;
}

It sometimes works but not neatly. It leaves gaps, saves doubles. This is way too hard for me, does someone know any code that does work? Or sees the mistakes I made immediately?

In javascript, whats return keyword of functions? [closed]

I started javascript, and understood the main key to learn coding is not to actually memorise it but to understand it. I learnt functions almost 1 week ago but I can’t understand what return; does? I mean many said it returns the value but I still didn’t understood it.

I saw many youtube courses..and even searched google.

Outlook WebAddin doesnt show up on Outlook Client

I have created an outlook web addin with visualstudio 2022 (I used the default template)
It works in the Outlook Web Client (OWA).
On an Outlook client with Office365 the addin is not displayed.
If I go to the addins under Installed web addins, I can see that the addin is active.

Could it be that there is an error in my manifest?

I would be grateful for any tips

<?xml version="1.0" encoding="UTF-8"?>
<!--Created:ce44715c-8c4e-446b-879c-ea9ebe0f09c8-->
<!-- Weitere Informationen zum XML-Manifest für Office-Add-Ins finden Sie unter https://go.microsoft.com/fwlink/?linkid=2252563. -->
<OfficeApp 
          xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 
          xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" 
          xsi:type="MailApp">


  <!-- WICHTIG: Die ID muss eindeutig für Ihr Add-In sein. Wenn Sie dieses Manifest erneut verwenden, stellen Sie sicher, dass Sie diese ID in eine neue GUID ändern. -->
  <Id>cf933e3b-9987-4ef0-a079-a4b3defd66d0</Id>

  <Version>1.0.0.0</Version>
  <ProviderName>Spam Reporting</ProviderName>
  <DefaultLocale>de-CH</DefaultLocale>
  <!-- Der Anzeigename Ihres Add-Ins. Er wird im Store und an verschiedenen Stellen in der Office-Benutzeroberfläche verwendet, z. B. im Dialogfeld "Add-Ins". -->
  <DisplayName DefaultValue="Email Melden" />
  <Description DefaultValue="Email Melden"/>

  <IconUrl DefaultValue="https://reporting.test.ch/Images/icon32.png"/>
  <HighResolutionIconUrl DefaultValue="https://reporting.test.ch/Images/icon80.png"/>

  <SupportUrl DefaultValue="http://www.exchange.test.ch" />
  <!-- Domänen, die beim Navigieren zulässig sind. Wenn Sie z. B. "ShowTaskpane" verwenden und dann ein href-Link auftritt, ist die Navigation nur zulässig, wenn sich die Domäne in dieser Liste befindet. -->
  <AppDomains>
    <AppDomain>exchange.test.ch</AppDomain>
    <AppDomain>owa.test.ch</AppDomain>
  </AppDomains>
  
  <Hosts>
    <Host Name="Mailbox" />
  </Hosts>
  <Requirements>
    <Sets>
      <Set Name="Mailbox" MinVersion="1.1" />
    </Sets>
  </Requirements>
    
  <FormSettings>
    <Form xsi:type="ItemRead">
      <DesktopSettings>
        <SourceLocation DefaultValue="https://reporting.test.ch/MessageRead.html"/>
        <RequestedHeight>250</RequestedHeight>
      </DesktopSettings>
    </Form>
  </FormSettings>

  <Permissions>ReadWriteMailbox</Permissions>
  
  <Rule xsi:type="RuleCollection" Mode="Or">
    <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
  </Rule>

  <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
    <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
      <Requirements>
        <bt:Sets DefaultMinVersion="1.1">
          <bt:Set Name="Mailbox" />
        </bt:Sets>
      </Requirements>
      <Hosts>
        <Host xsi:type="MailHost">

          <DesktopFormFactor>
            <FunctionFile resid="functionFile" />

            <ExtensionPoint xsi:type="MessageReadCommandSurface">
              <OfficeTab id="TabDefault">
                <Group id="msgReadGroup">
                  <Label resid="groupLabel" />
                  <Control xsi:type="Button" id="msgReadOpenPaneButton">
                    <Label resid="taskPaneButtonLabel" />
                    <Supertip>
                      <Title resid="taskPaneButtonLabel" />
                      <Description resid="taskPaneButtonDescription" />
                    </Supertip>
                    <Icon>
                      <bt:Image size="16" resid="icon16" />
                      <bt:Image size="32" resid="icon32" />
                      <bt:Image size="80" resid="icon80" />
                    </Icon>
                    <Action xsi:type="ShowTaskpane">
                      <SourceLocation resid="messageReadTaskPaneUrl" />
                      
                    </Action>
                  </Control>
                  
                </Group>
              </OfficeTab>
            </ExtensionPoint>
          </DesktopFormFactor>
        </Host>
      </Hosts>

      <Resources>
        <bt:Images>
          <bt:Image id="icon16" DefaultValue="https://reporting.test.ch/Images/icon16.png"/>
          <bt:Image id="icon32" DefaultValue="https://reporting.talus.ch/Images/icon32.png"/>
          <bt:Image id="icon80" DefaultValue="https://reporting.test.ch/Images/icon80.png"/>
        </bt:Images>
        <bt:Urls>
          <bt:Url id="messageReadTaskPaneUrl" DefaultValue="https://reporting.test.ch/MessageRead.html"/>
        </bt:Urls>
        <bt:ShortStrings>
          <bt:String id="groupLabel" DefaultValue="My Add-in Group"/>        
          <bt:String id="taskPaneButtonLabel" DefaultValue="E-Mail melden"/>
        </bt:ShortStrings>
        <bt:LongStrings>
          <bt:String id="taskPaneButtonDescription" DefaultValue="Melden"/>
         
        </bt:LongStrings>
      </Resources>
    </VersionOverrides>
  </VersionOverrides>
</OfficeApp>

How to remove the display of the upper border of the chart grid in ngx-echarts?

I am trying to create a beautiful chart to visualize the dynamics of school students’ performance.

I am using Angular version 17.3.0, ECharts version 5.5.1, and ngx-echarts version 17.2.0.

I have this config:

{
    grid: {
      left: 20,
      right: 25,
      top: 0,
      bottom: 40,
    },
    xAxis: {
      type: 'category',
      data: [
        '9 фев',
        '16 фев',
        '23 фев',
        '1 мар',
        '8 мар',
        '15 мар',
        '22 мар',
        '29 мар',
        '5 апр',
        '12 апр',
      ],
      boundaryGap: false,
      axisLabel: {
        interval: 0,
        overflow: 'truncate',
        color: '#86909C',
        fontSize: 14,
        fontWeight: 'normal',
        margin: 10,
        formatter: (value: string) => {
          const [day, month] = value.split(' ');
          return `{a|${day}}n{b|${month}}`;
        },
        rich: {
          a: {
            fontSize: 14,
            align: 'center',
          },
          b: {
            fontSize: 14,
            align: 'center',
          },
        },
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          type: 'dashed',
        },
      },
    },
    yAxis: {
      type: 'value',
      min: 1,
      max: 6,
      interval: 1,
      position: 'right',
      splitLine: {
        show: true,
        lineStyle: {
          type: 'solid',
        },
      },
      axisLabel: {
        color: '#0B1F33',
        fontSize: 16,
        fontWeight: 'bold',
        margin: 12,
        formatter: (value: number) => {
          if (value < 2 || value > 5) {
            return '';
          } else {
            return value.toString();
          }
        },
      },
    },
    series: [
      {
        name: 'Test',
        type: 'line',
        smooth: true,
        data: [null, 4, null, null, 4.57, 5, 4],
        connectNulls: true,
        emphasis: {
          focus: 'series',
        },
        itemStyle: {
          color: '#0E69D5',
          borderColor: '#ffffff',
          borderWidth: 1,
        },
        lineStyle: {
          color: '#185AC5',
        },
        symbol: 'circle',
        symbolSize: 7,
        markLine: {
          symbol: 'none',
          tooltip: { show: false },
          label: { show: false },
          data: [
            {
              xAxis: 6,
              lineStyle: {
                color: '#0E69D5',
                width: 1,
                type: 'solid',
              },
              emphasis: {
                disabled: true,
              },
            },
          ],
        },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              { offset: 0, color: 'rgba(192, 216, 253, 0.80)' },
              { offset: 1, color: 'rgba(237, 247, 255, 0.08)' },
            ],
          },
        },
      },
    ],
  }

This is how it looks:
my chart
How can I hide the line I marked in red? Or is this impossible to do with ECharts? If it’s impossible, could you please recommend other libraries?

I can’t load the jspdf script in my content.js script in Google Chrome extension

Context

Hello, I’ll start saying that I’m sorry if my code sucks, I am a backend dev, not a frontend, I know a little of JS.
Btw I am trying to do this Chrome extension with manifest V3 that basically gets all the modules, all the units in the modules, of a Microsoft Learning Path and it exports them as a PDF using the jspdf library.

Structure & Code

My extension has those files:

  • content.js
  • jspdf.umd.min.js -> This is the jspdf script
  • manifest.js
  • popup.html
  • popup.js
  • styles.css

manifest.json

    {
    "manifest_version": 3,
    "name": "MS Learn Exporter",
    "version": "1.0",
    "description": "Esports Microsoft Learning Path in.pdf format.",
    "permissions": ["activeTab", "downloads", "scripting"],
    "host_permissions": [
        "https://learn.microsoft.com/*"
    ],
    "action": {
        "default_popup": "popup.html"
    },
    "web_accessible_resources": [
        {
            "resources": ["jspdf.umd.min.js"],
            "matches": ["<all_urls>"]
        }
    ]
}

popup.js

document.getElementById('startExport').addEventListener('click', () => {
    console.log('Popup button clicked');

    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (!tabs || tabs.length === 0) {
            console.error('No active tabs found');
            return;
        }

        chrome.scripting.executeScript({
            target: { tabId: tabs[0].id },
            files: ['content.js']
        }, () => {
            if (chrome.runtime.lastError) {
                console.error('Error injecting script:', chrome.runtime.lastError);
            } else {
                console.log('Content script injected successfully');
                chrome.tabs.sendMessage(tabs[0].id, { action: 'startExport' });
            }
        });
    });
});

content.js

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "startExport") {
        console.log("Received message to start export");
        loadJsPDF().then(() => {
            startExport();
            sendResponse({ status: "Export started" });
        }).catch(error => {
            console.error('Failed to load jsPDF:', error);
            sendResponse({ status: "Failed to load jsPDF" });
        });
    }
});

function loadJsPDF() {
    return new Promise((resolve, reject) => {
      if (typeof jsPDF !== 'undefined') {
        // jsPDF is already loaded, resolve immediately
        resolve();
      } else {
        // jsPDF is not loaded, try to load it.
        const script = document.createElement('script');
        script.src = chrome.runtime.getURL('./jspdf.umd.min.js'); // Use the UMD version of jsPDF
        script.type = 'text/javascript';
        script.async = true; // Load asynchronously
        script.onload = () => {
          if (typeof jsPDF !== 'undefined') {
            // jsPDF loaded successfully
            resolve();
          } else {
            // jsPDF was not loaded
            reject(new Error('jsPDF is undefined after loading'));
          }
        };
        script.onerror = (error) => {
          reject(new Error(`Failed to load jsPDF: ${error}`));
        };
        document.head.appendChild(script);
      }
    });
  }

// Funzione principale per l'export
function startExport() {
    console.log('Export function started');

    const learningPathLinks = getLearningPathLinks();
    console.log('Found learning paths:', learningPathLinks);

    let completeContent = '';

    (async () => {
        for (const pathLink of learningPathLinks) {
            completeContent += await processLearningPath(pathLink);
        }

        generatePDF(completeContent);
        console.log('Export completed!');
    })();
}

// Ottiene i link dei learning paths
function getLearningPathLinks() {
    return [...document.querySelectorAll('#study-guide-list a')]
        .map(a => a.href)
        .filter((value, index, self) => self.indexOf(value) === index); // Rimuove duplicati
}

// Processa ogni learning path per estrarre le unità tramite API
async function processLearningPath(pathUrl) {
    console.log(`Processing learning path: ${pathUrl}`);

    const pathIdMatch = pathUrl.match(/paths/(.*?)//);
    if (!pathIdMatch) {
        console.warn(`No valid path ID found in URL: ${pathUrl}`);
        return '';
    }

    const pathId = pathIdMatch[1];
    const apiUrl = `https://learn.microsoft.com/api/hierarchy/paths/learn.wwl.${pathId}?locale=en-us`;

    try {
        const response = await fetch(apiUrl, {
            headers: {
                'Accept': 'application/json'
            }
        });

        if (!response.ok) {
            console.error(`API request failed for ${apiUrl}:`, response.status);
            return '';
        }

        const data = await response.json();
        const moduleLinks = data.modules?.[0]?.units?.map(unit => unit.url) || [];

        console.log("Module Links:", moduleLinks);

        let pathContent = '';

        for (const link of moduleLinks) {
            pathContent += await processUnit(link);
        }

        return pathContent;

    } catch (error) {
        console.error(`Error fetching API for module ${pathUrl}:`, error);
        return '';
    }
}

// Scarica il contenuto di ogni unità
async function processUnit(unitUrl) {
    const fullUrl = `https://learn.microsoft.com/en-us${unitUrl}`;
    console.log(`Processing unit: ${fullUrl}`);
    const unitDocument = await fetchPage(fullUrl);

    const content = unitDocument.querySelector('#unit-inner-section');
    if (content) {
        return content.innerText + 'nn';
    } else {
        console.warn(`No content found in unit: ${fullUrl}`);
        return '';
    }
}

// Funzione per ottenere e parse-are una pagina HTML
async function fetchPage(url) {
    const response = await fetch(url);
    const text = await response.text();
    const parser = new DOMParser();
    return parser.parseFromString(text, 'text/html');
}

// Funzione per generare un PDF
function generatePDF(content) {
    if (!window.jspdf || !window.jspdf.jsPDF) {
        console.error('jsPDF is not available yet.');
        return;
    }

    const { jsPDF } = window.jspdf;
    const doc = new jsPDF();
    const pageWidth = doc.internal.pageSize.getWidth() - 20;

    const splitContent = doc.splitTextToSize(content, pageWidth);
    doc.text(splitContent, 10, 10);

    doc.save('exported_content.pdf');
}

Issue

When I check my browser source the jspdf.umd.min.js is present, but I get those errors:

content.js:8 Failed to load jsPDF: Error: jsPDF is undefined after loading
    at script.onload (content.js:52:20)
(anonymous) @ content.js:8
Promise.catch
(anonymous) @ content.js:7Understand this errorAI
VM4133 content.js:8 Failed to load jsPDF: Error: jsPDF is undefined after loading
    at script.onload (content.js:52:20)

could you help me understand where/what I am doing wrong?

Thank you

Cannot use Static dictionaries as keys of a dictionary

I have a class Unit where I define static Actions for a game

class Unit {
  static Actions = { 
    Idle: 0, 
    Move: 1, 
    Attack: 2
  }
}

I can then refer to this Actions by its name, instead of the value. So in my code I can do something like

if (soldier.action == Unit.Actions.Move) {
  // Moving logic
}

However I noticed that when creating a dictionary, I cannot use this as keys, neither using the dot syntax or the [] syntax.

let myDict= {
  0: "This is Action Idle"                           // Correct
  Unit.Actions.Move:  "This is Action Move",          // Incorrect
  Unit["Actions"]["Attack"]:  "This is Action Attack" // Incorrect
}

Uncaught SyntaxError SyntaxError: Unexpected token '.'

Uncaught SyntaxError SyntaxError: Unexpected token '['

Of course I can use the values of each action (0, 1, 2) but this defeats the purpose of the static dictionary, which is to refer to the Actions by their names, making the code much more clear.

How can I use this static Actions as dictionary keys?

Not recieving messages on clients in Meteor

So I am a newbie at Meteor and in Bigbluebutton 2.4 I have to make a simple message that goes to all the users in a meeting. even without the meeting criteria I still cant get them on my frontend In the code I can subscribe to the ptzmessage and I see it in Meteor.connection._subscriptions list on the browser

componentDidMount() {
    const { onVideoItemMount, cameraId } = this.props;

    console.log("subscribing")
    Meteor.subscribe('ptzmessage').ready()

    this.subscription = Meteor.subscribe('ptzmessage');
    // Retry after 5 seconds if not ready
    this.retryTimeout = setTimeout(() => {
        if (!this.subscription.ready()) {
            console.log("Retrying subscription...");
            this.subscription = Meteor.subscribe('ptzmessage');
        }
        console.log(" subscription completed...");
    }, 5000);

    this.tracker = Tracker.autorun(() => {
      console.log("Fetching messages...");
      const messages = PtzMessages.find({}).fetch();
      this.setState({ messages });
      console.log('Received Messages:', messages);
    });

this is the Api file in import/api/ptzmessage and imported in the frontend and the backend

import { Mongo } from 'meteor/mongo';

// Define PtzMessages collection
export const PtzMessages = new Mongo.Collection('ptzmessages');
if (Meteor.isServer) {
  PtzMessages._ensureIndex({ meetingId: 1 });
}

this is the server code when I press button send message it adds it in the collection

import { Meteor } from 'meteor/meteor';
import { PtzMessages } from '../index.js';

Meteor.publish('ptzmessage', function () {
  console.log(`Client ${this.connection.id} subscribed to ptzmessage`);

  return PtzMessages.find();
});

Meteor.methods({
  sendPtzMessage(message) {
    console.log("#####################################################################################api hit")
    PtzMessages.insert({ text: message, createdAt: new Date() }, (err, res) => {
      if (err) {
        console.error("Insert Error:", err);
      } else {
        console.log("Message inserted successfully:", res);
      }
    });
  }
});

infact in the frontend Meteor.connection._mongo_livedata_collections.ptzmessages.find().fetch();
get me the latest messages but I dont get them real time on the Tracker.
Stuff like this is done in other code that works what am I doing wrong?

Clear Firefox overlays after SPA login

Firefox provides autocomplete and other warning overlays on my SPA login form. After a successful login the overlays remain hovering over the page content, requiring a user interaction to remove them. I can’t get rid of them programmatically except with an annoying full-page reload which should not be necessary.

According to my coworker Chrome has similar artifacts for them but it doesn’t happen to me. Regardless, an SPA should be able to login without page reloads and not have pesky browser overlays hanging around long after the relevant forms disappear and even the page route changes.

example of firefox overlay

How can I use a persistent context with extensions when connecting remotely via Aerokube Moon in Playwright?

I am working with Playwright and setting up tests using Aerokube Moon. In my configuration, I connect remotely to the browser using chromium.connect, which allows me to pass additional parameters through the URL. Here’s an example of my code:

`if (moonConfig.enableMoon) {
console.log(“Starting test via Aerokube – Moon”);

const wsUrl = new URL(moonConfig.remoteUrl);
// For each key in capabilities:
// If the value is an array, add each element as a separate parameter,
// otherwise set the parameter directly.
for (const [key, value] of Object.entries(moonConfig.capabilities)) {
    if (Array.isArray(value)) {
        for (const item of value) {
            wsUrl.searchParams.append(key, item);
        }
    } else {
        wsUrl.searchParams.set(key, value);
    }
}

this.browser = await chromium.connect({ wsEndpoint: wsUrl.toString() });
this.context = await this.browser.newContext({ viewport: { width: 1920, height: 1080 } });
this.page = await this.context.newPage();

}`

The issue is that using newContext() creates an incognito context where I cannot use extensions. I need a persistent context (to load extensions), which is typically created using the launchPersistentContext method.

However, if I try to use launchPersistentContext, I get type errors because this method is not supported when using remote connections via chromium.connect.

My question:
Is there any workaround to use a persistent context (with extension support) when connecting remotely via Aerokube Moon? Or do I need to separate the launch logic based on the environment (i.e., local for persistent context and remote for incognito context)?

Any advice or alternative approaches would be greatly appreciated.

Thank you!

I attempted to use launchPersistentContext when connecting via Aerokube Moon, expecting to get a persistent context with extension support. Instead, I got type errors since persistent contexts aren’t supported with remote connections; chromium.connect only allows creating incognito contexts via newContext()

How to Embed Power BI Report to a Web App in Mobile Layout?

I am trying to embed a Power BI report into a Web App but I want it shown in a mobile layout. I have tried to follow this documentation by adding this parameter layoutType: models.LayoutType.MobilePortrait, but it didn’t work. For more context, to create the Web App and the embed logic, I used the Microsoft Power BI template that is available in their Github Repository. The report that I try to embed already has the mobile layout. Below is my index.js code that contains the embed configuration.

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.


$(function () {
    var reportContainer = $("#report-container").get(0);

    // Initialize iframe for embedding report
    powerbi.bootstrap(reportContainer, {type: "report"});
    
    var models = window["powerbi-client"].models;
    var reportLoadConfig = {
        type: "report",
        tokenType: models.TokenType.Embed,
        settings: {
            layoutType: models.LayoutType.MobilePortrait,
            background: models.BackgroundType.Transparent,
            filterPaneEnabled: false,
            navContentPaneEnabled: false
        }
    };

    $.ajax({
        type: "GET",
        url: "/getembedinfo",
        dataType: "json",
        success: function (data) {
            embedData = $.parseJSON(JSON.stringify(data));
            reportLoadConfig.accessToken = embedData.accessToken;

            // You can embed different reports as per your need
            reportLoadConfig.embedUrl = embedData.reportConfig[0].embedUrl;

            // Use the token expiry to regenerate Embed token for seamless end user experience
            // Refer https://aka.ms/RefreshEmbedToken
            tokenExpiry = embedData.tokenExpiry;

            // Embed Power BI report when Access token and Embed URL are available
            var report = powerbi.embed(reportContainer, reportLoadConfig);

            // Triggers when a report schema is successfully loaded
            report.on("loaded", function () {
                console.log("Report load successful")
            });

            // Triggers when a report is successfully embedded in UI
            report.on("rendered", function () {
                console.log("Report render successful")
            });

            // Clear any other error handler event
            report.off("error");

            // Below patch of code is for handling errors that occur during embedding
            report.on("error", function (event) {
                var errorMsg = event.detail;

                // Use errorMsg variable to log error in any destination of choice
                console.error(errorMsg);
                return;
            });
        },
        error: function (err) {

            // Show error container
            var errorContainer = $(".error-container");
            $(".embed-container").hide();
            errorContainer.show();

            // Format error message
            var errMessageHtml = "<strong> Error Details: </strong> <br/>" + $.parseJSON(err.responseText)["errorMsg"];
            errMessageHtml = errMessageHtml.split("n").join("<br/>")

            // Show error message on UI
            errorContainer.html(errMessageHtml);
        }
    });
});

Based on my explanation, is there something that I missed or need to add to my code?

Thank you.

how to write a debounce which return exactly the same type as the passed function in typescript?

I hope that the debounce function can realize that the type of the returned function is exactly the same as the type of the passed function (whose return value is void or Promise<void>). I have write the following code

type VoidFunction = (...args: any[]) => void;
type PromiseVoidFunction = (...args: any[]) => Promise<void>;
export function debounce<T extends VoidFunction>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void;

export function debounce<T extends PromiseVoidFunction>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => Promise<void>;
export function debounce<T extends(VoidFunction | PromiseVoidFunction)>(fn: T, delay: number) {
  let timer: number | null = null;
  return function (this: ThisParameterType<T>, ...args: Parameters<T>): Promise<void> | void {
    if (timer) {
      clearTimeout(timer);
    }
    if (fn.constructor.name === 'AsyncFunction') {
      return new Promise<void>((resolve) => {
        timer = setTimeout(() => {
          timer = null;
          Promise.resolve(fn.apply(this, args)).then(resolve);
        }, delay);
      });
    }
    timer = setTimeout(() => {
      timer = null;
      fn.apply(this, args);
    }, delay);
  };
};

However, when I use this debounce and passed a function has a Promise<void>return type, typescript still infers the return type of the returned function as void

Uploading an image with AJAX after Heic2Any conversion

I am using the Heic2Any.js library to convert .heic files before uploading them. Once the file is converted I append it to a placeholder (#target) so I can see that the conversion worked, and it does. The image element has a JPG blob in it.

If the image is not .heic the form submits and it uploads as normal, using AJAX.

My question is, if it is a .heic file, how do I remove the .heic version from the FormData and replace it with the new JPG version, before submitting the form?

Also, what value do I put into the fetch call?

// HEIC to JPG Conversion

$('.image-input').on('change', function(e) {
    e.preventDefault();

    var fileextension = $(this).val().split('.').pop().toLowerCase();

    if (fileextension == 'heic') {
        fetch(????) 
            .then((res) => res.blob())
            .then((blob) => heic2any({
                blob,
                toType: "image/jpeg",
            }))
            .then((conversionResult) => {
                var url = URL.createObjectURL(conversionResult);
                $(".image-upload-form").find('.image-preview').append('<img src="' + url + '" />');
            })
            .catch((e) => {
                console.log(e);
            });
    } 

    // I want to add the new JPG to the form and then submit it to the AJAX uploader below. 
    $(this).closest(".image-upload-form").submit();
});

// AJAX uploader (this part works great already)

var form = $(this);

$.ajax({
    url: base_url + "upload-image",
    type: "POST",
    data:  new FormData(this),
    dataType: 'json',
    mimeType:"multipart/form-data",
    contentType: false,
    cache: false,
    processData: false,
    success: function(response) {
        if (response.message == 'error') {
            // show error
        } else {
            var src = base_url + 'assets/uploads/' + response.filename;
            form.find('.image-preview').append('<img src="' + src + '" />');
        }
    },
    error: function(error) {
      console.log(error);
    }
});

Safetest netflix implementation- import.meta.webpackContext is undefined – React

I have a react app with jest configuration already.

I am trying to setup netflix safe test. I changed my app as per instructions here at:
https://github.com/kolodny/safetest

I am facing some issue with: import_meta.webpackContext undefined.

`import React from ‘react’;
import { createRoot } from ‘react-dom/client’;
import { bootstrap } from ‘safetest/react’;

import App from ‘./App.jsx’;

const container = document.getElementById(‘app’);
const root = createRoot(container);
const isDev = process.env.NODE_ENV !== ‘production’;

bootstrap({
container,
element: (
<React.StrictMode>
<App />
</React.StrictMode>
),
webpackContext:
isDev &&
import.meta.webpackContext(‘.’, {
recursive: true,
regExp: /.safetest$/,
mode: ‘lazy’,
}),
render: (element) => root.render(element),
});
`

Error:

index.jsx:20 Uncaught TypeError: import_meta.webpackContext is not a function at eval (index.jsx:20:1) at 98963 (app.js:2978:1) at __webpack_require__ (app.js:9773:42) at app.js:9988:89 at __webpack_require__.O (app.js:9815:23) at webpackJsonpCallback (app.js:9974:40) at vendor.js:9:79

webpack version: 5.94.0
Node.js version: 18.20.2

I am wondering what is causing this issue and why my app is not loading:
Is webpack loader is causing this?

{ test: /.(js|jsx)$/, exclude: /node_modules/, loader: 'esbuild-loader', options: { loader: 'jsx', jsx: 'automatic', }, }, { test: /.tsx?$/, use: 'ts-loader', exclude: /node_modules/, },

I even tried using require.context (still didnt work)

Any help will be appreciated, thank you!!