diff --git a/src/pretix/control/templates/pretixcontrol/event/fragment_geodata.html b/src/pretix/control/templates/pretixcontrol/event/fragment_geodata.html new file mode 100644 index 000000000..c36a4de90 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/event/fragment_geodata.html @@ -0,0 +1,33 @@ +{% load i18n %} +{% load bootstrap3 %} +{% load static %} + +
+ {% bootstrap_field form.location layout="control" %} + {% include "pretixcontrol/event/fragment_geodata_autoupdate.html" %} +
+ +
+ {% bootstrap_field form.geo_lat layout="inline" %} + {% if global_settings.opencagedata_apikey %} +

+ + {% trans "Geocoding data © OpenStreetMap" %} + +

+ {% endif %} +
+
+ {% bootstrap_field form.geo_lon layout="inline" %} +
+
+
+
+
diff --git a/src/pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html b/src/pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html new file mode 100644 index 000000000..55b11acfe --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html @@ -0,0 +1,8 @@ +{% load i18n %} + + + + + {% trans "Geo coordinates updated" %} + New geo coordinates. + diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 7bae6c27d..b02bfc53f 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -24,34 +24,7 @@ {% endif %} {% bootstrap_field form.date_from layout="control" %} {% bootstrap_field form.date_to layout="control" %} -
- {% bootstrap_field form.location layout="control" %} -
- -
- {% bootstrap_field form.geo_lat layout="inline" %} - {% if global_settings.opencagedata_apikey %} -

- - {% trans "Geocoding data © OpenStreetMap" %} - -

- {% endif %} -
-
- {% bootstrap_field form.geo_lon layout="inline" %} -
-
-
-
-
+ {% include "pretixcontrol/event/fragment_geodata.html" %} {% bootstrap_field form.date_admission layout="control" %} {% bootstrap_field form.currency layout="control" %} {% bootstrap_field sform.contact_mail layout="control" %} diff --git a/src/pretix/control/templates/pretixcontrol/events/create_basics.html b/src/pretix/control/templates/pretixcontrol/events/create_basics.html index 4dcd3859f..be0933345 100644 --- a/src/pretix/control/templates/pretixcontrol/events/create_basics.html +++ b/src/pretix/control/templates/pretixcontrol/events/create_basics.html @@ -39,30 +39,7 @@ {% if form.date_to %} {% bootstrap_field form.date_to layout="control" %} {% endif %} -
- {% bootstrap_field form.location layout="control" %} -
- -
- {% bootstrap_field form.geo_lat layout="inline" %} - {% if global_settings.opencagedata_apikey %} -

- - {% trans "Geocoding data © OpenStreetMap" %} - -

- {% endif %} -
-
- {% bootstrap_field form.geo_lon layout="inline" %} -
-
-
-
-
+ {% include "pretixcontrol/event/fragment_geodata.html" %} {% bootstrap_field form.currency layout="control" %} {% bootstrap_field form.tax_rate addon_after="%" layout="control" %} diff --git a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html index 67abffe44..ee43c58f6 100644 --- a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html +++ b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html @@ -384,30 +384,7 @@ {% trans "General information" %} {% bootstrap_field form.name layout="control" %} {% bootstrap_field form.active layout="control" %} -
- {% bootstrap_field form.location layout="control" %} -
- -
- {% bootstrap_field form.geo_lat layout="inline" %} - {% if global_settings.opencagedata_apikey %} -

- - {% trans "Geocoding data © OpenStreetMap" %} - -

- {% endif %} -
-
- {% bootstrap_field form.geo_lon layout="inline" %} -
-
-
-
-
+ {% include "pretixcontrol/event/fragment_geodata.html" %} {% bootstrap_field form.frontpage_text layout="control" %} {% bootstrap_field form.is_public layout="control" %} {% if meta_forms %} diff --git a/src/pretix/control/templates/pretixcontrol/subevents/bulk_edit.html b/src/pretix/control/templates/pretixcontrol/subevents/bulk_edit.html index 292a4ba91..b1c10d9bb 100644 --- a/src/pretix/control/templates/pretixcontrol/subevents/bulk_edit.html +++ b/src/pretix/control/templates/pretixcontrol/subevents/bulk_edit.html @@ -32,6 +32,7 @@ {% bootstrap_field form.active layout="bulkedit" %}
{% bootstrap_field form.location layout="bulkedit" %} + {% include "pretixcontrol/event/fragment_geodata_autoupdate.html" %}
- {% bootstrap_field form.location layout="control" %} -
- -
- {% bootstrap_field form.geo_lat layout="inline" %} - {% if global_settings.opencagedata_apikey %} -

