diff --git a/src/Resources/contao/templates/be_widget_leaflet_geocode.html5 b/src/Resources/contao/templates/be_widget_leaflet_geocode.html5 index 8e62c01..e3b0d6c 100644 --- a/src/Resources/contao/templates/be_widget_leaflet_geocode.html5 +++ b/src/Resources/contao/templates/be_widget_leaflet_geocode.html5 @@ -38,7 +38,9 @@ $GLOBALS['TL_JAVASCRIPT'][] = 'bundles/leafletgeocodewidget/js/geocode.widget.js searchPositionLabel: '', applyPositionLabel: '', modalTitle: 'label ?>'radius): ?>, - radius: 'ctrl_radius ?>' + radius: radius) ?>, + picker: LeafletGeocodeCirclePicker + }) } ); diff --git a/src/Resources/public/css/backend.css b/src/Resources/public/css/backend.css index 8da0ca1..12a099b 100644 --- a/src/Resources/public/css/backend.css +++ b/src/Resources/public/css/backend.css @@ -3,6 +3,12 @@ display: inline-block; } +.leaflet-geocode-map { + width: 100%; + height: 50vh; + min-height: 400px +} + .leaflet-submit-btn { margin-top: 10px; display: block; diff --git a/src/Resources/public/js/geocode.widget.js b/src/Resources/public/js/geocode.widget.js index b4f2f86..1f077eb 100644 --- a/src/Resources/public/js/geocode.widget.js +++ b/src/Resources/public/js/geocode.widget.js @@ -8,155 +8,96 @@ * @filesource */ -var LeafletGeocodeWidget = L.Class.extend({ - options: { - mapTemplate: '
', - modalWidth: 800, - modalTitle: 'Choose coordinates', - searchPositionLabel: 'Search', - applyPositionLabel: 'Apply', - radius: null, - mapOptions: { - maxZoom: 15, - minZoom: 2 - }, - bboxPadding: [0, 70] - }, - initialize: function (options) { +var LeafletGeocodeAbstractPicker = L.Class.extend({ + initialize: function (map, options) { L.Util.setOptions(this, options); - - this.element = $(this.options.id); - this.toggle = $(this.options.id + '_toggle'); - this.toggle.addEvent('click', this._showMap.bind(this)); - - if (this.options.radius) { - this.radius = $(this.options.radius); - } + this.map = map; }, - _showMap: function () { - var content, modal; - - // Create modal window. - content = L.Util.template(this.options.mapTemplate, this.options); - modal = this._createModal(); - - modal.show({'title': this.options.modalTitle, 'contents': content}); - - // Initialize map after showing modal so element exists. - this._createMap(modal); - }, - _createModal: function () { - return new SimpleModal({ - 'width': this.options.modalWidth, - 'hideFooter': true, - 'draggable': false, - 'overlayOpacity': .5, - 'onShow': function () { - document.body.setStyle('overflow', 'hidden'); - }, - 'onHide': function () { - document.body.setStyle('overflow', 'auto'); - } - }); - }, - _createMap: function (modal) { - var map = L.map('leaflet_geocode_widget_map_' + this.options.id, this.options.mapOptions).setView([0, 0], 2); - - L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' - }).addTo(map); - - var geoCoder = L.Control.geocoder({ - defaultMarkGeocode: false, - collapsed: false, - placeholder: this.options.searchPositionLabel - }).addTo(map); - - geoCoder.on('markgeocode', function (event) { - this._handleGeoCode.call(this, modal, map, event); - }.bind(this)); - - if (this.element.value) { - var value = this.element.value.split(/,/); - this._createMarker(value, map); - - if (this.radius) { - map.fitBounds(this._geocodeMarker.getBounds()); - } else { - map.setZoom(this.options.mapOptions.maxZoom); - map.panTo(value); - } - } - - return map; - }, - _handleGeoCode: function (modal, map, event) { - var container = document.createElement('div'); - var link = document.createElement('button'); - var result = event.geocode; - - link.set('class', 'leaflet-submit-btn'); - link.appendText(this.options.applyPositionLabel); - link.addEvent('click', function (e) { - e.stop(); - - this._updateInputs(result.center); - modal.hide(); - }.bind(this)); - - container.appendHTML(result.html || result.name); - container.appendChild(link); - - if (this._geocodeMarker) { - map.removeLayer(this._geocodeMarker); - } - - map.fitBounds(result.bbox, { padding: this.options.bboxPadding}); - map.panTo(result.center); - - this._createMarker(result.center, map); - - if (!this.radius) { - this._geocodeMarker.bindPopup(container, { - keepInView: true, - autoClose: false, - closeOnClick: false, - autoPanPaddingTopLeft: this.options.bboxPadding - }).openPopup(); - } - }, - _createMarker: function (position, map) { - if (this.radius) { - this._geocodeMarker = L.circle(position, { radius: this.radius.get('value') }); - this._geocodeMarker.addTo(map); - map.fitBounds(this._geocodeMarker.getBounds()); - - this._geocodeMarker.on('pm:markerdragend', function (event) { - var radius = Math.floor(this._geocodeMarker.getRadius()); - - this._updateInputs(event.target._latlng, radius); - this._geocodeMarker.pm._outerMarker.setTooltipContent(this._formatRadius(radius)); - map.fitBounds(this._geocodeMarker.getBounds()); - }.bind(this)); - this._geocodeMarker.pm.enable({dragable: false}); - - this._geocodeMarker.pm._outerMarker.bindTooltip( - this._formatRadius(this._geocodeMarker.getRadius()), - {permanent: true, direction: 'right', offset: [10, 0] } - ); + show: function (position) { + if (!this.marker) { + this._createMarker(position); } else { - this._geocodeMarker = L.marker(position, {draggable: true}).addTo(map); - this._geocodeMarker.on('dragend', function (event) { - this._updateInputs(event.target._latlng); - }.bind(this)); + this._updateCoordinates(position); } - }, - _updateInputs: function (coordinates, radius) { - this.element.set('value', coordinates.lat + ',' + coordinates.lng); - if (this.radius && typeof(radius) !== 'undefined') { - this.radius.set('value', radius); - } + this._panTo(position); + }, + _updateCoordinates: function (position) { + this.marker.setLatLng(position); + } +}); + +var LeafletGeocodeMarkerPicker = LeafletGeocodeAbstractPicker.extend({ + apply: function (coordinatesInput) { + var coordinates = this.marker + ? ( this.marker.getLatLng().lat + ',' + this.marker.getLatLng().lng) + : ''; + + coordinatesInput.set('value', coordinates); + }, + _panTo: function (position) { + this.map.setZoom(this.map.getMaxZoom()); + this.map.panTo(position); + }, + _createMarker: function (position) { + this.marker = L.marker(position, {draggable: true}).addTo(this.map); + this.marker.on('dragend', function () { + this.map.panTo(this.marker.getLatLng()); + }.bind(this)); + } +}); + +var LeafletGeocodeCirclePicker = LeafletGeocodeAbstractPicker.extend({ + apply: function (coordinatesInput, radiusInput) { + var coordinates = this.marker + ? ( this.marker.getLatLng().lat + ',' + this.marker.getLatLng().lng) + : ''; + + coordinatesInput.set('value', coordinates); + radiusInput.set('value', this.marker ? Math.round(this.marker.getRadius()) : ''); + }, + _panTo: function () { + this.map.fitBounds(this.marker.getBounds()); + }, + _createMarker: function (position) { + this.marker = L.circle(position, { radius: this.options.radius.default }); + this.marker.addTo(this.map); + + this.marker.on('pm:markerdragend', function () { + var radius = this.marker.getRadius(); + + if (this.options.radius.min > 0 && this.options.radius.min > radius) { + radius = this.options.radius.min; + } + + if (this.options.radius.max > 0 && this.options.radius.max < radius) { + radius = this.options.radius.max; + } + + if (radius != this.marker.getRadius()) { + this.marker.pm.disable(); + this.marker.setRadius(radius); + this._enableEditMode(); + } else { + this.marker.pm._outerMarker.setTooltipContent(this._formatRadius(radius)); + } + + this.map.fitBounds(this.marker.getBounds()); + }.bind(this)); + + this._enableEditMode(); + }, + _updateCoordinates: function (position) { + this.marker.pm.disable(); + this.marker.setLatLng(position); + this.marker.pm.enable(); + }, + _enableEditMode: function () { + this.marker.pm.enable(); + this.marker.pm._outerMarker.bindTooltip( + this._formatRadius(this.marker.getRadius()), + {permanent: true, direction: 'right', offset: [10, 0] } + ); }, _formatRadius: function (radius) { var unit = 'm'; @@ -171,3 +112,89 @@ var LeafletGeocodeWidget = L.Class.extend({ return radius.toString() + ' ' + unit; } }); + +var LeafletGeocodeWidget = L.Class.extend({ + options: { + mapTemplate: '
', + modalWidth: 800, + modalTitle: 'Choose coordinates', + searchPositionLabel: 'Search', + applyPositionLabel: 'Apply', + radius: null, + picker: LeafletGeocodeMarkerPicker, + map: { + maxZoom: 15, + minZoom: 2 + }, + bboxPadding: [0, 70] + }, + initialize: function (options) { + L.Util.setOptions(this, options); + + this.element = $(this.options.id); + this.toggle = $(this.options.id + '_toggle'); + this.toggle.addEvent('click', this._showMap.bind(this)); + + if (this.options.radius) { + this.radius = $(this.options.radius.element); + + if (this.radius.get('value').length > 0) { + this.options.radius.default = parseInt(this.radius.get('value')); + } + } + }, + _showMap: function () { + // Create modal window. + var content = L.Util.template(this.options.mapTemplate, this.options); + this.modal = this._createModal(); + + this.modal.show({title: this.options.modalTitle, contents: content}); + + // Initialize map after showing modal so element exists. + this._createMap(); + }, + _createModal: function () { + var modal = new SimpleModal({ + width: this.options.modalWidth, + hideFooter: false, + draggable: false, + overlayOpacity: .5, + btn_ok: Contao.lang.close, + onShow: function () { + document.body.setStyle('overflow', 'hidden'); + }, + onHide: function () { + document.body.setStyle('overflow', 'auto'); + } + }); + + modal.addButton(Contao.lang.apply, 'btn', function () { + this.picker.apply(this.element, this.radius); + modal.hide(); + }.bind(this)); + + return modal; + }, + _createMap: function () { + var map = L.map('leaflet_geocode_widget_map_' + this.options.id, this.options.map).setView([0, 0], 2); + this.picker = new this.options.picker(map, this.options); + + L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(map); + + var geoCoder = L.Control.geocoder({ + defaultMarkGeocode: false, + collapsed: false, + placeholder: this.options.searchPositionLabel + }).addTo(map); + + geoCoder.on('markgeocode', function (event) { + this.picker.show(event.geocode.center); + }.bind(this)); + + if (this.element.value) { + this.picker.show(L.latLng(this.element.value.split(/,/))); + } + } +}); diff --git a/src/Widget/GeocodeWidget.php b/src/Widget/GeocodeWidget.php index 06a0721..566d6b4 100644 --- a/src/Widget/GeocodeWidget.php +++ b/src/Widget/GeocodeWidget.php @@ -123,7 +123,7 @@ class GeocodeWidget extends \Widget 'attributes' => $this->getAttributes(), 'wizard' => $this->wizard, 'label' => $this->strLabel, - 'radius' => $this->radius + 'radius' => $this->buildRadiusOptions() ] ); @@ -132,4 +132,35 @@ class GeocodeWidget extends \Widget return $buffer; } + + /** + * Build the radius options. + * + * @return array|null + * + * @SuppressWarnings(PHPMD.Superglobals) + */ + private function buildRadiusOptions() + { + if (!$this->radius || !isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->radius])) { + return null; + } + + $options = [ + 'element' => 'ctrl_' . $this->radius, + 'min' => 0, + 'max' => 0, + 'defaultValue' => 0 + ]; + + if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->radius]['eval'])) { + $config = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->radius]['eval']; + + $options['min'] = isset($config['minval']) ? (int) $config['minval'] : 0; + $options['max'] = isset($config['maxval']) ? (int) $config['maxval'] : 0; + $options['defaultValue'] = isset($config['default']) ? (int) $config['default'] : 0; + } + + return $options; + } }