1 //= require download_util
 
   3 L.OSM.share = function (options) {
 
   4   const control = L.OSM.sidebarPane(options, "share", "javascripts.share.title", "javascripts.share.title"),
 
   5         marker = L.marker([0, 0], { draggable: true, icon: OSM.getMarker({ color: "var(--marker-blue)" }) }),
 
   6         locationFilter = new L.LocationFilter({
 
  11   function init(map, $ui) {
 
  14     $ui.find("#link_marker").on("change", toggleMarker);
 
  16     $ui.find(".btn-group .btn")
 
  17       .on("shown.bs.tab", () => {
 
  18         $ui.find(".tab-pane.active [id]")
 
  22     $ui.find(".share-tab [id]").on("click", select);
 
  26     $ui.find("#mapnik_scale").on("change", update);
 
  28     $ui.find("#image_filter").bind("change", toggleFilter);
 
  30     const csrfInput = $ui.find("#csrf_export")[0];
 
  31     [[csrfInput.name, csrfInput.value]] = Object.entries(OSM.csrf);
 
  33     document.getElementById("export-image")
 
  34       .addEventListener("turbo:submit-end",
 
  35                         OSM.getTurboBlobHandler(OSM.i18n.t("javascripts.share.filename")));
 
  37     document.getElementById("export-image")
 
  38       .addEventListener("turbo:before-fetch-response", OSM.turboHtmlResponseHandler);
 
  44     marker.on("dragend", movedMarker);
 
  45     map.on("move", movedMap);
 
  46     map.on("moveend baselayerchange overlayadd overlayremove", update);
 
  55       $("#mapnik_scale").val(getScale());
 
  60       map.removeLayer(marker);
 
  61       map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
 
  62       locationFilter.disable();
 
  66     function toggleMarker() {
 
  67       if ($(this).is(":checked")) {
 
  68         marker.setLatLng(map.getCenter());
 
  70         map.options.scrollWheelZoom = map.options.doubleClickZoom = "center";
 
  72         map.removeLayer(marker);
 
  73         map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
 
  78     function toggleFilter() {
 
  79       if ($(this).is(":checked")) {
 
  80         locationFilter.setBounds(map.getBounds().pad(-0.2));
 
  81         locationFilter.enable();
 
  83         locationFilter.disable();
 
  89       marker.setLatLng(map.getCenter());
 
  93     function movedMarker() {
 
  94       if (map.hasLayer(marker)) {
 
  95         map.off("move", movedMap);
 
  96         map.on("moveend", updateOnce);
 
  97         map.panTo(marker.getLatLng());
 
 101     function updateOnce() {
 
 102       map.off("moveend", updateOnce);
 
 103       map.on("move", movedMap);
 
 107     function escapeHTML(string) {
 
 108       const htmlEscapes = {
 
 115       return string === null ? "" : String(string).replace(/[&<>"']/g, function (match) {
 
 116         return htmlEscapes[match];
 
 121       const layer = map.getMapBaseLayer();
 
 122       const canEmbed = Boolean(layer && layer.options.canEmbed);
 
 123       let bounds = map.getBounds();
 
 126         .prop("checked", map.hasLayer(marker));
 
 129         .prop("checked", locationFilter.isEnabled());
 
 133       $("#short_input").val(map.getShortUrl(marker));
 
 134       $("#long_input").val(map.getUrl(marker));
 
 135       $("#short_link").attr("href", map.getShortUrl(marker));
 
 136       $("#long_link").attr("href", map.getUrl(marker));
 
 138       const params = new URLSearchParams({
 
 139         bbox: bounds.toBBoxString(),
 
 140         layer: map.getMapBaseLayerId()
 
 143       if (map.hasLayer(marker)) {
 
 144         const latLng = marker.getLatLng().wrap();
 
 145         params.set("marker", latLng.lat + "," + latLng.lng);
 
 148       if (!canEmbed && $("#nav-embed").hasClass("active")) {
 
 149         bootstrap.Tab.getOrCreateInstance($("#long_link")).show();
 
 152         .toggleClass("disabled", !canEmbed)
 
 154         .tooltip(canEmbed ? "disable" : "enable");
 
 156       $("#embed_html").val(
 
 157         "<iframe width=\"425\" height=\"350\" src=\"" +
 
 158           escapeHTML(OSM.SERVER_PROTOCOL + "://" + OSM.SERVER_URL + "/export/embed.html?" + params) +
 
 159           "\" style=\"border: 1px solid black\"></iframe><br/>" +
 
 160           "<small><a href=\"" + escapeHTML(map.getUrl(marker)) + "\">" +
 
 161           escapeHTML(OSM.i18n.t("javascripts.share.view_larger_map")) + "</a></small>");
 
 166         .attr("href", map.getGeoUri(marker))
 
 167         .text(map.getGeoUri(marker));
 
 171       if (locationFilter.isEnabled()) {
 
 172         bounds = locationFilter.getBounds();
 
 175       let scale = $("#mapnik_scale").val();
 
 176       const size = L.bounds(L.CRS.EPSG3857.project(bounds.getSouthWest()),
 
 177                             L.CRS.EPSG3857.project(bounds.getNorthEast())).getSize(),
 
 178             maxScale = Math.floor(Math.sqrt(size.x * size.y / 0.3136));
 
 180       $("#mapnik_minlon").val(bounds.getWest());
 
 181       $("#mapnik_minlat").val(bounds.getSouth());
 
 182       $("#mapnik_maxlon").val(bounds.getEast());
 
 183       $("#mapnik_maxlat").val(bounds.getNorth());
 
 185       if (scale < maxScale) {
 
 186         scale = roundScale(maxScale);
 
 187         $("#mapnik_scale").val(scale);
 
 190       const mapWidth = Math.round(size.x / scale / 0.00028);
 
 191       const mapHeight = Math.round(size.y / scale / 0.00028);
 
 192       $("#mapnik_image_width").text(mapWidth);
 
 193       $("#mapnik_image_height").text(mapHeight);
 
 195       const canDownloadImage = Boolean(layer && layer.options.canDownloadImage);
 
 197       $("#mapnik_image_layer").text(canDownloadImage ? layer.options.name : "");
 
 198       $("#map_format").val(canDownloadImage ? layer.options.layerId : "");
 
 200       $("#map_zoom").val(map.getZoom());
 
 201       $("#mapnik_lon").val(map.getCenter().lng);
 
 202       $("#mapnik_lat").val(map.getCenter().lat);
 
 203       $("#map_width").val(mapWidth);
 
 204       $("#map_height").val(mapHeight);
 
 206       $("#export-image").toggle(canDownloadImage);
 
 207       $("#export-warning").toggle(!canDownloadImage);
 
 208       $("#mapnik_scale_row").toggle(canDownloadImage && layer.options.layerId === "mapnik");
 
 212       $(this).trigger("select");
 
 215     function getScale() {
 
 216       const bounds = map.getBounds(),
 
 217             centerLat = bounds.getCenter().lat,
 
 218             halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
 
 219             meters = halfWorldMeters * (bounds.getEast() - bounds.getWest()) / 180,
 
 220             pixelsPerMeter = map.getSize().x / meters,
 
 221             metersPerPixel = 1 / (92 * 39.3701);
 
 222       return Math.round(1 / (pixelsPerMeter * metersPerPixel));
 
 225     function roundScale(scale) {
 
 226       const precision = 5 * Math.pow(10, Math.floor(Math.LOG10E * Math.log(scale)) - 2);
 
 227       return precision * Math.ceil(scale / precision);
 
 231   control.onAddPane = function (map, button, $ui) {
 
 232     $("#content").addClass("overlay-right-sidebar");
 
 234     control.onContentLoaded = () => init(map, $ui);
 
 235     $ui.one("show", control.loadContent);