Source: bemap-leaflet/bemap-map-leaflet-polygon.js

/**
 * Add a bemap.Polygon to the layer
 * @public
 * @param {bemap.Polygon} bemap.Polygon.
 * @param {object} options
 * @return {bemap.LeafletMap}
 */
bemap.LeafletMap.prototype.addPolygon = function(polygon, options) {
  if (polygon && bemap.inheritsof(polygon, bemap.Polygon)) {

    var style = polygon.getStyle();
    if (!style) {
      style = new bemap.PolygonStyle({
        fillColor: new bemap.Color(0, 102, 204, 0.2),
        borderColor: new bemap.Color(0, 102, 204, 1),
        borderWidth: 2
      });
    }
    var fillColor = style.getFillColor();
    var borderColor = style.getBorderColor();

    // Resolve the target layer FIRST so we can pass its path pane to
    // L.polygon at construction (Leaflet honours `pane` only on init).
    var opts = options || {};
    var l = null;
    if (opts.layer !== undefined && (bemap.inheritsof(opts.layer, bemap.VectorLayer))) {
      l = opts.layer;
    } else {
      l = this.getLayerByName(bemap.Map.DEFAULT_LAYER.POLYGON);
      if (l === null) {
        l = new bemap.VectorLayer({
          name: bemap.Map.DEFAULT_LAYER.POLYGON
        });
        this.addLayer(l);
      }
    }

    var polygonOpts = {
      fillColor: fillColor.getHex(),
      fillOpacity: fillColor.getAlpha(),
      color: borderColor.getHex(),
      opacity: borderColor.getAlpha(),
      weight: style.getBorderWidth()
    };
    if (l && l._paneNamePath) polygonOpts.pane = l._paneNamePath;
    polygon.native = L.polygon(polygon.getLatLonArrays(), polygonOpts);

    if (polygon.map === null) {
      polygon.map = this;
    }

    if (polygon.layer === null) {
      polygon.layer = l;
    }

    polygon.native.addTo(l.native);
  }
  return this;
};

/**
 * Sync a polygon's MODEL coordinates FROM its native geometry.
 *
 * Direction matters, and this engine had it backwards (BEMAP-1898 audit).
 * `bemap.Polygon.getCoordinates()` calls this and *then* returns `this.coords`,
 * so the contract is a READ: native → model. That is what the OL engine does.
 * Leaflet instead did `native.setLatLngs(polygon.getLatLonArrays())` — model →
 * native — with two consequences:
 *   • any native-side change (a vertex edit, a drag) was silently OVERWRITTEN by
 *     the stale model on the next getCoordinates() call, and never reported;
 *   • inside draggablePolygon's mousemove the native was reset to the previous
 *     position, so the shape rendered one mousemove behind the cursor and landed
 *     one delta short. The handler compensated with a manual delta loop, which
 *     is removed now that this reads the right way round.
 * @public
 * @param {bemap.Polygon} polygon
 * @return {bemap.LeafletMap} this
 */
bemap.LeafletMap.prototype.updatePolygonCoordinates = function(polygon) {
  if (polygon && polygon.native && bemap.inheritsof(polygon, bemap.Polygon)) {
    // L.Polygon nests rings; descend until the elements are LatLngs, so a simple
    // polygon ([[LatLng,…]]) and a multi-ring one both resolve to the outer ring.
    var ring = polygon.native.getLatLngs();
    while (ring && ring.length && ring[0] && ring[0].lat === undefined) {
      ring = ring[0];
    }
    if (!ring) return this;

    for (var i = 0; i < ring.length; i++) {
      var coord = polygon.coords[i];
      if (coord && coord !== null && bemap.inheritsof(coord, bemap.Coordinate)) {
        // Mutate in place, like OL: a consumer may hold a reference.
        coord.setLon(ring[i].lng);
        coord.setLat(ring[i].lat);
      } else {
        polygon.coords[i] = new bemap.Coordinate(ring[i].lng, ring[i].lat);
      }
    }
    // Drop any model vertices the native no longer has (same as OL).
    if (polygon.coords.length > ring.length) {
      for (var j = polygon.coords.length; j > ring.length; j--) {
        polygon.coords.pop();
      }
    }
  }
  return this;
};

