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