- - {% trans "Geocoding data © OpenStreetMap" %} - -

- {% endif %} -
-
- {% bootstrap_field form.geo_lon layout="inline" %} -
-
-
-
-
+ {% include "pretixcontrol/event/fragment_geodata.html" %} {% bootstrap_field form.date_admission layout="control" %} {% bootstrap_field form.frontpage_text layout="control" %} {% bootstrap_field form.is_public layout="control" %} diff --git a/src/pretix/static/pretixcontrol/js/ui/geo.js b/src/pretix/static/pretixcontrol/js/ui/geo.js index 1dc5eddf2..3864c96bb 100644 --- a/src/pretix/static/pretixcontrol/js/ui/geo.js +++ b/src/pretix/static/pretixcontrol/js/ui/geo.js @@ -1,128 +1,160 @@ /*globals $*/ $(function () { - var j = 0; - $(".geodata-section").each(function () { - // Geocoding - var $sec = $(this); - var $inp = $(this).find("textarea[lang=en], input[lang=en]").first(); - if ($inp.length === 0) { - $inp = $(this).find("textarea, input").first(); + function cleanup(l) { + return $.trim(l.replace(/\n/g, ", ")); } - var timer, timer2; - var touched = $sec.find("input[name$=geo_lat]").val() !== ""; + $(".geodata-section").each(function () { + // Geocoding + // detach notifications and append them to first label (should be from location) + var $notifications = $(".geodata-autoupdate", this).detach().appendTo($("label", this).first()); + var $lat = $("input[name$=geo_lat]", this).first(); + var $lon = $("input[name$=geo_lon]", this).first(); + var lat; + var lon; + var $updateButton = $("[data-action=update]", this); + var $location = $("textarea[lang=en], input[lang=en]", this).first(); + if (!$location.length) $location = $("textarea, input[type=text]", this).first(); - function load() { - window.clearTimeout(timer); - var q = $.trim($inp.val().replace(/\n/g, ", ")); - if ((touched && $sec.find("input[name$=geo_lat]").val() !== "") || $.trim(q) === "") { - return; - } - $sec.find(".col-md-1").html(""); - $.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) { - var q2 = $.trim($inp.val().replace(/\n/g, ", ")); - if (q2 !== q) { - return; // lost race + if (!$lat.length || !$lon.length || !$location.length) { + return; } - if (res.results) { - $sec.find("input[name$=geo_lat]").val(res.results[0].lat); - $sec.find("input[name$=geo_lon]").val(res.results[0].lon); - center(13); + + var debounceLoad, debounceLatLonChange, delayUpdateDismissal; + var touched = $lat.val() !== ""; + var xhr; + var lastLocation = cleanup($location.val()); + + function load() { + window.clearTimeout(debounceLoad); + if (xhr) { + xhr.abort(); + xhr = null; + } + + var q = cleanup($location.val()); + if (q === "" || q === lastLocation) return; + + lastLocation = q; + $notifications.attr("data-notify", "loading"); + + xhr = $.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) { + if (!res.results || !res.results.length) { + $notifications.attr("data-notify", "error"); + return; + } + + lat = res.results[0].lat; + lon = res.results[0].lon; + if (touched) { + $notifications.attr("data-notify", "confirm"); + } + else { + $notifications.attr("data-notify", ""); + $lat.val(lat); + $lon.val(lon); + center(13); + } + }) } - $sec.find(".col-md-1").html(""); - }) - } - $sec.find("input[name$=geo_lat], input[name$=geo_lon]").change(function () { - touched = $sec.find("input[name$=geo_lat]").val() !== ""; - center(13); - }).keyup(function () { - if (timer2) { - window.clearTimeout(timer2); - } - timer2 = window.setTimeout(center, 300); - }); + $lat.add($lon).change(function () { + if (this.value !== "") touched = true; + center(13); + }).keyup(function () { + window.clearTimeout(debounceLatLonChange); + debounceLatLonChange = window.setTimeout(center, 300); + }); - $inp.change(load); - $inp.keyup(function () { - if (timer) { - window.clearTimeout(timer); - } - timer = window.setTimeout(load, 1000); - }); + $location.change(load); + $location.keyup(function () { + window.clearTimeout(debounceLoad); + debounceLoad = window.setTimeout(load, 1000); + if ($notifications.attr("data-notify") == "confirm" && lastLocation !== cleanup(this.value)) $notifications.attr("data-notify", ""); + }); - // Map - var $grp = $sec.find(".geodata-group"); - var tiles = $grp.attr("data-tiles"); - var attrib = $grp.attr("data-attrib"); - if (tiles) { - var $map = $("
"); - $grp.append($("
").addClass("col-md-9 col-md-offset-3").append($map)); - var map = L.map($map.get(0)); - L.tileLayer(tiles, { - attribution: attrib, - maxZoom: 18, - }).addTo(map); - var $lat = $sec.find("input[name$=geo_lat]"); - var $lon = $sec.find("input[name$=geo_lon]"); + $updateButton.click(function() { + $lat.val(lat); + $lon.val(lon).trigger("change");// change-event is needed by bulk-edit + touched = false; + center(13); + $notifications.attr("data-notify", "updated"); + delayUpdateDismissal = window.setTimeout(function() { + if ($notifications.attr("data-notify") == "updated") $notifications.attr("data-notify", ""); + }, 2500); + }); - function getpoint() { - if ($lat.val() !== "" && $lon.val() !== "") { - var p = [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))]; - // Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently - if (p[0] < -90) p[0] = -90 - if (p[0] > 90) p[0] = 90 - if (p[1] < -180) p[1] = -180 - if (p[1] > 180) p[1] = 180 - return p + // Map + var $grp = $(".geodata-group", this); + var tiles = $grp.attr("data-tiles"); + var attrib = $grp.attr("data-attrib"); + if (tiles) { + var $map = $("
"); + $grp.append($("
").addClass("col-md-9 col-md-offset-3").append($map)); + var map = L.map($map.get(0)); + L.tileLayer(tiles, { + attribution: attrib, + maxZoom: 18, + }).addTo(map); + + function getpoint() { + if ($lat.val() !== "" && $lon.val() !== "") { + var p = [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))]; + // Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently + if (p[0] < -90) p[0] = -90 + if (p[0] > 90) p[0] = 90 + if (p[1] < -180) p[1] = -180 + if (p[1] > 180) p[1] = 180 + return p + } else { + return [0.0, 0.0]; + } + } + + var marker = L.marker(getpoint(), { + draggable: 'true', + icon: L.icon({ + iconUrl: $grp.attr("data-icon"), + shadowUrl: $grp.attr("data-shadow"), + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + shadowSize: [41, 41] + }) + }); + marker.addTo(map); + marker.on("dragend", function (event) { + var position = marker.getLatLng(); + marker.setLatLng(position, { + draggable: 'true' + }).bindPopup(position).update(); + $lat.val(position.lat.toFixed(7)); + $lon.val(position.lng.toFixed(7)); + touched = true; + center(null); + }); + + function center(zoom) { + if ($lat.val() !== "" && $lon.val() !== "") { + if (zoom) { + map.setView(getpoint(), zoom); + } else { + map.panTo(getpoint()); + } + marker.setLatLng(getpoint(), { + draggable: 'true' + }).bindPopup(getpoint()).update(); + } else { + map.fitWorld(); + } + } + + center(13); } else { - return [0.0, 0.0]; + function center(zoom) { + } } - } - var marker = L.marker(getpoint(), { - draggable: 'true', - icon: L.icon({ - iconUrl: $grp.attr("data-icon"), - shadowUrl: $grp.attr("data-shadow"), - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - tooltipAnchor: [16, -28], - shadowSize: [41, 41] - }) - }); - marker.addTo(map); - marker.on("dragend", function (event) { - var position = marker.getLatLng(); - marker.setLatLng(position, { - draggable: 'true' - }).bindPopup(position).update(); - $lat.val(position.lat.toFixed(7)); - $lon.val(position.lng.toFixed(7)); - center(null); - }); - - function center(zoom) { - if ($lat.val() !== "" && $lon.val() !== "") { - if (zoom) { - map.setView(getpoint(), zoom); - } else { - map.panTo(getpoint()); - } - marker.setLatLng(getpoint(), { - draggable: 'true' - }).bindPopup(getpoint()).update(); - } else { - map.fitWorld(); - } - } - - center(13); - } else { - function center(zoom) { - } - } - - }); + }); }); diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index d79fe15a7..763fc7a8c 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -241,6 +241,23 @@ div.mail-preview { input[type=number].short { width: 80px !important; } + +.geodata-autoupdate { + display: block; +} +.geodata-autoupdate .btn-link { + padding: 0; +} +.geodata-autoupdate [data-notification] { + display: none; +} +[data-notify=error] [data-notification=error], +[data-notify=loading] [data-notification=loading], +[data-notify=updated] [data-notification=updated], +[data-notify=confirm] [data-notification=confirm] { + display: block; +} + .geodata-group { .form-group { margin: 0 0 10px 0;