Local Debugging Digital Goods API with Google Play Billing gives OperationError: unsupported context

I’m trying to add google play billing to my PWA website, which I’m wrapping into an android app with PWABuilder.

I’m tring to figure out how to test/debug locally.

This page on the docs says it can done like so:

  • Ensure you are on Android 9 or greater with developer mode enabled (my phone has android 11)
  • Install Chrome 101 or newer. (my phone has chrome v133.0.6946.121)
  • Enable the following flags in Chrome by navigating to chrome://flags and searching for the flag by name: #enable-debug-for-store-billing (i have set this to enabled on my phone)
  • Ensure that the site is hosted using a https protocol. Using http will cause the API to be undefined (this was hard but I created a self signed certificate using openssl (command below) and it gave me a key.pem and cert.pem files, which I added to my project root.

ssl key generation command: C:Program FilesOpenSSL-Win64bin>OpenSSL req -x509 -sha256 -newkey rsa:4096 -keyout u:key.pem -out u:cert.pem -days 365

I loaded my cert like this (both in my vite client and expressjs server):

const privateKey = fs.readFileSync('./key.pem', 'utf8');
const certificate = fs.readFileSync('./cert.pem', 'utf8');
const credentials = {key: privateKey, cert: certificate, passphrase: 'password'};

vite looks like this:

export default defineConfig({
    plugins: [react(), svgr()],
    server: {
        port: 5173,
        https: credentials
    }
});

the server looks like this:

const privateKey = fs.readFileSync('./key.pem', 'utf8');
const certificate = fs.readFileSync('./cert.pem', 'utf8');
const credentials = {key: privateKey, cert: certificate, passphrase: 'password'};

import https from 'https';

const server = https.createServer(credentials, app);

server.listen(port, host, () => {
    console.log(`nServer started at https://${host}:${port}...n`);
});

both the client and server seem to be able to connect via https from my phone, after I click the accept risks and continue button for each.

finally I attempt to connect to the digital goods service like so:

    console.log('are we in a secure context? ', window.isSecureContext);
    console.log('is the Digital Goods API supported? ', !!window.getDigitalGoodsService);
    console.log('document.refferer: ', document.referrer);
    if ('getDigitalGoodsService' in window) {
        // Digital Goods API is supported!
        try {
          const service = await window.getDigitalGoodsService('https://play.google.com/billing');
          console.log('billing supported');
      
        } catch (error) {
          console.error('Error connecting to Digital Goods API:', error);
          return;
        }
      }

but it just keeps returning the following error:

Purchase.tsx:40 are we in a secure context?  true
Purchase.tsx:41 is the Digital Goods API supported?  true
Purchase.tsx:42 document.refferer:  https://192.168.1.10:5173/shop
Purchase.tsx:52 Error connecting to Digital Goods API: OperationError: unsupported context

It seems to me like I met all the requirements described in the documentation for testing locally, yet I cannot access the API.

What am I doing wrong, or what can I try to help debug this?

styling not applied on mobile

I have been having issues with the audio controls. First of all, I think it’s a shame how limited you are with styling the controls. I did somewhat style them for my site and got it to a spot i’m happy with. On inspect element when I change it to the mobile dimensions, it looks totally fine. However when I actually deployed the site, the styles are not appearing on mobile devices. The audio controls are still there and I can play the audio, they just didn’t apply the styling

Here’s my code.

.cs-audio {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            margin: 0 0 clamp(1.75rem, 4vw, 2.5rem);
            justify-content: center;
        }

        .cs-audio-item {
            position: relative;
            display: block;
        }

        .cs-audio-title {
            font-size: calc(16 / 16 * 1rem);
            line-height: 1.2em;
            margin: 0;
            position: absolute;
            z-index: 100;
            top: 5px;
            left: 50%;
            transform: translateX(-30%);
        }

        audio::-webkit-media-controls-play-button {
            background-color: var(--primaryLight);
            border-radius: 50%;
        }

        audio::-webkit-media-controls-play-button:hover {
            background-color: #b1d4e0b3;
        }

 <div class="cs-audio">
                    <div class="cs-audio-item">
                        <span class="cs-audio-title">Commercial</span>
                        <audio controls>
                            <source
                                src="/assets/audio/demo.mp3"
                                type="audio/mpeg"
                            />
                        </audio>
                    </div>
                    <div class="cs-audio-item">
                        <span class="cs-audio-title">Narration</span>
                        <audio controls>
                            <source
                                src="/assets/audio/demo.mp3"
                                type="audio/mpeg"
                            />
                        </audio>
                    </div>
                    <div class="cs-audio-item">
                        <span class="cs-audio-title">Interactive</span>
                        <audio controls>
                            <source
                                src="/assets/audio/demo.mp3"
                                type="audio/mpeg"
                            />
                        </audio>
                    </div>
                    <div class="cs-audio-item">
                        <span class="cs-audio-title">Animation</span>
                        <audio controls>
                            <source
                                src="/assets/audio/demo.mp3"
                                type="audio/mpeg"
                            />
                        </audio>
                    </div>
                </div>

I tried testing my code on the dev mode’s inspect element and changing the dimensions to mobile and they look completely fine so I don’t think it’s a CSS issue. When I deployed the site I also went to inspect element and the styles are still there. It is only when I checked on my iphone that I saw that the styling was gone on the audio controls. I tried both safari and chrome, neither of them have the styling.

here’s what it looks like on codepen:
https://codepen.io/JreyIV/pen/vEYXejd

you can see that the styling works there too.

Why can’t i move the divs in my grid when i add them through a for loop in JS?

I made this 10×10 grid consisting of 100 divs where one of them is supposed to be able to move on input (one row/column up, left, down, right per button click). To do this i added a for loop to add all these divs so they dont clutter my HTML file.
I noticed the div (its named “you” in the code) stays in place, though. So I tried the ugly version of 100 div elements in my HTML and it worked just fine, despite there seemingly being no difference. I even removed the divs altogether and the movement still worked fine. But as soon as the for loop is added again, the div just stays in place again.
To be clear: I’m not looking for other solutions or workarounds, I want to learn what I did wrong and why this doesn’t work.

I tried to find the mistake by console.logging the position before and after inputs, as well as the gridRow/gridColumn values but everything looks like it should be working: the position values change after every input and the gridRow/gridColumn values reflect that. I changed CSS properties of the divs and the grid container around to make sure there’s no problem there, I tried seperating the eventListener-call from the .innerHTML call that adds the divs…none of it got me any closer to a conclusion.There are no error messages either to follow up on.

const gridContainer = document.getElementById("grid-container");
const you = document.getElementById("you");
const me = document.getElementById("me");
const upBtn = document.getElementById("up-btn");
const leftBtn = document.getElementById("left-btn");
const downBtn = document.getElementById("down-btn");
const rightBtn = document.getElementById("right-btn");

let mePosition = {
  x: 10,
  y: 10
};
let youPosition = {
  x: 1,
  y: 1
}

const moveUp = () => {
  if (youPosition.y > 1) {
    youPosition.y--;
    you.style.gridRow = youPosition.y;
  }
  console.log(youPosition);
  console.log(you.style.gridRow + " " + you.style.gridColumn);
}
const moveLeft = () => {
  if (youPosition.x > 1) {
    youPosition.x--;
    you.style.gridColumn = youPosition.x;
  }
  console.log(youPosition);
  console.log(you.style.gridRow + " " + you.style.gridColumn);
}
const moveDown = () => {
  if (youPosition.y < 10) {
    youPosition.y++;
    you.style.gridRow = youPosition.y;
  }
  console.log(youPosition);
  console.log(you.style.gridRow + " " + you.style.gridColumn);
}
const moveRight = () => {
  if (youPosition.x < 10) {
    youPosition.x++;
    you.style.gridColumn = youPosition.x;
  }
  console.log(youPosition);
  console.log(you.style.gridRow + " " + you.style.gridColumn);
}

const initiate = () => {
  for (let i = 0; i < 98; i++) { // this somehow fixes the divs in place and nothing will move; if the exact same divs are added manually it works, same if there are none
    gridContainer.innerHTML += `<div class="empty-div"></div>`;
  }
  upBtn.addEventListener("click", moveUp);
  leftBtn.addEventListener("click", moveLeft);
  downBtn.addEventListener("click", moveDown);
  rightBtn.addEventListener("click", moveRight);
}
.grid-container {
  width: 545px;
  height: 545px;
  background-color: rgb(211, 206, 133);
  display: grid;
  grid-template-columns: repeat(10, 50px);
  grid-template-rows: repeat(10, 50px);
  margin: 50px auto 0;
  padding: 5px;
  gap: 5px;
  border: 4px solid rgb(62, 62, 62);
}

.empty-div {
  background-color: beige;
  border: 1px black solid;
  box-sizing: border-box;
}

.you {
  grid-row: 1;
  grid-column: 1;
  background-color: rgb(162, 143, 120);
  border: 1px black solid;
  box-sizing: border-box;
}

.me {
  grid-row: 10;
  grid-column: 10;
  background-color: rgb(162, 143, 120);
  border: 1px black solid;
  box-sizing: border-box;
}
<body onload=initiate()>
  <div id="grid-container" class="grid-container">
    <div id="you" class="you">
      <p>You</p>
    </div>
    <div id="me" class="me">
      <p>Me</p>
    </div>
  </div>
  <div class="controls">
    <div class="upper-controls">
      <button id="up-btn" class="control-btn">UP</button>
    </div>
    <div class="lower-controls">
      <button id="left-btn" class="control-btn">LEFT</button>
      <button id="down-btn" class="control-btn">DOWN</button>
      <button id="right-btn" class="control-btn">RIGHT</button>
    </div>
  </div>
  <script src="script.js"></script>
</body>

```

Detecting end of Javascript expressions [duplicate]

JavaScript allows omitting the semicolon at the end of a statement. It is not advised nowadays (while many coders still omit the semicolons carelessly), but the standard is very vague about it. ECMAScript standard says that the parser inserts a semicolon at the end of the blocks and when not having it would break the code (well it is not exactly inserted, but anyway) But how can this be detected? Does it mean that for detecting these missing semicolons, we should have a fully working parser that covers every aspect of the ECMAScript standard? Or is there a shorter way?

Consider this for example:

while(some_condition) foo();
bar();   // bar will be called after the loop

now this does the same and is valid, and perfectly fine again if foo always returns true (of course not a good coding practice):

while(some_condition && foo());
bar();

as you see the semicolon means no expression in the loop itself. but what about this:

while(some_condition && foo())
bar()

now the standard is a little vague, although we can guess that bar() will be called inside the loop (the semicolon will be inserted after bar() call as not having it after the while(...) doesn’t technically break the code, but it is obvious that the results could be very different). Now the question is not about the specific aspects of the standard in this case, the question is about whether a simple procedure exists for the detection of the right place for that missing semicolons in every case. I’m not looking for a working code. A pseudo-code or even a general but well-thought description of the procedure would be very helpful. Thanks.

Using javascript and/or jQuery, trying to update multiple dropdowns from a master dropdown, but only the last dropdown is getting updated

Here is a jsFiddle showing the issue I am having. I have a dynamic number of records coming in from a database. I’m inserting these records into a table, and the very first row of the table is a “bulk insert” row. If a user selects a value from the dropdown in the first row I want it to update all the other rows. For some reason it’s only updating the last row. Can somebody look and tell me what I’m doing wrong? Please!?!?

<div class="table-responsive">
  <table
    id="insTblDates"
    class="table table-sm table-striped"
    style="font-size: small"
  >
    <thead>
      <tr>
        <th>From</th>
        <th>To</th>

        <th>Location</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td></td>
        <td></td>

        <td>
          <div class="dropdown d-grid d-md-block text-center">
            <button
              id="copy_btnLocationArea"
              type="button"
              class="btn btn-outline-dark btn-sm dropdown-toggle mx-1"
              data-bs-toggle="dropdown"
              data-bs-auto-close="outside"
              style="overflow: visible"
              aria-expanded="false"
            >
              <span class="fa fa-globe"></span>&nbsp;Location
            </button>
            <ul
              id="copy_ddLocationAreas"
              class="dropdown-menu csr_lnk"
              style="max-height: 400px; width: auto; overflow: auto"
            >
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline copy_ddLocationArea"
                  id="copy_ddLocationArea1"
                  value="1"
                  name="copy_ddlocationArea"
                  onclick="copy_ddLocationAreaChanged(this.value);"
                /><label
                  for="copy_ddLocationArea1"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >In County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline copy_ddLocationArea"
                  id="copy_ddLocationArea2"
                  value="2"
                  name="copy_ddlocationArea"
                  onclick="copy_ddLocationAreaChanged(this.value);"
                /><label
                  for="copy_ddLocationArea2"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline copy_ddLocationArea"
                  id="copy_ddLocationArea3"
                  value="3"
                  name="copy_ddlocationArea"
                  onclick="copy_ddLocationAreaChanged(this.value);"
                /><label
                  for="copy_ddLocationArea3"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of Country</label
                >
              </li>
            </ul>
          </div>
        </td>
      </tr>
      <tr id="f3112025|t3112025">
        <td>3/11/2025</td>
        <td>3/11/2025</td>
        <td>
          <div class="dropdown d-grid d-md-block text-center">
            <button
              type="button"
              class="btn btn-outline-dark btn-sm dropdown-toggle new_btnLocationArea mx-1"
              data-bs-toggle="dropdown"
              data-bs-auto-close="outside"
              style="overflow: visible"
              aria-expanded="false"
            >
              <span class="fa fa-globe"></span>&nbsp;Location
            </button>
            <ul
              id="new_ddLocationAreas3112025"
              class="dropdown-menu csr_lnk new_ddLocationAreas"
              style="max-height: 400px; width: auto; overflow: auto"
            >
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea1_3112025"
                  value="1"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea1_3112025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >In County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea2_3112025"
                  value="2"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea2_3112025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea3_3112025"
                  value="3"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea3_3112025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of Country</label
                >
              </li>
            </ul>
          </div>
        </td>
      </tr>
      <tr id="f3182025|t3182025">
        <td>3/18/2025</td>
        <td>3/18/2025</td>
        <td>
          <div class="dropdown d-grid d-md-block text-center">
            <button
              type="button"
              class="btn btn-outline-dark btn-sm dropdown-toggle new_btnLocationArea mx-1"
              data-bs-toggle="dropdown"
              data-bs-auto-close="outside"
              style="overflow: visible"
              aria-expanded="false"
            >
              <span class="fa fa-globe"></span>&nbsp;Location
            </button>
            <ul
              id="new_ddLocationAreas3182025"
              class="dropdown-menu csr_lnk new_ddLocationAreas"
              style="max-height: 400px; width: auto; overflow: auto"
            >
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea1_3182025"
                  value="1"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea1_3182025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >In County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea2_3182025"
                  value="2"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea2_3182025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea3_3182025"
                  value="3"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea3_3182025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of Country</label
                >
              </li>
            </ul>
          </div>
        </td>
      </tr>
      <tr id="f3252025|t3252025">
        <td>3/25/2025</td>
        <td>3/25/2025</td>
        <td>
          <div class="dropdown d-grid d-md-block text-center">
            <button
              type="button"
              class="btn btn-outline-dark btn-sm dropdown-toggle new_btnLocationArea mx-1"
              data-bs-toggle="dropdown"
              data-bs-auto-close="outside"
              style="overflow: visible"
            >
              <span class="fa fa-globe"></span>&nbsp;Location
            </button>
            <ul
              id="new_ddLocationAreas3252025"
              class="dropdown-menu csr_lnk new_ddLocationAreas"
              style="max-height: 400px; width: auto; overflow: auto"
            >
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea1_3252025"
                  value="1"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea1_3252025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >In County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea2_3252025"
                  value="2"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea2_3252025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of County</label
                >
              </li>
              <li>
                &nbsp;<input
                  type="radio"
                  class="d-inline new_ddLocationArea"
                  id="new_ddLocationArea3_3252025"
                  value="3"
                  name="new_ddlocationArea"
                  onclick="new_ddLocationAreaChanged(this.value);"
                /><label
                  for="new_ddLocationArea3_3252025"
                  class="d-inline input small dropdownLabel_NormalFont"
                  >Out of Country</label
                >
              </li>
            </ul>
          </div>
        </td>
      </tr>
    </tbody>
  </table>
</div>

function copy_ddLocationAreaChanged(val) {
    $(".new_ddLocationArea").each(function () {
        if ($(this).val() == val) {
            $(this).prop("checked", true)
        }
    })
}

Disabling checkboxes from DataTable across pagination depending on checked limit

I have a form that presents as a DataTable with checkmarks on each row. The user should be able to check up to 10 checkboxes. Once the tenth is checked, all the unchecked checkboxes need to be disabled until the user unchecks at least one to go back below the limit. Everything works fine, except when I start playing with the number of rows per page. If I go from 25 to 10 rows and then go back and forth through different pages, some checkboxes are still disabled while others are not. The total count of checked checkboxes is always right though. I’m not sure how to refresh the ‘disabled’ attribute when the table is redrawn.

This is the HTML code:

<div class="card-header pb-0 card-no-border">
    <span>Select up to 10 playlists for this song. You can select up to 10 playlists daily.</span>
</div>
<div class="alert alert-warning inverse alert-dismissible fade show" role="alert"
    id="pageMessages">
    <i class="icon-info-alt"></i>
    <p id="limitCounter">0 playlists selected from limit of 10</p>
</div>

<div class="card-body">
    <div class="table-responsive HideScrollbar">
        <form method="POST" action="submission_agreement.php">
            <table class="display" id="basic-1" data-page-length="25">
                <thead>
                    <tr>
                    <th class="no-sort">Cover</th>
                    <th>Details</th>
                    <th>Saves</th>
                    <th>Songs</th>
                    <th>Updated</th>
                    <th class="no-sort">Select</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td><img src="assets/images/playlists/playlist-cover-1.png" alt=""></td>
                        <td>
                        <h6>Contemporary Piano</h6>
                        <span>
                            by <a href="#">antonioromopiano</a><br>
                            Submissions: 1,013 | Approval rate: 43%<br>
                            neo-classical · instrumental · cinematic · calming
                        </span>
                        </td>
                        <td>539 <i class="fa fa-heart-o fa-sm"></i></td>
                        <td>150 <i class="fa fa-music"></i></td>
                        <td class="font-success">2023 05 27</td>
                        <td>
                            <input type="checkbox" id="checkpoint1" class="checkpoint ms-4" name="checkpoints"
                            value="{{playlist.id}}">
                        </td>
                    </tr>
                    <tr>
                        ... many rows like the one above ...
                    </tr>
                </tbody>
            </table>
            <div class="col text-center mt-3">
                <button class="btn btn-primary btn-next" type="submit" id="done_selecting">Done Selecting Playlists</button>
            </div>
        </form>
    </div>
</div>

And this is my JavaScript:

<script>

  $(document).ready(function () {
    $("input[id^='checkpoint']").change(function () {
      const checkbox_limit = 10;
      const checkboxes = document.querySelectorAll('input[type="checkbox"]');

      var element = document.getElementById("pageMessages");
      var $table = $('#basic-1').dataTable();
      var allNodes = $table.fnGetNodes();
      var total_checked = 0;

      $(allNodes).each(function () {
        $(this).change(updatetotal_checked);
      });
    
      updatetotal_checked();

      function updatetotal_checked () {
    
        total_checked = $(allNodes).find("input[name='checkpoints']:checked").length;
  
        $("#total_checked").text(total_checked);
        if (total_checked > 0) {         
          $("#status").toggle(total_checked > 0);
        }
          
      };

      /* Disable unchecked checkboxes if total checked = 10 */
      checkboxes.forEach((checkbox) => {
        checkbox.disabled = total_checked >= checkbox_limit && !checkbox.checked;
      });

      /* Disable submit button if at least one playlist hasn't been selected */
      if (total_checked == 0) {
        document.getElementById('done_selecting').setAttribute('disabled', '');
      }
      else {
        document.getElementById('done_selecting').removeAttribute('disabled');
      }

      /* Change alert color if total checked = 10 */
      if (total_checked >= checkbox_limit) {
        element.classList.remove("alert-warning");
        element.classList.add("alert-danger");
      } else {
        element.classList.remove("alert-danger");
        element.classList.add("alert-warning");
      }

      /* Update alert text with total checked */
      const limitCounter = document.getElementById('limitCounter');
      limitCounter.textContent = `${total_checked} playlists selected from limit of ${checkbox_limit}`;      

    });

    /* To set initial state */
    $("input[id^='checkpoint']").trigger('change');

  });

</script>

Any help is greatly appreciated!

Can browser autofill be prevented when input value changed programatically?

Turn on autofill in Chrome or Firefox. Using autofill in a form will change an input value unless it has been changed by a user.

Is it possible to prevent autofill from replacing input value if the value has been programmatically changed?

For added context to that question: Is there any attribute or event that can be triggered while programmatically changing input value? Would prefer to not have to change autocomplete or readonly attribute on the input field unless that’s the only way to accomplish it.

<h1>Browser autofill</h1>
<p>
  <label for="name">Name</label>
  <input name="name" type="text" />
</p>
<p>
  <label for="street">Street</label>
  <input name="street" type="text" />
</p>
<p>
  <label for="province">Province</label>
  <input name="province" type="text" />
</p>
<p>
  <label for="country">Country</label>
  <input id="country" name="country" type="text" />
</p>
<div class="card">
  <button
    onClick="{ document.getElementById('country').value = 'Australia'; }"
    type="button"
  >
    Programmatically change Country
  </button>
</div>

Getting details on public key in nodejs?

Given a public key of the following form (invalidated in example), how would I find the algorithm used in Node.js, using the crypto package?

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhZPjbR5OWOjXeAT/MvrK
TeS5aKhSZJgdm6/Mq7DWgUWIQ3aIHqiXRpWd9WPG7n9/YSPmwcbmiJLUVETt3400
gSd9AoabzOMZsJYz6fsxC9+M54RV4x8Ef5Rej2cc6m9iTfzc9uRpJtDulYGNGWCF
crOLLbMVGgeKjDmqF57s2OWG2xSmy1+MpY9GqWgDBWvdEF7+pNrc1HcHMd2ELOKS
QKbUcE6w24ro71bLSW9FZexWwQNr6fYRqBxyrlobYserl9kKZNkbBsBdezFT8S8c
ZM8WUVzYpNR2mXdrs+fumKFljSG32dLfnI70eVeEX1oMSB/9bzvtPfS36KSZ+PPs
FQIDAQAB
-----END PUBLIC KEY-----

The main goal is to ensure that the provided key is valid on application initialisation and that it is using an algorithm that is supported by Node.js.

I did look at crypto.createVerify, but that requires knowing the algorithm before hand, hence looking for other approaches.

Replace middle chars with dots

I have a example regex to remove the middle characters, and output only the last five digits

Output for this (?<=d{5})d(?=d{4}) is 75685XXXXXXXXXX76575

Now is it possiable to have this 0x388c818ca8b9251b393131c08a736a67ccb19297 and output like this 0x388c ... cb19297

Swiper JS hides Main image section when thumbnail linked

I am trying to create an image section with thumbnail images.
And using Swiper JS for this functionality.
Below is my swiper configuration code

let galleryThumbsLife = new Swiper('.gallery-thumbs-life', {
    loop: true,
    spaceBetween: 10,
    slidesPerView: 4,
    freeMode: true,
    watchSlidesProgress: true,
});

var galleryTopLife = new Swiper('.gallery-top-life', {
    loop: true,
    spaceBetween: 10,
    navigation: {
        nextEl: ".swiper-button-next-top",
        prevEl: ".swiper-button-prev-top",
    },
    thumbs: {
        swiper: galleryThumbsLife,
    },
});

The issue here is that I am just seeing thumbnail section and not ‘gallary-top-life’ section.

How to add a filter to a popup window generated by javascript in Odoo 13

I’m trying to extend file relational_fields.js from the web module in Odoo 13, so that in the request for quotation form, when i start entering a vendor name and the autocomplete options show with a Search More… link, when i click that link it adds a Vendors filter to the search.

I’ve found a way to get what I want if I edit the relational_fields.js file directly, though I cannot get it to work if I add a custom js file.

Any ideas on what I should do?

Here is the custom file I’m loading through the manifest. I’ve tried loading it by adding assets to the manifest as well as creating a view for the assets.

odoo.define('your_module_name.custom_relational_fields', function (require) {
    "use strict";

    var relational_fields = require('web.relational_fields');
    var FieldMany2One = relational_fields.FieldMany2One;

    var CustomFieldMany2One = FieldMany2One.extend({
        _manageSearchMore: function (values, search_val, domain, context) {
            values = values.slice(0, this.limit);
            values.push({
                label: _t("Search More..."),
                action: function () {
                    var prom;
                    if (context.res_partner_search_mode === 'supplier') {
                        domain = domain.concat([['supplier_rank', '>', 0]]);
                    }
                    if (search_val !== '') {
                        prom = this._rpc({
                            model: this.field.relation,
                            method: 'name_search',
                            kwargs: {
                                name: search_val,
                                args: domain,
                                operator: "ilike",
                                limit: this.SEARCH_MORE_LIMIT,
                                context: context,
                            },
                        });
                    }
                    Promise.resolve(prom).then(function (results) {
                        var dynamicFilters;
                        if (results) {
                            var ids = _.map(results, function (x) {
                                return x[0];
                            });
                            dynamicFilters = [{
                                description: _.str.sprintf(_t('Quick search: %s'), search_val),
                                domain: [['id', 'in', ids]],
                            }];
                            if (context.res_partner_search_mode === 'supplier') {
                                dynamicFilters = [{
                                    description: _.str.sprintf(_t('Quick search: %s, Vendors'), search_val),
                                    domain: [['id', 'in', ids], ['supplier_rank', '>', 0]],
                                }];
                            }
                        }
                        this._searchCreatePopup("search", false, {}, dynamicFilters);
                    }.bind(this));
                }.bind(this),
                classname: 'o_m2o_dropdown_option',
            });
            return values;
        },
    });

    return {
        CustomFieldMany2One: CustomFieldMany2One,
    };
});

I got the help from copilot for this, but it doesn’t seem so effective at the moment.
Here is my custom_relational_fields.js, which I have set in a custom pre-existing module, under static/src/js directory.

Here is my manifest file:

{
    "name": "my module name| Purchase enhancement",
    "summary": "Apply changes to core Odoo purchasing module to adapt changes to the customer requirement.",
    "license": "LGPL-3",
    "author": "much. GmbH",
    "auto_install": False,
    "application": False,
    "sequence": 100,
    "category": "Purchase",
    "version": "13.0.6.26.0",
    "depends": ["base", "product", "purchase", "purchase_stock", "web"],
    "data": [
        "views/purchase_order.xml",
        "views/product_views.xml",
        "views/res_partner_views.xml",
        "views/stock_picking_view.xml",
        "views/assets.xml",
        "wizard/display_dialog_box.xml",
        "data/ir_config_parameter_data.xml",
    ],
    # "assets": {
    #      "web.assets_backend": [
    #          "my_module/static/src/js/custom_relational_fields.js",
    #      ],
    #  },




}

Here is the assets.xml recommended by co-pilot:

<odoo>
    <template id="assets_backend" name="custom assets" inherit_id="web.assets_backend">
        <xpath expr="." position="inside">
            <script type="text/javascript" src="/my_module/static/src/js/custom_relational_fields.js"></script>
        </xpath>
    </template>
</odoo>

Find the original relational_fields.js at:
https://github.com/odoo/odoo/blob/13.0/addons/web/static/src/js/fields/relational_fields.js

And the updated part of the code which I found to be working inside the relational_fields file:

   _manageSearchMore: function (values, search_val, domain, context) {
        var self = this;
        values = values.slice(0, this.limit);
        values.push({
            label: _t("Search More..."),
            action: function () {
                var prom;
                /* if context res_parnter_search_mode key is supplier, then domain should*/
                /* be updated to include the supplier filter */
                if (context.res_partner_search_mode === 'supplier') {
                    domain = domain.concat([['supplier_rank', '>', 0]]);
                }
                if (search_val !== '') {
                    prom = self._rpc({
                        model: self.field.relation,
                        method: 'name_search',
                        kwargs: {
                            name: search_val,
                            args: domain,
                            operator: "ilike",
                            limit: self.SEARCH_MORE_LIMIT,
                            context: context,
                        },
                    });
                }
                Promise.resolve(prom).then(function (results) {
                    var dynamicFilters;
                    if (results) {
                        var ids = _.map(results, function (x) {
                            return x[0];
                        });
                        dynamicFilters = [{
                            description: _.str.sprintf(_t('Quick search: %s'), search_val),
                            domain: [['id', 'in', ids]],
                        }];
                        if (context.res_partner_search_mode === 'supplier') {
                            dynamicFilters= [{
                             description: _.str.sprintf(_t('Quick search: %s, Vendors'), search_val),
                             domain: [['id', 'in', ids], ['supplier_rank', '>', 0]],
                            }];
                            }

                    }
                    self._searchCreatePopup("search", false, {}, dynamicFilters);
                });
            },
            classname: 'o_m2o_dropdown_option',
        });
        return values;
    },

Unwanted JavaScript Code Injected into Magento 2 CMS Static Block [closed]

I am facing an issue where unwanted JavaScript code is being injected into my Magento 2 CMS static blocks. Every time I manually remove this obfuscated JavaScript code from a static block, it reappears automatically after about 5 minutes.
enter image description here

Steps I Followed:

  1. Navigated to Content → Blocks in Magento 2 admin.
  2. Edited the affected static block and removed the injected JavaScript.
  3. Saved the block.
  4. After a few minutes, the unwanted JavaScript was injected back into the block.

Troubleshooting Done:

  • Checked the core_config_data table to see if any suspicious scripts are stored in the database.
  • Scanned Magento files for possible malware injections.
  • Reviewed Magento logs (var/log/system.log, var/log/exception.log) but found no relevant errors.
  • Disabled third-party extensions temporarily to see if any extension is causing this issue.
  • Checked for any automated cron jobs that might be modifying the CMS block content.

Possible Causes I Suspect:

  1. Malware Infection: There might be a malicious script running on the server injecting this code.
  2. Compromised Admin Account: A script or extension might be modifying CMS content using Magento’s API.
  3. Database Injection: The JavaScript could be stored directly in the database and injected whenever the page loads.

My Questions:

  1. How can I prevent this JavaScript from being re-added to my CMS blocks?
  2. Are there any specific Magento logs or database tables I should inspect further?
  3. Has anyone encountered a similar issue before, and what was the resolution?

Any guidance would be greatly appreciated! Thanks in advance.

TypeScript works with ts-node but not with tsc: Errors related to Map, Set, and ES2015 features

I am working on a TypeScript project where I’m using the Zod library. When I run the project with ts-node, everything works fine, but when I try to compile with tsc, I get the following errors:

1. node_modules/zod/lib/helpers/util.d.ts:19:47 - error TS2339: Property 'isInteger' does not exist on type 'NumberConstructor'.
2. node_modules/zod/lib/types.d.ts:612:17 - error TS2583: Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.
3. node_modules/zod/lib/types.d.ts:619:23 - error TS2583: Cannot find name 'Map'.
4. node_modules/zod/lib/types.d.ts:689:121 - error TS2583: Cannot find name 'Map'.
5. node_modules/zod/lib/types.d.ts:707:84 - error TS2583: Cannot find name 'Set'.
6. node_modules/zod/lib/types.d.ts:772:5 - error TS18028: Private identifiers are only available when targeting ECMAScript 2015 and higher.
7. node_modules/zod/lib/types.d.ts:923:14 - error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.
8. node_modules/zod/lib/types.d.ts:925:34 - error TS2583: Cannot find name 'Map'.

Code I am running:

import { z } from "zod";

const UserSchema = z.object({
  username: z.string(),
});

const user = { username: "ddi" };

console.log(UserSchema.parse(user));

Questions:
What could be causing tsc to fail while ts-node works perfectly fine?
Are there any additional settings in tsconfig.json I should modify to make tsc work as expected?
Is there something specific I need to configure to ensure compatibility with Zod and other ES2015+ features when using tsc?

Why do I get Status 500 on my API call after changing my domain name?

I am currently in the middle of deploying my Portfolio, which uses Gemini API to make up a chatbot. Everything is working well on a development server, and all was working fine on deployment too, however, upon changing my Domain Name after deploying, I started running into the Status 500 Error when making this API call. All of my env variables are linked to Vercel, however it just stopped working. This is the code that sends a request to my route `

const question = history[history.length - 1].parts[0].text;

try {
  const response = await fetch("/api/chat", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ question }),
  });
  const data = await response.json();
  if (!response.ok) {
    throw new Error(data.error.message || "Something went wrong!");
  }
  const apiResponseText = data.candidates[0].content.parts[0].text;
  const formattedResponse = formatApiResponse(apiResponseText);
  updateHistory(formattedResponse);
} catch (error) {
  updateHistory(error.message, true);
}
setIsLoading(false);

}`

My API route located in /api/chat looks like this

export default async function aiHandler(req, res) {
if (req.method === "POST") {
const { question } = req.body;

const requestOptions = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contents: [
      {
        parts: [
          {
            text: aiPrompt(question),
          },
        ],
      },
    ],
  }),
};

try {
  const apiResponse = await fetch(
    process.env.GEMINI_API_URL,
    requestOptions
  );
  const data = await apiResponse.json();
  if (!apiResponse.ok)
    throw new Error(data.message || "Failed to fetch API");

  res.status(200).json(data);
} catch (error) {
  res.status(500).json({ message: error.message });
}

The Vercel log points specifically to the function that sends a request to the /api/chat endpoint, but im not quite sure what the issue is regarding the code as it was working prior to changing the Domain Name.