| 1 | /* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|---|
| 2 | * full list of contributors). Published under the Clear BSD license. |
|---|
| 3 | * See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|---|
| 4 | * full text of the license. */ |
|---|
| 5 | |
|---|
| 6 | /** |
|---|
| 7 | * @requires OpenLayers/Popup/Anchored.js |
|---|
| 8 | */ |
|---|
| 9 | |
|---|
| 10 | /** |
|---|
| 11 | * Class: OpenLayers.Popup.Framed |
|---|
| 12 | * |
|---|
| 13 | * Inherits from: |
|---|
| 14 | * - <OpenLayers.Popup.Anchored> |
|---|
| 15 | */ |
|---|
| 16 | OpenLayers.Popup.Framed = |
|---|
| 17 | OpenLayers.Class(OpenLayers.Popup.Anchored, { |
|---|
| 18 | |
|---|
| 19 | /** |
|---|
| 20 | * Property: imageSrc |
|---|
| 21 | * {String} location of the image to be used as the popup frame |
|---|
| 22 | */ |
|---|
| 23 | imageSrc: null, |
|---|
| 24 | |
|---|
| 25 | /** |
|---|
| 26 | * Property: imageSize |
|---|
| 27 | * {<OpenLayers.Size>} Size (measured in pixels) of the image located |
|---|
| 28 | * by the 'imageSrc' property. |
|---|
| 29 | */ |
|---|
| 30 | imageSize: null, |
|---|
| 31 | |
|---|
| 32 | /** |
|---|
| 33 | * APIProperty: isAlphaImage |
|---|
| 34 | * {Boolean} The image has some alpha and thus needs to use the alpha |
|---|
| 35 | * image hack. Note that setting this to true will have no noticeable |
|---|
| 36 | * effect in FF or IE7 browsers, but will all but crush the ie6 |
|---|
| 37 | * browser. |
|---|
| 38 | * Default is false. |
|---|
| 39 | */ |
|---|
| 40 | isAlphaImage: false, |
|---|
| 41 | |
|---|
| 42 | /** |
|---|
| 43 | * Property: positionBlocks |
|---|
| 44 | * {Object} Hash of different position blocks (Object/Hashs). Each block |
|---|
| 45 | * will be keyed by a two-character 'relativePosition' |
|---|
| 46 | * code string (ie "tl", "tr", "bl", "br"). Block properties are |
|---|
| 47 | * 'offset', 'padding' (self-explanatory), and finally the 'blocks' |
|---|
| 48 | * parameter, which is an array of the block objects. |
|---|
| 49 | * |
|---|
| 50 | * Each block object must have 'size', 'anchor', and 'position' |
|---|
| 51 | * properties. |
|---|
| 52 | * |
|---|
| 53 | * Note that positionBlocks should never be modified at runtime. |
|---|
| 54 | */ |
|---|
| 55 | positionBlocks: null, |
|---|
| 56 | |
|---|
| 57 | /** |
|---|
| 58 | * Property: blocks |
|---|
| 59 | * {Array[Object]} Array of objects, each of which is one "block" of the |
|---|
| 60 | * popup. Each block has a 'div' and an 'image' property, both of |
|---|
| 61 | * which are DOMElements, and the latter of which is appended to the |
|---|
| 62 | * former. These are reused as the popup goes changing positions for |
|---|
| 63 | * great economy and elegance. |
|---|
| 64 | */ |
|---|
| 65 | blocks: null, |
|---|
| 66 | |
|---|
| 67 | /** |
|---|
| 68 | * APIProperty: fixedRelativePosition |
|---|
| 69 | * {Boolean} We want the framed popup to work dynamically placed relative |
|---|
| 70 | * to its anchor but also in just one fixed position. A well designed |
|---|
| 71 | * framed popup will have the pixels and logic to display itself in |
|---|
| 72 | * any of the four relative positions, but (understandably), this will |
|---|
| 73 | * not be the case for all of them. By setting this property to 'true', |
|---|
| 74 | * framed popup will not recalculate for the best placement each time |
|---|
| 75 | * it's open, but will always open the same way. |
|---|
| 76 | * Note that if this is set to true, it is generally advisable to also |
|---|
| 77 | * set the 'panIntoView' property to true so that the popup can be |
|---|
| 78 | * scrolled into view (since it will often be offscreen on open) |
|---|
| 79 | * Default is false. |
|---|
| 80 | */ |
|---|
| 81 | fixedRelativePosition: false, |
|---|
| 82 | |
|---|
| 83 | /** |
|---|
| 84 | * Constructor: OpenLayers.Popup.Framed |
|---|
| 85 | * |
|---|
| 86 | * Parameters: |
|---|
| 87 | * id - {String} |
|---|
| 88 | * lonlat - {<OpenLayers.LonLat>} |
|---|
| 89 | * contentSize - {<OpenLayers.Size>} |
|---|
| 90 | * contentHTML - {String} |
|---|
| 91 | * anchor - {Object} Object to which we'll anchor the popup. Must expose |
|---|
| 92 | * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) |
|---|
| 93 | * (Note that this is generally an <OpenLayers.Icon>). |
|---|
| 94 | * closeBox - {Boolean} |
|---|
| 95 | * closeBoxCallback - {Function} Function to be called on closeBox click. |
|---|
| 96 | */ |
|---|
| 97 | initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, |
|---|
| 98 | closeBoxCallback) { |
|---|
| 99 | |
|---|
| 100 | OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments); |
|---|
| 101 | |
|---|
| 102 | if (this.fixedRelativePosition) { |
|---|
| 103 | //based on our decided relativePostion, set the current padding |
|---|
| 104 | // this keeps us from getting into trouble |
|---|
| 105 | this.updateRelativePosition(); |
|---|
| 106 | |
|---|
| 107 | //make calculateRelativePosition always return the specified |
|---|
| 108 | // fixed position. |
|---|
| 109 | this.calculateRelativePosition = function(px) { |
|---|
| 110 | return this.relativePosition; |
|---|
| 111 | }; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | this.contentDiv.style.position = "absolute"; |
|---|
| 115 | this.contentDiv.style.zIndex = 1; |
|---|
| 116 | |
|---|
| 117 | if (closeBox) { |
|---|
| 118 | this.closeDiv.style.zIndex = 1; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | this.groupDiv.style.position = "absolute"; |
|---|
| 122 | this.groupDiv.style.top = "0px"; |
|---|
| 123 | this.groupDiv.style.left = "0px"; |
|---|
| 124 | this.groupDiv.style.height = "100%"; |
|---|
| 125 | this.groupDiv.style.width = "100%"; |
|---|
| 126 | }, |
|---|
| 127 | |
|---|
| 128 | /** |
|---|
| 129 | * APIMethod: destroy |
|---|
| 130 | */ |
|---|
| 131 | destroy: function() { |
|---|
| 132 | this.imageSrc = null; |
|---|
| 133 | this.imageSize = null; |
|---|
| 134 | this.isAlphaImage = null; |
|---|
| 135 | |
|---|
| 136 | this.fixedRelativePosition = false; |
|---|
| 137 | this.positionBlocks = null; |
|---|
| 138 | |
|---|
| 139 | //remove our blocks |
|---|
| 140 | for(var i = 0; i < this.blocks.length; i++) { |
|---|
| 141 | var block = this.blocks[i]; |
|---|
| 142 | |
|---|
| 143 | if (block.image) { |
|---|
| 144 | block.div.removeChild(block.image); |
|---|
| 145 | } |
|---|
| 146 | block.image = null; |
|---|
| 147 | |
|---|
| 148 | if (block.div) { |
|---|
| 149 | this.groupDiv.removeChild(block.div); |
|---|
| 150 | } |
|---|
| 151 | block.div = null; |
|---|
| 152 | } |
|---|
| 153 | this.blocks = null; |
|---|
| 154 | |
|---|
| 155 | OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); |
|---|
| 156 | }, |
|---|
| 157 | |
|---|
| 158 | /** |
|---|
| 159 | * APIMethod: setBackgroundColor |
|---|
| 160 | */ |
|---|
| 161 | setBackgroundColor:function(color) { |
|---|
| 162 | //does nothing since the framed popup's entire scheme is based on a |
|---|
| 163 | // an image -- changing the background color makes no sense. |
|---|
| 164 | }, |
|---|
| 165 | |
|---|
| 166 | /** |
|---|
| 167 | * APIMethod: setBorder |
|---|
| 168 | */ |
|---|
| 169 | setBorder:function() { |
|---|
| 170 | //does nothing since the framed popup's entire scheme is based on a |
|---|
| 171 | // an image -- changing the popup's border makes no sense. |
|---|
| 172 | }, |
|---|
| 173 | |
|---|
| 174 | /** |
|---|
| 175 | * Method: setOpacity |
|---|
| 176 | * Sets the opacity of the popup. |
|---|
| 177 | * |
|---|
| 178 | * Parameters: |
|---|
| 179 | * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). |
|---|
| 180 | */ |
|---|
| 181 | setOpacity:function(opacity) { |
|---|
| 182 | //does nothing since we suppose that we'll never apply an opacity |
|---|
| 183 | // to a framed popup |
|---|
| 184 | }, |
|---|
| 185 | |
|---|
| 186 | /** |
|---|
| 187 | * APIMethod: setSize |
|---|
| 188 | * Overridden here, because we need to update the blocks whenever the size |
|---|
| 189 | * of the popup has changed. |
|---|
| 190 | * |
|---|
| 191 | * Parameters: |
|---|
| 192 | * contentSize - {<OpenLayers.Size>} the new size for the popup's |
|---|
| 193 | * contents div (in pixels). |
|---|
| 194 | */ |
|---|
| 195 | setSize:function(contentSize) { |
|---|
| 196 | OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); |
|---|
| 197 | |
|---|
| 198 | this.updateBlocks(); |
|---|
| 199 | }, |
|---|
| 200 | |
|---|
| 201 | /** |
|---|
| 202 | * Method: updateRelativePosition |
|---|
| 203 | * When the relative position changes, we need to set the new padding |
|---|
| 204 | * BBOX on the popup, reposition the close div, and update the blocks. |
|---|
| 205 | */ |
|---|
| 206 | updateRelativePosition: function() { |
|---|
| 207 | |
|---|
| 208 | //update the padding |
|---|
| 209 | this.padding = this.positionBlocks[this.relativePosition].padding; |
|---|
| 210 | |
|---|
| 211 | //update the position of our close box to new padding |
|---|
| 212 | if (this.closeDiv) { |
|---|
| 213 | // use the content div's css padding to determine if we should |
|---|
| 214 | // padd the close div |
|---|
| 215 | var contentDivPadding = this.getContentDivPadding(); |
|---|
| 216 | |
|---|
| 217 | this.closeDiv.style.right = contentDivPadding.right + |
|---|
| 218 | this.padding.right + "px"; |
|---|
| 219 | this.closeDiv.style.top = contentDivPadding.top + |
|---|
| 220 | this.padding.top + "px"; |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | this.updateBlocks(); |
|---|
| 224 | }, |
|---|
| 225 | |
|---|
| 226 | /** |
|---|
| 227 | * Method: calculateNewPx |
|---|
| 228 | * Besides the standard offset as determined by the Anchored class, our |
|---|
| 229 | * Framed popups have a special 'offset' property for each of their |
|---|
| 230 | * positions, which is used to offset the popup relative to its anchor. |
|---|
| 231 | * |
|---|
| 232 | * Parameters: |
|---|
| 233 | * px - {<OpenLayers.Pixel>} |
|---|
| 234 | * |
|---|
| 235 | * Returns: |
|---|
| 236 | * {<OpenLayers.Pixel>} The the new px position of the popup on the screen |
|---|
| 237 | * relative to the passed-in px. |
|---|
| 238 | */ |
|---|
| 239 | calculateNewPx:function(px) { |
|---|
| 240 | var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( |
|---|
| 241 | this, arguments |
|---|
| 242 | ); |
|---|
| 243 | |
|---|
| 244 | newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); |
|---|
| 245 | |
|---|
| 246 | return newPx; |
|---|
| 247 | }, |
|---|
| 248 | |
|---|
| 249 | /** |
|---|
| 250 | * Method: createBlocks |
|---|
| 251 | */ |
|---|
| 252 | createBlocks: function() { |
|---|
| 253 | this.blocks = []; |
|---|
| 254 | |
|---|
| 255 | //since all positions contain the same number of blocks, we can |
|---|
| 256 | // just pick the first position and use its blocks array to create |
|---|
| 257 | // our blocks array |
|---|
| 258 | var firstPosition = null; |
|---|
| 259 | for(var key in this.positionBlocks) { |
|---|
| 260 | firstPosition = key; |
|---|
| 261 | break; |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | var position = this.positionBlocks[firstPosition]; |
|---|
| 265 | for (var i = 0; i < position.blocks.length; i++) { |
|---|
| 266 | |
|---|
| 267 | var block = {}; |
|---|
| 268 | this.blocks.push(block); |
|---|
| 269 | |
|---|
| 270 | var divId = this.id + '_FrameDecorationDiv_' + i; |
|---|
| 271 | block.div = OpenLayers.Util.createDiv(divId, |
|---|
| 272 | null, null, null, "absolute", null, "hidden", null |
|---|
| 273 | ); |
|---|
| 274 | |
|---|
| 275 | var imgId = this.id + '_FrameDecorationImg_' + i; |
|---|
| 276 | var imageCreator = |
|---|
| 277 | (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv |
|---|
| 278 | : OpenLayers.Util.createImage; |
|---|
| 279 | |
|---|
| 280 | block.image = imageCreator(imgId, |
|---|
| 281 | null, this.imageSize, this.imageSrc, |
|---|
| 282 | "absolute", null, null, null |
|---|
| 283 | ); |
|---|
| 284 | |
|---|
| 285 | block.div.appendChild(block.image); |
|---|
| 286 | this.groupDiv.appendChild(block.div); |
|---|
| 287 | } |
|---|
| 288 | }, |
|---|
| 289 | |
|---|
| 290 | /** |
|---|
| 291 | * Method: updateBlocks |
|---|
| 292 | * Internal method, called on initialize and when the popup's relative |
|---|
| 293 | * position has changed. This function takes care of re-positioning |
|---|
| 294 | * the popup's blocks in their appropropriate places. |
|---|
| 295 | */ |
|---|
| 296 | updateBlocks: function() { |
|---|
| 297 | if (!this.blocks) { |
|---|
| 298 | this.createBlocks(); |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | if (this.size && this.relativePosition) { |
|---|
| 302 | var position = this.positionBlocks[this.relativePosition]; |
|---|
| 303 | for (var i = 0; i < position.blocks.length; i++) { |
|---|
| 304 | |
|---|
| 305 | var positionBlock = position.blocks[i]; |
|---|
| 306 | var block = this.blocks[i]; |
|---|
| 307 | |
|---|
| 308 | // adjust sizes |
|---|
| 309 | var l = positionBlock.anchor.left; |
|---|
| 310 | var b = positionBlock.anchor.bottom; |
|---|
| 311 | var r = positionBlock.anchor.right; |
|---|
| 312 | var t = positionBlock.anchor.top; |
|---|
| 313 | |
|---|
| 314 | //note that we use the isNaN() test here because if the |
|---|
| 315 | // size object is initialized with a "auto" parameter, the |
|---|
| 316 | // size constructor calls parseFloat() on the string, |
|---|
| 317 | // which will turn it into NaN |
|---|
| 318 | // |
|---|
| 319 | var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) |
|---|
| 320 | : positionBlock.size.w; |
|---|
| 321 | |
|---|
| 322 | var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) |
|---|
| 323 | : positionBlock.size.h; |
|---|
| 324 | |
|---|
| 325 | block.div.style.width = (w < 0 ? 0 : w) + 'px'; |
|---|
| 326 | block.div.style.height = (h < 0 ? 0 : h) + 'px'; |
|---|
| 327 | |
|---|
| 328 | block.div.style.left = (l != null) ? l + 'px' : ''; |
|---|
| 329 | block.div.style.bottom = (b != null) ? b + 'px' : ''; |
|---|
| 330 | block.div.style.right = (r != null) ? r + 'px' : ''; |
|---|
| 331 | block.div.style.top = (t != null) ? t + 'px' : ''; |
|---|
| 332 | |
|---|
| 333 | block.image.style.left = positionBlock.position.x + 'px'; |
|---|
| 334 | block.image.style.top = positionBlock.position.y + 'px'; |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | this.contentDiv.style.left = this.padding.left + "px"; |
|---|
| 338 | this.contentDiv.style.top = this.padding.top + "px"; |
|---|
| 339 | } |
|---|
| 340 | }, |
|---|
| 341 | |
|---|
| 342 | CLASS_NAME: "OpenLayers.Popup.Framed" |
|---|
| 343 | }); |
|---|