Source: bemap-maplibre/bemap-maplibre-v5/bemap-map-maplibre-popup.js

/**
 * BeNomad BeMap JavaScript API - MapLibre v5 - Popup
 */

bemap.MapLibreMap.prototype.addPopup = function(popup, options) {
  if (!popup || !bemap.inheritsof(popup, bemap.Popup)) return this;

  popup.native = new maplibregl.Popup({ closeOnClick: true, maxWidth: '300px' });
  if (popup.information) popup.native.setHTML(popup.information);
  if (popup.map === null) popup.map = this;

  // Track OUR popups per map instance so clearPopup() can be surgical. It used
  // to sweep `document.querySelectorAll('.maplibregl-popup')`, which reached
  // every popup on the page — other bemap maps' popups and any the host app had
  // created itself (BEMAP-1898 audit).
  if (!this._bemapPopups) this._bemapPopups = [];
  if (this._bemapPopups.indexOf(popup) === -1) this._bemapPopups.push(popup);

  if (popup.coordinate && popup.visible !== false) {
    popup.native.setLngLat([popup.coordinate.getLon(), popup.coordinate.getLat()]);
    popup.native.addTo(this.native);
    popup.visible = true;
  } else if (!popup.coordinate && popup.visible === true) {
    // Fallback: use map center (same as OlMap behavior)
    var center = this.native.getCenter();
    popup.coordinate = new bemap.Coordinate(center.lng, center.lat);
    popup.native.setLngLat([center.lng, center.lat]);
    popup.native.addTo(this.native);
  } else {
    popup.visible = false;
  }

  return this;
};

bemap.MapLibreMap.prototype.removePopup = function(popup) {
  if (popup && popup.native) {
    popup.native.remove();
    popup.visible = false;
    popup.map = null;
    popup.native = null;
  }
  // Drop the tracking entry whether or not it still had a native, so a repeated
  // remove cannot leave a stale reference behind.
  if (popup && this._bemapPopups) {
    var at = this._bemapPopups.indexOf(popup);
    if (at !== -1) this._bemapPopups.splice(at, 1);
  }
  return this;
};

bemap.MapLibreMap.prototype.setVisiblePopup = function(popup, visible) {
  if (popup && popup.native) {
    popup.visible = visible;
    if (visible && popup.coordinate) {
      popup.native.setLngLat([popup.coordinate.getLon(), popup.coordinate.getLat()]);
      popup.native.addTo(this.native);
    } else {
      popup.native.remove();
    }
  }
  return this;
};

/**
 * Remove all the popups THIS map owns.
 *
 * Used to be `document.querySelectorAll('.maplibregl-popup')` + `Element.remove()`,
 * which had three faults (BEMAP-1898 audit):
 *   • document-wide — it deleted popups belonging to other bemap maps on the
 *     page, and any `maplibregl.Popup` the host application had created itself;
 *   • it removed the DOM node instead of calling the popup's own `remove()`, so
 *     MapLibre never detached its map listeners — each open/clear cycle leaked a
 *     move + click + remove handler set;
 *   • the bemap.Popup models were left reporting `isVisible() === true`.
 * @public
 * @return {bemap.MapLibreMap} this
 */
bemap.MapLibreMap.prototype.clearPopup = function() {
  var owned = this._bemapPopups ? this._bemapPopups.slice() : [];
  for (var i = 0; i < owned.length; i++) {
    // Goes through removePopup so the native's own remove() runs (detaching its
    // listeners), the model is marked hidden, and the tracking entry is dropped.
    this.removePopup(owned[i]);
  }
  this._bemapPopups = [];
  return this;
};

bemap.MapLibreMap.prototype.setCoordinatePopup = function(popup, coordinate, options) {
  if (popup && popup.native && coordinate) {
    popup.coordinate = coordinate;
    popup.native.setLngLat([coordinate.getLon(), coordinate.getLat()]);
    if (popup.information) popup.native.setHTML(popup.information);
    if (!popup.visible) {
      popup.native.addTo(this.native);
      popup.visible = true;
    }
  }
  return this;
};