/** * Copyright (c) 2008-2010 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. */ /** * @include GeoExt/widgets/MapPanel.js */ /** api: (define) * module = GeoExt * class = Popup * base_link = `Ext.Window `_ */ Ext.namespace("GeoExt"); /** api: example * Sample code to create a popup anchored to a feature: * * .. code-block:: javascript * * var popup = new GeoExt.Popup({ * title: "My Popup", * location: feature, * width: 200, * html: "
Popup content
", * collapsible: true * }); */ /** api: constructor * .. class:: Popup(config) * * Popups are a specialized Window that supports anchoring * to a particular location in a MapPanel. When a popup * is anchored to a location, that means that the popup * will visibly point to the location on the map, and move * accordingly when the map is panned or zoomed. */ GeoExt.Popup = Ext.extend(Ext.Window, { /** api: config[anchored] * ``Boolean`` The popup begins anchored to its location. Default is * ``true``. */ anchored: true, /** api: config[map] * ``OpenLayers.Map`` or :class:`GeoExt.MapPanel` * The map this popup will be anchored to (only required if ``anchored`` * is set to true and the map cannot be derived from the ``location``'s * layer. */ map: null, /** api: config[panIn] * ``Boolean`` The popup should pan the map so that the popup is * fully in view when it is rendered. Default is ``true``. */ panIn: true, /** api: config[unpinnable] * ``Boolean`` The popup should have a "unpin" tool that unanchors it from * its location. Default is ``true``. */ unpinnable: true, /** api: config[location] * ``OpenLayers.Feature.Vector`` or ``OpenLayers.LonLat`` or * ``OpenLayers.Pixel`` or ``OpenLayers.Geometry`` A location for this * popup's anchor. */ /** private: property[location] * ``OpenLayers.LonLat`` */ location: null, /** * Some Ext.Window defaults need to be overriden here * because some Ext.Window behavior is not currently supported. */ /** private: config[animCollapse] * ``Boolean`` Animate the transition when the panel is collapsed. * Default is ``false``. Collapsing animation is not supported yet for * popups. */ animCollapse: false, /** private: config[draggable] * ``Boolean`` Enable dragging of this Panel. Defaults to ``false`` * because the popup defaults to being anchored, and anchored popups * should not be draggable. */ draggable: false, /** private: config[shadow] * ``Boolean`` Give the popup window a shadow. Defaults to ``false`` * because shadows are not supported yet for popups (the shadow does * not look good with the anchor). */ shadow: false, /** api: config[popupCls] * ``String`` CSS class name for the popup DOM elements. Default is * "gx-popup". */ popupCls: "gx-popup", /** api: config[ancCls] * ``String`` CSS class name for the popup's anchor. */ ancCls: null, /** private: method[initComponent] * Initializes the popup. */ initComponent: function() { if(this.map instanceof GeoExt.MapPanel) { this.map = this.map.map; } if(!this.map && this.location instanceof OpenLayers.Feature.Vector && this.location.layer) { this.map = this.location.layer.map; } if (this.location instanceof OpenLayers.Feature.Vector) { this.location = this.location.geometry; } if (this.location instanceof OpenLayers.Geometry) { if (typeof this.location.getCentroid == "function") { this.location = this.location.getCentroid(); } this.location = this.location.getBounds().getCenterLonLat(); } else if (this.location instanceof OpenLayers.Pixel) { this.location = this.map.getLonLatFromViewPortPx(this.location); } if(this.anchored) { this.addAnchorEvents(); } this.baseCls = this.popupCls + " " + this.baseCls; this.elements += ',anc'; GeoExt.Popup.superclass.initComponent.call(this); }, /** private: method[onRender] * Executes when the popup is rendered. */ 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); }, /** private: method[initTools] * Initializes the tools on the popup. In particular, * it adds the 'unpin' tool if the popup is unpinnable. */ initTools : function() { if(this.unpinnable) { this.addTool({ id: 'unpin', handler: this.unanchorPopup.createDelegate(this, []) }); } GeoExt.Popup.superclass.initTools.call(this); }, /** private: method[show] * Override. */ show: function() { GeoExt.Popup.superclass.show.apply(this, arguments); if(this.anchored) { this.position(); if(this.panIn && !this._mapMove) { this.panIntoView(); } } }, /** private: method[maximize] * Override. */ maximize: function() { if(!this.maximized && this.anc) { this.unanchorPopup(); } GeoExt.Popup.superclass.maximize.apply(this, arguments); }, /** api: method[setSize] * :param w: ``Integer`` * :param h: ``Integer`` * * Sets the size of the popup, taking into account the size of the anchor. */ setSize: function(w, h) { if(this.anc) { var ancSize = this.anc.getSize(); if(typeof w == 'object') { h = w.height - ancSize.height; w = w.width; } else if(!isNaN(h)){ h = h - ancSize.height; } } GeoExt.Popup.superclass.setSize.call(this, w, h); }, /** private: method[position] * Positions the popup relative to its location */ position: function() { if(this._mapMove === true) { var visible = this.map.getExtent().containsLonLat(this.location); if(visible !== this.isVisible()) { this.setVisible(visible); } } 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); } }, /** private: method[unanchorPopup] * Unanchors a popup from its location. This removes the popup from its * MapPanel and adds it to the page body. */ unanchorPopup: function() { this.removeAnchorEvents(); //make the window draggable this.draggable = true; this.header.addClass("x-window-draggable"); this.dd = new Ext.Window.DD(this); //remove anchor this.anc.remove(); this.anc = null; //hide unpin tool this.tools.unpin.hide(); }, /** private: method[panIntoView] * Pans the MapPanel's map so that an anchored popup can come entirely * into view, with padding specified as per normal OpenLayers.Map popup * padding. */ panIntoView: function() { var mapBox = Ext.fly(this.map.div).getBox(); //assumed viewport takes up whole body element of map panel var popupPos = this.getPosition(true); popupPos[0] -= mapBox.x; popupPos[1] -= mapBox.y; var panelSize = [mapBox.width, mapBox.height]; // [X,Y] var popupSize = this.getSize(); var newPos = [popupPos[0], popupPos[1]]; //For now, using native OpenLayers popup padding. This may not be ideal. var padding = this.map.paddingForPopups; // X if(popupPos[0] < padding.left) { newPos[0] = padding.left; } else if(popupPos[0] + popupSize.width > panelSize[0] - padding.right) { newPos[0] = panelSize[0] - padding.right - popupSize.width; } // Y if(popupPos[1] < padding.top) { newPos[1] = padding.top; } else if(popupPos[1] + popupSize.height > panelSize[1] - padding.bottom) { newPos[1] = panelSize[1] - padding.bottom - popupSize.height; } var dx = popupPos[0] - newPos[0]; var dy = popupPos[1] - newPos[1]; this.map.pan(dx, dy); }, /** private: method[onMapMove] */ onMapMove: function() { this._mapMove = true; this.position(); delete this._mapMove; }, /** private: method[addAnchorEvents] */ addAnchorEvents: function() { this.map.events.on({ "move" : this.onMapMove, scope : this }); this.on({ "resize": this.position, "collapse": this.position, "expand": this.position, scope: this }); }, /** private: method[removeAnchorEvents] */ removeAnchorEvents: function() { //stop position with location this.map.events.un({ "move" : this.onMapMove, scope : this }); this.un("resize", this.position, this); this.un("collapse", this.position, this); this.un("expand", this.position, this); }, /** private: method[beforeDestroy] * Cleanup events before destroying the popup. */ beforeDestroy: function() { if(this.anchored) { this.removeAnchorEvents(); } GeoExt.Popup.superclass.beforeDestroy.call(this); } }); /** api: xtype = gx_popup */ Ext.reg('gx_popup', GeoExt.Popup);