]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/leaflet.share.js
Merge remote-tracking branch 'upstream/pull/6159'
[rails.git] / app / assets / javascripts / leaflet.share.js
1 L.OSM.share = function (options) {
2   const control = L.OSM.sidebarPane(options, "share", "javascripts.share.title", "javascripts.share.title"),
3         marker = L.marker([0, 0], { draggable: true }),
4         locationFilter = new L.LocationFilter({
5           enableButton: false,
6           adjustButton: false
7         });
8
9   function init(map, $ui) {
10     // Link / Embed
11
12     $ui.find("#link_marker").on("change", toggleMarker);
13
14     $ui.find(".btn-group .btn")
15       .on("shown.bs.tab", () => {
16         $ui.find(".tab-pane.active [id]")
17           .trigger("select");
18       });
19
20     $ui.find(".share-tab [id]").on("click", select);
21
22     // Image
23
24     $ui.find("#mapnik_scale").on("change", update);
25
26     $ui.find("#image_filter").bind("change", toggleFilter);
27
28     const csrfInput = $ui.find("#csrf_export")[0];
29     [[csrfInput.name, csrfInput.value]] = Object.entries(OSM.csrf);
30
31     locationFilter
32       .on("change", update)
33       .addTo(map);
34
35     marker.on("dragend", movedMarker);
36     map.on("move", movedMap);
37     map.on("moveend baselayerchange overlayadd overlayremove", update);
38
39     $ui
40       .on("show", shown)
41       .on("hide", hidden);
42
43     update();
44
45     function shown() {
46       $("#mapnik_scale").val(getScale());
47       update();
48     }
49
50     function hidden() {
51       map.removeLayer(marker);
52       map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
53       locationFilter.disable();
54       update();
55     }
56
57     function toggleMarker() {
58       if ($(this).is(":checked")) {
59         marker.setLatLng(map.getCenter());
60         map.addLayer(marker);
61         map.options.scrollWheelZoom = map.options.doubleClickZoom = "center";
62       } else {
63         map.removeLayer(marker);
64         map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
65       }
66       update();
67     }
68
69     function toggleFilter() {
70       if ($(this).is(":checked")) {
71         locationFilter.setBounds(map.getBounds().pad(-0.2));
72         locationFilter.enable();
73       } else {
74         locationFilter.disable();
75       }
76       update();
77     }
78
79     function movedMap() {
80       marker.setLatLng(map.getCenter());
81       update();
82     }
83
84     function movedMarker() {
85       if (map.hasLayer(marker)) {
86         map.off("move", movedMap);
87         map.on("moveend", updateOnce);
88         map.panTo(marker.getLatLng());
89       }
90     }
91
92     function updateOnce() {
93       map.off("moveend", updateOnce);
94       map.on("move", movedMap);
95       update();
96     }
97
98     function escapeHTML(string) {
99       const htmlEscapes = {
100         "&": "&",
101         "<": "&lt;",
102         ">": "&gt;",
103         "\"": "&quot;",
104         "'": "&#x27;"
105       };
106       return string === null ? "" : String(string).replace(/[&<>"']/g, function (match) {
107         return htmlEscapes[match];
108       });
109     }
110
111     function update() {
112       const layer = map.getMapBaseLayer();
113       const canEmbed = Boolean(layer && layer.options.canEmbed);
114       let bounds = map.getBounds();
115
116       $("#link_marker")
117         .prop("checked", map.hasLayer(marker));
118
119       $("#image_filter")
120         .prop("checked", locationFilter.isEnabled());
121
122       // Link / Embed
123
124       $("#short_input").val(map.getShortUrl(marker));
125       $("#long_input").val(map.getUrl(marker));
126       $("#short_link").attr("href", map.getShortUrl(marker));
127       $("#long_link").attr("href", map.getUrl(marker));
128
129       const params = new URLSearchParams({
130         bbox: bounds.toBBoxString(),
131         layer: map.getMapBaseLayerId()
132       });
133
134       if (map.hasLayer(marker)) {
135         const latLng = marker.getLatLng().wrap();
136         params.set("marker", latLng.lat + "," + latLng.lng);
137       }
138
139       if (!canEmbed && $("#nav-embed").hasClass("active")) {
140         bootstrap.Tab.getOrCreateInstance($("#long_link")).show();
141       }
142       $("#embed_link")
143         .toggleClass("disabled", !canEmbed)
144         .parent()
145         .tooltip(canEmbed ? "disable" : "enable");
146
147       $("#embed_html").val(
148         "<iframe width=\"425\" height=\"350\" src=\"" +
149           escapeHTML(OSM.SERVER_PROTOCOL + "://" + OSM.SERVER_URL + "/export/embed.html?" + params) +
150           "\" style=\"border: 1px solid black\"></iframe><br/>" +
151           "<small><a href=\"" + escapeHTML(map.getUrl(marker)) + "\">" +
152           escapeHTML(OSM.i18n.t("javascripts.share.view_larger_map")) + "</a></small>");
153
154       // Geo URI
155
156       $("#geo_uri")
157         .attr("href", map.getGeoUri(marker))
158         .text(map.getGeoUri(marker));
159
160       // Image
161
162       if (locationFilter.isEnabled()) {
163         bounds = locationFilter.getBounds();
164       }
165
166       let scale = $("#mapnik_scale").val();
167       const size = L.bounds(L.CRS.EPSG3857.project(bounds.getSouthWest()),
168                             L.CRS.EPSG3857.project(bounds.getNorthEast())).getSize(),
169             maxScale = Math.floor(Math.sqrt(size.x * size.y / 0.3136));
170
171       $("#mapnik_minlon").val(bounds.getWest());
172       $("#mapnik_minlat").val(bounds.getSouth());
173       $("#mapnik_maxlon").val(bounds.getEast());
174       $("#mapnik_maxlat").val(bounds.getNorth());
175
176       if (scale < maxScale) {
177         scale = roundScale(maxScale);
178         $("#mapnik_scale").val(scale);
179       }
180
181       const mapWidth = Math.round(size.x / scale / 0.00028);
182       const mapHeight = Math.round(size.y / scale / 0.00028);
183       $("#mapnik_image_width").text(mapWidth);
184       $("#mapnik_image_height").text(mapHeight);
185
186       const canDownloadImage = Boolean(layer && layer.options.canDownloadImage);
187
188       $("#mapnik_image_layer").text(canDownloadImage ? layer.options.name : "");
189       $("#map_format").val(canDownloadImage ? layer.options.layerId : "");
190
191       $("#map_zoom").val(map.getZoom());
192       $("#mapnik_lon").val(map.getCenter().lng);
193       $("#mapnik_lat").val(map.getCenter().lat);
194       $("#map_width").val(mapWidth);
195       $("#map_height").val(mapHeight);
196
197       $("#export-image").toggle(canDownloadImage);
198       $("#export-warning").toggle(!canDownloadImage);
199       $("#mapnik_scale_row").toggle(canDownloadImage && layer.options.layerId === "mapnik");
200     }
201
202     function select() {
203       $(this).trigger("select");
204     }
205
206     function getScale() {
207       const bounds = map.getBounds(),
208             centerLat = bounds.getCenter().lat,
209             halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
210             meters = halfWorldMeters * (bounds.getEast() - bounds.getWest()) / 180,
211             pixelsPerMeter = map.getSize().x / meters,
212             metersPerPixel = 1 / (92 * 39.3701);
213       return Math.round(1 / (pixelsPerMeter * metersPerPixel));
214     }
215
216     function roundScale(scale) {
217       const precision = 5 * Math.pow(10, Math.floor(Math.LOG10E * Math.log(scale)) - 2);
218       return precision * Math.ceil(scale / precision);
219     }
220   }
221
222   control.onAddPane = function (map, button, $ui) {
223     $("#content").addClass("overlay-right-sidebar");
224
225     control.onContentLoaded = () => init(map, $ui);
226     $ui.one("show", control.loadContent);
227   };
228
229   return control;
230 };