Permalink
Browse files

Merge pull request #6 from fredj/flexible-popup

Flexible popup, patches from @ahocevar, @chrismayer and @fredj
  • Loading branch information...
fredj committed Dec 9, 2011
2 parents 625d65d + e50ec63 commit 2c43e3e14b3dab316de5d13ee1168800d7e592af
@@ -0,0 +1,29 @@
<html>
<head>
<title>GeoExt Popup Example With Dynamic Position</title>
<script type="text/javascript" src="http://extjs.cachefly.net/ext-3.4.0/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="http://extjs.cachefly.net/ext-3.4.0/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-3.4.0/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-3.4.0/examples/shared/examples.css" />
<link rel="stylesheet" type="text/css" href="../resources/css/geoext-all.css" />
<script src="http://www.openlayers.org/api/2.11/OpenLayers.js"></script>
<script type="text/javascript" src="../lib/GeoExt.js"></script>
<script type="text/javascript" src="popup-auto-position.js"></script>
<style type="text/css">
div#map {
width: 650px;
height: 400px;
position: relative;
}
</style>
</head>
<body>
<h1>Popup Example With Dynamic Position</h1>
<p>
Click on the points in the map panel to open a popup.
See that the popup will be positioned automatically top or below and right and left of its location.
The js is not minified so it is readable. See <a href="popup-auto-position.js">popup-auto-position.js</a>.
</p>
<div id="container"></div>
</body>
</html>
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2008-2011 The Open Source Geospatial Foundation
*
* Published under the BSD license.
* See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
* of the license.
*/
/** api: example[popup]
* Feature Popup
* -------------
* Display a popup with feature information, which is positioned automatically.
*/
var mapPanel, popup;
Ext.onReady(function() {
// create a vector layer, add features into it
var vectorLayer = new OpenLayers.Layer.Vector("vector");
vectorLayer.addFeatures([
new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(-75, 45)
), new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(+75, -45)
), new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(+75, +45)
), new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(-75, -45)
)]
);
// create select feature control
var selectCtrl = new OpenLayers.Control.SelectFeature(vectorLayer);
// define "createPopup" function
var bogusMarkup = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
function createPopup(feature) {
popup = new GeoExt.Popup({
title: 'My Popup',
location: feature,
width:200,
html: bogusMarkup,
maximizable: true,
collapsible: true,
anchorPosition: "auto"
});
// unselect feature when the popup
// is closed
popup.on({
close: function() {
if(OpenLayers.Util.indexOf(vectorLayer.selectedFeatures,
this.feature) > -1) {
selectCtrl.unselect(this.feature);
}
}
});
popup.show();
}
// create popup on "featureselected"
vectorLayer.events.on({
featureselected: function(e) {
createPopup(e.feature);
}
});
// create Ext window including a map panel
var mapwin = new Ext.Window({
layout: "fit",
title: "Map",
closeAction: "hide",
width: 650,
height: 356,
x: 50,
y: 100,
items: {
xtype: "gx_mappanel",
region: "center",
layers: [
new OpenLayers.Layer.WMS(
"OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{layers: 'basic'} ),
vectorLayer
]
}
});
mapwin.show();
mapPanel = mapwin.items.get(0);
mapPanel.map.addControl(selectCtrl);
selectCtrl.activate();
});
@@ -121,6 +121,20 @@ GeoExt.Popup = Ext.extend(Ext.Window, {
*/
ancCls: null,
/** api: config[anchorPosition]
* ``String`` Controls the anchor position for the popup. If set to
* ``auto``, the anchor will be positioned on the top or the bottom of
* the window, minimizing map movement. Supported values are ``bottom-left``,
* ``bottom-right``,``top-left``, ``top-right`` or ``auto``.
* Defaults to ``auto``.
*/
/** private: property[anchorPosition]
* ``String`` Position of the popup anchor (``bottom-left``, ``bottom-right``
* or ``top-left``, ``bottom-right``).
*/
anchorPosition: "auto",
/** private: method[initComponent]
* Initializes the popup.
*/
@@ -166,7 +180,6 @@ GeoExt.Popup = Ext.extend(Ext.Window, {
onRender: function(ct, position) {
GeoExt.Popup.superclass.onRender.call(this, ct, position);
this.ancCls = this.popupCls + "-anc";
//create anchor dom element.
this.createElement("anc", this.el.dom);
},
@@ -236,23 +249,42 @@ GeoExt.Popup = Ext.extend(Ext.Window, {
this.insideViewport = this.map.getExtent().containsLonLat(this.location);
if(this.insideViewport !== this.isVisible()) {
this.setVisible(this.insideViewport);
}
}
if(this.isVisible()) {
var centerPx = this.map.getViewPortPxFromLonLat(this.location);
var mapBox = Ext.fly(this.map.div).getBox();
//This works for positioning with the anchor on the bottom.
var anc = this.anc;
var dx = anc.getLeft(true) + anc.getWidth() / 2;
var dy = this.el.getHeight();
//Assuming for now that the map viewport takes up
//the entire area of the MapPanel
this.setPosition(centerPx.x + mapBox.x - dx, centerPx.y + mapBox.y - dy);
var locationPx = this.map.getPixelFromLonLat(this.location),
mapBox = Ext.fly(this.map.div).getBox(true),
top = locationPx.y + mapBox.y,
left = locationPx.x + mapBox.x,
elSize = this.el.getSize(),
ancSize = this.anc.getSize(),
ancPos = this.anchorPosition;
if (ancPos.indexOf("right") > -1 || locationPx.x > mapBox.width / 2) {
// right
this.anc.addClass("right");
var ancRight = this.el.getX(true) + elSize.width -
this.anc.getX(true) - ancSize.width;
left -= elSize.width - ancRight - ancSize.width / 2;
} else {
// left
this.anc.removeClass("right");
var ancLeft = this.anc.getLeft(true);
left -= ancLeft + ancSize.width / 2;
}
if (ancPos.indexOf("bottom") > -1 || locationPx.y > mapBox.height / 2) {
// bottom
this.anc.removeClass("top");
top -= elSize.height + ancSize.height;
} else {
// top
this.anc.addClass("top");
top += ancSize.height; // ok
}
this.setPosition(left, top);
}
},
@@ -282,7 +314,7 @@ GeoExt.Popup = Ext.extend(Ext.Window, {
* padding.
*/
panIntoView: function() {
var mapBox = Ext.fly(this.map.div).getBox();
var mapBox = Ext.fly(this.map.div).getBox(true);
//assumed viewport takes up whole body element of map panel
var popupPos = this.getPosition(true);
@@ -1,9 +1,7 @@
.gx-popup-anc {
background: transparent url(../images/gray/anchor.png) no-repeat 0 0;
position: relative;
top:-1px;
left:5px;
z-index:2;
height:16px;
width:31px;
background-image: url(../images/gray/anchor.png);
}
.gx-popup-anc.top {
background-image: url(../images/gray/anchor-top.png);
}
@@ -1,9 +1,7 @@
.gx-popup-anc {
background: transparent url(../images/slate/anchor.png) no-repeat 0 0;
position: relative;
top: -1px;
left: 5px;
z-index: 2;
height: 16px;
width: 31px;
background-image: url(../images/slate/anchor.png);
}
.gx-popup-anc.top {
background-image: url(../images/slate/anchor-top.png);
}
View
@@ -1,9 +1,18 @@
.gx-popup-anc {
background: transparent url(../images/default/anchor.png) no-repeat 0 0;
position: relative;
top:-1px;
left:5px;
z-index:2;
height:16px;
width:31px;
position: absolute;
left: 5px;
z-index: 2;
height: 16px;
width: 31px;
}
.gx-popup-anc.top {
background: transparent url(../images/default/anchor-top.png) no-repeat 0 0;
top: -16px; /* height(gx-popup-anc) */
}
.gx-popup-anc.right {
left: auto;
right: 5px;
}
View
Binary file not shown.
View
Binary file not shown.
View
Binary file not shown.
@@ -43,6 +43,59 @@
mapPanel: mapPanel
};
}
function setupMultiFeatureContext() {
var map = new OpenLayers.Map({panMethod: null}); // avoid tween panning for tests
var layer = new OpenLayers.Layer("test", {isBaseLayer: true});
map.addLayer(layer);
var features = [
new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-250, 130), {
expectedAutoPosition: "top-left",
map: map,
removeFeatures: function() {}
}), new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(+250, 130), {
expectedAutoPosition: "top-right",
map: map,
removeFeatures: function() {}
}), new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(+250, -130), {
expectedAutoPosition: "bottom-right",
map: map,
removeFeatures: function() {}
}), new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-250, -130), {
expectedAutoPosition: "bottom-left",
map: map,
removeFeatures: function() {}
})
];
var mapPanel = new GeoExt.MapPanel({
// panel options
id: "map-panel2",
title: "GeoExt MapPanel",
renderTo: "mappanel",
height: 400,
width: 600,
// map panel-specific options
map: map,
center: new OpenLayers.LonLat(5, 45),
zoom: 4
});
return {
features: features,
mapPanel: mapPanel,
map: mapPanel.map
};
}
function tearDownMultiFeature(context) {
Ext.each(context.features, function(feat) {
feat.destroy();
});
context.map.destroy();
context.mapPanel.destroy();
}
function tearDown(context) {
context.feature.destroy();
@@ -299,6 +352,43 @@
tearDown(context);
}
function test_Popup_autoPosition(t) {
t.plan(8);
var context = setupMultiFeatureContext();
var pop;
Ext.each(context.features, function(feat) {
pop = popup(feat, context.mapPanel, {anchorPosition: "auto"});
pop.show();
// test if the popups have the correct anchor position
t.eq(pop.anchorPosition, feat.attributes.expectedAutoPosition, "auto position calculated correctly for " + feat.attributes.expectedAutoPosition);
// test if the popups with anchor on top have the extra CSS class needed
if (/^top-/.test(feat.attributes.expectedAutoPosition)) {
t.ok(pop.el.child("div.top"), "features with anchor on top have the corresponding CSS class");
} else {
t.ok(!pop.el.child("div.top"),"features with anchor at the bottom do not have an extra CSS class");
}
pop.destroy();
});
tearDownMultiFeature(context);
}
function test_Popup_fixedPosition(t) {
t.plan(8);
var context = setupMultiFeatureContext();
Ext.each(context.features, function(feat) {
var pop = popup(feat, context.mapPanel, {anchorPosition: "bottom-left"});
pop.show();
t.eq(pop.anchorPosition, "bottom-left", "fixed position set correctly");
t.ok(!pop.el.child("div.top"), "feature with fixed position 'bottom-left' do not have an extra CSS class");
pop.destroy();
});
tearDownMultiFeature(context);
}
</script>
<body>
<div id="mappanel"></div>

0 comments on commit 2c43e3e

Please sign in to comment.