]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/leaflet.share.js
1dcf6ee65ab4669baa6f6ae43d5355adca7405c3
[rails.git] / app / assets / javascripts / leaflet.share.js
1 L.OSM.share = function (options) {
2   var 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   control.onAddPane = function (map, button, $ui) {
10     // Link / Embed
11
12     var $linkSection = $("<div>")
13       .attr("class", "section share-link")
14       .appendTo($ui);
15
16     $("<h4>")
17       .text(I18n.t("javascripts.share.link"))
18       .appendTo($linkSection);
19
20     var $form = $("<form>")
21       .appendTo($linkSection);
22
23     $("<div>")
24       .attr("class", "form-check mb-3")
25       .appendTo($form)
26       .append($("<label>")
27         .attr("for", "link_marker")
28         .attr("class", "form-check-label")
29         .text(I18n.t("javascripts.share.include_marker")))
30       .append($("<input>")
31         .attr("id", "link_marker")
32         .attr("type", "checkbox")
33         .attr("class", "form-check-input")
34         .bind("change", toggleMarker));
35
36     $("<div class='btn-group btn-group-sm mb-2'>")
37       .appendTo($form)
38       .append($("<a class='btn btn-primary'>")
39         .addClass("active")
40         .attr("for", "long_input")
41         .attr("id", "long_link")
42         .text(I18n.t("javascripts.share.long_link")))
43       .append($("<a class='btn btn-primary'>")
44         .attr("for", "short_input")
45         .attr("id", "short_link")
46         .text(I18n.t("javascripts.share.short_link")))
47       .append($("<a class='btn btn-primary'>")
48         .attr("for", "embed_html")
49         .attr("id", "embed_link")
50         .attr("href", "#")
51         .text(I18n.t("javascripts.share.embed")))
52       .on("click", "a", function (e) {
53         e.preventDefault();
54         var id = "#" + $(this).attr("for");
55         $(this).siblings("a")
56           .removeClass("active");
57         $(this).addClass("active");
58         $linkSection.find(".share-tab")
59           .hide();
60         $linkSection.find(".share-tab:has(" + id + ")")
61           .show()
62           .find("input, textarea")
63           .select();
64       });
65
66     $("<div>")
67       .attr("class", "share-tab")
68       .appendTo($form)
69       .append($("<input>")
70         .attr("id", "long_input")
71         .attr("type", "text")
72         .attr("class", "form-control form-control-sm font-monospace")
73         .attr("readonly", true)
74         .on("click", select));
75
76     $("<div>")
77       .attr("class", "share-tab")
78       .hide()
79       .appendTo($form)
80       .append($("<input>")
81         .attr("id", "short_input")
82         .attr("type", "text")
83         .attr("class", "form-control form-control-sm font-monospace")
84         .attr("readonly", true)
85         .on("click", select));
86
87     $("<div>")
88       .attr("class", "share-tab")
89       .hide()
90       .appendTo($form)
91       .append(
92         $("<textarea>")
93           .attr("id", "embed_html")
94           .attr("class", "form-control form-control-sm font-monospace")
95           .attr("readonly", true)
96           .on("click", select))
97       .append(
98         $("<p>")
99           .attr("class", "text-muted")
100           .text(I18n.t("javascripts.share.paste_html")));
101
102     // Geo URI
103
104     var $geoUriSection = $("<div>")
105       .attr("class", "section share-geo-uri")
106       .appendTo($ui);
107
108     $("<h4>")
109       .text(I18n.t("javascripts.share.geo_uri"))
110       .appendTo($geoUriSection);
111
112     $("<div>")
113       .appendTo($geoUriSection)
114       .append($("<a>")
115         .attr("id", "geo_uri"));
116
117     // Image
118
119     var $imageSection = $("<div>")
120       .attr("class", "section share-image")
121       .appendTo($ui);
122
123     $("<h4>")
124       .text(I18n.t("javascripts.share.image"))
125       .appendTo($imageSection);
126
127     $("<div>")
128       .attr("id", "export-warning")
129       .attr("class", "text-muted")
130       .text(I18n.t("javascripts.share.only_standard_layer"))
131       .appendTo($imageSection);
132
133     $form = $("<form>")
134       .attr("id", "export-image")
135       .attr("action", "/export/finish")
136       .attr("method", "post")
137       .appendTo($imageSection);
138
139     $("<div>")
140       .appendTo($form)
141       .attr("class", "row mb-3")
142       .append($("<label>")
143         .attr("for", "mapnik_format")
144         .attr("class", "col-auto col-form-label")
145         .text(I18n.t("javascripts.share.format")))
146       .append($("<div>")
147         .attr("class", "col-auto")
148         .append($("<select>")
149           .attr("name", "mapnik_format")
150           .attr("id", "mapnik_format")
151           .attr("class", "form-select w-auto")
152           .append($("<option>").val("png").text("PNG").prop("selected", true))
153           .append($("<option>").val("jpeg").text("JPEG"))
154           .append($("<option>").val("svg").text("SVG"))
155           .append($("<option>").val("pdf").text("PDF"))));
156
157     $("<div>")
158       .appendTo($form)
159       .attr("class", "row mb-3")
160       .append($("<label>")
161         .attr("for", "mapnik_scale")
162         .attr("class", "col-auto col-form-label")
163         .text(I18n.t("javascripts.share.scale")))
164       .append($("<div>")
165         .attr("class", "col-auto")
166         .append($("<div>")
167           .attr("class", "input-group flex-nowrap")
168           .append($("<span>")
169             .attr("class", "input-group-text")
170             .text("1 : "))
171           .append($("<input>")
172             .attr("name", "mapnik_scale")
173             .attr("id", "mapnik_scale")
174             .attr("type", "text")
175             .attr("class", "form-control")
176             .on("change", update))));
177
178     $("<div>")
179       .attr("class", "row mb-3")
180       .appendTo($form)
181       .append($("<div>")
182         .attr("class", "col-auto")
183         .append($("<div>")
184           .attr("class", "form-check")
185           .append($("<label>")
186             .attr("for", "image_filter")
187             .attr("class", "form-check-label")
188             .text(I18n.t("javascripts.share.custom_dimensions")))
189           .append($("<input>")
190             .attr("id", "image_filter")
191             .attr("type", "checkbox")
192             .attr("class", "form-check-input")
193             .bind("change", toggleFilter))));
194
195     ["minlon", "minlat", "maxlon", "maxlat"].forEach(function (name) {
196       $("<input>")
197         .attr("id", "mapnik_" + name)
198         .attr("name", name)
199         .attr("type", "hidden")
200         .appendTo($form);
201     });
202
203     $("<input>")
204       .attr("name", "format")
205       .attr("value", "mapnik")
206       .attr("type", "hidden")
207       .appendTo($form);
208
209     var csrf_param = $("meta[name=csrf-param]").attr("content"),
210         csrf_token = $("meta[name=csrf-token]").attr("content");
211
212     $("<input>")
213       .attr("name", csrf_param)
214       .attr("value", csrf_token)
215       .attr("type", "hidden")
216       .appendTo($form);
217
218     var args = {
219       width: "<span id=\"mapnik_image_width\"></span>",
220       height: "<span id=\"mapnik_image_height\"></span>"
221     };
222
223     $("<p>")
224       .attr("class", "text-muted")
225       .html(I18n.t("javascripts.share.image_dimensions", args))
226       .appendTo($form);
227
228     $("<input>")
229       .attr("type", "submit")
230       .attr("class", "btn btn-primary")
231       .attr("value", I18n.t("javascripts.share.download"))
232       .appendTo($form);
233
234     locationFilter
235       .on("change", update)
236       .addTo(map);
237
238     marker.on("dragend", movedMarker);
239     map.on("move", movedMap);
240     map.on("moveend layeradd layerremove", update);
241
242     $ui
243       .on("show", shown)
244       .on("hide", hidden);
245
246     function shown() {
247       $("#mapnik_scale").val(getScale());
248       update();
249     }
250
251     function hidden() {
252       map.removeLayer(marker);
253       map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
254       locationFilter.disable();
255       update();
256     }
257
258     function toggleMarker() {
259       if ($(this).is(":checked")) {
260         marker.setLatLng(map.getCenter());
261         map.addLayer(marker);
262         map.options.scrollWheelZoom = map.options.doubleClickZoom = "center";
263       } else {
264         map.removeLayer(marker);
265         map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
266       }
267       update();
268     }
269
270     function toggleFilter() {
271       if ($(this).is(":checked")) {
272         locationFilter.setBounds(map.getBounds().pad(-0.2));
273         locationFilter.enable();
274       } else {
275         locationFilter.disable();
276       }
277       update();
278     }
279
280     function movedMap() {
281       marker.setLatLng(map.getCenter());
282       update();
283     }
284
285     function movedMarker() {
286       if (map.hasLayer(marker)) {
287         map.off("move", movedMap);
288         map.on("moveend", updateOnce);
289         map.panTo(marker.getLatLng());
290       }
291     }
292
293     function updateOnce() {
294       map.off("moveend", updateOnce);
295       map.on("move", movedMap);
296       update();
297     }
298
299     function escapeHTML(string) {
300       var htmlEscapes = {
301         "&": "&amp;",
302         "<": "&lt;",
303         ">": "&gt;",
304         "\"": "&quot;",
305         "'": "&#x27;"
306       };
307       return string === null ? "" : String(string).replace(/[&<>"']/g, function (match) {
308         return htmlEscapes[match];
309       });
310     }
311
312     function update() {
313       var canEmbed = map.getMapBaseLayerId() !== "tracestracktopo";
314       var bounds = map.getBounds();
315
316       $("#link_marker")
317         .prop("checked", map.hasLayer(marker));
318
319       $("#image_filter")
320         .prop("checked", locationFilter.isEnabled());
321
322       // Link / Embed
323
324       $("#short_input").val(map.getShortUrl(marker));
325       $("#long_input").val(map.getUrl(marker));
326       $("#short_link").attr("href", map.getShortUrl(marker));
327       $("#long_link").attr("href", map.getUrl(marker));
328
329       var params = {
330         bbox: bounds.toBBoxString(),
331         layer: map.getMapBaseLayerId()
332       };
333
334       if (map.hasLayer(marker)) {
335         var latLng = marker.getLatLng().wrap();
336         params.marker = latLng.lat + "," + latLng.lng;
337       }
338
339       $("#embed_link").toggleClass("disabled", !canEmbed);
340       if (!canEmbed && $("#embed_link").hasClass("active")) {
341         $("#long_link").click();
342       }
343
344       $("#embed_html").val(
345         "<iframe width=\"425\" height=\"350\" src=\"" +
346           escapeHTML(OSM.SERVER_PROTOCOL + "://" + OSM.SERVER_URL + "/export/embed.html?" + $.param(params)) +
347           "\" style=\"border: 1px solid black\"></iframe><br/>" +
348           "<small><a href=\"" + escapeHTML(map.getUrl(marker)) + "\">" +
349           escapeHTML(I18n.t("javascripts.share.view_larger_map")) + "</a></small>");
350
351       // Geo URI
352
353       $("#geo_uri")
354         .attr("href", map.getGeoUri(marker))
355         .html(map.getGeoUri(marker));
356
357       // Image
358
359       if (locationFilter.isEnabled()) {
360         bounds = locationFilter.getBounds();
361       }
362
363       var scale = $("#mapnik_scale").val(),
364           size = L.bounds(L.CRS.EPSG3857.project(bounds.getSouthWest()),
365                           L.CRS.EPSG3857.project(bounds.getNorthEast())).getSize(),
366           maxScale = Math.floor(Math.sqrt(size.x * size.y / 0.3136));
367
368       $("#mapnik_minlon").val(bounds.getWest());
369       $("#mapnik_minlat").val(bounds.getSouth());
370       $("#mapnik_maxlon").val(bounds.getEast());
371       $("#mapnik_maxlat").val(bounds.getNorth());
372
373       if (scale < maxScale) {
374         scale = roundScale(maxScale);
375         $("#mapnik_scale").val(scale);
376       }
377
378       $("#mapnik_image_width").text(Math.round(size.x / scale / 0.00028));
379       $("#mapnik_image_height").text(Math.round(size.y / scale / 0.00028));
380
381       if (map.getMapBaseLayerId() === "mapnik") {
382         $("#export-image").show();
383         $("#export-warning").hide();
384       } else {
385         $("#export-image").hide();
386         $("#export-warning").show();
387       }
388     }
389
390     function select() {
391       $(this).select();
392     }
393
394     function getScale() {
395       var bounds = map.getBounds(),
396           centerLat = bounds.getCenter().lat,
397           halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180),
398           meters = halfWorldMeters * (bounds.getEast() - bounds.getWest()) / 180,
399           pixelsPerMeter = map.getSize().x / meters,
400           metersPerPixel = 1 / (92 * 39.3701);
401       return Math.round(1 / (pixelsPerMeter * metersPerPixel));
402     }
403
404     function roundScale(scale) {
405       var precision = 5 * Math.pow(10, Math.floor(Math.LOG10E * Math.log(scale)) - 2);
406       return precision * Math.ceil(scale / precision);
407     }
408   };
409
410   return control;
411 };