/* 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.js
*/
/**
* Class: OpenLayers.Popup.Anchored
*
* Inherits from:
*  - <OpenLayers.Popup>
*/
OpenLayers.Popup.Anchored =
OpenLayers.Class(OpenLayers.Popup, {
/**
* Parameter: relativePosition
* {String} Relative position of the popup ("lr", "ll", "tr", or "tl").
*/
relativePosition: null,
/**
* Parameter: anchor
* {Object} Object to which we'll anchor the popup. Must expose a
*     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
*/
anchor: null,
/**
* Constructor: OpenLayers.Popup.Anchored
*
* Parameters:
* id - {String}
* lonlat - {<OpenLayers.LonLat>}
* size - {<OpenLayers.Size>}
* contentHTML - {String}
* anchor - {Object} Object which must expose a 'size' <OpenLayers.Size>
*     and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
* closeBox - {Boolean}
* closeBoxCallback - {Function} Function to be called on closeBox click.
*/
initialize:function(id, lonlat, size, contentHTML, anchor, closeBox,
closeBoxCallback) {
var newArguments = new Array(id, lonlat, size, contentHTML, closeBox,
closeBoxCallback);
OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
this.anchor = (anchor != null) ? anchor
: { size: new OpenLayers.Size(0,0),
offset: new OpenLayers.Pixel(0,0)};
},
/**
* 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>}
*/
moveTo: function(px) {
var oldRelativePosition = this.relativePosition;
this.relativePosition = this.calculateRelativePosition(px);
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
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {String} The relative position ("br" "tr" "tl "bl") at which the popup
*     should be placed.
*/
calculateRelativePosition:function(px) {
var lonlat = this.map.getLonLatFromLayerPx(px);
var extent = this.map.getExtent();
var quadrant = extent.determineQuadrant(lonlat);
return OpenLayers.Bounds.oppositeQuadrant(quadrant);
},
/**
* Method: updateRelativePosition
* The popup has been moved to a new relative location, so we may want to
*     make some cosmetic adjustments to it.
*
*     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.
*/
updateRelativePosition: function() {
//to be overridden by subclasses
},
/**
* Method: calculateNewPx
*
* 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 = px.offset(this.anchor.offset);
var top = (this.relativePosition.charAt(0) == 't');
newPx.y += (top) ? -this.size.h : this.anchor.size.h;
var left = (this.relativePosition.charAt(1) == 'l');
newPx.x += (left) ? -this.size.w : this.anchor.size.w;
return newPx;
},
CLASS_NAME: "OpenLayers.Popup.Anchored"
});
/* 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 = [];
//since all positions contain the same number of blocks, we can
// just pick the first position and use its blocks array to create
// our blocks array
var firstPosition = null;
for(var key in this.positionBlocks) {
firstPosition = key;
break;
}
var position = this.positionBlocks[firstPosition];
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();
}
if (this.relativePosition) {
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"
});
/* 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,
/**
* 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(28, 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(-29, 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(29, 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(-28, 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) {
this.imageSrc = OpenLayers.Util.getImagesLocation() + 'cloud-popup-relative.png';
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"
});
/* 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/Util.js
* @requires OpenLayers/Marker.js
* @requires OpenLayers/Popup/AnchoredBubble.js
*/
/**
* Class: OpenLayers.Feature
* Features are combinations of geography and attributes. The OpenLayers.Feature
*     class specifically combines a marker and a lonlat.
*/
OpenLayers.Feature = OpenLayers.Class({
/**
* Property: layer
* {<OpenLayers.Layer>}
*/
layer: null,
/**
* Property: id
* {String}
*/
id: null,
/**
* Property: lonlat
* {<OpenLayers.LonLat>}
*/
lonlat: null,
/**
* Property: data
* {Object}
*/
data: null,
/**
* Property: marker
* {<OpenLayers.Marker>}
*/
marker: null,
/**
* APIProperty: popupClass
* {<OpenLayers.Class>} The class which will be used to instantiate
*     a new Popup. Default is <OpenLayers.Popup.AnchoredBubble>.
*/
popupClass: OpenLayers.Popup.AnchoredBubble,
/**
* Property: popup
* {<OpenLayers.Popup>}
*/
popup: null,
/**
* Constructor: OpenLayers.Feature
* Constructor for features.
*
* Parameters:
* layer - {<OpenLayers.Layer>}
* lonlat - {<OpenLayers.LonLat>}
* data - {Object}
*
* Returns:
* {<OpenLayers.Feature>}
*/
initialize: function(layer, lonlat, data) {
this.layer = layer;
this.lonlat = lonlat;
this.data = (data != null) ? data : {};
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
},
/**
* Method: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
//remove the popup from the map
if ((this.layer != null) && (this.layer.map != null)) {
if (this.popup != null) {
this.layer.map.removePopup(this.popup);
}
}
this.layer = null;
this.id = null;
this.lonlat = null;
this.data = null;
if (this.marker != null) {
this.destroyMarker(this.marker);
this.marker = null;
}
if (this.popup != null) {
this.destroyPopup(this.popup);
this.popup = null;
}
},
/**
* Method: onScreen
*
* Returns:
* {Boolean} Whether or not the feature is currently visible on screen
*           (based on its 'lonlat' property)
*/
onScreen:function() {
var onScreen = false;
if ((this.layer != null) && (this.layer.map != null)) {
var screenBounds = this.layer.map.getExtent();
onScreen = screenBounds.containsLonLat(this.lonlat);
}
return onScreen;
},
/**
* Method: createMarker
* Based on the data associated with the Feature, create and return a marker object.
*
* Returns:
* {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
*          set in this.data. If no 'lonlat' is set, returns null. If no
*          'icon' is set, OpenLayers.Marker() will load the default image.
*
*          Note - this.marker is set to return value
*
*/
createMarker: function() {
if (this.lonlat != null) {
this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
}
return this.marker;
},
/**
* Method: destroyMarker
* Destroys marker.
* If user overrides the createMarker() function, s/he should be able
*   to also specify an alternative function for destroying it
*/
destroyMarker: function() {
this.marker.destroy();
},
/**
* Method: createPopup
* Creates a popup object created from the 'lonlat', 'popupSize',
*     and 'popupContentHTML' properties set in this.data. It uses
*     this.marker.icon as default anchor.
*
*  If no 'lonlat' is set, returns null.
*  If no this.marker has been created, no anchor is sent.
*
*  Note - the returned popup object is 'owned' by the feature, so you
*      cannot use the popup's destroy method to discard the popup.
*      Instead, you must use the feature's destroyPopup
*
*  Note - this.popup is set to return value
*
* Parameters:
* closeBox - {Boolean} create popup with closebox or not
*
* Returns:
* {<OpenLayers.Popup>} Returns the created popup, which is also set
*     as 'popup' property of this feature. Will be of whatever type
*     specified by this feature's 'popupClass' property, but must be
*     of type <OpenLayers.Popup>.
*
*/
createPopup: function(closeBox,func) {
if (this.lonlat != null) {
var id = this.id + "_popup";
var anchor = (this.marker) ? this.marker.icon : null;
if (!this.popup) {
this.popup = new this.popupClass(id,
this.lonlat,
this.data.popupSize,
this.data.popupContentHTML,
anchor,
closeBox,
func);
}
if (this.data.overflow != null) {
this.popup.contentDiv.style.overflow = this.data.overflow;
}
this.popup.feature = this;
}
return this.popup;
},
/**
* Method: destroyPopup
* Destroys the popup created via createPopup.
*
* As with the marker, if user overrides the createPopup() function, s/he
*   should also be able to override the destruction
*/
destroyPopup: function() {
if (this.popup) {
this.popup.feature = null;
this.popup.destroy();
this.popup = null;
}
},
CLASS_NAME: "OpenLayers.Feature"
});
/* 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. */
// TRASH THIS
OpenLayers.State = {
/** states */
UNKNOWN: 'Unknown',
INSERT: 'Insert',
UPDATE: 'Update',
DELETE: 'Delete'
};
/**
* @requires OpenLayers/Feature.js
* @requires OpenLayers/Util.js
*/
/**
* Class: OpenLayers.Feature.Vector
* Vector features use the OpenLayers.Geometry classes as geometry description.
* They have an 'attributes' property, which is the data object, and a 'style'
* property, the default values of which are defined in the
* <OpenLayers.Feature.Vector.style> objects.
*
* Inherits from:
*  - <OpenLayers.Feature>
*/
OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
/**
* Property: fid
* {String}
*/
fid: null,
/**
* APIProperty: geometry
* {<OpenLayers.Geometry>}
*/
geometry: null,
/**
* APIProperty: attributes
* {Object} This object holds arbitrary properties that describe the
*     feature.
*/
attributes: null,
/**
* Property: state
* {String}
*/
state: null,
/**
* APIProperty: style
* {Object}
*/
style: null,
/**
* Property: renderIntent
* {String} rendering intent currently being used
*/
renderIntent: "default",
/**
* Constructor: OpenLayers.Feature.Vector
* Create a vector feature.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>} The geometry that this feature
*     represents.
* attributes - {Object} An optional object that will be mapped to the
*     <attributes> property.
* style - {Object} An optional style object.
*/
initialize: function(geometry, attributes, style) {
OpenLayers.Feature.prototype.initialize.apply(this,
[null, null, attributes]);
this.lonlat = null;
this.geometry = geometry;
this.state = null;
this.attributes = {};
if (attributes) {
this.attributes = OpenLayers.Util.extend(this.attributes,
attributes);
}
this.style = style ? style : null;
},
/**
* Method: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
if (this.layer) {
this.layer.removeFeatures(this);
this.layer = null;
}
this.geometry = null;
OpenLayers.Feature.prototype.destroy.apply(this, arguments);
},
/**
* Method: clone
* Create a clone of this vector feature.  Does not set any non-standard
*     properties.
*
* Returns:
* {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
*/
clone: function () {
return new OpenLayers.Feature.Vector(this.geometry.clone(),
this.attributes,
this.style);
},
/**
* Method: onScreen
* Determine whether the feature is within the map viewport.  This method
*     tests for an intersection between the geometry and the viewport
*     bounds.  If a more effecient but less precise geometry bounds
*     intersection is desired, call the method with the boundsOnly
*     parameter true.
*
* Parameters:
* boundsOnly - {Boolean} Only test whether a feature's bounds intersects
*     the viewport bounds.  Default is false.  If false, the feature's
*     geometry must intersect the viewport for onScreen to return true.
*
* Returns:
* {Boolean} The feature is currently visible on screen (optionally
*     based on its bounds if boundsOnly is true).
*/
onScreen:function(boundsOnly) {
var onScreen = false;
if(this.layer && this.layer.map) {
var screenBounds = this.layer.map.getExtent();
if(boundsOnly) {
var featureBounds = this.geometry.getBounds();
onScreen = screenBounds.intersectsBounds(featureBounds);
} else {
var screenPoly = screenBounds.toGeometry();
onScreen = screenPoly.intersects(this.geometry);
}
}
return onScreen;
},
/**
* Method: createMarker
* HACK - we need to decide if all vector features should be able to
*     create markers
*
* Returns:
* {<OpenLayers.Marker>} For now just returns null
*/
createMarker: function() {
return null;
},
/**
* Method: destroyMarker
* HACK - we need to decide if all vector features should be able to
*     delete markers
*
* If user overrides the createMarker() function, s/he should be able
*   to also specify an alternative function for destroying it
*/
destroyMarker: function() {
// pass
},
/**
* Method: createPopup
* HACK - we need to decide if all vector features should be able to
*     create popups
*
* Returns:
* {<OpenLayers.Popup>} For now just returns null
*/
createPopup: function() {
return null;
},
/**
* Method: atPoint
* Determins whether the feature intersects with the specified location.
*
* Parameters:
* lonlat - {<OpenLayers.LonLat>}
* toleranceLon - {float} Optional tolerance in Geometric Coords
* toleranceLat - {float} Optional tolerance in Geographic Coords
*
* Returns:
* {Boolean} Whether or not the feature is at the specified location
*/
atPoint: function(lonlat, toleranceLon, toleranceLat) {
var atPoint = false;
if(this.geometry) {
atPoint = this.geometry.atPoint(lonlat, toleranceLon,
toleranceLat);
}
return atPoint;
},
/**
* Method: destroyPopup
* HACK - we need to decide if all vector features should be able to
* delete popups
*/
destroyPopup: function() {
// pass
},
/**
* Method: toState
* Sets the new state
*
* Parameters:
* state - {String}
*/
toState: function(state) {
if (state == OpenLayers.State.UPDATE) {
switch (this.state) {
case OpenLayers.State.UNKNOWN:
case OpenLayers.State.DELETE:
this.state = state;
break;
case OpenLayers.State.UPDATE:
case OpenLayers.State.INSERT:
break;
}
} else if (state == OpenLayers.State.INSERT) {
switch (this.state) {
case OpenLayers.State.UNKNOWN:
break;
default:
this.state = state;
break;
}
} else if (state == OpenLayers.State.DELETE) {
switch (this.state) {
case OpenLayers.State.INSERT:
// the feature should be destroyed
break;
case OpenLayers.State.DELETE:
break;
case OpenLayers.State.UNKNOWN:
case OpenLayers.State.UPDATE:
this.state = state;
break;
}
} else if (state == OpenLayers.State.UNKNOWN) {
this.state = state;
}
},
CLASS_NAME: "OpenLayers.Feature.Vector"
});
/**
* Constant: OpenLayers.Feature.Vector.style
* OpenLayers features can have a number of style attributes. The 'default'
*     style will typically be used if no other style is specified.
*
* Default style properties:
*
*  - fillColor: "#ee9900",
*  - fillOpacity: 0.4,
*  - hoverFillColor: "white",
*  - hoverFillOpacity: 0.8,
*  - strokeColor: "#ee9900",
*  - strokeOpacity: 1,
*  - strokeWidth: 1,
*  - strokeLinecap: "round",
*  - hoverStrokeColor: "red",
*  - hoverStrokeOpacity: 1,
*  - hoverStrokeWidth: 0.2,
*  - pointRadius: 6,
*  - hoverPointRadius: 1,
*  - hoverPointUnit: "%",
*  - pointerEvents: "visiblePainted"
*  - cursor: ""
*
* Other style properties that have no default values:
*
*  - externalGraphic,
*  - graphicWidth,
*  - graphicHeight,
*  - graphicOpacity
*  - graphicXOffset
*  - graphicYOffset
*  - display
*/
OpenLayers.Feature.Vector.style = {
'default': {
fillColor: "#ee9900",
fillOpacity: 0.4,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "#ee9900",
strokeOpacity: 1,
strokeWidth: 1,
strokeLinecap: "round",
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: ""
},
'select': {
fillColor: "blue",
fillOpacity: 0.4,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "blue",
strokeOpacity: 1,
strokeWidth: 2,
strokeLinecap: "round",
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: "pointer"
},
'temporary': {
fillColor: "yellow",
fillOpacity: 0.2,
hoverFillColor: "white",
hoverFillOpacity: 0.8,
strokeColor: "yellow",
strokeOpacity: 1,
strokeLinecap: "round",
strokeWidth: 4,
hoverStrokeColor: "red",
hoverStrokeOpacity: 1,
hoverStrokeWidth: 0.2,
pointRadius: 6,
hoverPointRadius: 1,
hoverPointUnit: "%",
pointerEvents: "visiblePainted",
cursor: ""
}
};
/* 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/Events.js
*/
/**
* Class: OpenLayers.Handler
* Base class to construct a higher-level handler for event sequences.  All
*     handlers have activate and deactivate methods.  In addition, they have
*     methods named like browser events.  When a handler is activated, any
*     additional methods named like a browser event is registered as a
*     listener for the corresponding event.  When a handler is deactivated,
*     those same methods are unregistered as event listeners.
*
* Handlers also typically have a callbacks object with keys named like
*     the abstracted events or event sequences that they are in charge of
*     handling.  The controls that wrap handlers define the methods that
*     correspond to these abstract events - so instead of listening for
*     individual browser events, they only listen for the abstract events
*     defined by the handler.
*
* Handlers are created by controls, which ultimately have the responsibility
*     of making changes to the the state of the application.  Handlers
*     themselves may make temporary changes, but in general are expected to
*     return the application in the same state that they found it.
*/
OpenLayers.Handler = OpenLayers.Class({
/**
* Property: id
* {String}
*/
id: null,
/**
* APIProperty: control
* {<OpenLayers.Control>}. The control that initialized this handler.  The
*     control is assumed to have a valid map property - that map is used
*     in the handler's own setMap method.
*/
control: null,
/**
* Property: map
* {<OpenLayers.Map>}
*/
map: null,
/**
* APIProperty: keyMask
* {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
*     constants to construct a keyMask.  The keyMask is used by
*     <checkModifiers>.  If the keyMask matches the combination of keys
*     down on an event, checkModifiers returns true.
*
* Example:
* (code)
*     // handler only responds if the Shift key is down
*     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
*
*     // handler only responds if Ctrl-Shift is down
*     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
*                       OpenLayers.Handler.MOD_CTRL;
* (end)
*/
keyMask: null,
/**
* Property: active
* {Boolean}
*/
active: false,
/**
* Property: evt
* {Event} This property references the last event handled by the handler.
*     Note that this property is not part of the stable API.  Use of the
*     evt property should be restricted to controls in the library
*     or other applications that are willing to update with changes to
*     the OpenLayers code.
*/
evt: null,
/**
* Constructor: OpenLayers.Handler
* Construct a handler.
*
* Parameters:
* control - {<OpenLayers.Control>} The control that initialized this
*     handler.  The control is assumed to have a valid map property; that
*     map is used in the handler's own setMap method.
* callbacks - {Object} An object whose properties correspond to abstracted
*     events or sequences of browser events.  The values for these
*     properties are functions defined by the control that get called by
*     the handler.
* options - {Object} An optional object whose properties will be set on
*     the handler.
*/
initialize: function(control, callbacks, options) {
OpenLayers.Util.extend(this, options);
this.control = control;
this.callbacks = callbacks;
if (control.map) {
this.setMap(control.map);
}
OpenLayers.Util.extend(this, options);
this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
},
/**
* Method: setMap
*/
setMap: function (map) {
this.map = map;
},
/**
* Method: checkModifiers
* Check the keyMask on the handler.  If no <keyMask> is set, this always
*     returns true.  If a <keyMask> is set and it matches the combination
*     of keys down on an event, this returns true.
*
* Returns:
* {Boolean} The keyMask matches the keys down on an event.
*/
checkModifiers: function (evt) {
if(this.keyMask == null) {
return true;
}
/* calculate the keyboard modifier mask for this event */
var keyModifiers =
(evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
(evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
(evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
/* if it differs from the handler object's key mask,
bail out of the event handler */
return (keyModifiers == this.keyMask);
},
/**
* APIMethod: activate
* Turn on the handler.  Returns false if the handler was already active.
*
* Returns:
* {Boolean} The handler was activated.
*/
activate: function() {
if(this.active) {
return false;
}
// register for event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
for (var i = 0; i < events.length; i++) {
if (this[events[i]]) {
this.register(events[i], this[events[i]]);
}
}
this.active = true;
return true;
},
/**
* APIMethod: deactivate
* Turn off the handler.  Returns false if the handler was already inactive.
*
* Returns:
* {Boolean} The handler was deactivated.
*/
deactivate: function() {
if(!this.active) {
return false;
}
// unregister event handlers defined on this class.
var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
for (var i = 0; i < events.length; i++) {
if (this[events[i]]) {
this.unregister(events[i], this[events[i]]);
}
}
this.active = false;
return true;
},
/**
* Method: callback
* Trigger the control's named callback with the given arguments
*
* Parameters:
* name - {String} The key for the callback that is one of the properties
*     of the handler's callbacks object.
* args - {Array(*)} An array of arguments (any type) with which to call
*     the callback (defined by the control).
*/
callback: function (name, args) {
if (name && this.callbacks[name]) {
this.callbacks[name].apply(this.control, args);
}
},
/**
* Method: register
* register an event on the map
*/
register: function (name, method) {
// TODO: deal with registerPriority in 3.0
this.map.events.registerPriority(name, this, method);
this.map.events.registerPriority(name, this, this.setEvent);
},
/**
* Method: unregister
* unregister an event from the map
*/
unregister: function (name, method) {
this.map.events.unregister(name, this, method);
this.map.events.unregister(name, this, this.setEvent);
},
/**
* Method: setEvent
* With each registered browser event, the handler sets its own evt
*     property.  This property can be accessed by controls if needed
*     to get more information about the event that the handler is
*     processing.
*
* This allows modifier keys on the event to be checked (alt, shift,
*     and ctrl cannot be checked with the keyboard handler).  For a
*     control to determine which modifier keys are associated with the
*     event that a handler is currently processing, it should access
*     (code)handler.evt.altKey || handler.evt.shiftKey ||
*     handler.evt.ctrlKey(end).
*
* Parameters:
* evt - {Event} The browser event.
*/
setEvent: function(evt) {
this.evt = evt;
return true;
},
/**
* Method: destroy
* Deconstruct the handler.
*/
destroy: function () {
// unregister event listeners
this.deactivate();
// eliminate circular references
this.control = this.map = null;
},
CLASS_NAME: "OpenLayers.Handler"
});
/**
* Constant: OpenLayers.Handler.MOD_NONE
* If set as the <keyMask>, <checkModifiers> returns false if any key is down.
*/
OpenLayers.Handler.MOD_NONE  = 0;
/**
* Constant: OpenLayers.Handler.MOD_SHIFT
* If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
*/
OpenLayers.Handler.MOD_SHIFT = 1;
/**
* Constant: OpenLayers.Handler.MOD_CTRL
* If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
*/
OpenLayers.Handler.MOD_CTRL  = 2;
/**
* Constant: OpenLayers.Handler.MOD_ALT
* If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
*/
OpenLayers.Handler.MOD_ALT   = 4;
/* 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/Handler.js
*/
/**
* Class: OpenLayers.Handler.Click
* A handler for mouse clicks.  The intention of this handler is to give
*     controls more flexibility with handling clicks.  Browsers trigger
*     click events twice for a double-click.  In addition, the mousedown,
*     mousemove, mouseup sequence fires a click event.  With this handler,
*     controls can decide whether to ignore clicks associated with a double
*     click.  By setting a <pixelTolerance>, controls can also ignore clicks
*     that include a drag.  Create a new instance with the
*     <OpenLayers.Handler.Click> constructor.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
/**
* APIProperty: delay
* {Number} Number of milliseconds between clicks before the event is
*     considered a double-click.
*/
delay: 300,
/**
* APIProperty: single
* {Boolean} Handle single clicks.  Default is true.  If false, clicks
* will not be reported.  If true, single-clicks will be reported.
*/
single: true,
/**
* APIProperty: double
* {Boolean} Handle double-clicks.  Default is false.
*/
'double': false,
/**
* APIProperty: pixelTolerance
* {Number} Maximum number of pixels between mouseup and mousedown for an
*     event to be considered a click.  Default is 0.  If set to an
*     integer value, clicks with a drag greater than the value will be
*     ignored.  This property can only be set when the handler is
*     constructed.
*/
pixelTolerance: 0,
/**
* APIProperty: stopSingle
* {Boolean} Stop other listeners from being notified of clicks.  Default
*     is false.  If true, any click listeners registered before this one
*     will not be notified of *any* click event (associated with double
*     or single clicks).
*/
stopSingle: false,
/**
* APIProperty: stopDouble
* {Boolean} Stop other listeners from being notified of double-clicks.
*     Default is false.  If true, any click listeners registered before
*     this one will not be notified of *any* double-click events.
*
* The one caveat with stopDouble is that given a map with two click
*     handlers, one with stopDouble true and the other with stopSingle
*     true, the stopSingle handler should be activated last to get
*     uniform cross-browser performance.  Since IE triggers one click
*     with a dblclick and FF triggers two, if a stopSingle handler is
*     activated first, all it gets in IE is a single click when the
*     second handler stops propagation on the dblclick.
*/
stopDouble: false,
/**
* Property: timerId
* {Number} The id of the timeout waiting to clear the <delayedCall>.
*/
timerId: null,
/**
* Property: down
* {<OpenLayers.Pixel>} The pixel location of the last mousedown.
*/
down: null,
/**
* Constructor: OpenLayers.Handler.Click
* Create a new click handler.
*
* Parameters:
* control - {<OpenLayers.Control>} The control that is making use of
*     this handler.  If a handler is being used without a control, the
*     handler's setMap method must be overridden to deal properly with
*     the map.
* callbacks - {Object} An object with keys corresponding to callbacks
*     that will be called by the handler. The callbacks should
*     expect to recieve a single argument, the click event.
*     Callbacks for 'click' and 'dblclick' are supported.
* options - {Object} Optional object whose properties will be set on the
*     handler.
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
// optionally register for mouseup and mousedown
if(this.pixelTolerance != null) {
this.mousedown = function(evt) {
this.down = evt.xy;
return true;
};
}
},
/**
* Method: mousedown
* Handle mousedown.  Only registered as a listener if pixelTolerance is
*     a non-zero value at construction.
*
* Returns:
* {Boolean} Continue propagating this event.
*/
mousedown: null,
/**
* Method: dblclick
* Handle dblclick.  For a dblclick, we get two clicks in some browsers
*     (FF) and one in others (IE).  So we need to always register for
*     dblclick to properly handle single clicks.
*
* Returns:
* {Boolean} Continue propagating this event.
*/
dblclick: function(evt) {
if(this.passesTolerance(evt)) {
if(this["double"]) {
this.callback('dblclick', [evt]);
}
this.clearTimer();
}
return !this.stopDouble;
},
/**
* Method: click
* Handle click.
*
* Returns:
* {Boolean} Continue propagating this event.
*/
click: function(evt) {
if(this.passesTolerance(evt)) {
if(this.timerId != null) {
// already received a click
this.clearTimer();
} else {
// set the timer, send evt only if single is true
//use a clone of the event object because it will no longer
//be a valid event object in IE in the timer callback
var clickEvent = this.single ?
OpenLayers.Util.extend({}, evt) : null;
this.timerId = window.setTimeout(
OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
this.delay
);
}
}
return !this.stopSingle;
},
/**
* Method: passesTolerance
* Determine whether the event is within the optional pixel tolerance.  Note
*     that the pixel tolerance check only works if mousedown events get to
*     the listeners registered here.  If they are stopped by other elements,
*     the <pixelTolerance> will have no effect here (this method will always
*     return true).
*
* Returns:
* {Boolean} The click is within the pixel tolerance (if specified).
*/
passesTolerance: function(evt) {
var passes = true;
if(this.pixelTolerance != null && this.down) {
var dpx = Math.sqrt(
Math.pow(this.down.x - evt.xy.x, 2) +
Math.pow(this.down.y - evt.xy.y, 2)
);
if(dpx > this.pixelTolerance) {
passes = false;
}
}
return passes;
},
/**
* Method: clearTimer
* Clear the timer and set <timerId> to null.
*/
clearTimer: function() {
if(this.timerId != null) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
},
/**
* Method: delayedCall
* Sets <timerId> to null.  And optionally triggers the click callback if
*     evt is set.
*/
delayedCall: function(evt) {
this.timerId = null;
if(evt) {
this.callback('click', [evt]);
}
},
/**
* APIMethod: deactivate
* Deactivate the handler.
*
* Returns:
* {Boolean} The handler was successfully deactivated.
*/
deactivate: function() {
var deactivated = false;
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
this.clearTimer();
this.down = null;
deactivated = true;
}
return deactivated;
},
CLASS_NAME: "OpenLayers.Handler.Click"
});
/* 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/Handler.js
*/
/**
* Class: OpenLayers.Handler.Hover
* The hover handler is to be used to emulate mouseovers on objects
*      on the map that aren't DOM elements. For example one can use
*      this handler to send WMS/GetFeatureInfo requests as the user
*      moves the mouve over the map.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
/**
* APIProperty: delay
* {Integer} - Number of milliseconds between mousemoves before
*      the event is considered a hover. Default is 500.
*/
delay: 500,
/**
* APIProperty: pixelTolerance
* {Integer} - Maximum number of pixels between mousemoves for
*      an event to be considered a hover. Default is null.
*/
pixelTolerance: null,
/**
* APIProperty: stopMove
* {Boolean} Stop other listeners from being notified on mousemoves.
*      Default is false.
*/
stopMove: false,
/**
* Property: px
* {<OpenLayers.Pixel>} The location of the last mousemove, expressed
*      in pixels.
*/
px: null,
/**
* Property: timerId
* {Number} The id of the timer.
*/
timerId: null,
/**
* Constructor: OpenLayers.Handler.Hover
* Construct a hover handler.
*
* Parameters:
* control - {<OpenLayers.Control>} The control that initialized this
*     handler.  The control is assumed to have a valid map property; that
*     map is used in the handler's own setMap method.
* callbacks - {Object} An object whose properties correspond to abstracted
*     events or sequences of browser events.  The values for these
*     properties are functions defined by the control that get called by
*     the handler.
* options - {Object} An optional object whose properties will be set on
*     the handler.
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
},
/**
* Method: mousemove
* Called when the mouse moves on the map.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*
* Returns:
* {Boolean} Continue propagating this event.
*/
mousemove: function(evt) {
if(this.passesTolerance(evt.xy)) {
this.clearTimer();
this.callback('move', [evt]);
this.px = evt.xy;
// clone the evt so original properties can be accessed even
// if the browser deletes them during the delay
evt = OpenLayers.Util.extend({}, evt);
this.timerId = window.setTimeout(
OpenLayers.Function.bind(this.delayedCall, this, evt),
this.delay
);
}
return !this.stopMove;
},
/**
* Method: mouseout
* Called when the mouse goes out of the map.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*
* Returns:
* {Boolean} Continue propagating this event.
*/
mouseout: function(evt) {
if (OpenLayers.Util.mouseLeft(evt, this.map.div)) {
this.clearTimer();
this.callback('move', [evt]);
}
return true;
},
/**
* Method: passesTolerance
* Determine whether the mouse move is within the optional pixel tolerance.
*
* Parameters:
* px - {<OpenLayers.Pixel>}
*
* Returns:
* {Boolean} The mouse move is within the pixel tolerance.
*/
passesTolerance: function(px) {
var passes = true;
if(this.pixelTolerance && this.px) {
var dpx = Math.sqrt(
Math.pow(this.px.x - px.x, 2) +
Math.pow(this.px.y - px.y, 2)
);
if(dpx < this.pixelTolerance) {
passes = false;
}
}
return passes;
},
/**
* Method: clearTimer
* Clear the timer and set <timerId> to null.
*/
clearTimer: function() {
if(this.timerId != null) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
},
/**
* Method: delayedCall
* Triggers pause callback.
*
* Parameters:
* evt - {<OpenLayers.Event>}
*/
delayedCall: function(evt) {
this.callback('pause', [evt]);
},
/**
* APIMethod: deactivate
* Deactivate the handler.
*
* Returns:
* {Boolean} The handler was successfully deactivated.
*/
deactivate: function() {
var deactivated = false;
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
this.clearTimer();
deactivated = true;
}
return deactivated;
},
CLASS_NAME: "OpenLayers.Handler.Hover"
});
/* 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/Handler.js
* @requires OpenLayers/Geometry/Point.js
*/
/**
* Class: OpenLayers.Handler.Point
* Handler to draw a point on the map.  Point is displayed on mouse down,
* moves on mouse move, and is finished on mouse up.  The handler triggers
* callbacks for 'done' and 'cancel'.  Create a new instance with the
* <OpenLayers.Handler.Point> constructor.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: point
* {<OpenLayers.Feature.Vector>} The currently drawn point
*/
point: null,
/**
* Property: layer
* {<OpenLayers.Layer.Vector>} The temporary drawing layer
*/
layer: null,
/**
* Property: drawing
* {Boolean} A point is being drawn
*/
drawing: false,
/**
* Property: mouseDown
* {Boolean} The mouse is down
*/
mouseDown: false,
/**
* Property: lastDown
* {<OpenLayers.Pixel>} Location of the last mouse down
*/
lastDown: null,
/**
* Property: lastUp
* {<OpenLayers.Pixel>}
*/
lastUp: null,
/**
* Constructor: OpenLayers.Handler.Point
* Create a new point handler.
*
* Parameters:
* control - {<OpenLayers.Control>} The control that owns this handler
* callbacks - {Object} An object with a 'done' property whose value is a
*             function to be called when the point drawing is finished.
*             The callback should expect to recieve a single argument,
*             the point geometry.  If the callbacks object contains a
*             'cancel' property, this function will be called when the
*             handler is deactivated while drawing.  The cancel should
*             expect to receive a geometry.
* options - {Object} An optional object with properties to be set on the
*           handler
*/
initialize: function(control, callbacks, options) {
// TBD: deal with style
this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
},
/**
* APIMethod: activate
* turn on the handler
*/
activate: function() {
if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
return false;
}
// create temporary vector layer for rendering geometry sketch
// TBD: this could be moved to initialize/destroy - setting visibility here
var options = {
displayInLayerSwitcher: false,
// indicate that the temp vector layer will never be out of range
// without this, resolution properties must be specified at the
// map-level for this temporary layer to init its resolutions
// correctly
calculateInRange: function() { return true; }
};
this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
this.map.addLayer(this.layer);
return true;
},
/**
* Method: createFeature
* Add temporary features
*/
createFeature: function() {
this.point = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point());
},
/**
* APIMethod: deactivate
* turn off the handler
*/
deactivate: function() {
if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
return false;
}
// call the cancel callback if mid-drawing
if(this.drawing) {
this.cancel();
}
// If a layer's map property is set to null, it means that that layer
// isn't added to the map. Since we ourself added the layer to the map
// in activate(), we can assume that if this.layer.map is null it means
// that the layer has been destroyed (as a result of map.destroy() for
// example.
if (this.layer.map != null) {
this.layer.destroy(false);
}
this.layer = null;
return true;
},
/**
* Method: destroyFeature
* Destroy the temporary geometries
*/
destroyFeature: function() {
if(this.point) {
this.point.destroy();
}
this.point = null;
},
/**
* Method: finalize
* Finish the geometry and call the "done" callback.
*/
finalize: function() {
this.layer.renderer.clear();
this.drawing = false;
this.mouseDown = false;
this.lastDown = null;
this.lastUp = null;
this.callback("done", [this.geometryClone()]);
this.destroyFeature();
},
/**
* APIMethod: cancel
* Finish the geometry and call the "cancel" callback.
*/
cancel: function() {
this.layer.renderer.clear();
this.drawing = false;
this.mouseDown = false;
this.lastDown = null;
this.lastUp = null;
this.callback("cancel", [this.geometryClone()]);
this.destroyFeature();
},
/**
* Method: click
* Handle clicks.  Clicks are stopped from propagating to other listeners
*     on map.events or other dom elements.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
click: function(evt) {
OpenLayers.Event.stop(evt);
return false;
},
/**
* Method: dblclick
* Handle double-clicks.  Double-clicks are stopped from propagating to other
*     listeners on map.events or other dom elements.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
dblclick: function(evt) {
OpenLayers.Event.stop(evt);
return false;
},
/**
* Method: drawFeature
* Render features on the temporary layer.
*/
drawFeature: function() {
this.layer.drawFeature(this.point, this.style);
},
/**
* Method: geometryClone
* Return a clone of the relevant geometry.
*
* Returns:
* {<OpenLayers.Geometry.Point>}
*/
geometryClone: function() {
return this.point.geometry.clone();
},
/**
* Method: mousedown
* Handle mouse down.  Adjust the geometry and redraw.
* Return determines whether to propagate the event on the map.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
mousedown: function(evt) {
// check keyboard modifiers
if(!this.checkModifiers(evt)) {
return true;
}
// ignore double-clicks
if(this.lastDown && this.lastDown.equals(evt.xy)) {
return true;
}
if(this.lastDown == null) {
this.createFeature();
}
this.lastDown = evt.xy;
this.drawing = true;
var lonlat = this.map.getLonLatFromPixel(evt.xy);
this.point.geometry.x = lonlat.lon;
this.point.geometry.y = lonlat.lat;
this.drawFeature();
return false;
},
/**
* Method: mousemove
* Handle mouse move.  Adjust the geometry and redraw.
* Return determines whether to propagate the event on the map.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
mousemove: function (evt) {
if(this.drawing) {
var lonlat = this.map.getLonLatFromPixel(evt.xy);
this.point.geometry.x = lonlat.lon;
this.point.geometry.y = lonlat.lat;
this.point.geometry.clearBounds();
this.drawFeature();
}
return true;
},
/**
* Method: mouseup
* Handle mouse up.  Send the latest point in the geometry to the control.
* Return determines whether to propagate the event on the map.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
mouseup: function (evt) {
if(this.drawing) {
this.finalize();
return false;
} else {
return true;
}
},
CLASS_NAME: "OpenLayers.Handler.Point"
});
/* 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/Handler.js
*/
/**
* Class: OpenLayers.Handler.Feature
* Handler to respond to mouse events related to a drawn feature.  Callbacks
*     with the following keys will be notified of the following events
*     associated with features: click, clickout, over, out, and dblclick.
*
* This handler stops event propagation for mousedown and mouseup if those
*     browser events target features that can be selected.
*/
OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: EVENTMAP
* {Object} A object mapping the browser events to objects with callback
*     keys for in and out.
*/
EVENTMAP: {
'click': {'in': 'click', 'out': 'clickout'},
'mousemove': {'in': 'over', 'out': 'out'},
'dblclick': {'in': 'dblclick', 'out': null},
'mousedown': {'in': null, 'out': null},
'mouseup': {'in': null, 'out': null}
},
/**
* Property: feature
* {<OpenLayers.Feature.Vector>} The last feature that was hovered.
*/
feature: null,
/**
* Property: lastFeature
* {<OpenLayers.Feature.Vector>} The last feature that was handled.
*/
lastFeature: null,
/**
* Property: down
* {<OpenLayers.Pixel>} The location of the last mousedown.
*/
down: null,
/**
* Property: up
* {<OpenLayers.Pixel>} The location of the last mouseup.
*/
up: null,
/**
* Property: clickoutTolerance
* {Number} The number of pixels the mouse can move during a click that
*     still constitutes a click out.  When dragging the map, clicks should
*     not trigger the clickout property unless this tolerance is reached.
*     Default is 4.
*/
clickoutTolerance: 4,
/**
* Property: geometryTypes
* To restrict dragging to a limited set of geometry types, send a list
* of strings corresponding to the geometry class names.
*
* @type Array(String)
*/
geometryTypes: null,
/**
* Property: stopClick
* {Boolean} If stopClick is set to true, handled clicks do not
*      propagate to other click listeners. Otherwise, handled clicks
*      do propagate. Unhandled clicks always propagate, whatever the
*      value of stopClick. Defaults to true.
*/
stopClick: true,
/**
* Property: stopDown
* {Boolean} If stopDown is set to true, handled mousedowns do not
*      propagate to other mousedown listeners. Otherwise, handled
*      mousedowns do propagate. Unhandled mousedowns always propagate,
*      whatever the value of stopDown. Defaults to true.
*/
stopDown: true,
/**
* Property: stopUp
* {Boolean} If stopUp is set to true, handled mouseups do not
*      propagate to other mouseup listeners. Otherwise, handled mouseups
*      do propagate. Unhandled mouseups always propagate, whatever the
*      value of stopUp. Defaults to true.
*/
stopUp: true,
/**
* Property: layerIndex
* {Int}
*/
layerIndex: null,
/**
* Constructor: OpenLayers.Handler.Feature
*
* Parameters:
* control - {<OpenLayers.Control>}
* layers - {Array(<OpenLayers.Layer.Vector>)}
* callbacks - {Object} An object with a 'over' property whos value is
*     a function to be called when the mouse is over a feature. The
*     callback should expect to recieve a single argument, the feature.
* options - {Object}
*/
initialize: function(control, layer, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
this.layer = layer;
},
/**
* Method: mousedown
* Handle mouse down.  Stop propagation if a feature is targeted by this
*     event (stops map dragging during feature selection).
*
* Parameters:
* evt - {Event}
*/
mousedown: function(evt) {
this.down = evt.xy;
return this.handle(evt) ? !this.stopDown : true;
},
/**
* Method: mouseup
* Handle mouse up.  Stop propagation if a feature is targeted by this
*     event.
*
* Parameters:
* evt - {Event}
*/
mouseup: function(evt) {
this.up = evt.xy;
return this.handle(evt) ? !this.stopUp : true;
},
/**
* Method: click
* Handle click.  Call the "click" callback if click on a feature,
*     or the "clickout" callback if click outside any feature.
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean}
*/
click: function(evt) {
return this.handle(evt) ? !this.stopClick : true;
},
/**
* Method: mousemove
* Handle mouse moves.  Call the "over" callback if moving in to a feature,
*     or the "out" callback if moving out of a feature.
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean}
*/
mousemove: function(evt) {
this.handle(evt);
return true;
},
/**
* Method: dblclick
* Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean}
*/
dblclick: function(evt) {
return !this.handle(evt);
},
/**
* Method: geometryTypeMatches
* Return true if the geometry type of the passed feature matches
*     one of the geometry types in the geometryTypes array.
*
* Parameters:
* feature - {<OpenLayers.Vector.Feature>}
*
* Returns:
* {Boolean}
*/
geometryTypeMatches: function(feature) {
return this.geometryTypes == null ||
OpenLayers.Util.indexOf(this.geometryTypes,
feature.geometry.CLASS_NAME) > -1;
},
/**
* Method: handle
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} The event occurred over a relevant feature.
*/
handle: function(evt) {
var type = evt.type;
var handled = false;
var previouslyIn = !!(this.feature); // previously in a feature
var click = (type == "click" || type == "dblclick");
this.feature = this.layer.getFeatureFromEvent(evt);
if(this.feature) {
var inNew = (this.feature != this.lastFeature);
if(this.geometryTypeMatches(this.feature)) {
// in to a feature
if(previouslyIn && inNew) {
// out of last feature and in to another
this.triggerCallback(type, 'out', [this.lastFeature]);
this.triggerCallback(type, 'in', [this.feature]);
} else if(!previouslyIn || click) {
// in feature for the first time
this.triggerCallback(type, 'in', [this.feature]);
}
this.lastFeature = this.feature;
handled = true;
} else {
// not in to a feature
if(previouslyIn && inNew || (click && this.lastFeature)) {
// out of last feature for the first time
this.triggerCallback(type, 'out', [this.lastFeature]);
}
// next time the mouse goes in a feature whose geometry type
// doesn't match we don't want to call the 'out' callback
// again, so let's set this.feature to null so that
// previouslyIn will evaluate to false the next time
// we enter handle. Yes, a bit hackish...
this.feature = null;
}
} else {
if(previouslyIn || (click && this.lastFeature)) {
this.triggerCallback(type, 'out', [this.lastFeature]);
}
}
return handled;
},
/**
* Method: triggerCallback
* Call the callback keyed in the event map with the supplied arguments.
*     For click out, the <clickoutTolerance> is checked first.
*
* Parameters:
* type - {String}
*/
triggerCallback: function(type, mode, args) {
var key = this.EVENTMAP[type][mode];
if(key) {
if(type == 'click' && mode == 'out' && this.up && this.down) {
// for clickout, only trigger callback if tolerance is met
var dpx = Math.sqrt(
Math.pow(this.up.x - this.down.x, 2) +
Math.pow(this.up.y - this.down.y, 2)
);
if(dpx <= this.clickoutTolerance) {
this.callback(key, args);
}
} else {
this.callback(key, args);
}
}
},
/**
* Method: activate
* Turn on the handler.  Returns false if the handler was already active.
*
* Returns:
* {Boolean}
*/
activate: function() {
var activated = false;
if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
this.layerIndex = this.layer.div.style.zIndex;
this.layer.div.style.zIndex = this.map.Z_INDEX_BASE['Popup'] - 1;
activated = true;
}
return activated;
},
/**
* Method: activate
* Turn of the handler.  Returns false if the handler was already active.
*
* Returns:
* {Boolean}
*/
deactivate: function() {
var deactivated = false;
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
if (this.layer && this.layer.div) {
this.layer.div.style.zIndex = this.layerIndex;
}
this.feature = null;
this.lastFeature = null;
this.down = null;
this.up = null;
deactivated = true;
}
return deactivated;
},
CLASS_NAME: "OpenLayers.Handler.Feature"
});
/* 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/Handler.js
*/
/**
* Class: OpenLayers.Handler.Drag
* The drag handler is used to deal with sequences of browser events related
*     to dragging.  The handler is used by controls that want to know when
*     a drag sequence begins, when a drag is happening, and when it has
*     finished.
*
* Controls that use the drag handler typically construct it with callbacks
*     for 'down', 'move', and 'done'.  Callbacks for these keys are called
*     when the drag begins, with each move, and when the drag is done.  In
*     addition, controls can have callbacks keyed to 'up' and 'out' if they
*     care to differentiate between the types of events that correspond with
*     the end of a drag sequence.  If no drag actually occurs (no mouse move)
*     the 'down' and 'up' callbacks will be called, but not the 'done'
*     callback.
*
* Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: started
* {Boolean} When a mousedown event is received, we want to record it, but
*     not set 'dragging' until the mouse moves after starting.
*/
started: false,
/**
* Property: stopDown
* {Boolean} Stop propagation of mousedown events from getting to listeners
*     on the same element.  Default is true.
*/
stopDown: true,
/**
* Property: dragging
* {Boolean}
*/
dragging: false,
/**
* Property: last
* {<OpenLayers.Pixel>} The last pixel location of the drag.
*/
last: null,
/**
* Property: start
* {<OpenLayers.Pixel>} The first pixel location of the drag.
*/
start: null,
/**
* Property: oldOnselectstart
* {Function}
*/
oldOnselectstart: null,
/**
* Constructor: OpenLayers.Handler.Drag
* Returns OpenLayers.Handler.Drag
*
* Parameters:
* control - {<OpenLayers.Control>} The control that is making use of
*     this handler.  If a handler is being used without a control, the
*     handlers setMap method must be overridden to deal properly with
*     the map.
* callbacks - {Object} An object containing a single function to be
*     called when the drag operation is finished. The callback should
*     expect to recieve a single argument, the pixel location of the event.
*     Callbacks for 'move' and 'done' are supported. You can also speficy
*     callbacks for 'down', 'up', and 'out' to respond to those events.
* options - {Object}
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
},
/**
* The four methods below (down, move, up, and out) are used by subclasses
*     to do their own processing related to these mouse events.
*/
/**
* Method: down
* This method is called during the handling of the mouse down event.
*     Subclasses can do their own processing here.
*
* Parameters:
* evt - {Event} The mouse down event
*/
down: function(evt) {
},
/**
* Method: move
* This method is called during the handling of the mouse move event.
*     Subclasses can do their own processing here.
*
* Parameters:
* evt - {Event} The mouse move event
*
*/
move: function(evt) {
},
/**
* Method: up
* This method is called during the handling of the mouse up event.
*     Subclasses can do their own processing here.
*
* Parameters:
* evt - {Event} The mouse up event
*/
up: function(evt) {
},
/**
* Method: out
* This method is called during the handling of the mouse out event.
*     Subclasses can do their own processing here.
*
* Parameters:
* evt - {Event} The mouse out event
*/
out: function(evt) {
},
/**
* The methods below are part of the magic of event handling.  Because
*     they are named like browser events, they are registered as listeners
*     for the events they represent.
*/
/**
* Method: mousedown
* Handle mousedown events
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} Let the event propagate.
*/
mousedown: function (evt) {
var propagate = true;
this.dragging = false;
if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
this.started = true;
this.start = evt.xy;
this.last = evt.xy;
// TBD replace with CSS classes
this.map.div.style.cursor = "move";
this.down(evt);
this.callback("down", [evt.xy]);
OpenLayers.Event.stop(evt);
if(!this.oldOnselectstart) {
this.oldOnselectstart = (document.onselectstart) ? document.onselectstart : function() { return true; };
document.onselectstart = function() {return false;};
}
propagate = !this.stopDown;
} else {
this.started = false;
this.start = null;
this.last = null;
}
return propagate;
},
/**
* Method: mousemove
* Handle mousemove events
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} Let the event propagate.
*/
mousemove: function (evt) {
if (this.started) {
if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
this.dragging = true;
this.move(evt);
this.callback("move", [evt.xy]);
if(!this.oldOnselectstart) {
this.oldOnselectstart = document.onselectstart;
document.onselectstart = function() {return false;};
}
this.last = evt.xy;
}
}
return true;
},
/**
* Method: mouseup
* Handle mouseup events
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} Let the event propagate.
*/
mouseup: function (evt) {
if (this.started) {
var dragged = (this.start != this.last);
this.started = false;
this.dragging = false;
// TBD replace with CSS classes
this.map.div.style.cursor = "";
this.up(evt);
this.callback("up", [evt.xy]);
if(dragged) {
this.callback("done", [evt.xy]);
}
document.onselectstart = this.oldOnselectstart;
}
return true;
},
/**
* Method: mouseout
* Handle mouseout events
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} Let the event propagate.
*/
mouseout: function (evt) {
if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
var dragged = (this.start != this.last);
this.started = false;
this.dragging = false;
// TBD replace with CSS classes
this.map.div.style.cursor = "";
this.out(evt);
this.callback("out", []);
if(dragged) {
this.callback("done", [evt.xy]);
}
if(document.onselectstart) {
document.onselectstart = this.oldOnselectstart;
}
}
return true;
},
/**
* Method: click
* The drag handler captures the click event.  If something else registers
*     for clicks on the same element, its listener will not be called
*     after a drag.
*
* Parameters:
* evt - {Event}
*
* Returns:
* {Boolean} Let the event propagate.
*/
click: function (evt) {
// let the click event propagate only if the mouse moved
return (this.start == this.last);
},
/**
* Method: activate
* Activate the handler.
*
* Returns:
* {Boolean} The handler was successfully activated.
*/
activate: function() {
var activated = false;
if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
this.dragging = false;
activated = true;
}
return activated;
},
/**
* Method: deactivate
* Deactivate the handler.
*
* Returns:
* {Boolean} The handler was successfully deactivated.
*/
deactivate: function() {
var deactivated = false;
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
this.started = false;
this.dragging = false;
this.start = null;
this.last = null;
deactivated = true;
}
return deactivated;
},
CLASS_NAME: "OpenLayers.Handler.Drag"
});
/* 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/Handler.js
* @requires OpenLayers/Handler/Drag.js
*/
/**
* Class: OpenLayers.Handler.Box
* Handler for dragging a rectangle across the map.  Box is displayed
* on mouse down, moves on mouse move, and is finished on mouse up.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: dragHandler
* {<OpenLayers.Handler.Drag>}
*/
dragHandler: null,
/**
* APIProperty: boxDivClassName
* {String} The CSS class to use for drawing the box. Default is
*     olHandlerBoxZoomBox
*/
boxDivClassName: 'olHandlerBoxZoomBox',
/**
* Constructor: OpenLayers.Handler.Box
*
* Parameters:
* control - {<OpenLayers.Control>}
* callbacks - {Object} An object containing a single function to be
*                          called when the drag operation is finished.
*                          The callback should expect to recieve a single
*                          argument, the point geometry.
* options - {Object}
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
var callbacks = {
"down": this.startBox,
"move": this.moveBox,
"out":  this.removeBox,
"up":   this.endBox
};
this.dragHandler = new OpenLayers.Handler.Drag(
this, callbacks, {keyMask: this.keyMask});
},
/**
* Method: setMap
*/
setMap: function (map) {
OpenLayers.Handler.prototype.setMap.apply(this, arguments);
if (this.dragHandler) {
this.dragHandler.setMap(map);
}
},
/**
* Method: startBox
*
* Parameters:
* evt - {Event}
*/
startBox: function (xy) {
this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
this.dragHandler.start);
this.zoomBox.className = this.boxDivClassName;
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.viewPortDiv.appendChild(this.zoomBox);
// TBD: use CSS classes instead
this.map.div.style.cursor = "crosshair";
},
/**
* Method: moveBox
*/
moveBox: function (xy) {
var deltaX = Math.abs(this.dragHandler.start.x - xy.x);
var deltaY = Math.abs(this.dragHandler.start.y - xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
if (xy.x < this.dragHandler.start.x) {
this.zoomBox.style.left = xy.x+"px";
}
if (xy.y < this.dragHandler.start.y) {
this.zoomBox.style.top = xy.y+"px";
}
},
/**
* Method: endBox
*/
endBox: function(end) {
var result;
if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
Math.abs(this.dragHandler.start.y - end.y) > 5) {
var start = this.dragHandler.start;
var top = Math.min(start.y, end.y);
var bottom = Math.max(start.y, end.y);
var left = Math.min(start.x, end.x);
var right = Math.max(start.x, end.x);
result = new OpenLayers.Bounds(left, bottom, right, top);
} else {
result = this.dragHandler.start.clone(); // i.e. OL.Pixel
}
this.removeBox();
// TBD: use CSS classes instead
this.map.div.style.cursor = "";
this.callback("done", [result]);
},
/**
* Method: removeBox
* Remove the zoombox from the screen and nullify our reference to it.
*/
removeBox: function() {
this.map.viewPortDiv.removeChild(this.zoomBox);
this.zoomBox = null;
},
/**
* Method: activate
*/
activate: function () {
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
this.dragHandler.activate();
return true;
} else {
return false;
}
},
/**
* Method: deactivate
*/
deactivate: function () {
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
this.dragHandler.deactivate();
return true;
} else {
return false;
}
},
CLASS_NAME: "OpenLayers.Handler.Box"
});
/* 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/Handler.js
*/
/**
* Class: OpenLayers.Handler.MouseWheel
* Handler for wheel up/down events.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: wheelListener
* {function}
*/
wheelListener: null,
/**
* Property: mousePosition
* {<OpenLayers.Pixel>} mousePosition is necessary because
* evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
* value from the last mousemove.
*/
mousePosition: null,
/**
* Constructor: OpenLayers.Handler.MouseWheel
*
* Parameters:
* control - {<OpenLayers.Control>}
* callbacks - {Object} An object containing a single function to be
*                          called when the drag operation is finished.
*                          The callback should expect to recieve a single
*                          argument, the point geometry.
* options - {Object}
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
this.wheelListener = OpenLayers.Function.bindAsEventListener(
this.onWheelEvent, this
);
},
/**
* Method: destroy
*/
destroy: function() {
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
this.wheelListener = null;
},
/**
*  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
*/
/**
* Method: onWheelEvent
* Catch the wheel event and handle it xbrowserly
*
* Parameters:
* e - {Event}
*/
onWheelEvent: function(e){
// make sure we have a map and check keyboard modifiers
if (!this.map || !this.checkModifiers(e)) {
return;
}
// Ride up the element's DOM hierarchy to determine if it or any of
//  its ancestors was:
//   * specifically marked as scrollable
//   * one of our layer divs
//   * the map div
//
var overScrollableDiv = false;
var overLayerDiv = false;
var overMapDiv = false;
var elem = OpenLayers.Event.element(e);
while((elem != null) && !overMapDiv && !overScrollableDiv) {
if (!overScrollableDiv) {
try {
if (elem.currentStyle) {
overflow = elem.currentStyle["overflow"];
} else {
var style =
document.defaultView.getComputedStyle(elem, null);
var overflow = style.getPropertyValue("overflow");
}
overScrollableDiv = ( overflow &&
(overflow == "auto") || (overflow == "scroll") );
} catch(err) {
//sometimes when scrolling in a popup, this causes
// obscure browser error
}
}
if (!overLayerDiv) {
for(var i=0; i < this.map.layers.length; i++) {
// Are we in the layer div? Note that we have two cases
// here: one is to catch EventPane layers, which have a
// pane above the layer (layer.pane)
if (elem == this.map.layers[i].div
|| elem == this.map.layers[i].pane) {
overLayerDiv = true;
break;
}
}
}
overMapDiv = (elem == this.map.div);
elem = elem.parentNode;
}
// Logic below is the following:
//
// If we are over a scrollable div or not over the map div:
//  * do nothing (let the browser handle scrolling)
//
//    otherwise
//
//    If we are over the layer div:
//     * zoom/in out
//     then
//     * kill event (so as not to also scroll the page after zooming)
//
//       otherwise
//
//       Kill the event (dont scroll the page if we wheel over the
//        layerswitcher or the pan/zoom control)
//
if (!overScrollableDiv && overMapDiv) {
if (overLayerDiv) {
this.wheelZoom(e);
}
OpenLayers.Event.stop(e);
}
},
/**
* Method: wheelZoom
* Given the wheel event, we carry out the appropriate zooming in or out,
*     based on the 'wheelDelta' or 'detail' property of the event.
*
* Parameters:
* e - {Event}
*/
wheelZoom: function(e) {
var delta = 0;
if (!e) {
e = window.event;
}
if (e.wheelDelta) {
delta = e.wheelDelta/120;
if (window.opera && window.opera.version() < 9.2) {
delta = -delta;
}
} else if (e.detail) {
delta = -e.detail / 3;
}
if (delta) {
// add the mouse position to the event because mozilla has
// a bug with clientX and clientY (see
// https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
// getLonLatFromViewPortPx(e) returns wrong values
if (this.mousePosition) {
e.xy = this.mousePosition;
}
if (!e.xy) {
// If the mouse hasn't moved over the map yet, then
// we don't have a mouse position (in FF), so we just
// act as if the mouse was at the center of the map.
// Note that we can tell we are in the map -- and
// this.map is ensured to be true above.
e.xy = this.map.getPixelFromLonLat(
this.map.getCenter()
);
}
if (delta < 0) {
this.callback("down", [e, delta]);
} else {
this.callback("up", [e, delta]);
}
}
},
/**
* Method: mousemove
* Update the stored mousePosition on every move.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
mousemove: function (evt) {
this.mousePosition = evt.xy;
},
/**
* Method: activate
*/
activate: function (evt) {
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
//register mousewheel events specifically on the window and document
var wheelListener = this.wheelListener;
OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
OpenLayers.Event.observe(window, "mousewheel", wheelListener);
OpenLayers.Event.observe(document, "mousewheel", wheelListener);
return true;
} else {
return false;
}
},
/**
* Method: deactivate
*/
deactivate: function (evt) {
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
// unregister mousewheel events specifically on the window and document
var wheelListener = this.wheelListener;
OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
return true;
} else {
return false;
}
},
CLASS_NAME: "OpenLayers.Handler.MouseWheel"
});
/* 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/Handler.js
* @requires OpenLayers/Events.js
*/
/**
* Class: OpenLayers.handler.Keyboard
* A handler for keyboard events.  Create a new instance with the
*     <OpenLayers.Handler.Keyboard> constructor.
*
* Inherits from:
*  - <OpenLayers.Handler>
*/
OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {
/* http://www.quirksmode.org/js/keys.html explains key x-browser
key handling quirks in pretty nice detail */
/**
* Constant: KEY_EVENTS
* keydown, keypress, keyup
*/
KEY_EVENTS: ["keydown", "keypress", "keyup"],
/**
* Property: eventListener
* {Function}
*/
eventListener: null,
/**
* Constructor: OpenLayers.Handler.Keyboard
* Returns a new keyboard handler.
*
* Parameters:
* control - {<OpenLayers.Control>} The control that is making use of
*     this handler.  If a handler is being used without a control, the
*     handlers setMap method must be overridden to deal properly with
*     the map.
* callbacks - {Object} An object containing a single function to be
*     called when the drag operation is finished. The callback should
*     expect to recieve a single argument, the pixel location of the event.
*     Callbacks for 'keydown', 'keypress', and 'keyup' are supported.
* options - {Object} Optional object whose properties will be set on the
*     handler.
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
// cache the bound event listener method so it can be unobserved later
this.eventListener = OpenLayers.Function.bindAsEventListener(
this.handleKeyEvent, this
);
},
/**
* Method: destroy
*/
destroy: function() {
this.deactivate();
this.eventListener = null;
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
},
/**
* Method: activate
*/
activate: function() {
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
for (var i = 0; i < this.KEY_EVENTS.length; i++) {
OpenLayers.Event.observe(
window, this.KEY_EVENTS[i], this.eventListener);
}
return true;
} else {
return false;
}
},
/**
* Method: deactivate
*/
deactivate: function() {
var deactivated = false;
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
for (var i = 0; i < this.KEY_EVENTS.length; i++) {
OpenLayers.Event.stopObserving(
window, this.KEY_EVENTS[i], this.eventListener);
}
deactivated = true;
}
return deactivated;
},
/**
* Method: handleKeyEvent
*/
handleKeyEvent: function (evt) {
if (this.checkModifiers(evt)) {
this.callback(evt.type, [evt.charCode || evt.keyCode]);
}
},
CLASS_NAME: "OpenLayers.Handler.Keyboard"
});