Adding support for new generation 'framed' popups. This patch includes numerous improvements to the main popups, including the addition of autoSizing, panIntoView, and full support for overflow:auto of the contents div. Thanks go out to the CloudAmber folks, to Pierre in la belle France, to the guys at TOPP and of course, to senior cr5 for his patience and help in the last and final stretch. this is a huge improvement in the popup arena and couldn't have been done without the broad help of everyone out there in the community. Thank you everyone for making this possible. Big step for OpenLayers. (Closes #926)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@6718 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
euzuro
2008-03-31 04:43:36 +00:00
parent 0fa07edb04
commit 463dc45fbb
22 changed files with 4464 additions and 165 deletions

View File

@@ -53,27 +53,71 @@ OpenLayers.Popup.Anchored =
offset: new OpenLayers.Pixel(0,0)};
},
/**
* Method: draw
/**
* APIMethod: destroy
*/
destroy: function() {
this.anchor = null;
this.relativePosition = null;
OpenLayers.Popup.prototype.destroy.apply(this, arguments);
},
/**
* APIMethod: show
* Overridden from Popup since user might hide popup and then show() it
* in a new location (meaning we might want to update the relative
* position on the show)
*/
show: function() {
this.updatePosition();
OpenLayers.Popup.prototype.show.apply(this, arguments);
},
/**
* Method: moveTo
* Since the popup is moving to a new px, it might need also to be moved
* relative to where the marker is. We first calculate the new
* relativePosition, and then we calculate the new px where we will
* put the popup, based on the new relative position.
*
* If the relativePosition has changed, we must also call
* updateRelativePosition() to make any visual changes to the popup
* which are associated with putting it in a new relativePosition.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {DOMElement} Reference to a div that contains the drawn popup.
*/
draw: function(px) {
if (px == null) {
if ((this.lonlat != null) && (this.map != null)) {
px = this.map.getLayerPxFromLonLat(this.lonlat);
}
}
//calculate relative position
moveTo: function(px) {
var oldRelativePosition = this.relativePosition;
this.relativePosition = this.calculateRelativePosition(px);
return OpenLayers.Popup.prototype.draw.apply(this, arguments);
var newPx = this.calculateNewPx(px);
var newArguments = new Array(newPx);
OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
//if this move has caused the popup to change its relative position,
// we need to make the appropriate cosmetic changes.
if (this.relativePosition != oldRelativePosition) {
this.updateRelativePosition();
}
},
/**
* APIMethod: setSize
*
* Parameters:
* size - {<OpenLayers.Size>}
*/
setSize:function(size) {
OpenLayers.Popup.prototype.setSize.apply(this, arguments);
if ((this.lonlat) && (this.map)) {
var px = this.map.getLayerPxFromLonLat(this.lonlat);
this.moveTo(px);
}
},
/**
* Method: calculateRelativePosition
@@ -95,37 +139,19 @@ OpenLayers.Popup.Anchored =
},
/**
* Method: moveTo
* Since the popup is moving to a new px, it might need also to be moved
* relative to where the marker is.
* Method: updateRelativePosition
* The popup has been moved to a new relative location, so we may want to
* make some cosmetic adjustments to it.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
* Note that in the classic Anchored popup, there is nothing to do
* here, since the popup looks exactly the same in all four positions.
* Subclasses such as the AnchoredBubble and Framed, however, will
* want to do something special here.
*/
moveTo: function(px) {
this.relativePosition = this.calculateRelativePosition(px);
var newPx = this.calculateNewPx(px);
var newArguments = new Array(newPx);
OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
updateRelativePosition: function() {
//to be overridden by subclasses
},
/**
* Method: setSize
*
* Parameters:
* size - {<OpenLayers.Size>}
*/
setSize:function(size) {
OpenLayers.Popup.prototype.setSize.apply(this, arguments);
if ((this.lonlat) && (this.map)) {
var px = this.map.getLayerPxFromLonLat(this.lonlat);
this.moveTo(px);
}
},
/**
* Method: calculateNewPx
*

View File

@@ -38,6 +38,11 @@ OpenLayers.Popup.AnchoredBubble =
*/
initialize:function(id, lonlat, size, contentHTML, anchor, closeBox,
closeBoxCallback) {
this.padding = new OpenLayers.Bounds(
0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
);
OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
},
@@ -64,17 +69,12 @@ OpenLayers.Popup.AnchoredBubble =
},
/**
* Method: moveTo
* The popup may have been moved to a new relative location, in which case
* Method: updateRelativePosition
* The popup has been moved to a new relative location, in which case
* we will want to re-do the rico corners.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*/
moveTo: function(px) {
OpenLayers.Popup.Anchored.prototype.moveTo.apply(this, arguments);
this.setRicoCorners(!this.rounded);
this.rounded = true;
updateRelativePosition: function() {
this.setRicoCorners();
},
/**
@@ -85,22 +85,8 @@ OpenLayers.Popup.AnchoredBubble =
*/
setSize:function(size) {
OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
if (this.contentDiv != null) {
var contentSize = this.size.clone();
contentSize.h -= (2 * OpenLayers.Popup.AnchoredBubble.CORNER_SIZE);
contentSize.h -= (2 * this.padding);
this.contentDiv.style.height = contentSize.h + "px";
this.contentDiv.style.width = contentSize.w + "px";
if (this.map) {
//size has changed - must redo corners
this.setRicoCorners(!this.rounded);
this.rounded = true;
}
}
this.setRicoCorners();
},
/**
@@ -117,7 +103,7 @@ OpenLayers.Popup.AnchoredBubble =
if (this.div != null) {
if (this.contentDiv != null) {
this.div.style.background = "transparent";
OpenLayers.Rico.Corner.changeColor(this.contentDiv,
OpenLayers.Rico.Corner.changeColor(this.groupDiv,
this.backgroundColor);
}
}
@@ -154,11 +140,8 @@ OpenLayers.Popup.AnchoredBubble =
/**
* Method: setRicoCorners
* Update RICO corners according to the popup's current relative postion.
*
* Parameters:
* firstTime - {Boolean} This the first time the corners are being rounded.
*/
setRicoCorners:function(firstTime) {
setRicoCorners:function() {
var corners = this.getCornersToRound(this.relativePosition);
var options = {corners: corners,
@@ -166,8 +149,9 @@ OpenLayers.Popup.AnchoredBubble =
bgColor: "transparent",
blend: false};
if (firstTime) {
if (!this.rounded) {
OpenLayers.Rico.Corner.round(this.div, options);
this.rounded = true;
} else {
OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
//set the popup color and opacity

View File

@@ -0,0 +1,331 @@
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license. */
/**
* @requires OpenLayers/Popup/Anchored.js
*/
/**
* Class: OpenLayers.Popup.Framed
*
* Inherits from:
* - <OpenLayers.Popup.Anchored>
*/
OpenLayers.Popup.Framed =
OpenLayers.Class(OpenLayers.Popup.Anchored, {
/**
* Property: imageSrc
* {String} location of the image to be used as the popup frame
*/
imageSrc: null,
/**
* Property: imageSize
* {<OpenLayers.Size>} Size (measured in pixels) of the image located
* by the 'imageSrc' property.
*/
imageSize: null,
/**
* APIProperty: isAlphaImage
* {Boolean} The image has some alpha and thus needs to use the alpha
* image hack. Note that setting this to true will have no noticeable
* effect in FF or IE7 browsers, but will all but crush the ie6
* browser.
* Default is false.
*/
isAlphaImage: false,
/**
* Property: positionBlocks
* {Object} Hash of different position blocks (Object/Hashs). Each block
* will be keyed by a two-character 'relativePosition'
* code string (ie "tl", "tr", "bl", "br"). Block properties are
* 'offset', 'padding' (self-explanatory), and finally the 'blocks'
* parameter, which is an array of the block objects.
*
* Each block object must have 'size', 'anchor', and 'position'
* properties.
*
* Note that positionBlocks should never be modified at runtime.
*/
positionBlocks: null,
/**
* Property: blocks
* {Array[Object]} Array of objects, each of which is one "block" of the
* popup. Each block has a 'div' and an 'image' property, both of
* which are DOMElements, and the latter of which is appended to the
* former. These are reused as the popup goes changing positions for
* great economy and elegance.
*/
blocks: null,
/**
* APIProperty: fixedRelativePosition
* {Boolean} We want the framed popup to work dynamically placed relative
* to its anchor but also in just one fixed position. A well designed
* framed popup will have the pixels and logic to display itself in
* any of the four relative positions, but (understandably), this will
* not be the case for all of them. By setting this property to 'true',
* framed popup will not recalculate for the best placement each time
* it's open, but will always open the same way.
* Note that if this is set to true, it is generally advisable to also
* set the 'panIntoView' property to true so that the popup can be
* scrolled into view (since it will often be offscreen on open)
* Default is false.
*/
fixedRelativePosition: false,
/**
* Constructor: OpenLayers.Popup.Framed
*
* Parameters:
* id - {String}
* lonlat - {<OpenLayers.LonLat>}
* size - {<OpenLayers.Size>}
* contentHTML - {String}
* anchor - {Object} Object to which we'll anchor the popup. Must expose
* a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
* (Note that this is generally an <OpenLayers.Icon>).
* closeBox - {Boolean}
* closeBoxCallback - {Function} Function to be called on closeBox click.
*/
initialize:function(id, lonlat, size, contentHTML, anchor, closeBox,
closeBoxCallback) {
OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
if (this.fixedRelativePosition) {
//based on our decided relativePostion, set the current padding
// this keeps us from getting into trouble
this.updateRelativePosition();
//make calculateRelativePosition always returnt the specified
// fiexed position.
this.calculateRelativePosition = function(px) {
return this.relativePosition;
};
}
this.contentDiv.style.position = "absolute";
this.contentDiv.style.zIndex = 1;
if (closeBox) {
this.closeDiv.style.zIndex = 1;
}
this.groupDiv.style.position = "absolute";
this.groupDiv.style.top = "0px";
this.groupDiv.style.left = "0px";
this.groupDiv.style.height = "100%";
this.groupDiv.style.width = "100%";
},
/**
* APIMethod: destroy
*/
destroy: function() {
this.imageSrc = null;
this.imageSize = null;
this.isAlphaImage = null;
this.fixedRelativePosition = false;
this.positionBlocks = null;
//remove our blocks
for(var i = 0; i < this.blocks.length; i++) {
var block = this.blocks[i];
if (block.image) {
block.div.removeChild(block.image);
}
block.image = null;
if (block.div) {
this.groupDiv.removeChild(block.div);
}
block.div = null;
}
this.blocks = null;
OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
},
/**
* APIMethod: setBackgroundColor
*/
setBackgroundColor:function(color) {
//does nothing since the framed popup's entire scheme is based on a
// an image -- changing the background color makes no sense.
},
/**
* APIMethod: setBorder
*/
setBorder:function() {
//does nothing since the framed popup's entire scheme is based on a
// an image -- changing the popup's border makes no sense.
},
/**
* Method: setOpacity
* Sets the opacity of the popup.
*
* Parameters:
* opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
*/
setOpacity:function(opacity) {
//does nothing since we suppose that we'll never apply an opacity
// to a framed popup
},
/**
* APIMethod: setSize
* Overridden here, because we need to update the blocks whenever the size
* of the popup has changed.
*
* Parameters:
* size - {<OpenLayers.Size>}
*/
setSize:function(size) {
OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
this.updateBlocks();
},
/**
* Method: updateRelativePosition
* When the relative position changes, we need to set the new padding
* BBOX on the popup, reposition the close div, and update the blocks.
*/
updateRelativePosition: function() {
//update the padding
this.padding = this.positionBlocks[this.relativePosition].padding;
//update the position of our close box to new padding
if (this.closeDiv) {
// use the content div's css padding to determine if we should
// padd the close div
var contentDivPadding = this.getContentDivPadding();
this.closeDiv.style.right = contentDivPadding.right +
this.padding.right + "px";
this.closeDiv.style.top = contentDivPadding.top +
this.padding.top + "px";
}
this.updateBlocks();
},
/**
* Method: calculateNewPx
* Besides the standard offset as determined by the Anchored class, our
* Framed popups have a special 'offset' property for each of their
* positions, which is used to offset the popup relative to its anchor.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {<OpenLayers.Pixel>} The the new px position of the popup on the screen
* relative to the passed-in px.
*/
calculateNewPx:function(px) {
var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
this, arguments
);
newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
return newPx;
},
/**
* Method: createBlocks
*/
createBlocks: function() {
this.blocks = [];
var position = this.positionBlocks[this.relativePosition];
for (var i = 0; i < position.blocks.length; i++) {
var block = {};
this.blocks.push(block);
var divId = this.id + '_FrameDecorationDiv_' + i;
block.div = OpenLayers.Util.createDiv(divId,
null, null, null, "absolute", null, "hidden", null
);
var imgId = this.id + '_FrameDecorationImg_' + i;
var imageCreator =
(this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
: OpenLayers.Util.createImage;
block.image = imageCreator(imgId,
null, this.imageSize, this.imageSrc,
"absolute", null, null, null
);
block.div.appendChild(block.image);
this.groupDiv.appendChild(block.div);
}
},
/**
* Method: updateBlocks
* Internal method, called on initialize and when the popup's relative
* position has changed. This function takes care of re-positioning
* the popup's blocks in their appropropriate places.
*/
updateBlocks: function() {
if (!this.blocks) {
this.createBlocks();
}
var position = this.positionBlocks[this.relativePosition];
for (var i = 0; i < position.blocks.length; i++) {
var positionBlock = position.blocks[i];
var block = this.blocks[i];
// adjust sizes
var l = positionBlock.anchor.left;
var b = positionBlock.anchor.bottom;
var r = positionBlock.anchor.right;
var t = positionBlock.anchor.top;
//note that we use the isNaN() test here because if the
// size object is initialized with a "auto" parameter, the
// size constructor calls parseFloat() on the string,
// which will turn it into NaN
//
var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l)
: positionBlock.size.w;
var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t)
: positionBlock.size.h;
block.div.style.width = w + 'px';
block.div.style.height = h + 'px';
block.div.style.left = (l != null) ? l + 'px' : '';
block.div.style.bottom = (b != null) ? b + 'px' : '';
block.div.style.right = (r != null) ? r + 'px' : '';
block.div.style.top = (t != null) ? t + 'px' : '';
block.image.style.left = positionBlock.position.x + 'px';
block.image.style.top = positionBlock.position.y + 'px';
}
this.contentDiv.style.left = this.padding.left + "px";
this.contentDiv.style.top = this.padding.top + "px";
},
CLASS_NAME: "OpenLayers.Popup.Framed"
});

View File

@@ -0,0 +1,229 @@
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license. */
/**
* @requires OpenLayers/Popup/Framed.js
* @requires OpenLayers/Util.js
*/
/**
* Class: OpenLayers.Popup.FramedCloud
*
* Inherits from:
* - <OpenLayers.Popup.Framed>
*/
OpenLayers.Popup.FramedCloud =
OpenLayers.Class(OpenLayers.Popup.Framed, {
/**
* APIProperty: autoSize
* {Boolean} Framed Cloud is autosizing by default.
*/
autoSize: true,
/**
* APIProperty: panMapIfOutOfView
* {Boolean} Framed Cloud does pan into view by default.
*/
panMapIfOutOfView: true,
/**
* Property: imageSrc
* {String}
*/
imageSrc: OpenLayers.Util.getImagesLocation() + 'cloud-popup-relative.png',
/**
* APIProperty: imageSize
* {<OpenLayers.Size>}
*/
imageSize: new OpenLayers.Size(676, 736),
/**
* APIProperty: isAlphaImage
* {Boolean} The FramedCloud does not use an alpha image (in honor of the
* good ie6 folk out there)
*/
isAlphaImage: false,
/**
* APIProperty: fixedRelativePosition
* {Boolean} The Framed Cloud popup works in just one fixed position.
*/
fixedRelativePosition: false,
/**
* Property: positionBlocks
* {Object} Hash of differen position blocks, keyed by relativePosition
* two-character code string (ie "tl", "tr", "bl", "br")
*/
positionBlocks: {
"tl": {
'offset': new OpenLayers.Pixel(44, 0),
'padding': new OpenLayers.Bounds(8, 40, 8, 9),
'blocks': [
{ // top-left
size: new OpenLayers.Size('auto', 'auto'),
anchor: new OpenLayers.Bounds(0, 51, 22, 0),
position: new OpenLayers.Pixel(0, 0)
},
{ //top-right
size: new OpenLayers.Size(22, 'auto'),
anchor: new OpenLayers.Bounds(null, 50, 0, 0),
position: new OpenLayers.Pixel(-638, 0)
},
{ //bottom-left
size: new OpenLayers.Size('auto', 21),
anchor: new OpenLayers.Bounds(0, 32, 80, null),
position: new OpenLayers.Pixel(0, -629)
},
{ //bottom-right
size: new OpenLayers.Size(22, 21),
anchor: new OpenLayers.Bounds(null, 32, 0, null),
position: new OpenLayers.Pixel(-638, -629)
},
{ // stem
size: new OpenLayers.Size(81, 54),
anchor: new OpenLayers.Bounds(null, 0, 0, null),
position: new OpenLayers.Pixel(0, -668)
}
]
},
"tr": {
'offset': new OpenLayers.Pixel(-45, 0),
'padding': new OpenLayers.Bounds(8, 40, 8, 9),
'blocks': [
{ // top-left
size: new OpenLayers.Size('auto', 'auto'),
anchor: new OpenLayers.Bounds(0, 51, 22, 0),
position: new OpenLayers.Pixel(0, 0)
},
{ //top-right
size: new OpenLayers.Size(22, 'auto'),
anchor: new OpenLayers.Bounds(null, 50, 0, 0),
position: new OpenLayers.Pixel(-638, 0)
},
{ //bottom-left
size: new OpenLayers.Size('auto', 21),
anchor: new OpenLayers.Bounds(0, 32, 22, null),
position: new OpenLayers.Pixel(0, -629)
},
{ //bottom-right
size: new OpenLayers.Size(22, 21),
anchor: new OpenLayers.Bounds(null, 32, 0, null),
position: new OpenLayers.Pixel(-638, -629)
},
{ // stem
size: new OpenLayers.Size(81, 54),
anchor: new OpenLayers.Bounds(0, 0, null, null),
position: new OpenLayers.Pixel(-215, -668)
}
]
},
"bl": {
'offset': new OpenLayers.Pixel(45, 0),
'padding': new OpenLayers.Bounds(8, 9, 8, 40),
'blocks': [
{ // top-left
size: new OpenLayers.Size('auto', 'auto'),
anchor: new OpenLayers.Bounds(0, 21, 22, 32),
position: new OpenLayers.Pixel(0, 0)
},
{ //top-right
size: new OpenLayers.Size(22, 'auto'),
anchor: new OpenLayers.Bounds(null, 21, 0, 32),
position: new OpenLayers.Pixel(-638, 0)
},
{ //bottom-left
size: new OpenLayers.Size('auto', 21),
anchor: new OpenLayers.Bounds(0, 0, 22, null),
position: new OpenLayers.Pixel(0, -629)
},
{ //bottom-right
size: new OpenLayers.Size(22, 21),
anchor: new OpenLayers.Bounds(null, 0, 0, null),
position: new OpenLayers.Pixel(-638, -629)
},
{ // stem
size: new OpenLayers.Size(81, 54),
anchor: new OpenLayers.Bounds(null, null, 0, 0),
position: new OpenLayers.Pixel(-101, -674)
}
]
},
"br": {
'offset': new OpenLayers.Pixel(-44, 0),
'padding': new OpenLayers.Bounds(8, 9, 8, 40),
'blocks': [
{ // top-left
size: new OpenLayers.Size('auto', 'auto'),
anchor: new OpenLayers.Bounds(0, 21, 22, 32),
position: new OpenLayers.Pixel(0, 0)
},
{ //top-right
size: new OpenLayers.Size(22, 'auto'),
anchor: new OpenLayers.Bounds(null, 21, 0, 32),
position: new OpenLayers.Pixel(-638, 0)
},
{ //bottom-left
size: new OpenLayers.Size('auto', 21),
anchor: new OpenLayers.Bounds(0, 0, 22, null),
position: new OpenLayers.Pixel(0, -629)
},
{ //bottom-right
size: new OpenLayers.Size(22, 21),
anchor: new OpenLayers.Bounds(null, 0, 0, null),
position: new OpenLayers.Pixel(-638, -629)
},
{ // stem
size: new OpenLayers.Size(81, 54),
anchor: new OpenLayers.Bounds(0, null, null, 0),
position: new OpenLayers.Pixel(-311, -674)
}
]
}
},
/**
* APIProperty: minSize
* {<OpenLayers.Size>}
*/
minSize: new OpenLayers.Size(105, 10),
/**
* APIProperty: maxSize
* {<OpenLayers.Size>}
*/
maxSize: new OpenLayers.Size(600, 660),
/**
* Constructor: OpenLayers.Popup.FramedCloud
*
* Parameters:
* id - {String}
* lonlat - {<OpenLayers.LonLat>}
* size - {<OpenLayers.Size>}
* contentHTML - {String}
* anchor - {Object} Object to which we'll anchor the popup. Must expose
* a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
* (Note that this is generally an <OpenLayers.Icon>).
* closeBox - {Boolean}
* closeBoxCallback - {Function} Function to be called on closeBox click.
*/
initialize:function(id, lonlat, size, contentHTML, anchor, closeBox,
closeBoxCallback) {
OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
this.contentDiv.className = "olFramedCloudPopupContent";
},
/**
* APIMethod: destroy
*/
destroy: function() {
OpenLayers.Popup.Framed.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Popup.FramedCloud"
});