bemap.LeafletMap.prototype.removePolygon = function(polygon) {
  if (polygon && polygon.layer && polygon.layer.native && bemap.inheritsof(polygon, bemap.Polygon)) {
    // Clean up vertex handles if editing was enabled
    if (polygon._vertexHandlesLayer && polygon.map && polygon.map.native) {
      polygon.map.native.removeLayer(polygon._vertexHandlesLayer);
      polygon._vertexHandlesLayer = null;
    }
    polygon.map.native.removeLayer(polygon.native);
    polygon.layer = null;
    polygon.map = null;
  }
  return this;
};

/**
 * Set the listner when an specified eventType occur on bemap.Polygon.
 * @public
 * @param {bemap.Polygon} polygon
 * @param {bemap.Map.EventType} eventType Event type.
 * @param {function} callback Function will be called when the specified eventType is occur.
 * @param {object} options options.
 * @return {bemap.Listener} this.
 */
bemap.LeafletMap.prototype.onPolygon = function(polygon, eventType, callback, options) {

  var opts = options ? options : {};

  var nativeListener = polygon.native.on(eventType, function(evt) {
    var mapEvent = new bemap.MapEvent({
      native: evt,
      bemapObject: polygon,
      x: evt.containerPoint ? evt.containerPoint.x : undefined,
      y: evt.containerPoint ? evt.containerPoint.y : undefined,
      coordinate: evt.latlng ? new bemap.Coordinate(evt.latlng.lng, evt.latlng.lat) : undefined,
      properties: options,
      map: this
    });
    callback(mapEvent);
  });
  var listener = new bemap.Listener({
    native: nativeListener,
    callback: callback,
    key: eventType,
    bemapObject: polygon
  });

  return listener;
};

/**
 * Define the draggable capability for bemap.Polygon (Leaflet).
 * @param {bemap.Polygon} polygon
 * @param {function} callback
 * @param {object} options
 * @return {bemap.Listener}
 */
bemap.LeafletMap.prototype.draggablePolygon = function(polygon, callback, options) {
  var _this = this;

  polygon.native.on('mousedown', function(evt) {
    L.DomEvent.stopPropagation(evt);
    _this.native.dragging.disable();
    var mouseStartLat = evt.latlng.lat;
    var mouseStartLng = evt.latlng.lng;

    var onMove = function(e) {
      var dLat = e.latlng.lat - mouseStartLat;
      var dLng = e.latlng.lng - mouseStartLng;
      mouseStartLat = e.latlng.lat;
      mouseStartLng = e.latlng.lng;

      var rings = polygon.native.getLatLngs();
      var newRings = [];
      for (var r = 0; r < rings.length; r++) {
        var ring = rings[r];
        var newRing = [];
        for (var i = 0; i < ring.length; i++) {
          newRing.push(L.latLng(ring[i].lat + dLat, ring[i].lng + dLng));
        }
        newRings.push(newRing);
      }
      polygon.native.setLatLngs(newRings);

      // The native has already moved; pull the model from it. This used to add
      // the delta to the model BY HAND, which was only correct while
      // updatePolygonCoordinates pushed model → native (and even then it left
      // the shape a mousemove behind). Now that the sync reads native → model,
      // keeping the manual loop would apply the delta twice.
      _this.updatePolygonCoordinates(polygon);
    };

    _this.native.on('mousemove', onMove);
    _this.native.once('mouseup', function(e) {
      _this.native.off('mousemove', onMove);
      _this.native.dragging.enable();
      if (callback) {
        callback(new bemap.MapEvent({
          native: e, bemapObject: polygon,
          coordinate: e.latlng ? new bemap.Coordinate(e.latlng.lng, e.latlng.lat) : undefined,
          map: _this
        }));
      }
    });
  });

  return new bemap.Listener({ native: polygon.native, bemapObject: polygon, key: 'dragFeature' });
};