/* Copyright (c) 2015-present The Open Source Geospatial Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* An GeoExt.component.Popup can be used to displays a popup over the map.
*
* Example (hover over anything in the map to see a popup):
*
* @example preview
* var olMap = new ol.Map({
* layers: [
* new ol.layer.Tile({
* source: new ol.source.OSM()
* })
* ],
* view: new ol.View({
* center: ol.proj.fromLonLat([-8.751278, 40.611368]),
* zoom: 12
* })
* })
* var mapComponent = Ext.create('GeoExt.component.Map', {
* map: olMap,
* pointerRest: true,
* pointerRestInterval: 750,
* pointerRestPixelTolerance: 5,
* renderTo: Ext.getBody(),
* height: 200
* });
* var popup = Ext.create('GeoExt.component.Popup', {
* map: olMap,
* width: 140
* });
* mapComponent.on('pointerrest', function(evt) {
* var coord = evt.coordinate;
* var transformed = ol.proj.toLonLat(coord);
* var hdms = ol.coordinate.toStringHDMS(transformed);
* hdms = hdms.replace(/([NS])/, '$1<br>');
* popup.setHtml('<p><strong>Pointer rested on</strong>' +
* '<br /><code>' + hdms + '</code></p>');
* popup.position(coord);
* popup.show();
* });
* mapComponent.on('pointerrestout', popup.hide, popup);
*
* The above example loads the provided CSS-file `resources/css/gx-popup.css`
* and also uses the following inline CSS:
*
* .gx-popup p {
* padding: 5px 5px 0 5px;
* border-radius: 7px;
* background-color: rgba(255,255,255,0.85);
* border: 3px solid white;
* margin: 0;
* text-align: center;
* }
*
* @class GeoExt.component.Popup
*/
Ext.define('GeoExt.component.Popup', {
requires: [],
extend: 'Ext.Component',
alias: ['widget.gx_popup', 'widget.gx_component_popup'],
config: {
/**
*
*/
overlay: null,
/**
*
*/
map: null,
},
/**
* @private
*/
overlayElement: null,
/**
* @private
*/
overlayElementCreated: false,
/**
* The CSS class of the popup.
*/
cls: 'gx-popup',
/**
* Construct a popup.
*
* @param {Object} config The configuration object.
*/
constructor: function (config) {
const me = this;
const cfg = config || {};
let overlayElement;
if (!Ext.isDefined(cfg.map)) {
Ext.Error.raise("Required configuration 'map' not passed");
}
if (Ext.isDefined(cfg.renderTo)) {
// use the passed element/string
overlayElement = Ext.get(cfg.renderTo).dom;
} else {
// create a div we can reference in
// order to bind this div to an ol overlay
overlayElement = Ext.dom.Helper.append(Ext.getBody(), '<div>');
// keep track of the fact that we created the element, we should
// also clean it up once we are being destroyed.
me.overlayElementCreated = true;
}
cfg.renderTo = overlayElement;
me.overlayElement = overlayElement;
me.callParent([cfg]);
},
/**
* @private
*/
initComponent: function () {
const me = this;
me.updateLayout = me.updateLayout.bind(me);
me.on({
afterrender: me.setOverlayElement,
beforedestroy: me.onBeforeDestroy,
scope: me,
});
me.callParent();
me.setupOverlay();
},
/**
* @private
*/
setupOverlay: function () {
const me = this;
const overlay = new ol.Overlay({
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
me.getMap().addOverlay(overlay);
// fix layout of popup when its position changes
overlay.on('change:position', me.updateLayout);
// make accessible as member
me.setOverlay(overlay);
},
/**
* @private
*/
setOverlayElement: function () {
// bind our containing div to the ol overlay
this.getOverlay().set('element', this.overlayElement);
},
/**
* (Re-)Positions the popup to the given coordinates.
*
* @param {ol.Coordinate} coordinate The new position of the popup as
* `ol.Coordinate`.
*/
position: function (coordinate) {
const me = this;
me.getOverlay().setPosition(coordinate);
},
/**
* @private
*/
onBeforeDestroy: function () {
const me = this;
if (me.overlayElementCreated && me.overlayElement) {
const parent = me.overlayElement.parentNode;
parent.removeChild(me.overlayElement);
}
me.getOverlay().un('change:position', me.doLayout);
},
});