} the new size for the popup's
│ │ │ │ + * contents div (in pixels).
│ │ │ │ */
│ │ │ │ - move: function(location) {
│ │ │ │ + setSize: function(contentSize) {
│ │ │ │ + this.size = contentSize.clone();
│ │ │ │
│ │ │ │ - if (!this.layer || !this.geometry.move) {
│ │ │ │ - //do nothing if no layer or immoveable geometry
│ │ │ │ - return undefined;
│ │ │ │ + // if our contentDiv has a css 'padding' set on it by a stylesheet, we
│ │ │ │ + // must add that to the desired "size".
│ │ │ │ + var contentDivPadding = this.getContentDivPadding();
│ │ │ │ + var wPadding = contentDivPadding.left + contentDivPadding.right;
│ │ │ │ + var hPadding = contentDivPadding.top + contentDivPadding.bottom;
│ │ │ │ +
│ │ │ │ + // take into account the popup's 'padding' property
│ │ │ │ + this.fixPadding();
│ │ │ │ + wPadding += this.padding.left + this.padding.right;
│ │ │ │ + hPadding += this.padding.top + this.padding.bottom;
│ │ │ │ +
│ │ │ │ + // make extra space for the close div
│ │ │ │ + if (this.closeDiv) {
│ │ │ │ + var closeDivWidth = parseInt(this.closeDiv.style.width);
│ │ │ │ + wPadding += closeDivWidth + contentDivPadding.right;
│ │ │ │ }
│ │ │ │
│ │ │ │ - var pixel;
│ │ │ │ - if (location.CLASS_NAME == "OpenLayers.LonLat") {
│ │ │ │ - pixel = this.layer.getViewPortPxFromLonLat(location);
│ │ │ │ - } else {
│ │ │ │ - pixel = location;
│ │ │ │ + //increase size of the main popup div to take into account the
│ │ │ │ + // users's desired padding and close div.
│ │ │ │ + this.size.w += wPadding;
│ │ │ │ + this.size.h += hPadding;
│ │ │ │ +
│ │ │ │ + //now if our browser is IE, we need to actually make the contents
│ │ │ │ + // div itself bigger to take its own padding into effect. this makes
│ │ │ │ + // me want to shoot someone, but so it goes.
│ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") {
│ │ │ │ + this.contentSize.w +=
│ │ │ │ + contentDivPadding.left + contentDivPadding.right;
│ │ │ │ + this.contentSize.h +=
│ │ │ │ + contentDivPadding.bottom + contentDivPadding.top;
│ │ │ │ }
│ │ │ │
│ │ │ │ - var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
│ │ │ │ - var res = this.layer.map.getResolution();
│ │ │ │ - this.geometry.move(res * (pixel.x - lastPixel.x),
│ │ │ │ - res * (lastPixel.y - pixel.y));
│ │ │ │ - this.layer.drawFeature(this);
│ │ │ │ - return lastPixel;
│ │ │ │ + if (this.div != null) {
│ │ │ │ + this.div.style.width = this.size.w + "px";
│ │ │ │ + this.div.style.height = this.size.h + "px";
│ │ │ │ + }
│ │ │ │ + if (this.contentDiv != null) {
│ │ │ │ + this.contentDiv.style.width = contentSize.w + "px";
│ │ │ │ + this.contentDiv.style.height = contentSize.h + "px";
│ │ │ │ + }
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: toState
│ │ │ │ - * Sets the new state
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * state - {String}
│ │ │ │ + * APIMethod: updateSize
│ │ │ │ + * Auto size the popup so that it precisely fits its contents (as
│ │ │ │ + * determined by this.contentDiv.innerHTML). Popup size will, of
│ │ │ │ + * course, be limited by the available space on the current map
│ │ │ │ */
│ │ │ │ - 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. These
│ │ │ │ - * styles correspond for the most part, to the styling properties defined
│ │ │ │ - * by the SVG standard.
│ │ │ │ - * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
│ │ │ │ - * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
│ │ │ │ - *
│ │ │ │ - * Symbolizer properties:
│ │ │ │ - * fill - {Boolean} Set to false if no fill is desired.
│ │ │ │ - * fillColor - {String} Hex fill color. Default is "#ee9900".
│ │ │ │ - * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
│ │ │ │ - * stroke - {Boolean} Set to false if no stroke is desired.
│ │ │ │ - * strokeColor - {String} Hex stroke color. Default is "#ee9900".
│ │ │ │ - * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
│ │ │ │ - * strokeWidth - {Number} Pixel stroke width. Default is 1.
│ │ │ │ - * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
│ │ │ │ - * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
│ │ │ │ - * graphic - {Boolean} Set to false if no graphic is desired.
│ │ │ │ - * pointRadius - {Number} Pixel point radius. Default is 6.
│ │ │ │ - * pointerEvents - {String} Default is "visiblePainted".
│ │ │ │ - * cursor - {String} Default is "".
│ │ │ │ - * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
│ │ │ │ - * graphicWidth - {Number} Pixel width for sizing an external graphic.
│ │ │ │ - * graphicHeight - {Number} Pixel height for sizing an external graphic.
│ │ │ │ - * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
│ │ │ │ - * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
│ │ │ │ - * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
│ │ │ │ - * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
│ │ │ │ - * graphicZIndex - {Number} The integer z-index value to use in rendering.
│ │ │ │ - * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
│ │ │ │ - * "square", "star", "x", "cross", "triangle".
│ │ │ │ - * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
│ │ │ │ - * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
│ │ │ │ - * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
│ │ │ │ - * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
│ │ │ │ - * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
│ │ │ │ - * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
│ │ │ │ - * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
│ │ │ │ - * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
│ │ │ │ - * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
│ │ │ │ - * fillText or mozDrawText to be available.
│ │ │ │ - * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
│ │ │ │ - * composed of two characters. The first character is for the horizontal alignment, the second for the vertical
│ │ │ │ - * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
│ │ │ │ - * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
│ │ │ │ - * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
│ │ │ │ - * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
│ │ │ │ - * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
│ │ │ │ - * Default is false.
│ │ │ │ - * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
│ │ │ │ - * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
│ │ │ │ - * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
│ │ │ │ - * fontColor - {String} The font color for the label, to be provided like CSS.
│ │ │ │ - * fontOpacity - {Number} Opacity (0-1) for the label
│ │ │ │ - * fontFamily - {String} The font family for the label, to be provided like in CSS.
│ │ │ │ - * fontSize - {String} The font size for the label, to be provided like in CSS.
│ │ │ │ - * fontStyle - {String} The font style for the label, to be provided like in CSS.
│ │ │ │ - * fontWeight - {String} The font weight for the label, to be provided like in CSS.
│ │ │ │ - * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
│ │ │ │ - */
│ │ │ │ -OpenLayers.Feature.Vector.style = {
│ │ │ │ - 'default': {
│ │ │ │ - fillColor: "#ee9900",
│ │ │ │ - fillOpacity: 0.4,
│ │ │ │ - hoverFillColor: "white",
│ │ │ │ - hoverFillOpacity: 0.8,
│ │ │ │ - strokeColor: "#ee9900",
│ │ │ │ - strokeOpacity: 1,
│ │ │ │ - strokeWidth: 1,
│ │ │ │ - strokeLinecap: "round",
│ │ │ │ - strokeDashstyle: "solid",
│ │ │ │ - hoverStrokeColor: "red",
│ │ │ │ - hoverStrokeOpacity: 1,
│ │ │ │ - hoverStrokeWidth: 0.2,
│ │ │ │ - pointRadius: 6,
│ │ │ │ - hoverPointRadius: 1,
│ │ │ │ - hoverPointUnit: "%",
│ │ │ │ - pointerEvents: "visiblePainted",
│ │ │ │ - cursor: "inherit",
│ │ │ │ - fontColor: "#000000",
│ │ │ │ - labelAlign: "cm",
│ │ │ │ - labelOutlineColor: "white",
│ │ │ │ - labelOutlineWidth: 3
│ │ │ │ - },
│ │ │ │ - 'select': {
│ │ │ │ - fillColor: "blue",
│ │ │ │ - fillOpacity: 0.4,
│ │ │ │ - hoverFillColor: "white",
│ │ │ │ - hoverFillOpacity: 0.8,
│ │ │ │ - strokeColor: "blue",
│ │ │ │ - strokeOpacity: 1,
│ │ │ │ - strokeWidth: 2,
│ │ │ │ - strokeLinecap: "round",
│ │ │ │ - strokeDashstyle: "solid",
│ │ │ │ - hoverStrokeColor: "red",
│ │ │ │ - hoverStrokeOpacity: 1,
│ │ │ │ - hoverStrokeWidth: 0.2,
│ │ │ │ - pointRadius: 6,
│ │ │ │ - hoverPointRadius: 1,
│ │ │ │ - hoverPointUnit: "%",
│ │ │ │ - pointerEvents: "visiblePainted",
│ │ │ │ - cursor: "pointer",
│ │ │ │ - fontColor: "#000000",
│ │ │ │ - labelAlign: "cm",
│ │ │ │ - labelOutlineColor: "white",
│ │ │ │ - labelOutlineWidth: 3
│ │ │ │ + updateSize: function() {
│ │ │ │
│ │ │ │ - },
│ │ │ │ - 'temporary': {
│ │ │ │ - fillColor: "#66cccc",
│ │ │ │ - fillOpacity: 0.2,
│ │ │ │ - hoverFillColor: "white",
│ │ │ │ - hoverFillOpacity: 0.8,
│ │ │ │ - strokeColor: "#66cccc",
│ │ │ │ - strokeOpacity: 1,
│ │ │ │ - strokeLinecap: "round",
│ │ │ │ - strokeWidth: 2,
│ │ │ │ - strokeDashstyle: "solid",
│ │ │ │ - hoverStrokeColor: "red",
│ │ │ │ - hoverStrokeOpacity: 1,
│ │ │ │ - hoverStrokeWidth: 0.2,
│ │ │ │ - pointRadius: 6,
│ │ │ │ - hoverPointRadius: 1,
│ │ │ │ - hoverPointUnit: "%",
│ │ │ │ - pointerEvents: "visiblePainted",
│ │ │ │ - cursor: "inherit",
│ │ │ │ - fontColor: "#000000",
│ │ │ │ - labelAlign: "cm",
│ │ │ │ - labelOutlineColor: "white",
│ │ │ │ - labelOutlineWidth: 3
│ │ │ │ + // determine actual render dimensions of the contents by putting its
│ │ │ │ + // contents into a fake contentDiv (for the CSS) and then measuring it
│ │ │ │ + var preparedHTML = "" +
│ │ │ │ + this.contentDiv.innerHTML +
│ │ │ │ + "
";
│ │ │ │
│ │ │ │ - },
│ │ │ │ - 'delete': {
│ │ │ │ - display: "none"
│ │ │ │ - }
│ │ │ │ -};
│ │ │ │ -/* ======================================================================
│ │ │ │ - OpenLayers/Style.js
│ │ │ │ - ====================================================================== */
│ │ │ │ + var containerElement = (this.map) ? this.map.div : document.body;
│ │ │ │ + var realSize = OpenLayers.Util.getRenderedDimensions(
│ │ │ │ + preparedHTML, null, {
│ │ │ │ + displayClass: this.displayClass,
│ │ │ │ + containerElement: containerElement
│ │ │ │ + }
│ │ │ │ + );
│ │ │ │
│ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ - * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ - * full text of the license. */
│ │ │ │ + // is the "real" size of the div is safe to display in our map?
│ │ │ │ + var safeSize = this.getSafeContentSize(realSize);
│ │ │ │
│ │ │ │ + var newSize = null;
│ │ │ │ + if (safeSize.equals(realSize)) {
│ │ │ │ + //real size of content is small enough to fit on the map,
│ │ │ │ + // so we use real size.
│ │ │ │ + newSize = realSize;
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ - * @requires OpenLayers/Util.js
│ │ │ │ - * @requires OpenLayers/Feature/Vector.js
│ │ │ │ - */
│ │ │ │ + } else {
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * Class: OpenLayers.Style
│ │ │ │ - * This class represents a UserStyle obtained
│ │ │ │ - * from a SLD, containing styling rules.
│ │ │ │ - */
│ │ │ │ -OpenLayers.Style = OpenLayers.Class({
│ │ │ │ + // make a new 'size' object with the clipped dimensions
│ │ │ │ + // set or null if not clipped.
│ │ │ │ + var fixedSize = {
│ │ │ │ + w: (safeSize.w < realSize.w) ? safeSize.w : null,
│ │ │ │ + h: (safeSize.h < realSize.h) ? safeSize.h : null
│ │ │ │ + };
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: id
│ │ │ │ - * {String} A unique id for this session.
│ │ │ │ - */
│ │ │ │ - id: null,
│ │ │ │ + if (fixedSize.w && fixedSize.h) {
│ │ │ │ + //content is too big in both directions, so we will use
│ │ │ │ + // max popup size (safeSize), knowing well that it will
│ │ │ │ + // overflow both ways.
│ │ │ │ + newSize = safeSize;
│ │ │ │ + } else {
│ │ │ │ + //content is clipped in only one direction, so we need to
│ │ │ │ + // run getRenderedDimensions() again with a fixed dimension
│ │ │ │ + var clippedSize = OpenLayers.Util.getRenderedDimensions(
│ │ │ │ + preparedHTML, fixedSize, {
│ │ │ │ + displayClass: this.contentDisplayClass,
│ │ │ │ + containerElement: containerElement
│ │ │ │ + }
│ │ │ │ + );
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: name
│ │ │ │ - * {String}
│ │ │ │ - */
│ │ │ │ - name: null,
│ │ │ │ + //if the clipped size is still the same as the safeSize,
│ │ │ │ + // that means that our content must be fixed in the
│ │ │ │ + // offending direction. If overflow is 'auto', this means
│ │ │ │ + // we are going to have a scrollbar for sure, so we must
│ │ │ │ + // adjust for that.
│ │ │ │ + //
│ │ │ │ + var currentOverflow = OpenLayers.Element.getStyle(
│ │ │ │ + this.contentDiv, "overflow"
│ │ │ │ + );
│ │ │ │ + if ((currentOverflow != "hidden") &&
│ │ │ │ + (clippedSize.equals(safeSize))) {
│ │ │ │ + var scrollBar = OpenLayers.Util.getScrollbarWidth();
│ │ │ │ + if (fixedSize.w) {
│ │ │ │ + clippedSize.h += scrollBar;
│ │ │ │ + } else {
│ │ │ │ + clippedSize.w += scrollBar;
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: title
│ │ │ │ - * {String} Title of this style (set if included in SLD)
│ │ │ │ - */
│ │ │ │ - title: null,
│ │ │ │ + newSize = this.getSafeContentSize(clippedSize);
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ + this.setSize(newSize);
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: description
│ │ │ │ - * {String} Description of this style (set if abstract is included in SLD)
│ │ │ │ + * Method: setBackgroundColor
│ │ │ │ + * Sets the background color of the popup.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * color - {String} the background color. eg "#FFBBBB"
│ │ │ │ */
│ │ │ │ - description: null,
│ │ │ │ + setBackgroundColor: function(color) {
│ │ │ │ + if (color != undefined) {
│ │ │ │ + this.backgroundColor = color;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: layerName
│ │ │ │ - * {} name of the layer that this style belongs to, usually
│ │ │ │ - * according to the NamedLayer attribute of an SLD document.
│ │ │ │ - */
│ │ │ │ - layerName: null,
│ │ │ │ + if (this.div != null) {
│ │ │ │ + this.div.style.backgroundColor = this.backgroundColor;
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: isDefault
│ │ │ │ - * {Boolean}
│ │ │ │ + * Method: setOpacity
│ │ │ │ + * Sets the opacity of the popup.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).
│ │ │ │ */
│ │ │ │ - isDefault: false,
│ │ │ │ + setOpacity: function(opacity) {
│ │ │ │ + if (opacity != undefined) {
│ │ │ │ + this.opacity = opacity;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: rules
│ │ │ │ - * {Array()}
│ │ │ │ - */
│ │ │ │ - rules: null,
│ │ │ │ + if (this.div != null) {
│ │ │ │ + // for Mozilla and Safari
│ │ │ │ + this.div.style.opacity = this.opacity;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: context
│ │ │ │ - * {Object} An optional object with properties that symbolizers' property
│ │ │ │ - * values should be evaluated against. If no context is specified,
│ │ │ │ - * feature.attributes will be used
│ │ │ │ - */
│ │ │ │ - context: null,
│ │ │ │ + // for IE
│ │ │ │ + this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: defaultStyle
│ │ │ │ - * {Object} hash of style properties to use as default for merging
│ │ │ │ - * rule-based style symbolizers onto. If no rules are defined,
│ │ │ │ - * createSymbolizer will return this style. If is set to
│ │ │ │ - * true, the defaultStyle will only be taken into account if there are
│ │ │ │ - * rules defined.
│ │ │ │ + * Method: setBorder
│ │ │ │ + * Sets the border style of the popup.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * border - {String} The border style value. eg 2px
│ │ │ │ */
│ │ │ │ - defaultStyle: null,
│ │ │ │ + setBorder: function(border) {
│ │ │ │ + if (border != undefined) {
│ │ │ │ + this.border = border;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: defaultsPerSymbolizer
│ │ │ │ - * {Boolean} If set to true, the will extend the symbolizer
│ │ │ │ - * of every rule. Properties of the will also be used to set
│ │ │ │ - * missing symbolizer properties if the symbolizer has stroke, fill or
│ │ │ │ - * graphic set to true. Default is false.
│ │ │ │ - */
│ │ │ │ - defaultsPerSymbolizer: false,
│ │ │ │ + if (this.div != null) {
│ │ │ │ + this.div.style.border = this.border;
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: propertyStyles
│ │ │ │ - * {Hash of Boolean} cache of style properties that need to be parsed for
│ │ │ │ - * propertyNames. Property names are keys, values won't be used.
│ │ │ │ - */
│ │ │ │ - propertyStyles: null,
│ │ │ │ -
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Constructor: OpenLayers.Style
│ │ │ │ - * Creates a UserStyle.
│ │ │ │ + * Method: setContentHTML
│ │ │ │ + * Allows the user to set the HTML content of the popup.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * style - {Object} Optional hash of style properties that will be
│ │ │ │ - * used as default style for this style object. This style
│ │ │ │ - * applies if no rules are specified. Symbolizers defined in
│ │ │ │ - * rules will extend this default style.
│ │ │ │ - * options - {Object} An optional object with properties to set on the
│ │ │ │ - * style.
│ │ │ │ - *
│ │ │ │ - * Valid options:
│ │ │ │ - * rules - {Array()} List of rules to be added to the
│ │ │ │ - * style.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {}
│ │ │ │ + * contentHTML - {String} HTML for the div.
│ │ │ │ */
│ │ │ │ - initialize: function(style, options) {
│ │ │ │ + setContentHTML: function(contentHTML) {
│ │ │ │
│ │ │ │ - OpenLayers.Util.extend(this, options);
│ │ │ │ - this.rules = [];
│ │ │ │ - if (options && options.rules) {
│ │ │ │ - this.addRules(options.rules);
│ │ │ │ + if (contentHTML != null) {
│ │ │ │ + this.contentHTML = contentHTML;
│ │ │ │ }
│ │ │ │
│ │ │ │ - // use the default style from OpenLayers.Feature.Vector if no style
│ │ │ │ - // was given in the constructor
│ │ │ │ - this.setDefaultStyle(style ||
│ │ │ │ - OpenLayers.Feature.Vector.style["default"]);
│ │ │ │ + if ((this.contentDiv != null) &&
│ │ │ │ + (this.contentHTML != null) &&
│ │ │ │ + (this.contentHTML != this.contentDiv.innerHTML)) {
│ │ │ │
│ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ - },
│ │ │ │ + this.contentDiv.innerHTML = this.contentHTML;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: destroy
│ │ │ │ - * nullify references to prevent circular references and memory leaks
│ │ │ │ - */
│ │ │ │ - destroy: function() {
│ │ │ │ - for (var i = 0, len = this.rules.length; i < len; i++) {
│ │ │ │ - this.rules[i].destroy();
│ │ │ │ - this.rules[i] = null;
│ │ │ │ + if (this.autoSize) {
│ │ │ │ +
│ │ │ │ + //if popup has images, listen for when they finish
│ │ │ │ + // loading and resize accordingly
│ │ │ │ + this.registerImageListeners();
│ │ │ │ +
│ │ │ │ + //auto size the popup to its current contents
│ │ │ │ + this.updateSize();
│ │ │ │ + }
│ │ │ │ }
│ │ │ │ - this.rules = null;
│ │ │ │ - this.defaultStyle = null;
│ │ │ │ +
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: createSymbolizer
│ │ │ │ - * creates a style by applying all feature-dependent rules to the base
│ │ │ │ - * style.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * feature - {} feature to evaluate rules for
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Object} symbolizer hash
│ │ │ │ + * Method: registerImageListeners
│ │ │ │ + * Called when an image contained by the popup loaded. this function
│ │ │ │ + * updates the popup size, then unregisters the image load listener.
│ │ │ │ */
│ │ │ │ - createSymbolizer: function(feature) {
│ │ │ │ - var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
│ │ │ │ - OpenLayers.Util.extend({}, this.defaultStyle), feature);
│ │ │ │ -
│ │ │ │ - var rules = this.rules;
│ │ │ │ -
│ │ │ │ - var rule, context;
│ │ │ │ - var elseRules = [];
│ │ │ │ - var appliedRules = false;
│ │ │ │ - for (var i = 0, len = rules.length; i < len; i++) {
│ │ │ │ - rule = rules[i];
│ │ │ │ - // does the rule apply?
│ │ │ │ - var applies = rule.evaluate(feature);
│ │ │ │ + registerImageListeners: function() {
│ │ │ │
│ │ │ │ - if (applies) {
│ │ │ │ - if (rule instanceof OpenLayers.Rule && rule.elseFilter) {
│ │ │ │ - elseRules.push(rule);
│ │ │ │ - } else {
│ │ │ │ - appliedRules = true;
│ │ │ │ - this.applySymbolizer(rule, style, feature);
│ │ │ │ - }
│ │ │ │ + // As the images load, this function will call updateSize() to
│ │ │ │ + // resize the popup to fit the content div (which presumably is now
│ │ │ │ + // bigger than when the image was not loaded).
│ │ │ │ + //
│ │ │ │ + // If the 'panMapIfOutOfView' property is set, we will pan the newly
│ │ │ │ + // resized popup back into view.
│ │ │ │ + //
│ │ │ │ + // Note that this function, when called, will have 'popup' and
│ │ │ │ + // 'img' properties in the context.
│ │ │ │ + //
│ │ │ │ + var onImgLoad = function() {
│ │ │ │ + if (this.popup.id === null) { // this.popup has been destroyed!
│ │ │ │ + return;
│ │ │ │ }
│ │ │ │ - }
│ │ │ │ + this.popup.updateSize();
│ │ │ │
│ │ │ │ - // if no other rules apply, apply the rules with else filters
│ │ │ │ - if (appliedRules == false && elseRules.length > 0) {
│ │ │ │ - appliedRules = true;
│ │ │ │ - for (var i = 0, len = elseRules.length; i < len; i++) {
│ │ │ │ - this.applySymbolizer(elseRules[i], style, feature);
│ │ │ │ + if (this.popup.visible() && this.popup.panMapIfOutOfView) {
│ │ │ │ + this.popup.panIntoView();
│ │ │ │ }
│ │ │ │ - }
│ │ │ │
│ │ │ │ - // don't display if there were rules but none applied
│ │ │ │ - if (rules.length > 0 && appliedRules == false) {
│ │ │ │ - style.display = "none";
│ │ │ │ - }
│ │ │ │ + OpenLayers.Event.stopObserving(
│ │ │ │ + this.img, "load", this.img._onImgLoad
│ │ │ │ + );
│ │ │ │
│ │ │ │ - if (style.label != null && typeof style.label !== "string") {
│ │ │ │ - style.label = String(style.label);
│ │ │ │ - }
│ │ │ │ + };
│ │ │ │
│ │ │ │ - return style;
│ │ │ │ - },
│ │ │ │ + //cycle through the images and if their size is 0x0, that means that
│ │ │ │ + // they haven't been loaded yet, so we attach the listener, which
│ │ │ │ + // will fire when the images finish loading and will resize the
│ │ │ │ + // popup accordingly to its new size.
│ │ │ │ + var images = this.contentDiv.getElementsByTagName("img");
│ │ │ │ + for (var i = 0, len = images.length; i < len; i++) {
│ │ │ │ + var img = images[i];
│ │ │ │ + if (img.width == 0 || img.height == 0) {
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: applySymbolizer
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * rule - {}
│ │ │ │ - * style - {Object}
│ │ │ │ - * feature - {}
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Object} A style with new symbolizer applied.
│ │ │ │ - */
│ │ │ │ - applySymbolizer: function(rule, style, feature) {
│ │ │ │ - var symbolizerPrefix = feature.geometry ?
│ │ │ │ - this.getSymbolizerPrefix(feature.geometry) :
│ │ │ │ - OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
│ │ │ │ + var context = {
│ │ │ │ + 'popup': this,
│ │ │ │ + 'img': img
│ │ │ │ + };
│ │ │ │
│ │ │ │ - var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
│ │ │ │ + //expando this function to the image itself before registering
│ │ │ │ + // it. This way we can easily and properly unregister it.
│ │ │ │ + img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
│ │ │ │
│ │ │ │ - if (this.defaultsPerSymbolizer === true) {
│ │ │ │ - var defaults = this.defaultStyle;
│ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ - pointRadius: defaults.pointRadius
│ │ │ │ - });
│ │ │ │ - if (symbolizer.stroke === true || symbolizer.graphic === true) {
│ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ - strokeWidth: defaults.strokeWidth,
│ │ │ │ - strokeColor: defaults.strokeColor,
│ │ │ │ - strokeOpacity: defaults.strokeOpacity,
│ │ │ │ - strokeDashstyle: defaults.strokeDashstyle,
│ │ │ │ - strokeLinecap: defaults.strokeLinecap
│ │ │ │ - });
│ │ │ │ - }
│ │ │ │ - if (symbolizer.fill === true || symbolizer.graphic === true) {
│ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ - fillColor: defaults.fillColor,
│ │ │ │ - fillOpacity: defaults.fillOpacity
│ │ │ │ - });
│ │ │ │ - }
│ │ │ │ - if (symbolizer.graphic === true) {
│ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ - pointRadius: this.defaultStyle.pointRadius,
│ │ │ │ - externalGraphic: this.defaultStyle.externalGraphic,
│ │ │ │ - graphicName: this.defaultStyle.graphicName,
│ │ │ │ - graphicOpacity: this.defaultStyle.graphicOpacity,
│ │ │ │ - graphicWidth: this.defaultStyle.graphicWidth,
│ │ │ │ - graphicHeight: this.defaultStyle.graphicHeight,
│ │ │ │ - graphicXOffset: this.defaultStyle.graphicXOffset,
│ │ │ │ - graphicYOffset: this.defaultStyle.graphicYOffset
│ │ │ │ - });
│ │ │ │ + OpenLayers.Event.observe(img, 'load', img._onImgLoad);
│ │ │ │ }
│ │ │ │ }
│ │ │ │ -
│ │ │ │ - // merge the style with the current style
│ │ │ │ - return this.createLiterals(
│ │ │ │ - OpenLayers.Util.extend(style, symbolizer), feature);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: createLiterals
│ │ │ │ - * creates literals for all style properties that have an entry in
│ │ │ │ - * .
│ │ │ │ + * APIMethod: getSafeContentSize
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * style - {Object} style to create literals for. Will be modified
│ │ │ │ - * inline.
│ │ │ │ - * feature - {Object}
│ │ │ │ + * size - {} Desired size to make the popup.
│ │ │ │ *
│ │ │ │ * Returns:
│ │ │ │ - * {Object} the modified style
│ │ │ │ + * {} A size to make the popup which is neither smaller
│ │ │ │ + * than the specified minimum size, nor bigger than the maximum
│ │ │ │ + * size (which is calculated relative to the size of the viewport).
│ │ │ │ */
│ │ │ │ - createLiterals: function(style, feature) {
│ │ │ │ - var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
│ │ │ │ - OpenLayers.Util.extend(context, this.context);
│ │ │ │ + getSafeContentSize: function(size) {
│ │ │ │
│ │ │ │ - for (var i in this.propertyStyles) {
│ │ │ │ - style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
│ │ │ │ + var safeContentSize = size.clone();
│ │ │ │ +
│ │ │ │ + // if our contentDiv has a css 'padding' set on it by a stylesheet, we
│ │ │ │ + // must add that to the desired "size".
│ │ │ │ + var contentDivPadding = this.getContentDivPadding();
│ │ │ │ + var wPadding = contentDivPadding.left + contentDivPadding.right;
│ │ │ │ + var hPadding = contentDivPadding.top + contentDivPadding.bottom;
│ │ │ │ +
│ │ │ │ + // take into account the popup's 'padding' property
│ │ │ │ + this.fixPadding();
│ │ │ │ + wPadding += this.padding.left + this.padding.right;
│ │ │ │ + hPadding += this.padding.top + this.padding.bottom;
│ │ │ │ +
│ │ │ │ + if (this.closeDiv) {
│ │ │ │ + var closeDivWidth = parseInt(this.closeDiv.style.width);
│ │ │ │ + wPadding += closeDivWidth + contentDivPadding.right;
│ │ │ │ }
│ │ │ │ - return style;
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: findPropertyStyles
│ │ │ │ - * Looks into all rules for this style and the defaultStyle to collect
│ │ │ │ - * all the style hash property names containing ${...} strings that have
│ │ │ │ - * to be replaced using the createLiteral method before returning them.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Object} hash of property names that need createLiteral parsing. The
│ │ │ │ - * name of the property is the key, and the value is true;
│ │ │ │ - */
│ │ │ │ - findPropertyStyles: function() {
│ │ │ │ - var propertyStyles = {};
│ │ │ │ + // prevent the popup from being smaller than a specified minimal size
│ │ │ │ + if (this.minSize) {
│ │ │ │ + safeContentSize.w = Math.max(safeContentSize.w,
│ │ │ │ + (this.minSize.w - wPadding));
│ │ │ │ + safeContentSize.h = Math.max(safeContentSize.h,
│ │ │ │ + (this.minSize.h - hPadding));
│ │ │ │ + }
│ │ │ │
│ │ │ │ - // check the default style
│ │ │ │ - var style = this.defaultStyle;
│ │ │ │ - this.addPropertyStyles(propertyStyles, style);
│ │ │ │ + // prevent the popup from being bigger than a specified maximum size
│ │ │ │ + if (this.maxSize) {
│ │ │ │ + safeContentSize.w = Math.min(safeContentSize.w,
│ │ │ │ + (this.maxSize.w - wPadding));
│ │ │ │ + safeContentSize.h = Math.min(safeContentSize.h,
│ │ │ │ + (this.maxSize.h - hPadding));
│ │ │ │ + }
│ │ │ │
│ │ │ │ - // walk through all rules to check for properties in their symbolizer
│ │ │ │ - var rules = this.rules;
│ │ │ │ - var symbolizer, value;
│ │ │ │ - for (var i = 0, len = rules.length; i < len; i++) {
│ │ │ │ - symbolizer = rules[i].symbolizer;
│ │ │ │ - for (var key in symbolizer) {
│ │ │ │ - value = symbolizer[key];
│ │ │ │ - if (typeof value == "object") {
│ │ │ │ - // symbolizer key is "Point", "Line" or "Polygon"
│ │ │ │ - this.addPropertyStyles(propertyStyles, value);
│ │ │ │ - } else {
│ │ │ │ - // symbolizer is a hash of style properties
│ │ │ │ - this.addPropertyStyles(propertyStyles, symbolizer);
│ │ │ │ - break;
│ │ │ │ + //make sure the desired size to set doesn't result in a popup that
│ │ │ │ + // is bigger than the map's viewport.
│ │ │ │ + //
│ │ │ │ + if (this.map && this.map.size) {
│ │ │ │ +
│ │ │ │ + var extraX = 0,
│ │ │ │ + extraY = 0;
│ │ │ │ + if (this.keepInMap && !this.panMapIfOutOfView) {
│ │ │ │ + var px = this.map.getPixelFromLonLat(this.lonlat);
│ │ │ │ + switch (this.relativePosition) {
│ │ │ │ + case "tr":
│ │ │ │ + extraX = px.x;
│ │ │ │ + extraY = this.map.size.h - px.y;
│ │ │ │ + break;
│ │ │ │ + case "tl":
│ │ │ │ + extraX = this.map.size.w - px.x;
│ │ │ │ + extraY = this.map.size.h - px.y;
│ │ │ │ + break;
│ │ │ │ + case "bl":
│ │ │ │ + extraX = this.map.size.w - px.x;
│ │ │ │ + extraY = px.y;
│ │ │ │ + break;
│ │ │ │ + case "br":
│ │ │ │ + extraX = px.x;
│ │ │ │ + extraY = px.y;
│ │ │ │ + break;
│ │ │ │ + default:
│ │ │ │ + extraX = px.x;
│ │ │ │ + extraY = this.map.size.h - px.y;
│ │ │ │ + break;
│ │ │ │ }
│ │ │ │ }
│ │ │ │ +
│ │ │ │ + var maxY = this.map.size.h -
│ │ │ │ + this.map.paddingForPopups.top -
│ │ │ │ + this.map.paddingForPopups.bottom -
│ │ │ │ + hPadding - extraY;
│ │ │ │ +
│ │ │ │ + var maxX = this.map.size.w -
│ │ │ │ + this.map.paddingForPopups.left -
│ │ │ │ + this.map.paddingForPopups.right -
│ │ │ │ + wPadding - extraX;
│ │ │ │ +
│ │ │ │ + safeContentSize.w = Math.min(safeContentSize.w, maxX);
│ │ │ │ + safeContentSize.h = Math.min(safeContentSize.h, maxY);
│ │ │ │ }
│ │ │ │ - return propertyStyles;
│ │ │ │ +
│ │ │ │ + return safeContentSize;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: addPropertyStyles
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * propertyStyles - {Object} hash to add new property styles to. Will be
│ │ │ │ - * modified inline
│ │ │ │ - * symbolizer - {Object} search this symbolizer for property styles
│ │ │ │ - *
│ │ │ │ + * Method: getContentDivPadding
│ │ │ │ + * Glorious, oh glorious hack in order to determine the css 'padding' of
│ │ │ │ + * the contentDiv. IE/Opera return null here unless we actually add the
│ │ │ │ + * popup's main 'div' element (which contains contentDiv) to the DOM.
│ │ │ │ + * So we make it invisible and then add it to the document temporarily.
│ │ │ │ + *
│ │ │ │ + * Once we've taken the padding readings we need, we then remove it
│ │ │ │ + * from the DOM (it will actually get added to the DOM in
│ │ │ │ + * Map.js's addPopup)
│ │ │ │ + *
│ │ │ │ * Returns:
│ │ │ │ - * {Object} propertyStyles hash
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - addPropertyStyles: function(propertyStyles, symbolizer) {
│ │ │ │ - var property;
│ │ │ │ - for (var key in symbolizer) {
│ │ │ │ - property = symbolizer[key];
│ │ │ │ - if (typeof property == "string" &&
│ │ │ │ - property.match(/\$\{\w+\}/)) {
│ │ │ │ - propertyStyles[key] = true;
│ │ │ │ + getContentDivPadding: function() {
│ │ │ │ +
│ │ │ │ + //use cached value if we have it
│ │ │ │ + var contentDivPadding = this._contentDivPadding;
│ │ │ │ + if (!contentDivPadding) {
│ │ │ │ +
│ │ │ │ + if (this.div.parentNode == null) {
│ │ │ │ + //make the div invisible and add it to the page
│ │ │ │ + this.div.style.display = "none";
│ │ │ │ + document.body.appendChild(this.div);
│ │ │ │ }
│ │ │ │ - }
│ │ │ │ - return propertyStyles;
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: addRules
│ │ │ │ - * Adds rules to this style.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * rules - {Array()}
│ │ │ │ - */
│ │ │ │ - addRules: function(rules) {
│ │ │ │ - Array.prototype.push.apply(this.rules, rules);
│ │ │ │ - this.propertyStyles = this.findPropertyStyles();
│ │ │ │ - },
│ │ │ │ + //read the padding settings from css, put them in an OL.Bounds
│ │ │ │ + contentDivPadding = new OpenLayers.Bounds(
│ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
│ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
│ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
│ │ │ │ + OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
│ │ │ │ + );
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: setDefaultStyle
│ │ │ │ - * Sets the default style for this style object.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * style - {Object} Hash of style properties
│ │ │ │ - */
│ │ │ │ - setDefaultStyle: function(style) {
│ │ │ │ - this.defaultStyle = style;
│ │ │ │ - this.propertyStyles = this.findPropertyStyles();
│ │ │ │ - },
│ │ │ │ + //cache the value
│ │ │ │ + this._contentDivPadding = contentDivPadding;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: getSymbolizerPrefix
│ │ │ │ - * Returns the correct symbolizer prefix according to the
│ │ │ │ - * geometry type of the passed geometry
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * geometry - {}
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {String} key of the according symbolizer
│ │ │ │ - */
│ │ │ │ - getSymbolizerPrefix: function(geometry) {
│ │ │ │ - var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
│ │ │ │ - for (var i = 0, len = prefixes.length; i < len; i++) {
│ │ │ │ - if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
│ │ │ │ - return prefixes[i];
│ │ │ │ + if (this.div.parentNode == document.body) {
│ │ │ │ + //remove the div from the page and make it visible again
│ │ │ │ + document.body.removeChild(this.div);
│ │ │ │ + this.div.style.display = "";
│ │ │ │ }
│ │ │ │ }
│ │ │ │ + return contentDivPadding;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: clone
│ │ │ │ - * Clones this style.
│ │ │ │ + * Method: addCloseBox
│ │ │ │ *
│ │ │ │ - * Returns:
│ │ │ │ - * {} Clone of this style.
│ │ │ │ + * Parameters:
│ │ │ │ + * callback - {Function} The callback to be called when the close button
│ │ │ │ + * is clicked.
│ │ │ │ */
│ │ │ │ - clone: function() {
│ │ │ │ - var options = OpenLayers.Util.extend({}, this);
│ │ │ │ - // clone rules
│ │ │ │ - if (this.rules) {
│ │ │ │ - options.rules = [];
│ │ │ │ - for (var i = 0, len = this.rules.length; i < len; ++i) {
│ │ │ │ - options.rules.push(this.rules[i].clone());
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - // clone context
│ │ │ │ - options.context = this.context && OpenLayers.Util.extend({}, this.context);
│ │ │ │ - //clone default style
│ │ │ │ - var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
│ │ │ │ - return new OpenLayers.Style(defaultStyle, options);
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - CLASS_NAME: "OpenLayers.Style"
│ │ │ │ -});
│ │ │ │ -
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Function: createLiteral
│ │ │ │ - * converts a style value holding a combination of PropertyName and Literal
│ │ │ │ - * into a Literal, taking the property values from the passed features.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * value - {String} value to parse. If this string contains a construct like
│ │ │ │ - * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
│ │ │ │ - * will be replaced by the value of the "bar" attribute of the passed
│ │ │ │ - * feature.
│ │ │ │ - * context - {Object} context to take attribute values from
│ │ │ │ - * feature - {} optional feature to pass to
│ │ │ │ - * for evaluating functions in the
│ │ │ │ - * context.
│ │ │ │ - * property - {String} optional, name of the property for which the literal is
│ │ │ │ - * being created for evaluating functions in the context.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {String} the parsed value. In the example of the value parameter above, the
│ │ │ │ - * result would be "foo valueOfBar", assuming that the passed feature has an
│ │ │ │ - * attribute named "bar" with the value "valueOfBar".
│ │ │ │ - */
│ │ │ │ -OpenLayers.Style.createLiteral = function(value, context, feature, property) {
│ │ │ │ - if (typeof value == "string" && value.indexOf("${") != -1) {
│ │ │ │ - value = OpenLayers.String.format(value, context, [feature, property]);
│ │ │ │ - value = (isNaN(value) || !value) ? value : parseFloat(value);
│ │ │ │ - }
│ │ │ │ - return value;
│ │ │ │ -};
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
│ │ │ │ - * {Array} prefixes of the sld symbolizers. These are the
│ │ │ │ - * same as the main geometry types
│ │ │ │ - */
│ │ │ │ -OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
│ │ │ │ - 'Raster'
│ │ │ │ -];
│ │ │ │ -/* ======================================================================
│ │ │ │ - OpenLayers/Rule.js
│ │ │ │ - ====================================================================== */
│ │ │ │ + addCloseBox: function(callback) {
│ │ │ │
│ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ - * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ - * full text of the license. */
│ │ │ │ + this.closeDiv = OpenLayers.Util.createDiv(
│ │ │ │ + this.id + "_close", null, {
│ │ │ │ + w: 17,
│ │ │ │ + h: 17
│ │ │ │ + }
│ │ │ │ + );
│ │ │ │ + this.closeDiv.className = "olPopupCloseBox";
│ │ │ │
│ │ │ │ + // use the content div's css padding to determine if we should
│ │ │ │ + // padd the close div
│ │ │ │ + var contentDivPadding = this.getContentDivPadding();
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ - * @requires OpenLayers/Util.js
│ │ │ │ - * @requires OpenLayers/Style.js
│ │ │ │ - */
│ │ │ │ + this.closeDiv.style.right = contentDivPadding.right + "px";
│ │ │ │ + this.closeDiv.style.top = contentDivPadding.top + "px";
│ │ │ │ + this.groupDiv.appendChild(this.closeDiv);
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * Class: OpenLayers.Rule
│ │ │ │ - * This class represents an SLD Rule, as being used for rule-based SLD styling.
│ │ │ │ - */
│ │ │ │ -OpenLayers.Rule = OpenLayers.Class({
│ │ │ │ + var closePopup = callback || function(e) {
│ │ │ │ + this.hide();
│ │ │ │ + OpenLayers.Event.stop(e);
│ │ │ │ + };
│ │ │ │ + OpenLayers.Event.observe(this.closeDiv, "touchend",
│ │ │ │ + OpenLayers.Function.bindAsEventListener(closePopup, this));
│ │ │ │ + OpenLayers.Event.observe(this.closeDiv, "click",
│ │ │ │ + OpenLayers.Function.bindAsEventListener(closePopup, this));
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: id
│ │ │ │ - * {String} A unique id for this session.
│ │ │ │ + * Method: panIntoView
│ │ │ │ + * Pans the map such that the popup is totaly viewable (if necessary)
│ │ │ │ */
│ │ │ │ - id: null,
│ │ │ │ + panIntoView: function() {
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: name
│ │ │ │ - * {String} name of this rule
│ │ │ │ - */
│ │ │ │ - name: null,
│ │ │ │ + var mapSize = this.map.getSize();
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: title
│ │ │ │ - * {String} Title of this rule (set if included in SLD)
│ │ │ │ - */
│ │ │ │ - title: null,
│ │ │ │ + //start with the top left corner of the popup, in px,
│ │ │ │ + // relative to the viewport
│ │ │ │ + var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(
│ │ │ │ + parseInt(this.div.style.left),
│ │ │ │ + parseInt(this.div.style.top)
│ │ │ │ + ));
│ │ │ │ + var newTL = origTL.clone();
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: description
│ │ │ │ - * {String} Description of this rule (set if abstract is included in SLD)
│ │ │ │ - */
│ │ │ │ - description: null,
│ │ │ │ + //new left (compare to margins, using this.size to calculate right)
│ │ │ │ + if (origTL.x < this.map.paddingForPopups.left) {
│ │ │ │ + newTL.x = this.map.paddingForPopups.left;
│ │ │ │ + } else
│ │ │ │ + if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
│ │ │ │ + newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: context
│ │ │ │ - * {Object} An optional object with properties that the rule should be
│ │ │ │ - * evaluated against. If no context is specified, feature.attributes will
│ │ │ │ - * be used.
│ │ │ │ - */
│ │ │ │ - context: null,
│ │ │ │ + //new top (compare to margins, using this.size to calculate bottom)
│ │ │ │ + if (origTL.y < this.map.paddingForPopups.top) {
│ │ │ │ + newTL.y = this.map.paddingForPopups.top;
│ │ │ │ + } else
│ │ │ │ + if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
│ │ │ │ + newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: filter
│ │ │ │ - * {} Optional filter for the rule.
│ │ │ │ - */
│ │ │ │ - filter: null,
│ │ │ │ + var dx = origTL.x - newTL.x;
│ │ │ │ + var dy = origTL.y - newTL.y;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: elseFilter
│ │ │ │ - * {Boolean} Determines whether this rule is only to be applied only if
│ │ │ │ - * no other rules match (ElseFilter according to the SLD specification).
│ │ │ │ - * Default is false. For instances of OpenLayers.Rule, if elseFilter is
│ │ │ │ - * false, the rule will always apply. For subclasses, the else property is
│ │ │ │ - * ignored.
│ │ │ │ - */
│ │ │ │ - elseFilter: false,
│ │ │ │ + this.map.pan(dx, dy);
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: symbolizer
│ │ │ │ - * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
│ │ │ │ - * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
│ │ │ │ - * latter if useful if it is required to style e.g. vertices of a line
│ │ │ │ - * with a point symbolizer. Note, however, that this is not implemented
│ │ │ │ - * yet in OpenLayers, but it is the way how symbolizers are defined in
│ │ │ │ - * SLD.
│ │ │ │ + /**
│ │ │ │ + * Method: registerEvents
│ │ │ │ + * Registers events on the popup.
│ │ │ │ + *
│ │ │ │ + * Do this in a separate function so that subclasses can
│ │ │ │ + * choose to override it if they wish to deal differently
│ │ │ │ + * with mouse events
│ │ │ │ + *
│ │ │ │ + * Note in the following handler functions that some special
│ │ │ │ + * care is needed to deal correctly with mousing and popups.
│ │ │ │ + *
│ │ │ │ + * Because the user might select the zoom-rectangle option and
│ │ │ │ + * then drag it over a popup, we need a safe way to allow the
│ │ │ │ + * mousemove and mouseup events to pass through the popup when
│ │ │ │ + * they are initiated from outside. The same procedure is needed for
│ │ │ │ + * touchmove and touchend events.
│ │ │ │ + *
│ │ │ │ + * Otherwise, we want to essentially kill the event propagation
│ │ │ │ + * for all other events, though we have to do so carefully,
│ │ │ │ + * without disabling basic html functionality, like clicking on
│ │ │ │ + * hyperlinks or drag-selecting text.
│ │ │ │ */
│ │ │ │ - symbolizer: null,
│ │ │ │ + registerEvents: function() {
│ │ │ │ + this.events = new OpenLayers.Events(this, this.div, null, true);
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: symbolizers
│ │ │ │ - * {Array} Collection of symbolizers associated with this rule. If
│ │ │ │ - * provided at construction, the symbolizers array has precedence
│ │ │ │ - * over the deprecated symbolizer property. Note that multiple
│ │ │ │ - * symbolizers are not currently supported by the vector renderers.
│ │ │ │ - * Rules with multiple symbolizers are currently only useful for
│ │ │ │ - * maintaining elements in an SLD document.
│ │ │ │ - */
│ │ │ │ - symbolizers: null,
│ │ │ │ + function onTouchstart(evt) {
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ + }
│ │ │ │ + this.events.on({
│ │ │ │ + "mousedown": this.onmousedown,
│ │ │ │ + "mousemove": this.onmousemove,
│ │ │ │ + "mouseup": this.onmouseup,
│ │ │ │ + "click": this.onclick,
│ │ │ │ + "mouseout": this.onmouseout,
│ │ │ │ + "dblclick": this.ondblclick,
│ │ │ │ + "touchstart": onTouchstart,
│ │ │ │ + scope: this
│ │ │ │ + });
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: minScaleDenominator
│ │ │ │ - * {Number} or {String} minimum scale at which to draw the feature.
│ │ │ │ - * In the case of a String, this can be a combination of text and
│ │ │ │ - * propertyNames in the form "literal ${propertyName}"
│ │ │ │ - */
│ │ │ │ - minScaleDenominator: null,
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: maxScaleDenominator
│ │ │ │ - * {Number} or {String} maximum scale at which to draw the feature.
│ │ │ │ - * In the case of a String, this can be a combination of text and
│ │ │ │ - * propertyNames in the form "literal ${propertyName}"
│ │ │ │ + /**
│ │ │ │ + * Method: onmousedown
│ │ │ │ + * When mouse goes down within the popup, make a note of
│ │ │ │ + * it locally, and then do not propagate the mousedown
│ │ │ │ + * (but do so safely so that user can select text inside)
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - maxScaleDenominator: null,
│ │ │ │ + onmousedown: function(evt) {
│ │ │ │ + this.mousedown = true;
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Constructor: OpenLayers.Rule
│ │ │ │ - * Creates a Rule.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * options - {Object} An optional object with properties to set on the
│ │ │ │ - * rule
│ │ │ │ + * Method: onmousemove
│ │ │ │ + * If the drag was started within the popup, then
│ │ │ │ + * do not propagate the mousemove (but do so safely
│ │ │ │ + * so that user can select text inside)
│ │ │ │ *
│ │ │ │ - * Returns:
│ │ │ │ - * {}
│ │ │ │ + * Parameters:
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - initialize: function(options) {
│ │ │ │ - this.symbolizer = {};
│ │ │ │ - OpenLayers.Util.extend(this, options);
│ │ │ │ - if (this.symbolizers) {
│ │ │ │ - delete this.symbolizer;
│ │ │ │ + onmousemove: function(evt) {
│ │ │ │ + if (this.mousedown) {
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ }
│ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: destroy
│ │ │ │ - * nullify references to prevent circular references and memory leaks
│ │ │ │ + * Method: onmouseup
│ │ │ │ + * When mouse comes up within the popup, after going down
│ │ │ │ + * in it, reset the flag, and then (once again) do not
│ │ │ │ + * propagate the event, but do so safely so that user can
│ │ │ │ + * select text inside
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - destroy: function() {
│ │ │ │ - for (var i in this.symbolizer) {
│ │ │ │ - this.symbolizer[i] = null;
│ │ │ │ + onmouseup: function(evt) {
│ │ │ │ + if (this.mousedown) {
│ │ │ │ + this.mousedown = false;
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ }
│ │ │ │ - this.symbolizer = null;
│ │ │ │ - delete this.symbolizers;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: evaluate
│ │ │ │ - * evaluates this rule for a specific feature
│ │ │ │ + * Method: onclick
│ │ │ │ + * Ignore clicks, but allowing default browser handling
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * feature - {} feature to apply the rule to.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Boolean} true if the rule applies, false if it does not.
│ │ │ │ - * This rule is the default rule and always returns true.
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - evaluate: function(feature) {
│ │ │ │ - var context = this.getContext(feature);
│ │ │ │ - var applies = true;
│ │ │ │ -
│ │ │ │ - if (this.minScaleDenominator || this.maxScaleDenominator) {
│ │ │ │ - var scale = feature.layer.map.getScale();
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - // check if within minScale/maxScale bounds
│ │ │ │ - if (this.minScaleDenominator) {
│ │ │ │ - applies = scale >= OpenLayers.Style.createLiteral(
│ │ │ │ - this.minScaleDenominator, context);
│ │ │ │ - }
│ │ │ │ - if (applies && this.maxScaleDenominator) {
│ │ │ │ - applies = scale < OpenLayers.Style.createLiteral(
│ │ │ │ - this.maxScaleDenominator, context);
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - // check if optional filter applies
│ │ │ │ - if (applies && this.filter) {
│ │ │ │ - // feature id filters get the feature, others get the context
│ │ │ │ - if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
│ │ │ │ - applies = this.filter.evaluate(feature);
│ │ │ │ - } else {
│ │ │ │ - applies = this.filter.evaluate(context);
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - return applies;
│ │ │ │ + onclick: function(evt) {
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: getContext
│ │ │ │ - * Gets the context for evaluating this rule
│ │ │ │ + /**
│ │ │ │ + * Method: onmouseout
│ │ │ │ + * When mouse goes out of the popup set the flag to false so that
│ │ │ │ + * if they let go and then drag back in, we won't be confused.
│ │ │ │ *
│ │ │ │ - * Paramters:
│ │ │ │ - * feature - {} feature to take the context from if
│ │ │ │ - * none is specified.
│ │ │ │ + * Parameters:
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - getContext: function(feature) {
│ │ │ │ - var context = this.context;
│ │ │ │ - if (!context) {
│ │ │ │ - context = feature.attributes || feature.data;
│ │ │ │ - }
│ │ │ │ - if (typeof this.context == "function") {
│ │ │ │ - context = this.context(feature);
│ │ │ │ - }
│ │ │ │ - return context;
│ │ │ │ + onmouseout: function(evt) {
│ │ │ │ + this.mousedown = false;
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: clone
│ │ │ │ - * Clones this rule.
│ │ │ │ + /**
│ │ │ │ + * Method: ondblclick
│ │ │ │ + * Ignore double-clicks, but allowing default browser handling
│ │ │ │ *
│ │ │ │ - * Returns:
│ │ │ │ - * {} Clone of this rule.
│ │ │ │ + * Parameters:
│ │ │ │ + * evt - {Event}
│ │ │ │ */
│ │ │ │ - clone: function() {
│ │ │ │ - var options = OpenLayers.Util.extend({}, this);
│ │ │ │ - if (this.symbolizers) {
│ │ │ │ - // clone symbolizers
│ │ │ │ - var len = this.symbolizers.length;
│ │ │ │ - options.symbolizers = new Array(len);
│ │ │ │ - for (var i = 0; i < len; ++i) {
│ │ │ │ - options.symbolizers[i] = this.symbolizers[i].clone();
│ │ │ │ - }
│ │ │ │ - } else {
│ │ │ │ - // clone symbolizer
│ │ │ │ - options.symbolizer = {};
│ │ │ │ - var value, type;
│ │ │ │ - for (var key in this.symbolizer) {
│ │ │ │ - value = this.symbolizer[key];
│ │ │ │ - type = typeof value;
│ │ │ │ - if (type === "object") {
│ │ │ │ - options.symbolizer[key] = OpenLayers.Util.extend({}, value);
│ │ │ │ - } else if (type === "string") {
│ │ │ │ - options.symbolizer[key] = value;
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - // clone filter
│ │ │ │ - options.filter = this.filter && this.filter.clone();
│ │ │ │ - // clone context
│ │ │ │ - options.context = this.context && OpenLayers.Util.extend({}, this.context);
│ │ │ │ - return new OpenLayers.Rule(options);
│ │ │ │ + ondblclick: function(evt) {
│ │ │ │ + OpenLayers.Event.stop(evt, true);
│ │ │ │ },
│ │ │ │
│ │ │ │ - CLASS_NAME: "OpenLayers.Rule"
│ │ │ │ + CLASS_NAME: "OpenLayers.Popup"
│ │ │ │ });
│ │ │ │ +
│ │ │ │ +OpenLayers.Popup.WIDTH = 200;
│ │ │ │ +OpenLayers.Popup.HEIGHT = 200;
│ │ │ │ +OpenLayers.Popup.COLOR = "white";
│ │ │ │ +OpenLayers.Popup.OPACITY = 1;
│ │ │ │ +OpenLayers.Popup.BORDER = "0px";
│ │ │ │ /* ======================================================================
│ │ │ │ OpenLayers/Util/vendorPrefix.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ @@ -6660,485 +6297,14 @@
│ │ │ │
│ │ │ │ // used for testing
│ │ │ │ cssCache: cssCache,
│ │ │ │ jsCache: jsCache
│ │ │ │ };
│ │ │ │ }());
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Animation.js
│ │ │ │ - ====================================================================== */
│ │ │ │ -
│ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ - * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ - * full text of the license. */
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * @requires OpenLayers/SingleFile.js
│ │ │ │ - * @requires OpenLayers/Util/vendorPrefix.js
│ │ │ │ - */
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Animation
│ │ │ │ - * A collection of utility functions for executing methods that repaint a
│ │ │ │ - * portion of the browser window. These methods take advantage of the
│ │ │ │ - * browser's scheduled repaints where requestAnimationFrame is available.
│ │ │ │ - */
│ │ │ │ -OpenLayers.Animation = (function(window) {
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: isNative
│ │ │ │ - * {Boolean} true if a native requestAnimationFrame function is available
│ │ │ │ - */
│ │ │ │ - var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
│ │ │ │ - var isNative = !!(requestAnimationFrame);
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: requestFrame
│ │ │ │ - * Schedule a function to be called at the next available animation frame.
│ │ │ │ - * Uses the native method where available. Where requestAnimationFrame is
│ │ │ │ - * not available, setTimeout will be called with a 16ms delay.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * callback - {Function} The function to be called at the next animation frame.
│ │ │ │ - * element - {DOMElement} Optional element that visually bounds the animation.
│ │ │ │ - */
│ │ │ │ - var requestFrame = (function() {
│ │ │ │ - var request = window[requestAnimationFrame] ||
│ │ │ │ - function(callback, element) {
│ │ │ │ - window.setTimeout(callback, 16);
│ │ │ │ - };
│ │ │ │ - // bind to window to avoid illegal invocation of native function
│ │ │ │ - return function(callback, element) {
│ │ │ │ - request.apply(window, [callback, element]);
│ │ │ │ - };
│ │ │ │ - })();
│ │ │ │ -
│ │ │ │ - // private variables for animation loops
│ │ │ │ - var counter = 0;
│ │ │ │ - var loops = {};
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: start
│ │ │ │ - * Executes a method with in series for some
│ │ │ │ - * duration.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * callback - {Function} The function to be called at the next animation frame.
│ │ │ │ - * duration - {Number} Optional duration for the loop. If not provided, the
│ │ │ │ - * animation loop will execute indefinitely.
│ │ │ │ - * element - {DOMElement} Optional element that visually bounds the animation.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Number} Identifier for the animation loop. Used to stop animations with
│ │ │ │ - * .
│ │ │ │ - */
│ │ │ │ - function start(callback, duration, element) {
│ │ │ │ - duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
│ │ │ │ - var id = ++counter;
│ │ │ │ - var start = +new Date;
│ │ │ │ - loops[id] = function() {
│ │ │ │ - if (loops[id] && +new Date - start <= duration) {
│ │ │ │ - callback();
│ │ │ │ - if (loops[id]) {
│ │ │ │ - requestFrame(loops[id], element);
│ │ │ │ - }
│ │ │ │ - } else {
│ │ │ │ - delete loops[id];
│ │ │ │ - }
│ │ │ │ - };
│ │ │ │ - requestFrame(loops[id], element);
│ │ │ │ - return id;
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: stop
│ │ │ │ - * Terminates an animation loop started with .
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * id - {Number} Identifier returned from .
│ │ │ │ - */
│ │ │ │ - function stop(id) {
│ │ │ │ - delete loops[id];
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - return {
│ │ │ │ - isNative: isNative,
│ │ │ │ - requestFrame: requestFrame,
│ │ │ │ - start: start,
│ │ │ │ - stop: stop
│ │ │ │ - };
│ │ │ │ -
│ │ │ │ -})(window);
│ │ │ │ -/* ======================================================================
│ │ │ │ - OpenLayers/Tween.js
│ │ │ │ - ====================================================================== */
│ │ │ │ -
│ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ - * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ - * full text of the license. */
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ - * @requires OpenLayers/Animation.js
│ │ │ │ - */
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Tween
│ │ │ │ - */
│ │ │ │ -OpenLayers.Tween = OpenLayers.Class({
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: easing
│ │ │ │ - * {(Function)} Easing equation used for the animation
│ │ │ │ - * Defaultly set to OpenLayers.Easing.Expo.easeOut
│ │ │ │ - */
│ │ │ │ - easing: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: begin
│ │ │ │ - * {Object} Values to start the animation with
│ │ │ │ - */
│ │ │ │ - begin: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: finish
│ │ │ │ - * {Object} Values to finish the animation with
│ │ │ │ - */
│ │ │ │ - finish: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: duration
│ │ │ │ - * {int} duration of the tween (number of steps)
│ │ │ │ - */
│ │ │ │ - duration: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: callbacks
│ │ │ │ - * {Object} An object with start, eachStep and done properties whose values
│ │ │ │ - * are functions to be call during the animation. They are passed the
│ │ │ │ - * current computed value as argument.
│ │ │ │ - */
│ │ │ │ - callbacks: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: time
│ │ │ │ - * {int} Step counter
│ │ │ │ - */
│ │ │ │ - time: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: minFrameRate
│ │ │ │ - * {Number} The minimum framerate for animations in frames per second. After
│ │ │ │ - * each step, the time spent in the animation is compared to the calculated
│ │ │ │ - * time at this frame rate. If the animation runs longer than the calculated
│ │ │ │ - * time, the next step is skipped. Default is 30.
│ │ │ │ - */
│ │ │ │ - minFrameRate: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: startTime
│ │ │ │ - * {Number} The timestamp of the first execution step. Used for skipping
│ │ │ │ - * frames
│ │ │ │ - */
│ │ │ │ - startTime: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: animationId
│ │ │ │ - * {int} Loop id returned by OpenLayers.Animation.start
│ │ │ │ - */
│ │ │ │ - animationId: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: playing
│ │ │ │ - * {Boolean} Tells if the easing is currently playing
│ │ │ │ - */
│ │ │ │ - playing: false,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Constructor: OpenLayers.Tween
│ │ │ │ - * Creates a Tween.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * easing - {(Function)} easing function method to use
│ │ │ │ - */
│ │ │ │ - initialize: function(easing) {
│ │ │ │ - this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIMethod: start
│ │ │ │ - * Plays the Tween, and calls the callback method on each step
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * begin - {Object} values to start the animation with
│ │ │ │ - * finish - {Object} values to finish the animation with
│ │ │ │ - * duration - {int} duration of the tween (number of steps)
│ │ │ │ - * options - {Object} hash of options (callbacks (start, eachStep, done),
│ │ │ │ - * minFrameRate)
│ │ │ │ - */
│ │ │ │ - start: function(begin, finish, duration, options) {
│ │ │ │ - this.playing = true;
│ │ │ │ - this.begin = begin;
│ │ │ │ - this.finish = finish;
│ │ │ │ - this.duration = duration;
│ │ │ │ - this.callbacks = options.callbacks;
│ │ │ │ - this.minFrameRate = options.minFrameRate || 30;
│ │ │ │ - this.time = 0;
│ │ │ │ - this.startTime = new Date().getTime();
│ │ │ │ - OpenLayers.Animation.stop(this.animationId);
│ │ │ │ - this.animationId = null;
│ │ │ │ - if (this.callbacks && this.callbacks.start) {
│ │ │ │ - this.callbacks.start.call(this, this.begin);
│ │ │ │ - }
│ │ │ │ - this.animationId = OpenLayers.Animation.start(
│ │ │ │ - OpenLayers.Function.bind(this.play, this)
│ │ │ │ - );
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIMethod: stop
│ │ │ │ - * Stops the Tween, and calls the done callback
│ │ │ │ - * Doesn't do anything if animation is already finished
│ │ │ │ - */
│ │ │ │ - stop: function() {
│ │ │ │ - if (!this.playing) {
│ │ │ │ - return;
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - if (this.callbacks && this.callbacks.done) {
│ │ │ │ - this.callbacks.done.call(this, this.finish);
│ │ │ │ - }
│ │ │ │ - OpenLayers.Animation.stop(this.animationId);
│ │ │ │ - this.animationId = null;
│ │ │ │ - this.playing = false;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: play
│ │ │ │ - * Calls the appropriate easing method
│ │ │ │ - */
│ │ │ │ - play: function() {
│ │ │ │ - var value = {};
│ │ │ │ - for (var i in this.begin) {
│ │ │ │ - var b = this.begin[i];
│ │ │ │ - var f = this.finish[i];
│ │ │ │ - if (b == null || f == null || isNaN(b) || isNaN(f)) {
│ │ │ │ - throw new TypeError('invalid value for Tween');
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - var c = f - b;
│ │ │ │ - value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
│ │ │ │ - }
│ │ │ │ - this.time++;
│ │ │ │ -
│ │ │ │ - if (this.callbacks && this.callbacks.eachStep) {
│ │ │ │ - // skip frames if frame rate drops below threshold
│ │ │ │ - if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
│ │ │ │ - this.callbacks.eachStep.call(this, value);
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - if (this.time > this.duration) {
│ │ │ │ - this.stop();
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Create empty functions for all easing methods.
│ │ │ │ - */
│ │ │ │ - CLASS_NAME: "OpenLayers.Tween"
│ │ │ │ -});
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Easing
│ │ │ │ - *
│ │ │ │ - * Credits:
│ │ │ │ - * Easing Equations by Robert Penner,
│ │ │ │ - */
│ │ │ │ -OpenLayers.Easing = {
│ │ │ │ - /**
│ │ │ │ - * Create empty functions for all easing methods.
│ │ │ │ - */
│ │ │ │ - CLASS_NAME: "OpenLayers.Easing"
│ │ │ │ -};
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Easing.Linear
│ │ │ │ - */
│ │ │ │ -OpenLayers.Easing.Linear = {
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeIn
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeIn: function(t, b, c, d) {
│ │ │ │ - return c * t / d + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeOut: function(t, b, c, d) {
│ │ │ │ - return c * t / d + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeInOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeInOut: function(t, b, c, d) {
│ │ │ │ - return c * t / d + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Linear"
│ │ │ │ -};
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Easing.Expo
│ │ │ │ - */
│ │ │ │ -OpenLayers.Easing.Expo = {
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeIn
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeIn: function(t, b, c, d) {
│ │ │ │ - return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeOut: function(t, b, c, d) {
│ │ │ │ - return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeInOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeInOut: function(t, b, c, d) {
│ │ │ │ - if (t == 0) return b;
│ │ │ │ - if (t == d) return b + c;
│ │ │ │ - if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
│ │ │ │ - return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Expo"
│ │ │ │ -};
│ │ │ │ -
│ │ │ │ -/**
│ │ │ │ - * Namespace: OpenLayers.Easing.Quad
│ │ │ │ - */
│ │ │ │ -OpenLayers.Easing.Quad = {
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeIn
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeIn: function(t, b, c, d) {
│ │ │ │ - return c * (t /= d) * t + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeOut: function(t, b, c, d) {
│ │ │ │ - return -c * (t /= d) * (t - 2) + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Function: easeInOut
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * t - {Float} time
│ │ │ │ - * b - {Float} beginning position
│ │ │ │ - * c - {Float} total change
│ │ │ │ - * d - {Float} duration of the transition
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Float}
│ │ │ │ - */
│ │ │ │ - easeInOut: function(t, b, c, d) {
│ │ │ │ - if ((t /= d / 2) < 1) return c / 2 * t * t + b;
│ │ │ │ - return -c / 2 * ((--t) * (t - 2) - 1) + b;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Quad"
│ │ │ │ -};
│ │ │ │ -/* ======================================================================
│ │ │ │ OpenLayers/Events.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │ @@ -8307,14 +7473,485 @@
│ │ │ │
│ │ │ │ OpenLayers.Event.observe(element, 'MSPointerUp', cb);
│ │ │ │ },
│ │ │ │
│ │ │ │ CLASS_NAME: "OpenLayers.Events"
│ │ │ │ });
│ │ │ │ /* ======================================================================
│ │ │ │ + OpenLayers/Animation.js
│ │ │ │ + ====================================================================== */
│ │ │ │ +
│ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ + * full text of the license. */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/SingleFile.js
│ │ │ │ + * @requires OpenLayers/Util/vendorPrefix.js
│ │ │ │ + */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Animation
│ │ │ │ + * A collection of utility functions for executing methods that repaint a
│ │ │ │ + * portion of the browser window. These methods take advantage of the
│ │ │ │ + * browser's scheduled repaints where requestAnimationFrame is available.
│ │ │ │ + */
│ │ │ │ +OpenLayers.Animation = (function(window) {
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: isNative
│ │ │ │ + * {Boolean} true if a native requestAnimationFrame function is available
│ │ │ │ + */
│ │ │ │ + var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
│ │ │ │ + var isNative = !!(requestAnimationFrame);
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: requestFrame
│ │ │ │ + * Schedule a function to be called at the next available animation frame.
│ │ │ │ + * Uses the native method where available. Where requestAnimationFrame is
│ │ │ │ + * not available, setTimeout will be called with a 16ms delay.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * callback - {Function} The function to be called at the next animation frame.
│ │ │ │ + * element - {DOMElement} Optional element that visually bounds the animation.
│ │ │ │ + */
│ │ │ │ + var requestFrame = (function() {
│ │ │ │ + var request = window[requestAnimationFrame] ||
│ │ │ │ + function(callback, element) {
│ │ │ │ + window.setTimeout(callback, 16);
│ │ │ │ + };
│ │ │ │ + // bind to window to avoid illegal invocation of native function
│ │ │ │ + return function(callback, element) {
│ │ │ │ + request.apply(window, [callback, element]);
│ │ │ │ + };
│ │ │ │ + })();
│ │ │ │ +
│ │ │ │ + // private variables for animation loops
│ │ │ │ + var counter = 0;
│ │ │ │ + var loops = {};
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: start
│ │ │ │ + * Executes a method with in series for some
│ │ │ │ + * duration.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * callback - {Function} The function to be called at the next animation frame.
│ │ │ │ + * duration - {Number} Optional duration for the loop. If not provided, the
│ │ │ │ + * animation loop will execute indefinitely.
│ │ │ │ + * element - {DOMElement} Optional element that visually bounds the animation.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Number} Identifier for the animation loop. Used to stop animations with
│ │ │ │ + * .
│ │ │ │ + */
│ │ │ │ + function start(callback, duration, element) {
│ │ │ │ + duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
│ │ │ │ + var id = ++counter;
│ │ │ │ + var start = +new Date;
│ │ │ │ + loops[id] = function() {
│ │ │ │ + if (loops[id] && +new Date - start <= duration) {
│ │ │ │ + callback();
│ │ │ │ + if (loops[id]) {
│ │ │ │ + requestFrame(loops[id], element);
│ │ │ │ + }
│ │ │ │ + } else {
│ │ │ │ + delete loops[id];
│ │ │ │ + }
│ │ │ │ + };
│ │ │ │ + requestFrame(loops[id], element);
│ │ │ │ + return id;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: stop
│ │ │ │ + * Terminates an animation loop started with .
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * id - {Number} Identifier returned from .
│ │ │ │ + */
│ │ │ │ + function stop(id) {
│ │ │ │ + delete loops[id];
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + return {
│ │ │ │ + isNative: isNative,
│ │ │ │ + requestFrame: requestFrame,
│ │ │ │ + start: start,
│ │ │ │ + stop: stop
│ │ │ │ + };
│ │ │ │ +
│ │ │ │ +})(window);
│ │ │ │ +/* ======================================================================
│ │ │ │ + OpenLayers/Tween.js
│ │ │ │ + ====================================================================== */
│ │ │ │ +
│ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ + * full text of the license. */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ + * @requires OpenLayers/Animation.js
│ │ │ │ + */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Tween
│ │ │ │ + */
│ │ │ │ +OpenLayers.Tween = OpenLayers.Class({
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: easing
│ │ │ │ + * {(Function)} Easing equation used for the animation
│ │ │ │ + * Defaultly set to OpenLayers.Easing.Expo.easeOut
│ │ │ │ + */
│ │ │ │ + easing: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: begin
│ │ │ │ + * {Object} Values to start the animation with
│ │ │ │ + */
│ │ │ │ + begin: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: finish
│ │ │ │ + * {Object} Values to finish the animation with
│ │ │ │ + */
│ │ │ │ + finish: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: duration
│ │ │ │ + * {int} duration of the tween (number of steps)
│ │ │ │ + */
│ │ │ │ + duration: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: callbacks
│ │ │ │ + * {Object} An object with start, eachStep and done properties whose values
│ │ │ │ + * are functions to be call during the animation. They are passed the
│ │ │ │ + * current computed value as argument.
│ │ │ │ + */
│ │ │ │ + callbacks: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: time
│ │ │ │ + * {int} Step counter
│ │ │ │ + */
│ │ │ │ + time: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: minFrameRate
│ │ │ │ + * {Number} The minimum framerate for animations in frames per second. After
│ │ │ │ + * each step, the time spent in the animation is compared to the calculated
│ │ │ │ + * time at this frame rate. If the animation runs longer than the calculated
│ │ │ │ + * time, the next step is skipped. Default is 30.
│ │ │ │ + */
│ │ │ │ + minFrameRate: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: startTime
│ │ │ │ + * {Number} The timestamp of the first execution step. Used for skipping
│ │ │ │ + * frames
│ │ │ │ + */
│ │ │ │ + startTime: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: animationId
│ │ │ │ + * {int} Loop id returned by OpenLayers.Animation.start
│ │ │ │ + */
│ │ │ │ + animationId: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: playing
│ │ │ │ + * {Boolean} Tells if the easing is currently playing
│ │ │ │ + */
│ │ │ │ + playing: false,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Constructor: OpenLayers.Tween
│ │ │ │ + * Creates a Tween.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * easing - {(Function)} easing function method to use
│ │ │ │ + */
│ │ │ │ + initialize: function(easing) {
│ │ │ │ + this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIMethod: start
│ │ │ │ + * Plays the Tween, and calls the callback method on each step
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * begin - {Object} values to start the animation with
│ │ │ │ + * finish - {Object} values to finish the animation with
│ │ │ │ + * duration - {int} duration of the tween (number of steps)
│ │ │ │ + * options - {Object} hash of options (callbacks (start, eachStep, done),
│ │ │ │ + * minFrameRate)
│ │ │ │ + */
│ │ │ │ + start: function(begin, finish, duration, options) {
│ │ │ │ + this.playing = true;
│ │ │ │ + this.begin = begin;
│ │ │ │ + this.finish = finish;
│ │ │ │ + this.duration = duration;
│ │ │ │ + this.callbacks = options.callbacks;
│ │ │ │ + this.minFrameRate = options.minFrameRate || 30;
│ │ │ │ + this.time = 0;
│ │ │ │ + this.startTime = new Date().getTime();
│ │ │ │ + OpenLayers.Animation.stop(this.animationId);
│ │ │ │ + this.animationId = null;
│ │ │ │ + if (this.callbacks && this.callbacks.start) {
│ │ │ │ + this.callbacks.start.call(this, this.begin);
│ │ │ │ + }
│ │ │ │ + this.animationId = OpenLayers.Animation.start(
│ │ │ │ + OpenLayers.Function.bind(this.play, this)
│ │ │ │ + );
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIMethod: stop
│ │ │ │ + * Stops the Tween, and calls the done callback
│ │ │ │ + * Doesn't do anything if animation is already finished
│ │ │ │ + */
│ │ │ │ + stop: function() {
│ │ │ │ + if (!this.playing) {
│ │ │ │ + return;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + if (this.callbacks && this.callbacks.done) {
│ │ │ │ + this.callbacks.done.call(this, this.finish);
│ │ │ │ + }
│ │ │ │ + OpenLayers.Animation.stop(this.animationId);
│ │ │ │ + this.animationId = null;
│ │ │ │ + this.playing = false;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Method: play
│ │ │ │ + * Calls the appropriate easing method
│ │ │ │ + */
│ │ │ │ + play: function() {
│ │ │ │ + var value = {};
│ │ │ │ + for (var i in this.begin) {
│ │ │ │ + var b = this.begin[i];
│ │ │ │ + var f = this.finish[i];
│ │ │ │ + if (b == null || f == null || isNaN(b) || isNaN(f)) {
│ │ │ │ + throw new TypeError('invalid value for Tween');
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + var c = f - b;
│ │ │ │ + value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
│ │ │ │ + }
│ │ │ │ + this.time++;
│ │ │ │ +
│ │ │ │ + if (this.callbacks && this.callbacks.eachStep) {
│ │ │ │ + // skip frames if frame rate drops below threshold
│ │ │ │ + if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
│ │ │ │ + this.callbacks.eachStep.call(this, value);
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + if (this.time > this.duration) {
│ │ │ │ + this.stop();
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Create empty functions for all easing methods.
│ │ │ │ + */
│ │ │ │ + CLASS_NAME: "OpenLayers.Tween"
│ │ │ │ +});
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Easing
│ │ │ │ + *
│ │ │ │ + * Credits:
│ │ │ │ + * Easing Equations by Robert Penner,
│ │ │ │ + */
│ │ │ │ +OpenLayers.Easing = {
│ │ │ │ + /**
│ │ │ │ + * Create empty functions for all easing methods.
│ │ │ │ + */
│ │ │ │ + CLASS_NAME: "OpenLayers.Easing"
│ │ │ │ +};
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Easing.Linear
│ │ │ │ + */
│ │ │ │ +OpenLayers.Easing.Linear = {
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeIn
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeIn: function(t, b, c, d) {
│ │ │ │ + return c * t / d + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeOut: function(t, b, c, d) {
│ │ │ │ + return c * t / d + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeInOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeInOut: function(t, b, c, d) {
│ │ │ │ + return c * t / d + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Linear"
│ │ │ │ +};
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Easing.Expo
│ │ │ │ + */
│ │ │ │ +OpenLayers.Easing.Expo = {
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeIn
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeIn: function(t, b, c, d) {
│ │ │ │ + return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeOut: function(t, b, c, d) {
│ │ │ │ + return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeInOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeInOut: function(t, b, c, d) {
│ │ │ │ + if (t == 0) return b;
│ │ │ │ + if (t == d) return b + c;
│ │ │ │ + if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
│ │ │ │ + return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Expo"
│ │ │ │ +};
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Easing.Quad
│ │ │ │ + */
│ │ │ │ +OpenLayers.Easing.Quad = {
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeIn
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeIn: function(t, b, c, d) {
│ │ │ │ + return c * (t /= d) * t + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeOut: function(t, b, c, d) {
│ │ │ │ + return -c * (t /= d) * (t - 2) + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Function: easeInOut
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * t - {Float} time
│ │ │ │ + * b - {Float} beginning position
│ │ │ │ + * c - {Float} total change
│ │ │ │ + * d - {Float} duration of the transition
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Float}
│ │ │ │ + */
│ │ │ │ + easeInOut: function(t, b, c, d) {
│ │ │ │ + if ((t /= d / 2) < 1) return c / 2 * t * t + b;
│ │ │ │ + return -c / 2 * ((--t) * (t - 2) - 1) + b;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Quad"
│ │ │ │ +};
│ │ │ │ +/* ======================================================================
│ │ │ │ OpenLayers/Projection.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │ @@ -12933,248 +12570,265 @@
│ │ │ │ }
│ │ │ │ return bounds;
│ │ │ │ },
│ │ │ │
│ │ │ │ CLASS_NAME: "OpenLayers.Layer"
│ │ │ │ });
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Layer/HTTPRequest.js
│ │ │ │ + OpenLayers/Icon.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │ -
│ │ │ │ /**
│ │ │ │ - * @requires OpenLayers/Layer.js
│ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ */
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Class: OpenLayers.Layer.HTTPRequest
│ │ │ │ + * Class: OpenLayers.Icon
│ │ │ │ + *
│ │ │ │ + * The icon represents a graphical icon on the screen. Typically used in
│ │ │ │ + * conjunction with a to represent markers on a screen.
│ │ │ │ + *
│ │ │ │ + * An icon has a url, size and position. It also contains an offset which
│ │ │ │ + * allows the center point to be represented correctly. This can be
│ │ │ │ + * provided either as a fixed offset or a function provided to calculate
│ │ │ │ + * the desired offset.
│ │ │ │ *
│ │ │ │ - * Inherits from:
│ │ │ │ - * -
│ │ │ │ */
│ │ │ │ -OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
│ │ │ │ +OpenLayers.Icon = OpenLayers.Class({
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Constant: URL_HASH_FACTOR
│ │ │ │ - * {Float} Used to hash URL param strings for multi-WMS server selection.
│ │ │ │ - * Set to the Golden Ratio per Knuth's recommendation.
│ │ │ │ + * Property: url
│ │ │ │ + * {String} image url
│ │ │ │ */
│ │ │ │ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
│ │ │ │ + url: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: url
│ │ │ │ - * {Array(String) or String} This is either an array of url strings or
│ │ │ │ - * a single url string.
│ │ │ │ + * Property: size
│ │ │ │ + * {|Object} An OpenLayers.Size or
│ │ │ │ + * an object with a 'w' and 'h' properties.
│ │ │ │ */
│ │ │ │ - url: null,
│ │ │ │ + size: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: params
│ │ │ │ - * {Object} Hashtable of key/value parameters
│ │ │ │ + * Property: offset
│ │ │ │ + * {|Object} distance in pixels to offset the
│ │ │ │ + * image when being rendered. An OpenLayers.Pixel or an object
│ │ │ │ + * with a 'x' and 'y' properties.
│ │ │ │ */
│ │ │ │ - params: null,
│ │ │ │ + offset: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: reproject
│ │ │ │ - * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html
│ │ │ │ - * for information on the replacement for this functionality.
│ │ │ │ - * {Boolean} Whether layer should reproject itself based on base layer
│ │ │ │ - * locations. This allows reprojection onto commercial layers.
│ │ │ │ - * Default is false: Most layers can't reproject, but layers
│ │ │ │ - * which can create non-square geographic pixels can, like WMS.
│ │ │ │ - *
│ │ │ │ + * Property: calculateOffset
│ │ │ │ + * {Function} Function to calculate the offset (based on the size)
│ │ │ │ */
│ │ │ │ - reproject: false,
│ │ │ │ + calculateOffset: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Constructor: OpenLayers.Layer.HTTPRequest
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * name - {String}
│ │ │ │ - * url - {Array(String) or String}
│ │ │ │ - * params - {Object}
│ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer
│ │ │ │ + /**
│ │ │ │ + * Property: imageDiv
│ │ │ │ + * {DOMElement}
│ │ │ │ */
│ │ │ │ - initialize: function(name, url, params, options) {
│ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
│ │ │ │ - this.url = url;
│ │ │ │ - if (!this.params) {
│ │ │ │ - this.params = OpenLayers.Util.extend({}, params);
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + imageDiv: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: destroy
│ │ │ │ + /**
│ │ │ │ + * Property: px
│ │ │ │ + * {|Object} An OpenLayers.Pixel or an object
│ │ │ │ + * with a 'x' and 'y' properties.
│ │ │ │ */
│ │ │ │ - destroy: function() {
│ │ │ │ - this.url = null;
│ │ │ │ - this.params = null;
│ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments);
│ │ │ │ - },
│ │ │ │ + px: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: clone
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * obj - {Object}
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {} An exact clone of this
│ │ │ │ - *
│ │ │ │ + /**
│ │ │ │ + * Constructor: OpenLayers.Icon
│ │ │ │ + * Creates an icon, which is an image tag in a div.
│ │ │ │ + *
│ │ │ │ + * url - {String}
│ │ │ │ + * size - {|Object} An OpenLayers.Size or an
│ │ │ │ + * object with a 'w' and 'h'
│ │ │ │ + * properties.
│ │ │ │ + * offset - {|Object} An OpenLayers.Pixel or an
│ │ │ │ + * object with a 'x' and 'y'
│ │ │ │ + * properties.
│ │ │ │ + * calculateOffset - {Function}
│ │ │ │ */
│ │ │ │ - clone: function(obj) {
│ │ │ │ -
│ │ │ │ - if (obj == null) {
│ │ │ │ - obj = new OpenLayers.Layer.HTTPRequest(this.name,
│ │ │ │ - this.url,
│ │ │ │ - this.params,
│ │ │ │ - this.getOptions());
│ │ │ │ - }
│ │ │ │ + initialize: function(url, size, offset, calculateOffset) {
│ │ │ │ + this.url = url;
│ │ │ │ + this.size = size || {
│ │ │ │ + w: 20,
│ │ │ │ + h: 20
│ │ │ │ + };
│ │ │ │ + this.offset = offset || {
│ │ │ │ + x: -(this.size.w / 2),
│ │ │ │ + y: -(this.size.h / 2)
│ │ │ │ + };
│ │ │ │ + this.calculateOffset = calculateOffset;
│ │ │ │
│ │ │ │ - //get all additions from superclasses
│ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
│ │ │ │ + var id = OpenLayers.Util.createUniqueID("OL_Icon_");
│ │ │ │ + this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
│ │ │ │ + },
│ │ │ │
│ │ │ │ - // copy/set any non-init, non-simple values here
│ │ │ │ + /**
│ │ │ │ + * Method: destroy
│ │ │ │ + * Nullify references and remove event listeners to prevent circular
│ │ │ │ + * references and memory leaks
│ │ │ │ + */
│ │ │ │ + destroy: function() {
│ │ │ │ + // erase any drawn elements
│ │ │ │ + this.erase();
│ │ │ │
│ │ │ │ - return obj;
│ │ │ │ + OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
│ │ │ │ + this.imageDiv.innerHTML = "";
│ │ │ │ + this.imageDiv = null;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: setUrl
│ │ │ │ + * Method: clone
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * newUrl - {String}
│ │ │ │ + * Returns:
│ │ │ │ + * {} A fresh copy of the icon.
│ │ │ │ */
│ │ │ │ - setUrl: function(newUrl) {
│ │ │ │ - this.url = newUrl;
│ │ │ │ + clone: function() {
│ │ │ │ + return new OpenLayers.Icon(this.url,
│ │ │ │ + this.size,
│ │ │ │ + this.offset,
│ │ │ │ + this.calculateOffset);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: mergeNewParams
│ │ │ │ + * Method: setSize
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * newParams - {Object}
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn.
│ │ │ │ + * size - {|Object} An OpenLayers.Size or
│ │ │ │ + * an object with a 'w' and 'h' properties.
│ │ │ │ */
│ │ │ │ - mergeNewParams: function(newParams) {
│ │ │ │ - this.params = OpenLayers.Util.extend(this.params, newParams);
│ │ │ │ - var ret = this.redraw();
│ │ │ │ - if (this.map != null) {
│ │ │ │ - this.map.events.triggerEvent("changelayer", {
│ │ │ │ - layer: this,
│ │ │ │ - property: "params"
│ │ │ │ - });
│ │ │ │ + setSize: function(size) {
│ │ │ │ + if (size != null) {
│ │ │ │ + this.size = size;
│ │ │ │ }
│ │ │ │ - return ret;
│ │ │ │ + this.draw();
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: redraw
│ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not.
│ │ │ │ - *
│ │ │ │ + * Method: setUrl
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * force - {Boolean} Force redraw by adding random parameter.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Boolean} The layer was redrawn.
│ │ │ │ + * url - {String}
│ │ │ │ */
│ │ │ │ - redraw: function(force) {
│ │ │ │ - if (force) {
│ │ │ │ - return this.mergeNewParams({
│ │ │ │ - "_olSalt": Math.random()
│ │ │ │ - });
│ │ │ │ - } else {
│ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []);
│ │ │ │ + setUrl: function(url) {
│ │ │ │ + if (url != null) {
│ │ │ │ + this.url = url;
│ │ │ │ }
│ │ │ │ + this.draw();
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: selectUrl
│ │ │ │ - * selectUrl() implements the standard floating-point multiplicative
│ │ │ │ - * hash function described by Knuth, and hashes the contents of the
│ │ │ │ - * given param string into a float between 0 and 1. This float is then
│ │ │ │ - * scaled to the size of the provided urls array, and used to select
│ │ │ │ - * a URL.
│ │ │ │ - *
│ │ │ │ + /**
│ │ │ │ + * Method: draw
│ │ │ │ + * Move the div to the given pixel.
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * paramString - {String}
│ │ │ │ - * urls - {Array(String)}
│ │ │ │ + * px - {|Object} An OpenLayers.Pixel or an
│ │ │ │ + * object with a 'x' and 'y' properties.
│ │ │ │ *
│ │ │ │ * Returns:
│ │ │ │ - * {String} An entry from the urls array, deterministically selected based
│ │ │ │ - * on the paramString.
│ │ │ │ + * {DOMElement} A new DOM Image of this icon set at the location passed-in
│ │ │ │ */
│ │ │ │ - selectUrl: function(paramString, urls) {
│ │ │ │ - var product = 1;
│ │ │ │ - for (var i = 0, len = paramString.length; i < len; i++) {
│ │ │ │ - product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
│ │ │ │ - product -= Math.floor(product);
│ │ │ │ + draw: function(px) {
│ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,
│ │ │ │ + null,
│ │ │ │ + null,
│ │ │ │ + this.size,
│ │ │ │ + this.url,
│ │ │ │ + "absolute");
│ │ │ │ + this.moveTo(px);
│ │ │ │ + return this.imageDiv;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Method: erase
│ │ │ │ + * Erase the underlying image element.
│ │ │ │ + */
│ │ │ │ + erase: function() {
│ │ │ │ + if (this.imageDiv != null && this.imageDiv.parentNode != null) {
│ │ │ │ + OpenLayers.Element.remove(this.imageDiv);
│ │ │ │ }
│ │ │ │ - return urls[Math.floor(product * urls.length)];
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: getFullRequestString
│ │ │ │ - * Combine url with layer's params and these newParams.
│ │ │ │ - *
│ │ │ │ - * does checking on the serverPath variable, allowing for cases when it
│ │ │ │ - * is supplied with trailing ? or &, as well as cases where not.
│ │ │ │ - *
│ │ │ │ - * return in formatted string like this:
│ │ │ │ - * "server?key1=value1&key2=value2&key3=value3"
│ │ │ │ - *
│ │ │ │ - * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
│ │ │ │ + * Method: setOpacity
│ │ │ │ + * Change the icon's opacity
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * newParams - {Object}
│ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {String}
│ │ │ │ + * opacity - {float}
│ │ │ │ */
│ │ │ │ - getFullRequestString: function(newParams, altUrl) {
│ │ │ │ -
│ │ │ │ - // if not altUrl passed in, use layer's url
│ │ │ │ - var url = altUrl || this.url;
│ │ │ │ + setOpacity: function(opacity) {
│ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,
│ │ │ │ + null, null, null, null, opacity);
│ │ │ │
│ │ │ │ - // create a new params hashtable with all the layer params and the
│ │ │ │ - // new params together. then convert to string
│ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params);
│ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams);
│ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams);
│ │ │ │ + },
│ │ │ │
│ │ │ │ - // if url is not a string, it should be an array of strings,
│ │ │ │ - // in which case we will deterministically select one of them in
│ │ │ │ - // order to evenly distribute requests to different urls.
│ │ │ │ - //
│ │ │ │ - if (OpenLayers.Util.isArray(url)) {
│ │ │ │ - url = this.selectUrl(paramsString, url);
│ │ │ │ + /**
│ │ │ │ + * Method: moveTo
│ │ │ │ + * move icon to passed in px.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * px - {|Object} the pixel position to move to.
│ │ │ │ + * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
│ │ │ │ + */
│ │ │ │ + moveTo: function(px) {
│ │ │ │ + //if no px passed in, use stored location
│ │ │ │ + if (px != null) {
│ │ │ │ + this.px = px;
│ │ │ │ }
│ │ │ │
│ │ │ │ - // ignore parameters that are already in the url search string
│ │ │ │ - var urlParams =
│ │ │ │ - OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
│ │ │ │ - for (var key in allParams) {
│ │ │ │ - if (key.toUpperCase() in urlParams) {
│ │ │ │ - delete allParams[key];
│ │ │ │ + if (this.imageDiv != null) {
│ │ │ │ + if (this.px == null) {
│ │ │ │ + this.display(false);
│ │ │ │ + } else {
│ │ │ │ + if (this.calculateOffset) {
│ │ │ │ + this.offset = this.calculateOffset(this.size);
│ │ │ │ + }
│ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
│ │ │ │ + x: this.px.x + this.offset.x,
│ │ │ │ + y: this.px.y + this.offset.y
│ │ │ │ + });
│ │ │ │ }
│ │ │ │ }
│ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams);
│ │ │ │ + },
│ │ │ │
│ │ │ │ - return OpenLayers.Util.urlAppend(url, paramsString);
│ │ │ │ + /**
│ │ │ │ + * Method: display
│ │ │ │ + * Hide or show the icon
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * display - {Boolean}
│ │ │ │ + */
│ │ │ │ + display: function(display) {
│ │ │ │ + this.imageDiv.style.display = (display) ? "" : "none";
│ │ │ │ },
│ │ │ │
│ │ │ │ - CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIMethod: isDrawn
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Boolean} Whether or not the icon is drawn.
│ │ │ │ + */
│ │ │ │ + isDrawn: function() {
│ │ │ │ + // nodeType 11 for ie, whose nodes *always* have a parentNode
│ │ │ │ + // (of type document fragment)
│ │ │ │ + var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&
│ │ │ │ + (this.imageDiv.parentNode.nodeType != 11));
│ │ │ │ +
│ │ │ │ + return isDrawn;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + CLASS_NAME: "OpenLayers.Icon"
│ │ │ │ });
│ │ │ │ /* ======================================================================
│ │ │ │ OpenLayers/Tile.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ @@ -13468,3100 +13122,4314 @@
│ │ │ │ clear: function(draw) {
│ │ │ │ // to be extended by subclasses
│ │ │ │ },
│ │ │ │
│ │ │ │ CLASS_NAME: "OpenLayers.Tile"
│ │ │ │ });
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Tile/Image.js
│ │ │ │ + OpenLayers/Kinetic.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │ -
│ │ │ │ /**
│ │ │ │ - * @requires OpenLayers/Tile.js
│ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ * @requires OpenLayers/Animation.js
│ │ │ │ - * @requires OpenLayers/Util.js
│ │ │ │ */
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * Class: OpenLayers.Tile.Image
│ │ │ │ - * Instances of OpenLayers.Tile.Image are used to manage the image tiles
│ │ │ │ - * used by various layers. Create a new image tile with the
│ │ │ │ - * constructor.
│ │ │ │ - *
│ │ │ │ - * Inherits from:
│ │ │ │ - * -
│ │ │ │ - */
│ │ │ │ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
│ │ │ │ +OpenLayers.Kinetic = OpenLayers.Class({
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: events
│ │ │ │ - * {} An events object that handles all
│ │ │ │ - * events on the tile.
│ │ │ │ - *
│ │ │ │ - * Register a listener for a particular event with the following syntax:
│ │ │ │ - * (code)
│ │ │ │ - * tile.events.register(type, obj, listener);
│ │ │ │ - * (end)
│ │ │ │ - *
│ │ │ │ - * Supported event types (in addition to the events):
│ │ │ │ - * beforeload - Triggered before an image is prepared for loading, when the
│ │ │ │ - * url for the image is known already. Listeners may call on
│ │ │ │ - * the tile instance. If they do so, that image will be used and no new
│ │ │ │ - * one will be created.
│ │ │ │ - */
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: url
│ │ │ │ - * {String} The URL of the image being requested. No default. Filled in by
│ │ │ │ - * layer.getURL() function. May be modified by loadstart listeners.
│ │ │ │ - */
│ │ │ │ - url: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: imgDiv
│ │ │ │ - * {HTMLImageElement} The image for this tile.
│ │ │ │ + * Property: threshold
│ │ │ │ + * In most cases changing the threshold isn't needed.
│ │ │ │ + * In px/ms, default to 0.
│ │ │ │ */
│ │ │ │ - imgDiv: null,
│ │ │ │ + threshold: 0,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: frame
│ │ │ │ - * {DOMElement} The image element is appended to the frame. Any gutter on
│ │ │ │ - * the image will be hidden behind the frame. If no gutter is set,
│ │ │ │ - * this will be null.
│ │ │ │ - */
│ │ │ │ - frame: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Property: imageReloadAttempts
│ │ │ │ - * {Integer} Attempts to load the image.
│ │ │ │ + * Property: deceleration
│ │ │ │ + * {Float} the deseleration in px/ms², default to 0.0035.
│ │ │ │ */
│ │ │ │ - imageReloadAttempts: null,
│ │ │ │ + deceleration: 0.0035,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: layerAlphaHack
│ │ │ │ - * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
│ │ │ │ + * Property: nbPoints
│ │ │ │ + * {Integer} the number of points we use to calculate the kinetic
│ │ │ │ + * initial values.
│ │ │ │ */
│ │ │ │ - layerAlphaHack: null,
│ │ │ │ + nbPoints: 100,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: asyncRequestId
│ │ │ │ - * {Integer} ID of an request to see if request is still valid. This is a
│ │ │ │ - * number which increments by 1 for each asynchronous request.
│ │ │ │ + * Property: delay
│ │ │ │ + * {Float} time to consider to calculate the kinetic initial values.
│ │ │ │ + * In ms, default to 200.
│ │ │ │ */
│ │ │ │ - asyncRequestId: null,
│ │ │ │ + delay: 200,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: maxGetUrlLength
│ │ │ │ - * {Number} If set, requests that would result in GET urls with more
│ │ │ │ - * characters than the number provided will be made using form-encoded
│ │ │ │ - * HTTP POST. It is good practice to avoid urls that are longer than 2048
│ │ │ │ - * characters.
│ │ │ │ - *
│ │ │ │ - * Caution:
│ │ │ │ - * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most
│ │ │ │ - * Opera versions do not fully support this option. On all browsers,
│ │ │ │ - * transition effects are not supported if POST requests are used.
│ │ │ │ + * Property: points
│ │ │ │ + * List of points use to calculate the kinetic initial values.
│ │ │ │ */
│ │ │ │ - maxGetUrlLength: null,
│ │ │ │ + points: undefined,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: canvasContext
│ │ │ │ - * {CanvasRenderingContext2D} A canvas context associated with
│ │ │ │ - * the tile image.
│ │ │ │ + * Property: timerId
│ │ │ │ + * ID of the timer.
│ │ │ │ */
│ │ │ │ - canvasContext: null,
│ │ │ │ + timerId: undefined,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: crossOriginKeyword
│ │ │ │ - * The value of the crossorigin keyword to use when loading images. This is
│ │ │ │ - * only relevant when using for tiles from remote
│ │ │ │ - * origins and should be set to either 'anonymous' or 'use-credentials'
│ │ │ │ - * for servers that send Access-Control-Allow-Origin headers with their
│ │ │ │ - * tiles.
│ │ │ │ - */
│ │ │ │ - crossOriginKeyword: null,
│ │ │ │ -
│ │ │ │ - /** TBD 3.0 - reorder the parameters to the init function to remove
│ │ │ │ - * URL. the getUrl() function on the layer gets called on
│ │ │ │ - * each draw(), so no need to specify it here.
│ │ │ │ - */
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Constructor: OpenLayers.Tile.Image
│ │ │ │ - * Constructor for a new instance.
│ │ │ │ - *
│ │ │ │ + * Constructor: OpenLayers.Kinetic
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * layer - {} layer that the tile will go in.
│ │ │ │ - * position - {}
│ │ │ │ - * bounds - {}
│ │ │ │ - * url - {} Deprecated. Remove me in 3.0.
│ │ │ │ - * size - {}
│ │ │ │ * options - {Object}
│ │ │ │ */
│ │ │ │ - initialize: function(layer, position, bounds, url, size, options) {
│ │ │ │ - OpenLayers.Tile.prototype.initialize.apply(this, arguments);
│ │ │ │ -
│ │ │ │ - this.url = url; //deprecated remove me
│ │ │ │ -
│ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
│ │ │ │ -
│ │ │ │ - if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {
│ │ │ │ - // only create frame if it's needed
│ │ │ │ - this.frame = document.createElement("div");
│ │ │ │ - this.frame.style.position = "absolute";
│ │ │ │ - this.frame.style.overflow = "hidden";
│ │ │ │ - }
│ │ │ │ - if (this.maxGetUrlLength != null) {
│ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIMethod: destroy
│ │ │ │ - * nullify references to prevent circular references and memory leaks
│ │ │ │ - */
│ │ │ │ - destroy: function() {
│ │ │ │ - if (this.imgDiv) {
│ │ │ │ - this.clear();
│ │ │ │ - this.imgDiv = null;
│ │ │ │ - this.frame = null;
│ │ │ │ - }
│ │ │ │ - // don't handle async requests any more
│ │ │ │ - this.asyncRequestId = null;
│ │ │ │ - OpenLayers.Tile.prototype.destroy.apply(this, arguments);
│ │ │ │ + initialize: function(options) {
│ │ │ │ + OpenLayers.Util.extend(this, options);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: draw
│ │ │ │ - * Check that a tile should be drawn, and draw it.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned
│ │ │ │ - * false.
│ │ │ │ + * Method: begin
│ │ │ │ + * Begins the dragging.
│ │ │ │ */
│ │ │ │ - draw: function() {
│ │ │ │ - var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);
│ │ │ │ - if (shouldDraw) {
│ │ │ │ - // The layer's reproject option is deprecated.
│ │ │ │ - if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
│ │ │ │ - // getBoundsFromBaseLayer is defined in deprecated.js.
│ │ │ │ - this.bounds = this.getBoundsFromBaseLayer(this.position);
│ │ │ │ - }
│ │ │ │ - if (this.isLoading) {
│ │ │ │ - //if we're already loading, send 'reload' instead of 'loadstart'.
│ │ │ │ - this._loadEvent = "reload";
│ │ │ │ - } else {
│ │ │ │ - this.isLoading = true;
│ │ │ │ - this._loadEvent = "loadstart";
│ │ │ │ - }
│ │ │ │ - this.renderTile();
│ │ │ │ - this.positionTile();
│ │ │ │ - } else if (shouldDraw === false) {
│ │ │ │ - this.unload();
│ │ │ │ - }
│ │ │ │ - return shouldDraw;
│ │ │ │ + begin: function() {
│ │ │ │ + OpenLayers.Animation.stop(this.timerId);
│ │ │ │ + this.timerId = undefined;
│ │ │ │ + this.points = [];
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: renderTile
│ │ │ │ - * Internal function to actually initialize the image tile,
│ │ │ │ - * position it correctly, and set its url.
│ │ │ │ + * Method: update
│ │ │ │ + * Updates during the dragging.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * xy - {} The new position.
│ │ │ │ */
│ │ │ │ - renderTile: function() {
│ │ │ │ - if (this.layer.async) {
│ │ │ │ - // Asynchronous image requests call the asynchronous getURL method
│ │ │ │ - // on the layer to fetch an image that covers 'this.bounds'.
│ │ │ │ - var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;
│ │ │ │ - this.layer.getURLasync(this.bounds, function(url) {
│ │ │ │ - if (id == this.asyncRequestId) {
│ │ │ │ - this.url = url;
│ │ │ │ - this.initImage();
│ │ │ │ - }
│ │ │ │ - }, this);
│ │ │ │ - } else {
│ │ │ │ - // synchronous image requests get the url immediately.
│ │ │ │ - this.url = this.layer.getURL(this.bounds);
│ │ │ │ - this.initImage();
│ │ │ │ + update: function(xy) {
│ │ │ │ + this.points.unshift({
│ │ │ │ + xy: xy,
│ │ │ │ + tick: new Date().getTime()
│ │ │ │ + });
│ │ │ │ + if (this.points.length > this.nbPoints) {
│ │ │ │ + this.points.pop();
│ │ │ │ }
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: positionTile
│ │ │ │ - * Using the properties currenty set on the layer, position the tile correctly.
│ │ │ │ - * This method is used both by the async and non-async versions of the Tile.Image
│ │ │ │ - * code.
│ │ │ │ - */
│ │ │ │ - positionTile: function() {
│ │ │ │ - var style = this.getTile().style,
│ │ │ │ - size = this.frame ? this.size :
│ │ │ │ - this.layer.getImageSize(this.bounds),
│ │ │ │ - ratio = 1;
│ │ │ │ - if (this.layer instanceof OpenLayers.Layer.Grid) {
│ │ │ │ - ratio = this.layer.getServerResolution() / this.layer.map.getResolution();
│ │ │ │ - }
│ │ │ │ - style.left = this.position.x + "px";
│ │ │ │ - style.top = this.position.y + "px";
│ │ │ │ - style.width = Math.round(ratio * size.w) + "px";
│ │ │ │ - style.height = Math.round(ratio * size.h) + "px";
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: clear
│ │ │ │ - * Remove the tile from the DOM, clear it of any image related data so that
│ │ │ │ - * it can be reused in a new location.
│ │ │ │ + * Method: end
│ │ │ │ + * Ends the dragging, start the kinetic.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * xy - {} The last position.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} An object with two properties: "speed", and "theta". The
│ │ │ │ + * "speed" and "theta" values are to be passed to the move
│ │ │ │ + * function when starting the animation.
│ │ │ │ */
│ │ │ │ - clear: function() {
│ │ │ │ - OpenLayers.Tile.prototype.clear.apply(this, arguments);
│ │ │ │ - var img = this.imgDiv;
│ │ │ │ - if (img) {
│ │ │ │ - var tile = this.getTile();
│ │ │ │ - if (tile.parentNode === this.layer.div) {
│ │ │ │ - this.layer.div.removeChild(tile);
│ │ │ │ - }
│ │ │ │ - this.setImgSrc();
│ │ │ │ - if (this.layerAlphaHack === true) {
│ │ │ │ - img.style.filter = "";
│ │ │ │ + end: function(xy) {
│ │ │ │ + var last, now = new Date().getTime();
│ │ │ │ + for (var i = 0, l = this.points.length, point; i < l; i++) {
│ │ │ │ + point = this.points[i];
│ │ │ │ + if (now - point.tick > this.delay) {
│ │ │ │ + break;
│ │ │ │ }
│ │ │ │ - OpenLayers.Element.removeClass(img, "olImageLoadError");
│ │ │ │ + last = point;
│ │ │ │ }
│ │ │ │ - this.canvasContext = null;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: getImage
│ │ │ │ - * Returns or creates and returns the tile image.
│ │ │ │ - */
│ │ │ │ - getImage: function() {
│ │ │ │ - if (!this.imgDiv) {
│ │ │ │ - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);
│ │ │ │ -
│ │ │ │ - var style = this.imgDiv.style;
│ │ │ │ - if (this.frame) {
│ │ │ │ - var left = 0,
│ │ │ │ - top = 0;
│ │ │ │ - if (this.layer.gutter) {
│ │ │ │ - left = this.layer.gutter / this.layer.tileSize.w * 100;
│ │ │ │ - top = this.layer.gutter / this.layer.tileSize.h * 100;
│ │ │ │ - }
│ │ │ │ - style.left = -left + "%";
│ │ │ │ - style.top = -top + "%";
│ │ │ │ - style.width = (2 * left + 100) + "%";
│ │ │ │ - style.height = (2 * top + 100) + "%";
│ │ │ │ - }
│ │ │ │ - style.visibility = "hidden";
│ │ │ │ - style.opacity = 0;
│ │ │ │ - if (this.layer.opacity < 1) {
│ │ │ │ - style.filter = 'alpha(opacity=' +
│ │ │ │ - (this.layer.opacity * 100) +
│ │ │ │ - ')';
│ │ │ │ - }
│ │ │ │ - style.position = "absolute";
│ │ │ │ - if (this.layerAlphaHack) {
│ │ │ │ - // move the image out of sight
│ │ │ │ - style.paddingTop = style.height;
│ │ │ │ - style.height = "0";
│ │ │ │ - style.width = "100%";
│ │ │ │ - }
│ │ │ │ - if (this.frame) {
│ │ │ │ - this.frame.appendChild(this.imgDiv);
│ │ │ │ - }
│ │ │ │ + if (!last) {
│ │ │ │ + return;
│ │ │ │ }
│ │ │ │ -
│ │ │ │ - return this.imgDiv;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIMethod: setImage
│ │ │ │ - * Sets the image element for this tile. This method should only be called
│ │ │ │ - * from beforeload listeners.
│ │ │ │ - *
│ │ │ │ - * Parameters
│ │ │ │ - * img - {HTMLImageElement} The image to use for this tile.
│ │ │ │ - */
│ │ │ │ - setImage: function(img) {
│ │ │ │ - this.imgDiv = img;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: initImage
│ │ │ │ - * Creates the content for the frame on the tile.
│ │ │ │ - */
│ │ │ │ - initImage: function() {
│ │ │ │ - if (!this.url && !this.imgDiv) {
│ │ │ │ - // fast path out - if there is no tile url and no previous image
│ │ │ │ - this.isLoading = false;
│ │ │ │ + var time = new Date().getTime() - last.tick;
│ │ │ │ + var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +
│ │ │ │ + Math.pow(xy.y - last.xy.y, 2));
│ │ │ │ + var speed = dist / time;
│ │ │ │ + if (speed == 0 || speed < this.threshold) {
│ │ │ │ return;
│ │ │ │ }
│ │ │ │ - this.events.triggerEvent('beforeload');
│ │ │ │ - this.layer.div.appendChild(this.getTile());
│ │ │ │ - this.events.triggerEvent(this._loadEvent);
│ │ │ │ - var img = this.getImage();
│ │ │ │ - var src = img.getAttribute('src') || '';
│ │ │ │ - if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {
│ │ │ │ - this._loadTimeout = window.setTimeout(
│ │ │ │ - OpenLayers.Function.bind(this.onImageLoad, this), 0
│ │ │ │ - );
│ │ │ │ - } else {
│ │ │ │ - this.stopLoading();
│ │ │ │ - if (this.crossOriginKeyword) {
│ │ │ │ - img.removeAttribute("crossorigin");
│ │ │ │ - }
│ │ │ │ - OpenLayers.Event.observe(img, "load",
│ │ │ │ - OpenLayers.Function.bind(this.onImageLoad, this)
│ │ │ │ - );
│ │ │ │ - OpenLayers.Event.observe(img, "error",
│ │ │ │ - OpenLayers.Function.bind(this.onImageError, this)
│ │ │ │ - );
│ │ │ │ - this.imageReloadAttempts = 0;
│ │ │ │ - this.setImgSrc(this.url);
│ │ │ │ + var theta = Math.asin((xy.y - last.xy.y) / dist);
│ │ │ │ + if (last.xy.x <= xy.x) {
│ │ │ │ + theta = Math.PI - theta;
│ │ │ │ }
│ │ │ │ + return {
│ │ │ │ + speed: speed,
│ │ │ │ + theta: theta
│ │ │ │ + };
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: setImgSrc
│ │ │ │ - * Sets the source for the tile image
│ │ │ │ + * Method: move
│ │ │ │ + * Launch the kinetic move pan.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * url - {String} or undefined to hide the image
│ │ │ │ + * info - {Object} An object with two properties, "speed", and "theta".
│ │ │ │ + * These values are those returned from the "end" call.
│ │ │ │ + * callback - {Function} Function called on every step of the animation,
│ │ │ │ + * receives x, y (values to pan), end (is the last point).
│ │ │ │ */
│ │ │ │ - setImgSrc: function(url) {
│ │ │ │ - var img = this.imgDiv;
│ │ │ │ - if (url) {
│ │ │ │ - img.style.visibility = 'hidden';
│ │ │ │ - img.style.opacity = 0;
│ │ │ │ - // don't set crossOrigin if the url is a data URL
│ │ │ │ - if (this.crossOriginKeyword) {
│ │ │ │ - if (url.substr(0, 5) !== 'data:') {
│ │ │ │ - img.setAttribute("crossorigin", this.crossOriginKeyword);
│ │ │ │ - } else {
│ │ │ │ - img.removeAttribute("crossorigin");
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - img.src = url;
│ │ │ │ - } else {
│ │ │ │ - // Remove reference to the image, and leave it to the browser's
│ │ │ │ - // caching and garbage collection.
│ │ │ │ - this.stopLoading();
│ │ │ │ - this.imgDiv = null;
│ │ │ │ - if (img.parentNode) {
│ │ │ │ - img.parentNode.removeChild(img);
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + move: function(info, callback) {
│ │ │ │ + var v0 = info.speed;
│ │ │ │ + var fx = Math.cos(info.theta);
│ │ │ │ + var fy = -Math.sin(info.theta);
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: getTile
│ │ │ │ - * Get the tile's markup.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {DOMElement} The tile's markup
│ │ │ │ - */
│ │ │ │ - getTile: function() {
│ │ │ │ - return this.frame ? this.frame : this.getImage();
│ │ │ │ - },
│ │ │ │ + var initialTime = new Date().getTime();
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: createBackBuffer
│ │ │ │ - * Create a backbuffer for this tile. A backbuffer isn't exactly a clone
│ │ │ │ - * of the tile's markup, because we want to avoid the reloading of the
│ │ │ │ - * image. So we clone the frame, and steal the image from the tile.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {DOMElement} The markup, or undefined if the tile has no image
│ │ │ │ - * or if it's currently loading.
│ │ │ │ - */
│ │ │ │ - createBackBuffer: function() {
│ │ │ │ - if (!this.imgDiv || this.isLoading) {
│ │ │ │ - return;
│ │ │ │ - }
│ │ │ │ - var backBuffer;
│ │ │ │ - if (this.frame) {
│ │ │ │ - backBuffer = this.frame.cloneNode(false);
│ │ │ │ - backBuffer.appendChild(this.imgDiv);
│ │ │ │ - } else {
│ │ │ │ - backBuffer = this.imgDiv;
│ │ │ │ - }
│ │ │ │ - this.imgDiv = null;
│ │ │ │ - return backBuffer;
│ │ │ │ - },
│ │ │ │ + var lastX = 0;
│ │ │ │ + var lastY = 0;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: onImageLoad
│ │ │ │ - * Handler for the image onload event
│ │ │ │ - */
│ │ │ │ - onImageLoad: function() {
│ │ │ │ - var img = this.imgDiv;
│ │ │ │ - this.stopLoading();
│ │ │ │ - img.style.visibility = 'inherit';
│ │ │ │ - img.style.opacity = this.layer.opacity;
│ │ │ │ - this.isLoading = false;
│ │ │ │ - this.canvasContext = null;
│ │ │ │ - this.events.triggerEvent("loadend");
│ │ │ │ + var timerCallback = function() {
│ │ │ │ + if (this.timerId == null) {
│ │ │ │ + return;
│ │ │ │ + }
│ │ │ │
│ │ │ │ - if (this.layerAlphaHack === true) {
│ │ │ │ - img.style.filter =
│ │ │ │ - "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
│ │ │ │ - img.src + "', sizingMethod='scale')";
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + var t = new Date().getTime() - initialTime;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: onImageError
│ │ │ │ - * Handler for the image onerror event
│ │ │ │ - */
│ │ │ │ - onImageError: function() {
│ │ │ │ - var img = this.imgDiv;
│ │ │ │ - if (img.src != null) {
│ │ │ │ - this.imageReloadAttempts++;
│ │ │ │ - if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
│ │ │ │ - this.setImgSrc(this.layer.getURL(this.bounds));
│ │ │ │ - } else {
│ │ │ │ - OpenLayers.Element.addClass(img, "olImageLoadError");
│ │ │ │ - this.events.triggerEvent("loaderror");
│ │ │ │ - this.onImageLoad();
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;
│ │ │ │ + var x = p * fx;
│ │ │ │ + var y = p * fy;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: stopLoading
│ │ │ │ - * Stops a loading sequence so won't be executed.
│ │ │ │ - */
│ │ │ │ - stopLoading: function() {
│ │ │ │ - OpenLayers.Event.stopObservingElement(this.imgDiv);
│ │ │ │ - window.clearTimeout(this._loadTimeout);
│ │ │ │ - delete this._loadTimeout;
│ │ │ │ - },
│ │ │ │ + var args = {};
│ │ │ │ + args.end = false;
│ │ │ │ + var v = -this.deceleration * t + v0;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: getCanvasContext
│ │ │ │ - * Returns a canvas context associated with the tile image (with
│ │ │ │ - * the image drawn on it).
│ │ │ │ - * Returns undefined if the browser does not support canvas, if
│ │ │ │ - * the tile has no image or if it's currently loading.
│ │ │ │ - *
│ │ │ │ - * The function returns a canvas context instance but the
│ │ │ │ - * underlying canvas is still available in the 'canvas' property:
│ │ │ │ - * (code)
│ │ │ │ - * var context = tile.getCanvasContext();
│ │ │ │ - * if (context) {
│ │ │ │ - * var data = context.canvas.toDataURL('image/jpeg');
│ │ │ │ - * }
│ │ │ │ - * (end)
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Boolean}
│ │ │ │ - */
│ │ │ │ - getCanvasContext: function() {
│ │ │ │ - if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {
│ │ │ │ - if (!this.canvasContext) {
│ │ │ │ - var canvas = document.createElement("canvas");
│ │ │ │ - canvas.width = this.size.w;
│ │ │ │ - canvas.height = this.size.h;
│ │ │ │ - this.canvasContext = canvas.getContext("2d");
│ │ │ │ - this.canvasContext.drawImage(this.imgDiv, 0, 0);
│ │ │ │ + if (v <= 0) {
│ │ │ │ + OpenLayers.Animation.stop(this.timerId);
│ │ │ │ + this.timerId = null;
│ │ │ │ + args.end = true;
│ │ │ │ }
│ │ │ │ - return this.canvasContext;
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - CLASS_NAME: "OpenLayers.Tile.Image"
│ │ │ │
│ │ │ │ -});
│ │ │ │ + args.x = x - lastX;
│ │ │ │ + args.y = y - lastY;
│ │ │ │ + lastX = x;
│ │ │ │ + lastY = y;
│ │ │ │ + callback(args.x, args.y, args.end);
│ │ │ │ + };
│ │ │ │
│ │ │ │ -/**
│ │ │ │ - * Constant: OpenLayers.Tile.Image.IMAGE
│ │ │ │ - * {HTMLImageElement} The image for a tile.
│ │ │ │ - */
│ │ │ │ -OpenLayers.Tile.Image.IMAGE = (function() {
│ │ │ │ - var img = new Image();
│ │ │ │ - img.className = "olTileImage";
│ │ │ │ - // avoid image gallery menu in IE6
│ │ │ │ - img.galleryImg = "no";
│ │ │ │ - return img;
│ │ │ │ -}());
│ │ │ │ + this.timerId = OpenLayers.Animation.start(
│ │ │ │ + OpenLayers.Function.bind(timerCallback, this)
│ │ │ │ + );
│ │ │ │ + },
│ │ │ │
│ │ │ │ + CLASS_NAME: "OpenLayers.Kinetic"
│ │ │ │ +});
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Layer/Grid.js
│ │ │ │ + OpenLayers/Feature.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * @requires OpenLayers/Layer/HTTPRequest.js
│ │ │ │ - * @requires OpenLayers/Tile/Image.js
│ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ + * @requires OpenLayers/Util.js
│ │ │ │ */
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Class: OpenLayers.Layer.Grid
│ │ │ │ - * Base class for layers that use a lattice of tiles. Create a new grid
│ │ │ │ - * layer with the constructor.
│ │ │ │ - *
│ │ │ │ - * Inherits from:
│ │ │ │ - * -
│ │ │ │ + * Class: OpenLayers.Feature
│ │ │ │ + * Features are combinations of geography and attributes. The OpenLayers.Feature
│ │ │ │ + * class specifically combines a marker and a lonlat.
│ │ │ │ */
│ │ │ │ -OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
│ │ │ │ +OpenLayers.Feature = OpenLayers.Class({
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: tileSize
│ │ │ │ - * {}
│ │ │ │ + /**
│ │ │ │ + * Property: layer
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - tileSize: null,
│ │ │ │ + layer: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: tileOriginCorner
│ │ │ │ - * {String} If the property is not provided, the tile origin
│ │ │ │ - * will be derived from the layer's . The corner of the
│ │ │ │ - * used is determined by this property. Acceptable values
│ │ │ │ - * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
│ │ │ │ - * (bottom right). Default is "bl".
│ │ │ │ + /**
│ │ │ │ + * Property: id
│ │ │ │ + * {String}
│ │ │ │ */
│ │ │ │ - tileOriginCorner: "bl",
│ │ │ │ + id: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: tileOrigin
│ │ │ │ - * {} Optional origin for aligning the grid of tiles.
│ │ │ │ - * If provided, requests for tiles at all resolutions will be aligned
│ │ │ │ - * with this location (no tiles shall overlap this location). If
│ │ │ │ - * not provided, the grid of tiles will be aligned with the layer's
│ │ │ │ - * . Default is ``null``.
│ │ │ │ + /**
│ │ │ │ + * Property: lonlat
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - tileOrigin: null,
│ │ │ │ + lonlat: null,
│ │ │ │
│ │ │ │ - /** APIProperty: tileOptions
│ │ │ │ - * {Object} optional configuration options for instances
│ │ │ │ - * created by this Layer, if supported by the tile class.
│ │ │ │ + /**
│ │ │ │ + * Property: data
│ │ │ │ + * {Object}
│ │ │ │ */
│ │ │ │ - tileOptions: null,
│ │ │ │ + data: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: tileClass
│ │ │ │ - * {} The tile class to use for this layer.
│ │ │ │ - * Defaults is OpenLayers.Tile.Image.
│ │ │ │ + /**
│ │ │ │ + * Property: marker
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - tileClass: OpenLayers.Tile.Image,
│ │ │ │ + marker: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: grid
│ │ │ │ - * {Array(Array())} This is an array of rows, each row is
│ │ │ │ - * an array of tiles.
│ │ │ │ + * APIProperty: popupClass
│ │ │ │ + * {} The class which will be used to instantiate
│ │ │ │ + * a new Popup. Default is .
│ │ │ │ */
│ │ │ │ - grid: null,
│ │ │ │ + popupClass: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: singleTile
│ │ │ │ - * {Boolean} Moves the layer into single-tile mode, meaning that one tile
│ │ │ │ - * will be loaded. The tile's size will be determined by the 'ratio'
│ │ │ │ - * property. When the tile is dragged such that it does not cover the
│ │ │ │ - * entire viewport, it is reloaded.
│ │ │ │ + /**
│ │ │ │ + * Property: popup
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - singleTile: false,
│ │ │ │ + popup: null,
│ │ │ │
│ │ │ │ - /** APIProperty: ratio
│ │ │ │ - * {Float} Used only when in single-tile mode, this specifies the
│ │ │ │ - * ratio of the size of the single tile to the size of the map.
│ │ │ │ - * Default value is 1.5.
│ │ │ │ + /**
│ │ │ │ + * Constructor: OpenLayers.Feature
│ │ │ │ + * Constructor for features.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * layer - {}
│ │ │ │ + * lonlat - {}
│ │ │ │ + * data - {Object}
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - ratio: 1.5,
│ │ │ │ + initialize: function(layer, lonlat, data) {
│ │ │ │ + this.layer = layer;
│ │ │ │ + this.lonlat = lonlat;
│ │ │ │ + this.data = (data != null) ? data : {};
│ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: buffer
│ │ │ │ - * {Integer} Used only when in gridded mode, this specifies the number of
│ │ │ │ - * extra rows and colums of tiles on each side which will
│ │ │ │ - * surround the minimum grid tiles to cover the map.
│ │ │ │ - * For very slow loading layers, a larger value may increase
│ │ │ │ - * performance somewhat when dragging, but will increase bandwidth
│ │ │ │ - * use significantly.
│ │ │ │ + /**
│ │ │ │ + * Method: destroy
│ │ │ │ + * nullify references to prevent circular references and memory leaks
│ │ │ │ */
│ │ │ │ - buffer: 0,
│ │ │ │ + destroy: function() {
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: transitionEffect
│ │ │ │ - * {String} The transition effect to use when the map is zoomed.
│ │ │ │ - * Two posible values:
│ │ │ │ - *
│ │ │ │ - * "resize" - Existing tiles are resized on zoom to provide a visual
│ │ │ │ - * effect of the zoom having taken place immediately. As the
│ │ │ │ - * new tiles become available, they are drawn on top of the
│ │ │ │ - * resized tiles (this is the default setting).
│ │ │ │ - * "map-resize" - Existing tiles are resized on zoom and placed below the
│ │ │ │ - * base layer. New tiles for the base layer will cover existing tiles.
│ │ │ │ - * This setting is recommended when having an overlay duplicated during
│ │ │ │ - * the transition is undesirable (e.g. street labels or big transparent
│ │ │ │ - * fills).
│ │ │ │ - * null - No transition effect.
│ │ │ │ - *
│ │ │ │ - * Using "resize" on non-opaque layers can cause undesired visual
│ │ │ │ - * effects. Set transitionEffect to null in this case.
│ │ │ │ - */
│ │ │ │ - transitionEffect: "resize",
│ │ │ │ + //remove the popup from the map
│ │ │ │ + if ((this.layer != null) && (this.layer.map != null)) {
│ │ │ │ + if (this.popup != null) {
│ │ │ │ + this.layer.map.removePopup(this.popup);
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ + // remove the marker from the layer
│ │ │ │ + if (this.layer != null && this.marker != null) {
│ │ │ │ + this.layer.removeMarker(this.marker);
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIProperty: numLoadingTiles
│ │ │ │ - * {Integer} How many tiles are still loading?
│ │ │ │ - */
│ │ │ │ - numLoadingTiles: 0,
│ │ │ │ + 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;
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: serverResolutions
│ │ │ │ - * {Array(Number}} This property is documented in subclasses as
│ │ │ │ - * an API property.
│ │ │ │ + * Method: onScreen
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Boolean} Whether or not the feature is currently visible on screen
│ │ │ │ + * (based on its 'lonlat' property)
│ │ │ │ */
│ │ │ │ - serverResolutions: null,
│ │ │ │ + onScreen: function() {
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: loading
│ │ │ │ - * {Boolean} Indicates if tiles are being loaded.
│ │ │ │ - */
│ │ │ │ - loading: false,
│ │ │ │ + var onScreen = false;
│ │ │ │ + if ((this.layer != null) && (this.layer.map != null)) {
│ │ │ │ + var screenBounds = this.layer.map.getExtent();
│ │ │ │ + onScreen = screenBounds.containsLonLat(this.lonlat);
│ │ │ │ + }
│ │ │ │ + return onScreen;
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: backBuffer
│ │ │ │ - * {DOMElement} The back buffer.
│ │ │ │ - */
│ │ │ │ - backBuffer: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: gridResolution
│ │ │ │ - * {Number} The resolution of the current grid. Used for backbuffer and
│ │ │ │ - * client zoom. This property is updated every time the grid is
│ │ │ │ - * initialized.
│ │ │ │ + * Method: createMarker
│ │ │ │ + * Based on the data associated with the Feature, create and return a marker object.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {} 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
│ │ │ │ + *
│ │ │ │ */
│ │ │ │ - gridResolution: null,
│ │ │ │ + createMarker: function() {
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: backBufferResolution
│ │ │ │ - * {Number} The resolution of the current back buffer. This property is
│ │ │ │ - * updated each time a back buffer is created.
│ │ │ │ - */
│ │ │ │ - backBufferResolution: null,
│ │ │ │ + if (this.lonlat != null) {
│ │ │ │ + this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
│ │ │ │ + }
│ │ │ │ + return this.marker;
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: backBufferLonLat
│ │ │ │ - * {Object} The top-left corner of the current back buffer. Includes lon
│ │ │ │ - * and lat properties. This object is updated each time a back buffer
│ │ │ │ - * is created.
│ │ │ │ + * Method: destroyMarker
│ │ │ │ + * Destroys marker.
│ │ │ │ + * If user overrides the createMarker() function, s/he should be able
│ │ │ │ + * to also specify an alternative function for destroying it
│ │ │ │ */
│ │ │ │ - backBufferLonLat: null,
│ │ │ │ + destroyMarker: function() {
│ │ │ │ + this.marker.destroy();
│ │ │ │ + },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: backBufferTimerId
│ │ │ │ - * {Number} The id of the back buffer timer. This timer is used to
│ │ │ │ - * delay the removal of the back buffer, thereby preventing
│ │ │ │ - * flash effects caused by tile animation.
│ │ │ │ - */
│ │ │ │ - backBufferTimerId: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: removeBackBufferDelay
│ │ │ │ - * {Number} Delay for removing the backbuffer when all tiles have finished
│ │ │ │ - * loading. Can be set to 0 when no css opacity transitions for the
│ │ │ │ - * olTileImage class are used. Default is 0 for layers,
│ │ │ │ - * 2500 for tiled layers. See for more information on
│ │ │ │ - * tile animation.
│ │ │ │ - */
│ │ │ │ - removeBackBufferDelay: null,
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIProperty: className
│ │ │ │ - * {String} Name of the class added to the layer div. If not set in the
│ │ │ │ - * options passed to the constructor then className defaults to
│ │ │ │ - * "olLayerGridSingleTile" for single tile layers (see ),
│ │ │ │ - * and "olLayerGrid" for non single tile layers.
│ │ │ │ - *
│ │ │ │ - * Note:
│ │ │ │ + * 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.
│ │ │ │ *
│ │ │ │ - * The displaying of tiles is not animated by default for single tile
│ │ │ │ - * layers - OpenLayers' default theme (style.css) includes this:
│ │ │ │ - * (code)
│ │ │ │ - * .olLayerGrid .olTileImage {
│ │ │ │ - * -webkit-transition: opacity 0.2s linear;
│ │ │ │ - * -moz-transition: opacity 0.2s linear;
│ │ │ │ - * -o-transition: opacity 0.2s linear;
│ │ │ │ - * transition: opacity 0.2s linear;
│ │ │ │ - * }
│ │ │ │ - * (end)
│ │ │ │ - * To animate tile displaying for any grid layer the following
│ │ │ │ - * CSS rule can be used:
│ │ │ │ - * (code)
│ │ │ │ - * .olTileImage {
│ │ │ │ - * -webkit-transition: opacity 0.2s linear;
│ │ │ │ - * -moz-transition: opacity 0.2s linear;
│ │ │ │ - * -o-transition: opacity 0.2s linear;
│ │ │ │ - * transition: opacity 0.2s linear;
│ │ │ │ - * }
│ │ │ │ - * (end)
│ │ │ │ - * In that case, to avoid flash effects,
│ │ │ │ - * should not be zero.
│ │ │ │ + * 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:
│ │ │ │ + * {} 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 .
│ │ │ │ + *
│ │ │ │ */
│ │ │ │ - className: null,
│ │ │ │ + createPopup: function(closeBox) {
│ │ │ │ +
│ │ │ │ + if (this.lonlat != null) {
│ │ │ │ + if (!this.popup) {
│ │ │ │ + var anchor = (this.marker) ? this.marker.icon : null;
│ │ │ │ + var popupClass = this.popupClass ?
│ │ │ │ + this.popupClass : OpenLayers.Popup.Anchored;
│ │ │ │ + this.popup = new popupClass(this.id + "_popup",
│ │ │ │ + this.lonlat,
│ │ │ │ + this.data.popupSize,
│ │ │ │ + this.data.popupContentHTML,
│ │ │ │ + anchor,
│ │ │ │ + closeBox);
│ │ │ │ + }
│ │ │ │ + if (this.data.overflow != null) {
│ │ │ │ + this.popup.contentDiv.style.overflow = this.data.overflow;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + this.popup.feature = this;
│ │ │ │ + }
│ │ │ │ + return this.popup;
│ │ │ │ + },
│ │ │ │ +
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Register a listener for a particular event with the following syntax:
│ │ │ │ - * (code)
│ │ │ │ - * layer.events.register(type, obj, listener);
│ │ │ │ - * (end)
│ │ │ │ - *
│ │ │ │ - * Listeners will be called with a reference to an event object. The
│ │ │ │ - * properties of this event depends on exactly what happened.
│ │ │ │ - *
│ │ │ │ - * All event objects have at least the following properties:
│ │ │ │ - * object - {Object} A reference to layer.events.object.
│ │ │ │ - * element - {DOMElement} A reference to layer.events.element.
│ │ │ │ + * Method: destroyPopup
│ │ │ │ + * Destroys the popup created via createPopup.
│ │ │ │ *
│ │ │ │ - * Supported event types:
│ │ │ │ - * addtile - Triggered when a tile is added to this layer. Listeners receive
│ │ │ │ - * an object as first argument, which has a tile property that
│ │ │ │ - * references the tile that has been added.
│ │ │ │ - * tileloadstart - Triggered when a tile starts loading. Listeners receive
│ │ │ │ - * an object as first argument, which has a tile property that
│ │ │ │ - * references the tile that starts loading.
│ │ │ │ - * tileloaded - Triggered when each new tile is
│ │ │ │ - * loaded, as a means of progress update to listeners.
│ │ │ │ - * listeners can access 'numLoadingTiles' if they wish to keep
│ │ │ │ - * track of the loading progress. Listeners are called with an object
│ │ │ │ - * with a 'tile' property as first argument, making the loaded tile
│ │ │ │ - * available to the listener, and an 'aborted' property, which will be
│ │ │ │ - * true when loading was aborted and no tile data is available.
│ │ │ │ - * tileerror - Triggered before the tileloaded event (i.e. when the tile is
│ │ │ │ - * still hidden) if a tile failed to load. Listeners receive an object
│ │ │ │ - * as first argument, which has a tile property that references the
│ │ │ │ - * tile that could not be loaded.
│ │ │ │ - * retile - Triggered when the layer recreates its tile grid.
│ │ │ │ + * 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;
│ │ │ │ + }
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: gridLayout
│ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol,
│ │ │ │ - * startrow
│ │ │ │ - */
│ │ │ │ - gridLayout: null,
│ │ │ │ + CLASS_NAME: "OpenLayers.Feature"
│ │ │ │ +});
│ │ │ │ +/* ======================================================================
│ │ │ │ + OpenLayers/Request/XMLHttpRequest.js
│ │ │ │ + ====================================================================== */
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: rowSign
│ │ │ │ - * {Number} 1 for grids starting at the top, -1 for grids starting at the
│ │ │ │ - * bottom. This is used for several grid index and offset calculations.
│ │ │ │ - */
│ │ │ │ - rowSign: null,
│ │ │ │ +// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
│ │ │ │ +//
│ │ │ │ +// Licensed under the Apache License, Version 2.0 (the "License");
│ │ │ │ +// you may not use this file except in compliance with the License.
│ │ │ │ +// You may obtain a copy of the License at
│ │ │ │ +//
│ │ │ │ +// http://www.apache.org/licenses/LICENSE-2.0
│ │ │ │ +//
│ │ │ │ +// Unless required by applicable law or agreed to in writing, software
│ │ │ │ +// distributed under the License is distributed on an "AS IS" BASIS,
│ │ │ │ +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
│ │ │ │ +// See the License for the specific language governing permissions and
│ │ │ │ +// limitations under the License.
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: transitionendEvents
│ │ │ │ - * {Array} Event names for transitionend
│ │ │ │ - */
│ │ │ │ - transitionendEvents: [
│ │ │ │ - 'transitionend', 'webkitTransitionEnd', 'otransitionend',
│ │ │ │ - 'oTransitionEnd'
│ │ │ │ - ],
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/Request.js
│ │ │ │ + */
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Constructor: OpenLayers.Layer.Grid
│ │ │ │ - * Create a new grid layer
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * name - {String}
│ │ │ │ - * url - {String}
│ │ │ │ - * params - {Object}
│ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer
│ │ │ │ - */
│ │ │ │ - initialize: function(name, url, params, options) {
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,
│ │ │ │ - arguments);
│ │ │ │ - this.grid = [];
│ │ │ │ - this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
│ │ │ │ +(function() {
│ │ │ │
│ │ │ │ - this.initProperties();
│ │ │ │ + // Save reference to earlier defined object implementation (if any)
│ │ │ │ + var oXMLHttpRequest = window.XMLHttpRequest;
│ │ │ │
│ │ │ │ - this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1;
│ │ │ │ - },
│ │ │ │ + // Define on browser type
│ │ │ │ + var bGecko = !!window.controllers,
│ │ │ │ + bIE = window.document.all && !window.opera,
│ │ │ │ + bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: initProperties
│ │ │ │ - * Set any properties that depend on the value of singleTile.
│ │ │ │ - * Currently sets removeBackBufferDelay and className
│ │ │ │ - */
│ │ │ │ - initProperties: function() {
│ │ │ │ - if (this.options.removeBackBufferDelay === undefined) {
│ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500;
│ │ │ │ - }
│ │ │ │ + // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
│ │ │ │ + function fXMLHttpRequest() {
│ │ │ │ + this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
│ │ │ │ + this._listeners = [];
│ │ │ │ + };
│ │ │ │
│ │ │ │ - if (this.options.className === undefined) {
│ │ │ │ - this.className = this.singleTile ? 'olLayerGridSingleTile' :
│ │ │ │ - 'olLayerGrid';
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + // Constructor
│ │ │ │ + function cXMLHttpRequest() {
│ │ │ │ + return new fXMLHttpRequest;
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: setMap
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * map - {} The map.
│ │ │ │ - */
│ │ │ │ - setMap: function(map) {
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);
│ │ │ │ - OpenLayers.Element.addClass(this.div, this.className);
│ │ │ │ - },
│ │ │ │ + // BUGFIX: Firefox with Firebug installed would break pages if not executed
│ │ │ │ + if (bGecko && oXMLHttpRequest.wrapped)
│ │ │ │ + cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: removeMap
│ │ │ │ - * Called when the layer is removed from the map.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * map - {} The map.
│ │ │ │ - */
│ │ │ │ - removeMap: function(map) {
│ │ │ │ - this.removeBackBuffer();
│ │ │ │ - },
│ │ │ │ + // Constants
│ │ │ │ + cXMLHttpRequest.UNSENT = 0;
│ │ │ │ + cXMLHttpRequest.OPENED = 1;
│ │ │ │ + cXMLHttpRequest.HEADERS_RECEIVED = 2;
│ │ │ │ + cXMLHttpRequest.LOADING = 3;
│ │ │ │ + cXMLHttpRequest.DONE = 4;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: destroy
│ │ │ │ - * Deconstruct the layer and clear the grid.
│ │ │ │ - */
│ │ │ │ - destroy: function() {
│ │ │ │ - this.removeBackBuffer();
│ │ │ │ - this.clearGrid();
│ │ │ │ + // Public Properties
│ │ │ │ + cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
│ │ │ │ + cXMLHttpRequest.prototype.responseText = '';
│ │ │ │ + cXMLHttpRequest.prototype.responseXML = null;
│ │ │ │ + cXMLHttpRequest.prototype.status = 0;
│ │ │ │ + cXMLHttpRequest.prototype.statusText = '';
│ │ │ │
│ │ │ │ - this.grid = null;
│ │ │ │ - this.tileSize = null;
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
│ │ │ │ - },
│ │ │ │ + // Priority proposal
│ │ │ │ + cXMLHttpRequest.prototype.priority = "NORMAL";
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: mergeNewParams
│ │ │ │ - * Refetches tiles with new params merged, keeping a backbuffer. Each
│ │ │ │ - * loading new tile will have a css class of '.olTileReplacing'. If a
│ │ │ │ - * stylesheet applies a 'display: none' style to that class, any fade-in
│ │ │ │ - * transition will not apply, and backbuffers for each tile will be removed
│ │ │ │ - * as soon as the tile is loaded.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * newParams - {Object}
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn.
│ │ │ │ - */
│ │ │ │ + // Instance-level Events Handlers
│ │ │ │ + cXMLHttpRequest.prototype.onreadystatechange = null;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: clearGrid
│ │ │ │ - * Go through and remove all tiles from the grid, calling
│ │ │ │ - * destroy() on each of them to kill circular references
│ │ │ │ - */
│ │ │ │ - clearGrid: function() {
│ │ │ │ - if (this.grid) {
│ │ │ │ - for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {
│ │ │ │ - var row = this.grid[iRow];
│ │ │ │ - for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {
│ │ │ │ - var tile = row[iCol];
│ │ │ │ - this.destroyTile(tile);
│ │ │ │ + // Class-level Events Handlers
│ │ │ │ + cXMLHttpRequest.onreadystatechange = null;
│ │ │ │ + cXMLHttpRequest.onopen = null;
│ │ │ │ + cXMLHttpRequest.onsend = null;
│ │ │ │ + cXMLHttpRequest.onabort = null;
│ │ │ │ +
│ │ │ │ + // Public Methods
│ │ │ │ + cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
│ │ │ │ + // Delete headers, required when object is reused
│ │ │ │ + delete this._headers;
│ │ │ │ +
│ │ │ │ + // When bAsync parameter value is omitted, use true as default
│ │ │ │ + if (arguments.length < 3)
│ │ │ │ + bAsync = true;
│ │ │ │ +
│ │ │ │ + // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
│ │ │ │ + this._async = bAsync;
│ │ │ │ +
│ │ │ │ + // Set the onreadystatechange handler
│ │ │ │ + var oRequest = this,
│ │ │ │ + nState = this.readyState,
│ │ │ │ + fOnUnload;
│ │ │ │ +
│ │ │ │ + // BUGFIX: IE - memory leak on page unload (inter-page leak)
│ │ │ │ + if (bIE && bAsync) {
│ │ │ │ + fOnUnload = function() {
│ │ │ │ + if (nState != cXMLHttpRequest.DONE) {
│ │ │ │ + fCleanTransport(oRequest);
│ │ │ │ + // Safe to abort here since onreadystatechange handler removed
│ │ │ │ + oRequest.abort();
│ │ │ │ }
│ │ │ │ - }
│ │ │ │ - this.grid = [];
│ │ │ │ - this.gridResolution = null;
│ │ │ │ - this.gridLayout = null;
│ │ │ │ + };
│ │ │ │ + window.attachEvent("onunload", fOnUnload);
│ │ │ │ }
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: addOptions
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * newOptions - {Object}
│ │ │ │ - * reinitialize - {Boolean} If set to true, and if resolution options of the
│ │ │ │ - * current baseLayer were changed, the map will be recentered to make
│ │ │ │ - * sure that it is displayed with a valid resolution, and a
│ │ │ │ - * changebaselayer event will be triggered.
│ │ │ │ - */
│ │ │ │ - addOptions: function(newOptions, reinitialize) {
│ │ │ │ - var singleTileChanged = newOptions.singleTile !== undefined &&
│ │ │ │ - newOptions.singleTile !== this.singleTile;
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);
│ │ │ │ - if (this.map && singleTileChanged) {
│ │ │ │ - this.initProperties();
│ │ │ │ - this.clearGrid();
│ │ │ │ - this.tileSize = this.options.tileSize;
│ │ │ │ - this.setTileSize();
│ │ │ │ - this.moveTo(null, true);
│ │ │ │ + // Add method sniffer
│ │ │ │ + if (cXMLHttpRequest.onopen)
│ │ │ │ + cXMLHttpRequest.onopen.apply(this, arguments);
│ │ │ │ +
│ │ │ │ + if (arguments.length > 4)
│ │ │ │ + this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
│ │ │ │ + else
│ │ │ │ + if (arguments.length > 3)
│ │ │ │ + this._object.open(sMethod, sUrl, bAsync, sUser);
│ │ │ │ + else
│ │ │ │ + this._object.open(sMethod, sUrl, bAsync);
│ │ │ │ +
│ │ │ │ + this.readyState = cXMLHttpRequest.OPENED;
│ │ │ │ + fReadyStateChange(this);
│ │ │ │ +
│ │ │ │ + this._object.onreadystatechange = function() {
│ │ │ │ + if (bGecko && !bAsync)
│ │ │ │ + return;
│ │ │ │ +
│ │ │ │ + // Synchronize state
│ │ │ │ + oRequest.readyState = oRequest._object.readyState;
│ │ │ │ +
│ │ │ │ + //
│ │ │ │ + fSynchronizeValues(oRequest);
│ │ │ │ +
│ │ │ │ + // BUGFIX: Firefox fires unnecessary DONE when aborting
│ │ │ │ + if (oRequest._aborted) {
│ │ │ │ + // Reset readyState to UNSENT
│ │ │ │ + oRequest.readyState = cXMLHttpRequest.UNSENT;
│ │ │ │ +
│ │ │ │ + // Return now
│ │ │ │ + return;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + if (oRequest.readyState == cXMLHttpRequest.DONE) {
│ │ │ │ + // Free up queue
│ │ │ │ + delete oRequest._data;
│ │ │ │ + /* if (bAsync)
│ │ │ │ + fQueue_remove(oRequest);*/
│ │ │ │ + //
│ │ │ │ + fCleanTransport(oRequest);
│ │ │ │ + // Uncomment this block if you need a fix for IE cache
│ │ │ │ + /*
│ │ │ │ + // BUGFIX: IE - cache issue
│ │ │ │ + if (!oRequest._object.getResponseHeader("Date")) {
│ │ │ │ + // Save object to cache
│ │ │ │ + oRequest._cached = oRequest._object;
│ │ │ │ +
│ │ │ │ + // Instantiate a new transport object
│ │ │ │ + cXMLHttpRequest.call(oRequest);
│ │ │ │ +
│ │ │ │ + // Re-send request
│ │ │ │ + if (sUser) {
│ │ │ │ + if (sPassword)
│ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
│ │ │ │ + else
│ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync, sUser);
│ │ │ │ + }
│ │ │ │ + else
│ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync);
│ │ │ │ + oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
│ │ │ │ + // Copy headers set
│ │ │ │ + if (oRequest._headers)
│ │ │ │ + for (var sHeader in oRequest._headers)
│ │ │ │ + if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
│ │ │ │ + oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
│ │ │ │ +
│ │ │ │ + oRequest._object.onreadystatechange = function() {
│ │ │ │ + // Synchronize state
│ │ │ │ + oRequest.readyState = oRequest._object.readyState;
│ │ │ │ +
│ │ │ │ + if (oRequest._aborted) {
│ │ │ │ + //
│ │ │ │ + oRequest.readyState = cXMLHttpRequest.UNSENT;
│ │ │ │ +
│ │ │ │ + // Return
│ │ │ │ + return;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + if (oRequest.readyState == cXMLHttpRequest.DONE) {
│ │ │ │ + // Clean Object
│ │ │ │ + fCleanTransport(oRequest);
│ │ │ │ +
│ │ │ │ + // get cached request
│ │ │ │ + if (oRequest.status == 304)
│ │ │ │ + oRequest._object = oRequest._cached;
│ │ │ │ +
│ │ │ │ + //
│ │ │ │ + delete oRequest._cached;
│ │ │ │ +
│ │ │ │ + //
│ │ │ │ + fSynchronizeValues(oRequest);
│ │ │ │ +
│ │ │ │ + //
│ │ │ │ + fReadyStateChange(oRequest);
│ │ │ │ +
│ │ │ │ + // BUGFIX: IE - memory leak in interrupted
│ │ │ │ + if (bIE && bAsync)
│ │ │ │ + window.detachEvent("onunload", fOnUnload);
│ │ │ │ + }
│ │ │ │ + };
│ │ │ │ + oRequest._object.send(null);
│ │ │ │ +
│ │ │ │ + // Return now - wait until re-sent request is finished
│ │ │ │ + return;
│ │ │ │ + };
│ │ │ │ + */
│ │ │ │ + // BUGFIX: IE - memory leak in interrupted
│ │ │ │ + if (bIE && bAsync)
│ │ │ │ + window.detachEvent("onunload", fOnUnload);
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
│ │ │ │ + if (nState != oRequest.readyState)
│ │ │ │ + fReadyStateChange(oRequest);
│ │ │ │ +
│ │ │ │ + nState = oRequest.readyState;
│ │ │ │ }
│ │ │ │ - },
│ │ │ │ + };
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * APIMethod: clone
│ │ │ │ - * Create a clone of this layer
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * obj - {Object} Is this ever used?
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {} An exact clone of this OpenLayers.Layer.Grid
│ │ │ │ - */
│ │ │ │ - clone: function(obj) {
│ │ │ │ + function fXMLHttpRequest_send(oRequest) {
│ │ │ │ + oRequest._object.send(oRequest._data);
│ │ │ │
│ │ │ │ - if (obj == null) {
│ │ │ │ - obj = new OpenLayers.Layer.Grid(this.name,
│ │ │ │ - this.url,
│ │ │ │ - this.params,
│ │ │ │ - this.getOptions());
│ │ │ │ + // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
│ │ │ │ + if (bGecko && !oRequest._async) {
│ │ │ │ + oRequest.readyState = cXMLHttpRequest.OPENED;
│ │ │ │ +
│ │ │ │ + // Synchronize state
│ │ │ │ + fSynchronizeValues(oRequest);
│ │ │ │ +
│ │ │ │ + // Simulate missing states
│ │ │ │ + while (oRequest.readyState < cXMLHttpRequest.DONE) {
│ │ │ │ + oRequest.readyState++;
│ │ │ │ + fReadyStateChange(oRequest);
│ │ │ │ + // Check if we are aborted
│ │ │ │ + if (oRequest._aborted)
│ │ │ │ + return;
│ │ │ │ + }
│ │ │ │ }
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype.send = function(vData) {
│ │ │ │ + // Add method sniffer
│ │ │ │ + if (cXMLHttpRequest.onsend)
│ │ │ │ + cXMLHttpRequest.onsend.apply(this, arguments);
│ │ │ │
│ │ │ │ - //get all additions from superclasses
│ │ │ │ - obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
│ │ │ │ + if (!arguments.length)
│ │ │ │ + vData = null;
│ │ │ │
│ │ │ │ - // copy/set any non-init, non-simple values here
│ │ │ │ - if (this.tileSize != null) {
│ │ │ │ - obj.tileSize = this.tileSize.clone();
│ │ │ │ + // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
│ │ │ │ + // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
│ │ │ │ + // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
│ │ │ │ + if (vData && vData.nodeType) {
│ │ │ │ + vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
│ │ │ │ + if (!this._headers["Content-Type"])
│ │ │ │ + this._object.setRequestHeader("Content-Type", "application/xml");
│ │ │ │ }
│ │ │ │
│ │ │ │ - // we do not want to copy reference to grid, so we make a new array
│ │ │ │ - obj.grid = [];
│ │ │ │ - obj.gridResolution = null;
│ │ │ │ - // same for backbuffer
│ │ │ │ - obj.backBuffer = null;
│ │ │ │ - obj.backBufferTimerId = null;
│ │ │ │ - obj.loading = false;
│ │ │ │ - obj.numLoadingTiles = 0;
│ │ │ │ + this._data = vData;
│ │ │ │ + /*
│ │ │ │ + // Add to queue
│ │ │ │ + if (this._async)
│ │ │ │ + fQueue_add(this);
│ │ │ │ + else*/
│ │ │ │ + fXMLHttpRequest_send(this);
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype.abort = function() {
│ │ │ │ + // Add method sniffer
│ │ │ │ + if (cXMLHttpRequest.onabort)
│ │ │ │ + cXMLHttpRequest.onabort.apply(this, arguments);
│ │ │ │
│ │ │ │ - return obj;
│ │ │ │ - },
│ │ │ │ + // BUGFIX: Gecko - unnecessary DONE when aborting
│ │ │ │ + if (this.readyState > cXMLHttpRequest.UNSENT)
│ │ │ │ + this._aborted = true;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: moveTo
│ │ │ │ - * This function is called whenever the map is moved. All the moving
│ │ │ │ - * of actual 'tiles' is done by the map, but moveTo's role is to accept
│ │ │ │ - * a bounds and make sure the data that that bounds requires is pre-loaded.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * bounds - {}
│ │ │ │ - * zoomChanged - {Boolean}
│ │ │ │ - * dragging - {Boolean}
│ │ │ │ - */
│ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) {
│ │ │ │ + this._object.abort();
│ │ │ │
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
│ │ │ │ + // BUGFIX: IE - memory leak
│ │ │ │ + fCleanTransport(this);
│ │ │ │
│ │ │ │ - bounds = bounds || this.map.getExtent();
│ │ │ │ + this.readyState = cXMLHttpRequest.UNSENT;
│ │ │ │
│ │ │ │ - if (bounds != null) {
│ │ │ │ + delete this._data;
│ │ │ │ + /* if (this._async)
│ │ │ │ + fQueue_remove(this);*/
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
│ │ │ │ + return this._object.getAllResponseHeaders();
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
│ │ │ │ + return this._object.getResponseHeader(sName);
│ │ │ │ + };
│ │ │ │ + cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
│ │ │ │ + // BUGFIX: IE - cache issue
│ │ │ │ + if (!this._headers)
│ │ │ │ + this._headers = {};
│ │ │ │ + this._headers[sName] = sValue;
│ │ │ │
│ │ │ │ - // if grid is empty or zoom has changed, we *must* re-tile
│ │ │ │ - var forceReTile = !this.grid.length || zoomChanged;
│ │ │ │ + return this._object.setRequestHeader(sName, sValue);
│ │ │ │ + };
│ │ │ │
│ │ │ │ - // total bounds of the tiles
│ │ │ │ - var tilesBounds = this.getTilesBounds();
│ │ │ │ + // EventTarget interface implementation
│ │ │ │ + cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
│ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
│ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
│ │ │ │ + return;
│ │ │ │ + // Add listener
│ │ │ │ + this._listeners.push([sName, fHandler, bUseCapture]);
│ │ │ │ + };
│ │ │ │
│ │ │ │ - // the new map resolution
│ │ │ │ - var resolution = this.map.getResolution();
│ │ │ │ + cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
│ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
│ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
│ │ │ │ + break;
│ │ │ │ + // Remove listener
│ │ │ │ + if (oListener)
│ │ │ │ + this._listeners.splice(nIndex, 1);
│ │ │ │ + };
│ │ │ │
│ │ │ │ - // the server-supported resolution for the new map resolution
│ │ │ │ - var serverResolution = this.getServerResolution(resolution);
│ │ │ │ + cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
│ │ │ │ + var oEventPseudo = {
│ │ │ │ + 'type': oEvent.type,
│ │ │ │ + 'target': this,
│ │ │ │ + 'currentTarget': this,
│ │ │ │ + 'eventPhase': 2,
│ │ │ │ + 'bubbles': oEvent.bubbles,
│ │ │ │ + 'cancelable': oEvent.cancelable,
│ │ │ │ + 'timeStamp': oEvent.timeStamp,
│ │ │ │ + 'stopPropagation': function() {}, // There is no flow
│ │ │ │ + 'preventDefault': function() {}, // There is no default action
│ │ │ │ + 'initEvent': function() {} // Original event object should be initialized
│ │ │ │ + };
│ │ │ │
│ │ │ │ - if (this.singleTile) {
│ │ │ │ + // Execute onreadystatechange
│ │ │ │ + if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
│ │ │ │ + (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
│ │ │ │
│ │ │ │ - // We want to redraw whenever even the slightest part of the
│ │ │ │ - // current bounds is not contained by our tile.
│ │ │ │ - // (thus, we do not specify partial -- its default is false)
│ │ │ │ + // Execute listeners
│ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
│ │ │ │ + if (oListener[0] == oEventPseudo.type && !oListener[2])
│ │ │ │ + (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
│ │ │ │ + };
│ │ │ │
│ │ │ │ - if (forceReTile ||
│ │ │ │ - (!dragging && !tilesBounds.containsBounds(bounds))) {
│ │ │ │ + //
│ │ │ │ + cXMLHttpRequest.prototype.toString = function() {
│ │ │ │ + return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
│ │ │ │ + };
│ │ │ │
│ │ │ │ - // In single tile mode with no transition effect, we insert
│ │ │ │ - // a non-scaled backbuffer when the layer is moved. But if
│ │ │ │ - // a zoom occurs right after a move, i.e. before the new
│ │ │ │ - // image is received, we need to remove the backbuffer, or
│ │ │ │ - // an ill-positioned image will be visible during the zoom
│ │ │ │ - // transition.
│ │ │ │ + cXMLHttpRequest.toString = function() {
│ │ │ │ + return '[' + "XMLHttpRequest" + ']';
│ │ │ │ + };
│ │ │ │
│ │ │ │ - if (zoomChanged && this.transitionEffect !== 'resize') {
│ │ │ │ - this.removeBackBuffer();
│ │ │ │ - }
│ │ │ │ + // Helper function
│ │ │ │ + function fReadyStateChange(oRequest) {
│ │ │ │ + // Sniffing code
│ │ │ │ + if (cXMLHttpRequest.onreadystatechange)
│ │ │ │ + cXMLHttpRequest.onreadystatechange.apply(oRequest);
│ │ │ │
│ │ │ │ - if (!zoomChanged || this.transitionEffect === 'resize') {
│ │ │ │ - this.applyBackBuffer(resolution);
│ │ │ │ - }
│ │ │ │ + // Fake event
│ │ │ │ + oRequest.dispatchEvent({
│ │ │ │ + 'type': "readystatechange",
│ │ │ │ + 'bubbles': false,
│ │ │ │ + 'cancelable': false,
│ │ │ │ + 'timeStamp': new Date + 0
│ │ │ │ + });
│ │ │ │ + };
│ │ │ │
│ │ │ │ - this.initSingleTile(bounds);
│ │ │ │ - }
│ │ │ │ - } else {
│ │ │ │ + function fGetDocument(oRequest) {
│ │ │ │ + var oDocument = oRequest.responseXML,
│ │ │ │ + sResponse = oRequest.responseText;
│ │ │ │ + // Try parsing responseText
│ │ │ │ + if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
│ │ │ │ + oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
│ │ │ │ + oDocument.async = false;
│ │ │ │ + oDocument.validateOnParse = false;
│ │ │ │ + oDocument.loadXML(sResponse);
│ │ │ │ + }
│ │ │ │ + // Check if there is no error in document
│ │ │ │ + if (oDocument)
│ │ │ │ + if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
│ │ │ │ + return null;
│ │ │ │ + return oDocument;
│ │ │ │ + };
│ │ │ │
│ │ │ │ - // if the bounds have changed such that they are not even
│ │ │ │ - // *partially* contained by our tiles (e.g. when user has
│ │ │ │ - // programmatically panned to the other side of the earth on
│ │ │ │ - // zoom level 18), then moveGriddedTiles could potentially have
│ │ │ │ - // to run through thousands of cycles, so we want to reTile
│ │ │ │ - // instead (thus, partial true).
│ │ │ │ - forceReTile = forceReTile ||
│ │ │ │ - !tilesBounds.intersectsBounds(bounds, {
│ │ │ │ - worldBounds: this.map.baseLayer.wrapDateLine &&
│ │ │ │ - this.map.getMaxExtent()
│ │ │ │ - });
│ │ │ │ + function fSynchronizeValues(oRequest) {
│ │ │ │ + try {
│ │ │ │ + oRequest.responseText = oRequest._object.responseText;
│ │ │ │ + } catch (e) {}
│ │ │ │ + try {
│ │ │ │ + oRequest.responseXML = fGetDocument(oRequest._object);
│ │ │ │ + } catch (e) {}
│ │ │ │ + try {
│ │ │ │ + oRequest.status = oRequest._object.status;
│ │ │ │ + } catch (e) {}
│ │ │ │ + try {
│ │ │ │ + oRequest.statusText = oRequest._object.statusText;
│ │ │ │ + } catch (e) {}
│ │ │ │ + };
│ │ │ │
│ │ │ │ - if (forceReTile) {
│ │ │ │ - if (zoomChanged && (this.transitionEffect === 'resize' ||
│ │ │ │ - this.gridResolution === resolution)) {
│ │ │ │ - this.applyBackBuffer(resolution);
│ │ │ │ + function fCleanTransport(oRequest) {
│ │ │ │ + // BUGFIX: IE - memory leak (on-page leak)
│ │ │ │ + oRequest._object.onreadystatechange = new window.Function;
│ │ │ │ + };
│ │ │ │ + /*
│ │ │ │ + // Queue manager
│ │ │ │ + var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
│ │ │ │ + aQueueRunning = [];
│ │ │ │ + function fQueue_add(oRequest) {
│ │ │ │ + oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
│ │ │ │ + //
│ │ │ │ + setTimeout(fQueue_process);
│ │ │ │ + };
│ │ │ │ +
│ │ │ │ + function fQueue_remove(oRequest) {
│ │ │ │ + for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
│ │ │ │ + if (bFound)
│ │ │ │ + aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
│ │ │ │ + else
│ │ │ │ + if (aQueueRunning[nIndex] == oRequest)
│ │ │ │ + bFound = true;
│ │ │ │ + if (bFound)
│ │ │ │ + aQueueRunning.length--;
│ │ │ │ + //
│ │ │ │ + setTimeout(fQueue_process);
│ │ │ │ + };
│ │ │ │ +
│ │ │ │ + function fQueue_process() {
│ │ │ │ + if (aQueueRunning.length < 6) {
│ │ │ │ + for (var sPriority in oQueuePending) {
│ │ │ │ + if (oQueuePending[sPriority].length) {
│ │ │ │ + var oRequest = oQueuePending[sPriority][0];
│ │ │ │ + oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
│ │ │ │ + //
│ │ │ │ + aQueueRunning.push(oRequest);
│ │ │ │ + // Send request
│ │ │ │ + fXMLHttpRequest_send(oRequest);
│ │ │ │ + break;
│ │ │ │ }
│ │ │ │ - this.initGriddedTiles(bounds);
│ │ │ │ - } else {
│ │ │ │ - this.moveGriddedTiles();
│ │ │ │ }
│ │ │ │ }
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + };
│ │ │ │ + */
│ │ │ │ + // Internet Explorer 5.0 (missing apply)
│ │ │ │ + if (!window.Function.prototype.apply) {
│ │ │ │ + window.Function.prototype.apply = function(oRequest, oArguments) {
│ │ │ │ + if (!oArguments)
│ │ │ │ + oArguments = [];
│ │ │ │ + oRequest.__func = this;
│ │ │ │ + oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
│ │ │ │ + delete oRequest.__func;
│ │ │ │ + };
│ │ │ │ + };
│ │ │ │
│ │ │ │ + // Register new object with window
│ │ │ │ /**
│ │ │ │ - * Method: getTileData
│ │ │ │ - * Given a map location, retrieve a tile and the pixel offset within that
│ │ │ │ - * tile corresponding to the location. If there is not an existing
│ │ │ │ - * tile in the grid that covers the given location, null will be
│ │ │ │ - * returned.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * loc - {} map location
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Object} Object with the following properties: tile ({}),
│ │ │ │ - * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel
│ │ │ │ - * offset from top left).
│ │ │ │ + * Class: OpenLayers.Request.XMLHttpRequest
│ │ │ │ + * Standard-compliant (W3C) cross-browser implementation of the
│ │ │ │ + * XMLHttpRequest object. From
│ │ │ │ + * http://code.google.com/p/xmlhttprequest/.
│ │ │ │ */
│ │ │ │ - getTileData: function(loc) {
│ │ │ │ - var data = null,
│ │ │ │ - x = loc.lon,
│ │ │ │ - y = loc.lat,
│ │ │ │ - numRows = this.grid.length;
│ │ │ │ + if (!OpenLayers.Request) {
│ │ │ │ + /**
│ │ │ │ + * This allows for OpenLayers/Request.js to be included
│ │ │ │ + * before or after this script.
│ │ │ │ + */
│ │ │ │ + OpenLayers.Request = {};
│ │ │ │ + }
│ │ │ │ + OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
│ │ │ │ +})();
│ │ │ │ +/* ======================================================================
│ │ │ │ + OpenLayers/Request.js
│ │ │ │ + ====================================================================== */
│ │ │ │
│ │ │ │ - if (this.map && numRows) {
│ │ │ │ - var res = this.map.getResolution(),
│ │ │ │ - tileWidth = this.tileSize.w,
│ │ │ │ - tileHeight = this.tileSize.h,
│ │ │ │ - bounds = this.grid[0][0].bounds,
│ │ │ │ - left = bounds.left,
│ │ │ │ - top = bounds.top;
│ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ + * full text of the license. */
│ │ │ │
│ │ │ │ - if (x < left) {
│ │ │ │ - // deal with multiple worlds
│ │ │ │ - if (this.map.baseLayer.wrapDateLine) {
│ │ │ │ - var worldWidth = this.map.getMaxExtent().getWidth();
│ │ │ │ - var worldsAway = Math.ceil((left - x) / worldWidth);
│ │ │ │ - x += worldWidth * worldsAway;
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - // tile distance to location (fractional number of tiles);
│ │ │ │ - var dtx = (x - left) / (res * tileWidth);
│ │ │ │ - var dty = (top - y) / (res * tileHeight);
│ │ │ │ - // index of tile in grid
│ │ │ │ - var col = Math.floor(dtx);
│ │ │ │ - var row = Math.floor(dty);
│ │ │ │ - if (row >= 0 && row < numRows) {
│ │ │ │ - var tile = this.grid[row][col];
│ │ │ │ - if (tile) {
│ │ │ │ - data = {
│ │ │ │ - tile: tile,
│ │ │ │ - // pixel index within tile
│ │ │ │ - i: Math.floor((dtx - col) * tileWidth),
│ │ │ │ - j: Math.floor((dty - row) * tileHeight)
│ │ │ │ - };
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - return data;
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/Events.js
│ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js
│ │ │ │ + */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * TODO: deprecate me
│ │ │ │ + * Use OpenLayers.Request.proxy instead.
│ │ │ │ + */
│ │ │ │ +OpenLayers.ProxyHost = "";
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Namespace: OpenLayers.Request
│ │ │ │ + * The OpenLayers.Request namespace contains convenience methods for working
│ │ │ │ + * with XMLHttpRequests. These methods work with a cross-browser
│ │ │ │ + * W3C compliant class.
│ │ │ │ + */
│ │ │ │ +if (!OpenLayers.Request) {
│ │ │ │ + /**
│ │ │ │ + * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
│ │ │ │ + * before or after this script.
│ │ │ │ + */
│ │ │ │ + OpenLayers.Request = {};
│ │ │ │ +}
│ │ │ │ +OpenLayers.Util.extend(OpenLayers.Request, {
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Constant: DEFAULT_CONFIG
│ │ │ │ + * {Object} Default configuration for all requests.
│ │ │ │ + */
│ │ │ │ + DEFAULT_CONFIG: {
│ │ │ │ + method: "GET",
│ │ │ │ + url: window.location.href,
│ │ │ │ + async: true,
│ │ │ │ + user: undefined,
│ │ │ │ + password: undefined,
│ │ │ │ + params: null,
│ │ │ │ + proxy: OpenLayers.ProxyHost,
│ │ │ │ + headers: {},
│ │ │ │ + data: null,
│ │ │ │ + callback: function() {},
│ │ │ │ + success: null,
│ │ │ │ + failure: null,
│ │ │ │ + scope: null
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: destroyTile
│ │ │ │ + * Constant: URL_SPLIT_REGEX
│ │ │ │ + */
│ │ │ │ + URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: events
│ │ │ │ + * {} An events object that handles all
│ │ │ │ + * events on the {} object.
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * tile - {}
│ │ │ │ + * All event listeners will receive an event object with three properties:
│ │ │ │ + * request - {} The request object.
│ │ │ │ + * config - {Object} The config object sent to the specific request method.
│ │ │ │ + * requestUrl - {String} The request url.
│ │ │ │ + *
│ │ │ │ + * Supported event types:
│ │ │ │ + * complete - Triggered when we have a response from the request, if a
│ │ │ │ + * listener returns false, no further response processing will take
│ │ │ │ + * place.
│ │ │ │ + * success - Triggered when the HTTP response has a success code (200-299).
│ │ │ │ + * failure - Triggered when the HTTP response does not have a success code.
│ │ │ │ */
│ │ │ │ - destroyTile: function(tile) {
│ │ │ │ - this.removeTileMonitoringHooks(tile);
│ │ │ │ - tile.destroy();
│ │ │ │ - },
│ │ │ │ + events: new OpenLayers.Events(this),
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: getServerResolution
│ │ │ │ - * Return the closest server-supported resolution.
│ │ │ │ + * Method: makeSameOrigin
│ │ │ │ + * Using the specified proxy, returns a same origin url of the provided url.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * resolution - {Number} The base resolution. If undefined the
│ │ │ │ - * map resolution is used.
│ │ │ │ + * url - {String} An arbitrary url
│ │ │ │ + * proxy {String|Function} The proxy to use to make the provided url a
│ │ │ │ + * same origin url.
│ │ │ │ *
│ │ │ │ - * Returns:
│ │ │ │ - * {Number} The closest server resolution value.
│ │ │ │ + * Returns
│ │ │ │ + * {String} the same origin url. If no proxy is provided, the returned url
│ │ │ │ + * will be the same as the provided url.
│ │ │ │ */
│ │ │ │ - getServerResolution: function(resolution) {
│ │ │ │ - var distance = Number.POSITIVE_INFINITY;
│ │ │ │ - resolution = resolution || this.map.getResolution();
│ │ │ │ - if (this.serverResolutions &&
│ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
│ │ │ │ - var i, newDistance, newResolution, serverResolution;
│ │ │ │ - for (i = this.serverResolutions.length - 1; i >= 0; i--) {
│ │ │ │ - newResolution = this.serverResolutions[i];
│ │ │ │ - newDistance = Math.abs(newResolution - resolution);
│ │ │ │ - if (newDistance > distance) {
│ │ │ │ - break;
│ │ │ │ + makeSameOrigin: function(url, proxy) {
│ │ │ │ + var sameOrigin = url.indexOf("http") !== 0;
│ │ │ │ + var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
│ │ │ │ + if (urlParts) {
│ │ │ │ + var location = window.location;
│ │ │ │ + sameOrigin =
│ │ │ │ + urlParts[1] == location.protocol &&
│ │ │ │ + urlParts[3] == location.hostname;
│ │ │ │ + var uPort = urlParts[4],
│ │ │ │ + lPort = location.port;
│ │ │ │ + if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
│ │ │ │ + sameOrigin = sameOrigin && uPort == lPort;
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ + if (!sameOrigin) {
│ │ │ │ + if (proxy) {
│ │ │ │ + if (typeof proxy == "function") {
│ │ │ │ + url = proxy(url);
│ │ │ │ + } else {
│ │ │ │ + url = proxy + encodeURIComponent(url);
│ │ │ │ }
│ │ │ │ - distance = newDistance;
│ │ │ │ - serverResolution = newResolution;
│ │ │ │ }
│ │ │ │ - resolution = serverResolution;
│ │ │ │ }
│ │ │ │ - return resolution;
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: getServerZoom
│ │ │ │ - * Return the zoom value corresponding to the best matching server
│ │ │ │ - * resolution, taking into account and .
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Number} The closest server supported zoom. This is not the map zoom
│ │ │ │ - * level, but an index of the server's resolutions array.
│ │ │ │ - */
│ │ │ │ - getServerZoom: function() {
│ │ │ │ - var resolution = this.getServerResolution();
│ │ │ │ - return this.serverResolutions ?
│ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, resolution) :
│ │ │ │ - this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);
│ │ │ │ + return url;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: applyBackBuffer
│ │ │ │ - * Create, insert, scale and position a back buffer for the layer.
│ │ │ │ + * APIMethod: issue
│ │ │ │ + * Create a new XMLHttpRequest object, open it, set any headers, bind
│ │ │ │ + * a callback to done state, and send any data. It is recommended that
│ │ │ │ + * you use one , , , , , or .
│ │ │ │ + * This method is only documented to provide detail on the configuration
│ │ │ │ + * options available to all request methods.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * resolution - {Number} The resolution to transition to.
│ │ │ │ + * config - {Object} Object containing properties for configuring the
│ │ │ │ + * request. Allowed configuration properties are described below.
│ │ │ │ + * This object is modified and should not be reused.
│ │ │ │ + *
│ │ │ │ + * Allowed config properties:
│ │ │ │ + * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
│ │ │ │ + * OPTIONS. Default is GET.
│ │ │ │ + * url - {String} URL for the request.
│ │ │ │ + * async - {Boolean} Open an asynchronous request. Default is true.
│ │ │ │ + * user - {String} User for relevant authentication scheme. Set
│ │ │ │ + * to null to clear current user.
│ │ │ │ + * password - {String} Password for relevant authentication scheme.
│ │ │ │ + * Set to null to clear current password.
│ │ │ │ + * proxy - {String} Optional proxy. Defaults to
│ │ │ │ + * .
│ │ │ │ + * params - {Object} Any key:value pairs to be appended to the
│ │ │ │ + * url as a query string. Assumes url doesn't already include a query
│ │ │ │ + * string or hash. Typically, this is only appropriate for
│ │ │ │ + * requests where the query string will be appended to the url.
│ │ │ │ + * Parameter values that are arrays will be
│ │ │ │ + * concatenated with a comma (note that this goes against form-encoding)
│ │ │ │ + * as is done with .
│ │ │ │ + * headers - {Object} Object with header:value pairs to be set on
│ │ │ │ + * the request.
│ │ │ │ + * data - {String | Document} Optional data to send with the request.
│ │ │ │ + * Typically, this is only used with and requests.
│ │ │ │ + * Make sure to provide the appropriate "Content-Type" header for your
│ │ │ │ + * data. For and requests, the content type defaults to
│ │ │ │ + * "application-xml". If your data is a different content type, or
│ │ │ │ + * if you are using a different HTTP method, set the "Content-Type"
│ │ │ │ + * header to match your data type.
│ │ │ │ + * callback - {Function} Function to call when request is done.
│ │ │ │ + * To determine if the request failed, check request.status (200
│ │ │ │ + * indicates success).
│ │ │ │ + * success - {Function} Optional function to call if request status is in
│ │ │ │ + * the 200s. This will be called in addition to callback above and
│ │ │ │ + * would typically only be used as an alternative.
│ │ │ │ + * failure - {Function} Optional function to call if request status is not
│ │ │ │ + * in the 200s. This will be called in addition to callback above and
│ │ │ │ + * would typically only be used as an alternative.
│ │ │ │ + * scope - {Object} If callback is a public method on some object,
│ │ │ │ + * set the scope to that object.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {XMLHttpRequest} Request object. To abort the request before a response
│ │ │ │ + * is received, call abort() on the request object.
│ │ │ │ */
│ │ │ │ - applyBackBuffer: function(resolution) {
│ │ │ │ - if (this.backBufferTimerId !== null) {
│ │ │ │ - this.removeBackBuffer();
│ │ │ │ - }
│ │ │ │ - var backBuffer = this.backBuffer;
│ │ │ │ - if (!backBuffer) {
│ │ │ │ - backBuffer = this.createBackBuffer();
│ │ │ │ - if (!backBuffer) {
│ │ │ │ - return;
│ │ │ │ + issue: function(config) {
│ │ │ │ + // apply default config - proxy host may have changed
│ │ │ │ + var defaultConfig = OpenLayers.Util.extend(
│ │ │ │ + this.DEFAULT_CONFIG, {
│ │ │ │ + proxy: OpenLayers.ProxyHost
│ │ │ │ }
│ │ │ │ - if (resolution === this.gridResolution) {
│ │ │ │ - this.div.insertBefore(backBuffer, this.div.firstChild);
│ │ │ │ - } else {
│ │ │ │ - this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);
│ │ │ │ + );
│ │ │ │ + config = config || {};
│ │ │ │ + config.headers = config.headers || {};
│ │ │ │ + config = OpenLayers.Util.applyDefaults(config, defaultConfig);
│ │ │ │ + config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
│ │ │ │ + // Always set the "X-Requested-With" header to signal that this request
│ │ │ │ + // was issued through the XHR-object. Since header keys are case
│ │ │ │ + // insensitive and we want to allow overriding of the "X-Requested-With"
│ │ │ │ + // header through the user we cannot use applyDefaults, but have to
│ │ │ │ + // check manually whether we were called with a "X-Requested-With"
│ │ │ │ + // header.
│ │ │ │ + var customRequestedWithHeader = false,
│ │ │ │ + headerKey;
│ │ │ │ + for (headerKey in config.headers) {
│ │ │ │ + if (config.headers.hasOwnProperty(headerKey)) {
│ │ │ │ + if (headerKey.toLowerCase() === 'x-requested-with') {
│ │ │ │ + customRequestedWithHeader = true;
│ │ │ │ + }
│ │ │ │ }
│ │ │ │ - this.backBuffer = backBuffer;
│ │ │ │ -
│ │ │ │ - // set some information in the instance for subsequent
│ │ │ │ - // calls to applyBackBuffer where the same back buffer
│ │ │ │ - // is reused
│ │ │ │ - var topLeftTileBounds = this.grid[0][0].bounds;
│ │ │ │ - this.backBufferLonLat = {
│ │ │ │ - lon: topLeftTileBounds.left,
│ │ │ │ - lat: topLeftTileBounds.top
│ │ │ │ - };
│ │ │ │ - this.backBufferResolution = this.gridResolution;
│ │ │ │ + }
│ │ │ │ + if (customRequestedWithHeader === false) {
│ │ │ │ + // we did not have a custom "X-Requested-With" header
│ │ │ │ + config.headers['X-Requested-With'] = 'XMLHttpRequest';
│ │ │ │ }
│ │ │ │
│ │ │ │ - var ratio = this.backBufferResolution / resolution;
│ │ │ │ -
│ │ │ │ - // scale the tiles inside the back buffer
│ │ │ │ - var tiles = backBuffer.childNodes,
│ │ │ │ - tile;
│ │ │ │ - for (var i = tiles.length - 1; i >= 0; --i) {
│ │ │ │ - tile = tiles[i];
│ │ │ │ - tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';
│ │ │ │ - tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';
│ │ │ │ - tile.style.width = Math.round(ratio * tile._w) + 'px';
│ │ │ │ - tile.style.height = Math.round(ratio * tile._h) + 'px';
│ │ │ │ + // create request, open, and set headers
│ │ │ │ + var request = new OpenLayers.Request.XMLHttpRequest();
│ │ │ │ + var url = OpenLayers.Util.urlAppend(config.url,
│ │ │ │ + OpenLayers.Util.getParameterString(config.params || {}));
│ │ │ │ + url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
│ │ │ │ + request.open(
│ │ │ │ + config.method, url, config.async, config.user, config.password
│ │ │ │ + );
│ │ │ │ + for (var header in config.headers) {
│ │ │ │ + request.setRequestHeader(header, config.headers[header]);
│ │ │ │ }
│ │ │ │
│ │ │ │ - // and position it (based on the grid's top-left corner)
│ │ │ │ - var position = this.getViewPortPxFromLonLat(
│ │ │ │ - this.backBufferLonLat, resolution);
│ │ │ │ - var leftOffset = this.map.layerContainerOriginPx.x;
│ │ │ │ - var topOffset = this.map.layerContainerOriginPx.y;
│ │ │ │ - backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';
│ │ │ │ - backBuffer.style.top = Math.round(position.y - topOffset) + 'px';
│ │ │ │ - },
│ │ │ │ + var events = this.events;
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: createBackBuffer
│ │ │ │ - * Create a back buffer.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {DOMElement} The DOM element for the back buffer, undefined if the
│ │ │ │ - * grid isn't initialized yet.
│ │ │ │ - */
│ │ │ │ - createBackBuffer: function() {
│ │ │ │ - var backBuffer;
│ │ │ │ - if (this.grid.length > 0) {
│ │ │ │ - backBuffer = document.createElement('div');
│ │ │ │ - backBuffer.id = this.div.id + '_bb';
│ │ │ │ - backBuffer.className = 'olBackBuffer';
│ │ │ │ - backBuffer.style.position = 'absolute';
│ │ │ │ - var map = this.map;
│ │ │ │ - backBuffer.style.zIndex = this.transitionEffect === 'resize' ?
│ │ │ │ - this.getZIndex() - 1 :
│ │ │ │ - // 'map-resize':
│ │ │ │ - map.Z_INDEX_BASE.BaseLayer -
│ │ │ │ - (map.getNumLayers() - map.getLayerIndex(this));
│ │ │ │ - for (var i = 0, lenI = this.grid.length; i < lenI; i++) {
│ │ │ │ - for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {
│ │ │ │ - var tile = this.grid[i][j],
│ │ │ │ - markup = this.grid[i][j].createBackBuffer();
│ │ │ │ - if (markup) {
│ │ │ │ - markup._i = i;
│ │ │ │ - markup._j = j;
│ │ │ │ - markup._w = tile.size.w;
│ │ │ │ - markup._h = tile.size.h;
│ │ │ │ - markup.id = tile.id + '_bb';
│ │ │ │ - backBuffer.appendChild(markup);
│ │ │ │ + // we want to execute runCallbacks with "this" as the
│ │ │ │ + // execution scope
│ │ │ │ + var self = this;
│ │ │ │ +
│ │ │ │ + request.onreadystatechange = function() {
│ │ │ │ + if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
│ │ │ │ + var proceed = events.triggerEvent(
│ │ │ │ + "complete", {
│ │ │ │ + request: request,
│ │ │ │ + config: config,
│ │ │ │ + requestUrl: url
│ │ │ │ }
│ │ │ │ + );
│ │ │ │ + if (proceed !== false) {
│ │ │ │ + self.runCallbacks({
│ │ │ │ + request: request,
│ │ │ │ + config: config,
│ │ │ │ + requestUrl: url
│ │ │ │ + });
│ │ │ │ }
│ │ │ │ }
│ │ │ │ + };
│ │ │ │ +
│ │ │ │ + // send request (optionally with data) and return
│ │ │ │ + // call in a timeout for asynchronous requests so the return is
│ │ │ │ + // available before readyState == 4 for cached docs
│ │ │ │ + if (config.async === false) {
│ │ │ │ + request.send(config.data);
│ │ │ │ + } else {
│ │ │ │ + window.setTimeout(function() {
│ │ │ │ + if (request.readyState !== 0) { // W3C: 0-UNSENT
│ │ │ │ + request.send(config.data);
│ │ │ │ + }
│ │ │ │ + }, 0);
│ │ │ │ }
│ │ │ │ - return backBuffer;
│ │ │ │ + return request;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: removeBackBuffer
│ │ │ │ - * Remove back buffer from DOM.
│ │ │ │ + * Method: runCallbacks
│ │ │ │ + * Calls the complete, success and failure callbacks. Application
│ │ │ │ + * can listen to the "complete" event, have the listener
│ │ │ │ + * display a confirm window and always return false, and
│ │ │ │ + * execute OpenLayers.Request.runCallbacks if the user
│ │ │ │ + * hits "yes" in the confirm window.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * options - {Object} Hash containing request, config and requestUrl keys
│ │ │ │ */
│ │ │ │ - removeBackBuffer: function() {
│ │ │ │ - if (this._transitionElement) {
│ │ │ │ - for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {
│ │ │ │ - OpenLayers.Event.stopObserving(this._transitionElement,
│ │ │ │ - this.transitionendEvents[i], this._removeBackBuffer);
│ │ │ │ - }
│ │ │ │ - delete this._transitionElement;
│ │ │ │ + runCallbacks: function(options) {
│ │ │ │ + var request = options.request;
│ │ │ │ + var config = options.config;
│ │ │ │ +
│ │ │ │ + // bind callbacks to readyState 4 (done)
│ │ │ │ + var complete = (config.scope) ?
│ │ │ │ + OpenLayers.Function.bind(config.callback, config.scope) :
│ │ │ │ + config.callback;
│ │ │ │ +
│ │ │ │ + // optional success callback
│ │ │ │ + var success;
│ │ │ │ + if (config.success) {
│ │ │ │ + success = (config.scope) ?
│ │ │ │ + OpenLayers.Function.bind(config.success, config.scope) :
│ │ │ │ + config.success;
│ │ │ │ }
│ │ │ │ - if (this.backBuffer) {
│ │ │ │ - if (this.backBuffer.parentNode) {
│ │ │ │ - this.backBuffer.parentNode.removeChild(this.backBuffer);
│ │ │ │ +
│ │ │ │ + // optional failure callback
│ │ │ │ + var failure;
│ │ │ │ + if (config.failure) {
│ │ │ │ + failure = (config.scope) ?
│ │ │ │ + OpenLayers.Function.bind(config.failure, config.scope) :
│ │ │ │ + config.failure;
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
│ │ │ │ + request.responseText) {
│ │ │ │ + request.status = 200;
│ │ │ │ + }
│ │ │ │ + complete(request);
│ │ │ │ +
│ │ │ │ + if (!request.status || (request.status >= 200 && request.status < 300)) {
│ │ │ │ + this.events.triggerEvent("success", options);
│ │ │ │ + if (success) {
│ │ │ │ + success(request);
│ │ │ │ }
│ │ │ │ - this.backBuffer = null;
│ │ │ │ - this.backBufferResolution = null;
│ │ │ │ - if (this.backBufferTimerId !== null) {
│ │ │ │ - window.clearTimeout(this.backBufferTimerId);
│ │ │ │ - this.backBufferTimerId = null;
│ │ │ │ + }
│ │ │ │ + if (request.status && (request.status < 200 || request.status >= 300)) {
│ │ │ │ + this.events.triggerEvent("failure", options);
│ │ │ │ + if (failure) {
│ │ │ │ + failure(request);
│ │ │ │ }
│ │ │ │ }
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: moveByPx
│ │ │ │ - * Move the layer based on pixel vector.
│ │ │ │ + * APIMethod: GET
│ │ │ │ + * Send an HTTP GET request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to GET.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * dx - {Number}
│ │ │ │ - * dy - {Number}
│ │ │ │ - */
│ │ │ │ - moveByPx: function(dx, dy) {
│ │ │ │ - if (!this.singleTile) {
│ │ │ │ - this.moveGriddedTiles();
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * APIMethod: setTileSize
│ │ │ │ - * Check if we are in singleTile mode and if so, set the size as a ratio
│ │ │ │ - * of the map size (as specified by the layer's 'ratio' property).
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties.
│ │ │ │ + * This object is modified and should not be reused.
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * size - {}
│ │ │ │ + * Returns:
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - setTileSize: function(size) {
│ │ │ │ - if (this.singleTile) {
│ │ │ │ - size = this.map.getSize();
│ │ │ │ - size.h = parseInt(size.h * this.ratio, 10);
│ │ │ │ - size.w = parseInt(size.w * this.ratio, 10);
│ │ │ │ - }
│ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
│ │ │ │ + GET: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "GET"
│ │ │ │ + });
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: getTilesBounds
│ │ │ │ - * Return the bounds of the tile grid.
│ │ │ │ + * APIMethod: POST
│ │ │ │ + * Send a POST request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to POST and "Content-Type" header set to "application/xml".
│ │ │ │ *
│ │ │ │ + * Parameters:
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties. The
│ │ │ │ + * default "Content-Type" header will be set to "application-xml" if
│ │ │ │ + * none is provided. This object is modified and should not be reused.
│ │ │ │ + *
│ │ │ │ * Returns:
│ │ │ │ - * {} A Bounds object representing the bounds of all the
│ │ │ │ - * currently loaded tiles (including those partially or not at all seen
│ │ │ │ - * onscreen).
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - getTilesBounds: function() {
│ │ │ │ - var bounds = null;
│ │ │ │ -
│ │ │ │ - var length = this.grid.length;
│ │ │ │ - if (length) {
│ │ │ │ - var bottomLeftTileBounds = this.grid[length - 1][0].bounds,
│ │ │ │ - width = this.grid[0].length * bottomLeftTileBounds.getWidth(),
│ │ │ │ - height = this.grid.length * bottomLeftTileBounds.getHeight();
│ │ │ │ -
│ │ │ │ - bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,
│ │ │ │ - bottomLeftTileBounds.bottom,
│ │ │ │ - bottomLeftTileBounds.left + width,
│ │ │ │ - bottomLeftTileBounds.bottom + height);
│ │ │ │ + POST: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "POST"
│ │ │ │ + });
│ │ │ │ + // set content type to application/xml if it isn't already set
│ │ │ │ + config.headers = config.headers ? config.headers : {};
│ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
│ │ │ │ + config.headers["Content-Type"] = "application/xml";
│ │ │ │ }
│ │ │ │ - return bounds;
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: initSingleTile
│ │ │ │ + * APIMethod: PUT
│ │ │ │ + * Send an HTTP PUT request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to PUT and "Content-Type" header set to "application/xml".
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties. The
│ │ │ │ + * default "Content-Type" header will be set to "application-xml" if
│ │ │ │ + * none is provided. This object is modified and should not be reused.
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * bounds - {}
│ │ │ │ + * Returns:
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - initSingleTile: function(bounds) {
│ │ │ │ - this.events.triggerEvent("retile");
│ │ │ │ -
│ │ │ │ - //determine new tile bounds
│ │ │ │ - var center = bounds.getCenterLonLat();
│ │ │ │ - var tileWidth = bounds.getWidth() * this.ratio;
│ │ │ │ - var tileHeight = bounds.getHeight() * this.ratio;
│ │ │ │ -
│ │ │ │ - var tileBounds =
│ │ │ │ - new OpenLayers.Bounds(center.lon - (tileWidth / 2),
│ │ │ │ - center.lat - (tileHeight / 2),
│ │ │ │ - center.lon + (tileWidth / 2),
│ │ │ │ - center.lat + (tileHeight / 2));
│ │ │ │ -
│ │ │ │ - var px = this.map.getLayerPxFromLonLat({
│ │ │ │ - lon: tileBounds.left,
│ │ │ │ - lat: tileBounds.top
│ │ │ │ + PUT: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "PUT"
│ │ │ │ });
│ │ │ │ -
│ │ │ │ - if (!this.grid.length) {
│ │ │ │ - this.grid[0] = [];
│ │ │ │ - }
│ │ │ │ -
│ │ │ │ - var tile = this.grid[0][0];
│ │ │ │ - if (!tile) {
│ │ │ │ - tile = this.addTile(tileBounds, px);
│ │ │ │ -
│ │ │ │ - this.addTileMonitoringHooks(tile);
│ │ │ │ - tile.draw();
│ │ │ │ - this.grid[0][0] = tile;
│ │ │ │ - } else {
│ │ │ │ - tile.moveTo(tileBounds, px);
│ │ │ │ + // set content type to application/xml if it isn't already set
│ │ │ │ + config.headers = config.headers ? config.headers : {};
│ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
│ │ │ │ + config.headers["Content-Type"] = "application/xml";
│ │ │ │ }
│ │ │ │ -
│ │ │ │ - //remove all but our single tile
│ │ │ │ - this.removeExcessTiles(1, 1);
│ │ │ │ -
│ │ │ │ - // store the resolution of the grid
│ │ │ │ - this.gridResolution = this.getServerResolution();
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: calculateGridLayout
│ │ │ │ - * Generate parameters for the grid layout.
│ │ │ │ + /**
│ │ │ │ + * APIMethod: DELETE
│ │ │ │ + * Send an HTTP DELETE request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to DELETE.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * bounds - {|Object} OpenLayers.Bounds or an
│ │ │ │ - * object with a 'left' and 'top' properties.
│ │ │ │ - * origin - {|Object} OpenLayers.LonLat or an
│ │ │ │ - * object with a 'lon' and 'lat' properties.
│ │ │ │ - * resolution - {Number}
│ │ │ │ - *
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties.
│ │ │ │ + * This object is modified and should not be reused.
│ │ │ │ + *
│ │ │ │ * Returns:
│ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol,
│ │ │ │ - * startrow
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - calculateGridLayout: function(bounds, origin, resolution) {
│ │ │ │ - var tilelon = resolution * this.tileSize.w;
│ │ │ │ - var tilelat = resolution * this.tileSize.h;
│ │ │ │ -
│ │ │ │ - var offsetlon = bounds.left - origin.lon;
│ │ │ │ - var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;
│ │ │ │ -
│ │ │ │ - var rowSign = this.rowSign;
│ │ │ │ -
│ │ │ │ - var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);
│ │ │ │ - var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;
│ │ │ │ -
│ │ │ │ - return {
│ │ │ │ - tilelon: tilelon,
│ │ │ │ - tilelat: tilelat,
│ │ │ │ - startcol: tilecol,
│ │ │ │ - startrow: tilerow
│ │ │ │ - };
│ │ │ │ -
│ │ │ │ + DELETE: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "DELETE"
│ │ │ │ + });
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: getTileOrigin
│ │ │ │ - * Determine the origin for aligning the grid of tiles. If a
│ │ │ │ - * property is supplied, that will be returned. Otherwise, the origin
│ │ │ │ - * will be derived from the layer's property. In this case,
│ │ │ │ - * the tile origin will be the corner of the given by the
│ │ │ │ - * property.
│ │ │ │ + * APIMethod: HEAD
│ │ │ │ + * Send an HTTP HEAD request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to HEAD.
│ │ │ │ *
│ │ │ │ + * Parameters:
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties.
│ │ │ │ + * This object is modified and should not be reused.
│ │ │ │ + *
│ │ │ │ * Returns:
│ │ │ │ - * {} The tile origin.
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - getTileOrigin: function() {
│ │ │ │ - var origin = this.tileOrigin;
│ │ │ │ - if (!origin) {
│ │ │ │ - var extent = this.getMaxExtent();
│ │ │ │ - var edges = ({
│ │ │ │ - "tl": ["left", "top"],
│ │ │ │ - "tr": ["right", "top"],
│ │ │ │ - "bl": ["left", "bottom"],
│ │ │ │ - "br": ["right", "bottom"]
│ │ │ │ - })[this.tileOriginCorner];
│ │ │ │ - origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
│ │ │ │ - }
│ │ │ │ - return origin;
│ │ │ │ + HEAD: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "HEAD"
│ │ │ │ + });
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: getTileBoundsForGridIndex
│ │ │ │ + * APIMethod: OPTIONS
│ │ │ │ + * Send an HTTP OPTIONS request. Additional configuration properties are
│ │ │ │ + * documented in the method, with the method property set
│ │ │ │ + * to OPTIONS.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * row - {Number} The row of the grid
│ │ │ │ - * col - {Number} The column of the grid
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {} The bounds for the tile at (row, col)
│ │ │ │ - */
│ │ │ │ - getTileBoundsForGridIndex: function(row, col) {
│ │ │ │ - var origin = this.getTileOrigin();
│ │ │ │ - var tileLayout = this.gridLayout;
│ │ │ │ - var tilelon = tileLayout.tilelon;
│ │ │ │ - var tilelat = tileLayout.tilelat;
│ │ │ │ - var startcol = tileLayout.startcol;
│ │ │ │ - var startrow = tileLayout.startrow;
│ │ │ │ - var rowSign = this.rowSign;
│ │ │ │ - return new OpenLayers.Bounds(
│ │ │ │ - origin.lon + (startcol + col) * tilelon,
│ │ │ │ - origin.lat - (startrow + row * rowSign) * tilelat * rowSign,
│ │ │ │ - origin.lon + (startcol + col + 1) * tilelon,
│ │ │ │ - origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign
│ │ │ │ - );
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: initGriddedTiles
│ │ │ │ + * config - {Object} Object with properties for configuring the request.
│ │ │ │ + * See the method for documentation of allowed properties.
│ │ │ │ + * This object is modified and should not be reused.
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * bounds - {}
│ │ │ │ + * Returns:
│ │ │ │ + * {XMLHttpRequest} Request object.
│ │ │ │ */
│ │ │ │ - initGriddedTiles: function(bounds) {
│ │ │ │ - this.events.triggerEvent("retile");
│ │ │ │ + OPTIONS: function(config) {
│ │ │ │ + config = OpenLayers.Util.extend(config, {
│ │ │ │ + method: "OPTIONS"
│ │ │ │ + });
│ │ │ │ + return OpenLayers.Request.issue(config);
│ │ │ │ + }
│ │ │ │
│ │ │ │ - // work out mininum number of rows and columns; this is the number of
│ │ │ │ - // tiles required to cover the viewport plus at least one for panning
│ │ │ │ +});
│ │ │ │ +/* ======================================================================
│ │ │ │ + OpenLayers/Feature/Vector.js
│ │ │ │ + ====================================================================== */
│ │ │ │
│ │ │ │ - var viewSize = this.map.getSize();
│ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ + * full text of the license. */
│ │ │ │
│ │ │ │ - var origin = this.getTileOrigin();
│ │ │ │ - var resolution = this.map.getResolution(),
│ │ │ │ - serverResolution = this.getServerResolution(),
│ │ │ │ - ratio = resolution / serverResolution,
│ │ │ │ - tileSize = {
│ │ │ │ - w: this.tileSize.w / ratio,
│ │ │ │ - h: this.tileSize.h / ratio
│ │ │ │ - };
│ │ │ │ +// TRASH THIS
│ │ │ │ +OpenLayers.State = {
│ │ │ │ + /** states */
│ │ │ │ + UNKNOWN: 'Unknown',
│ │ │ │ + INSERT: 'Insert',
│ │ │ │ + UPDATE: 'Update',
│ │ │ │ + DELETE: 'Delete'
│ │ │ │ +};
│ │ │ │
│ │ │ │ - var minRows = Math.ceil(viewSize.h / tileSize.h) +
│ │ │ │ - 2 * this.buffer + 1;
│ │ │ │ - var minCols = Math.ceil(viewSize.w / tileSize.w) +
│ │ │ │ - 2 * this.buffer + 1;
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/Feature.js
│ │ │ │ + * @requires OpenLayers/Util.js
│ │ │ │ + */
│ │ │ │
│ │ │ │ - var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);
│ │ │ │ - this.gridLayout = tileLayout;
│ │ │ │ +/**
│ │ │ │ + * 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
│ │ │ │ + * objects.
│ │ │ │ + *
│ │ │ │ + * Inherits from:
│ │ │ │ + * -
│ │ │ │ + */
│ │ │ │ +OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
│ │ │ │
│ │ │ │ - var tilelon = tileLayout.tilelon;
│ │ │ │ - var tilelat = tileLayout.tilelat;
│ │ │ │ + /**
│ │ │ │ + * Property: fid
│ │ │ │ + * {String}
│ │ │ │ + */
│ │ │ │ + fid: null,
│ │ │ │
│ │ │ │ - var layerContainerDivLeft = this.map.layerContainerOriginPx.x;
│ │ │ │ - var layerContainerDivTop = this.map.layerContainerOriginPx.y;
│ │ │ │ + /**
│ │ │ │ + * APIProperty: geometry
│ │ │ │ + * {}
│ │ │ │ + */
│ │ │ │ + geometry: null,
│ │ │ │
│ │ │ │ - var tileBounds = this.getTileBoundsForGridIndex(0, 0);
│ │ │ │ - var startPx = this.map.getViewPortPxFromLonLat(
│ │ │ │ - new OpenLayers.LonLat(tileBounds.left, tileBounds.top)
│ │ │ │ - );
│ │ │ │ - startPx.x = Math.round(startPx.x) - layerContainerDivLeft;
│ │ │ │ - startPx.y = Math.round(startPx.y) - layerContainerDivTop;
│ │ │ │ + /**
│ │ │ │ + * APIProperty: attributes
│ │ │ │ + * {Object} This object holds arbitrary, serializable properties that
│ │ │ │ + * describe the feature.
│ │ │ │ + */
│ │ │ │ + attributes: null,
│ │ │ │
│ │ │ │ - var tileData = [],
│ │ │ │ - center = this.map.getCenter();
│ │ │ │ + /**
│ │ │ │ + * Property: bounds
│ │ │ │ + * {} The box bounding that feature's geometry, that
│ │ │ │ + * property can be set by an object when
│ │ │ │ + * deserializing the feature, so in most cases it represents an
│ │ │ │ + * information set by the server.
│ │ │ │ + */
│ │ │ │ + bounds: null,
│ │ │ │
│ │ │ │ - var rowidx = 0;
│ │ │ │ - do {
│ │ │ │ - var row = this.grid[rowidx];
│ │ │ │ - if (!row) {
│ │ │ │ - row = [];
│ │ │ │ - this.grid.push(row);
│ │ │ │ - }
│ │ │ │ + /**
│ │ │ │ + * Property: state
│ │ │ │ + * {String}
│ │ │ │ + */
│ │ │ │ + state: null,
│ │ │ │
│ │ │ │ - var colidx = 0;
│ │ │ │ - do {
│ │ │ │ - tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);
│ │ │ │ - var px = startPx.clone();
│ │ │ │ - px.x = px.x + colidx * Math.round(tileSize.w);
│ │ │ │ - px.y = px.y + rowidx * Math.round(tileSize.h);
│ │ │ │ - var tile = row[colidx];
│ │ │ │ - if (!tile) {
│ │ │ │ - tile = this.addTile(tileBounds, px);
│ │ │ │ - this.addTileMonitoringHooks(tile);
│ │ │ │ - row.push(tile);
│ │ │ │ - } else {
│ │ │ │ - tile.moveTo(tileBounds, px, false);
│ │ │ │ - }
│ │ │ │ - var tileCenter = tileBounds.getCenterLonLat();
│ │ │ │ - tileData.push({
│ │ │ │ - tile: tile,
│ │ │ │ - distance: Math.pow(tileCenter.lon - center.lon, 2) +
│ │ │ │ - Math.pow(tileCenter.lat - center.lat, 2)
│ │ │ │ - });
│ │ │ │ + /**
│ │ │ │ + * APIProperty: style
│ │ │ │ + * {Object}
│ │ │ │ + */
│ │ │ │ + style: null,
│ │ │ │
│ │ │ │ - colidx += 1;
│ │ │ │ - } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||
│ │ │ │ - colidx < minCols);
│ │ │ │ + /**
│ │ │ │ + * APIProperty: url
│ │ │ │ + * {String} If this property is set it will be taken into account by
│ │ │ │ + * {} when upadting or deleting the feature.
│ │ │ │ + */
│ │ │ │ + url: null,
│ │ │ │
│ │ │ │ - rowidx += 1;
│ │ │ │ - } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||
│ │ │ │ - rowidx < minRows);
│ │ │ │ + /**
│ │ │ │ + * Property: renderIntent
│ │ │ │ + * {String} rendering intent currently being used
│ │ │ │ + */
│ │ │ │ + renderIntent: "default",
│ │ │ │
│ │ │ │ - //shave off exceess rows and colums
│ │ │ │ - this.removeExcessTiles(rowidx, colidx);
│ │ │ │ + /**
│ │ │ │ + * APIProperty: modified
│ │ │ │ + * {Object} An object with the originals of the geometry and attributes of
│ │ │ │ + * the feature, if they were changed. Currently this property is only read
│ │ │ │ + * by , and written by
│ │ │ │ + * , which sets the geometry property.
│ │ │ │ + * Applications can set the originals of modified attributes in the
│ │ │ │ + * attributes property. Note that applications have to check if this
│ │ │ │ + * object and the attributes property is already created before using it.
│ │ │ │ + * After a change made with ModifyFeature, this object could look like
│ │ │ │ + *
│ │ │ │ + * (code)
│ │ │ │ + * {
│ │ │ │ + * geometry: >Object
│ │ │ │ + * }
│ │ │ │ + * (end)
│ │ │ │ + *
│ │ │ │ + * When an application has made changes to feature attributes, it could
│ │ │ │ + * have set the attributes to something like this:
│ │ │ │ + *
│ │ │ │ + * (code)
│ │ │ │ + * {
│ │ │ │ + * attributes: {
│ │ │ │ + * myAttribute: "original"
│ │ │ │ + * }
│ │ │ │ + * }
│ │ │ │ + * (end)
│ │ │ │ + *
│ │ │ │ + * Note that only checks for truthy values in
│ │ │ │ + * *modified.geometry* and the attribute names in *modified.attributes*,
│ │ │ │ + * but it is recommended to set the original values (and not just true) as
│ │ │ │ + * attribute value, so applications could use this information to undo
│ │ │ │ + * changes.
│ │ │ │ + */
│ │ │ │ + modified: null,
│ │ │ │
│ │ │ │ - var resolution = this.getServerResolution();
│ │ │ │ - // store the resolution of the grid
│ │ │ │ - this.gridResolution = resolution;
│ │ │ │ + /**
│ │ │ │ + * Constructor: OpenLayers.Feature.Vector
│ │ │ │ + * Create a vector feature.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * geometry - {} The geometry that this feature
│ │ │ │ + * represents.
│ │ │ │ + * attributes - {Object} An optional object that will be mapped to the
│ │ │ │ + * 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 ? geometry : null;
│ │ │ │ + this.state = null;
│ │ │ │ + this.attributes = {};
│ │ │ │ + if (attributes) {
│ │ │ │ + this.attributes = OpenLayers.Util.extend(this.attributes,
│ │ │ │ + attributes);
│ │ │ │ + }
│ │ │ │ + this.style = style ? style : null;
│ │ │ │ + },
│ │ │ │
│ │ │ │ - //now actually draw the tiles
│ │ │ │ - tileData.sort(function(a, b) {
│ │ │ │ - return a.distance - b.distance;
│ │ │ │ - });
│ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) {
│ │ │ │ - tileData[i].tile.draw();
│ │ │ │ + /**
│ │ │ │ + * 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;
│ │ │ │ + this.modified = null;
│ │ │ │ + OpenLayers.Feature.prototype.destroy.apply(this, arguments);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: getMaxExtent
│ │ │ │ - * Get this layer's maximum extent. (Implemented as a getter for
│ │ │ │ - * potential specific implementations in sub-classes.)
│ │ │ │ + * Method: clone
│ │ │ │ + * Create a clone of this vector feature. Does not set any non-standard
│ │ │ │ + * properties.
│ │ │ │ *
│ │ │ │ * Returns:
│ │ │ │ - * {}
│ │ │ │ + * {} An exact clone of this vector feature.
│ │ │ │ */
│ │ │ │ - getMaxExtent: function() {
│ │ │ │ - return this.maxExtent;
│ │ │ │ + clone: function() {
│ │ │ │ + return new OpenLayers.Feature.Vector(
│ │ │ │ + this.geometry ? this.geometry.clone() : null,
│ │ │ │ + this.attributes,
│ │ │ │ + this.style);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: addTile
│ │ │ │ - * Create a tile, initialize it, and add it to the layer div.
│ │ │ │ - *
│ │ │ │ - * Parameters
│ │ │ │ - * bounds - {}
│ │ │ │ - * position - {}
│ │ │ │ + * 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:
│ │ │ │ - * {} The added OpenLayers.Tile
│ │ │ │ + * {Boolean} The feature is currently visible on screen (optionally
│ │ │ │ + * based on its bounds if boundsOnly is true).
│ │ │ │ */
│ │ │ │ - addTile: function(bounds, position) {
│ │ │ │ - var tile = new this.tileClass(
│ │ │ │ - this, position, bounds, null, this.tileSize, this.tileOptions
│ │ │ │ - );
│ │ │ │ - this.events.triggerEvent("addtile", {
│ │ │ │ - tile: tile
│ │ │ │ - });
│ │ │ │ - return tile;
│ │ │ │ + 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: addTileMonitoringHooks
│ │ │ │ - * This function takes a tile as input and adds the appropriate hooks to
│ │ │ │ - * the tile so that the layer can keep track of the loading tiles.
│ │ │ │ + /**
│ │ │ │ + * Method: getVisibility
│ │ │ │ + * Determine whether the feature is displayed or not. It may not displayed
│ │ │ │ + * because:
│ │ │ │ + * - its style display property is set to 'none',
│ │ │ │ + * - it doesn't belong to any layer,
│ │ │ │ + * - the styleMap creates a symbolizer with display property set to 'none'
│ │ │ │ + * for it,
│ │ │ │ + * - the layer which it belongs to is not visible.
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * tile - {}
│ │ │ │ + * Returns:
│ │ │ │ + * {Boolean} The feature is currently displayed.
│ │ │ │ */
│ │ │ │ - addTileMonitoringHooks: function(tile) {
│ │ │ │ -
│ │ │ │ - var replacingCls = 'olTileReplacing';
│ │ │ │ -
│ │ │ │ - tile.onLoadStart = function() {
│ │ │ │ - //if that was first tile then trigger a 'loadstart' on the layer
│ │ │ │ - if (this.loading === false) {
│ │ │ │ - this.loading = true;
│ │ │ │ - this.events.triggerEvent("loadstart");
│ │ │ │ - }
│ │ │ │ - this.events.triggerEvent("tileloadstart", {
│ │ │ │ - tile: tile
│ │ │ │ - });
│ │ │ │ - this.numLoadingTiles++;
│ │ │ │ - if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {
│ │ │ │ - OpenLayers.Element.addClass(tile.getTile(), replacingCls);
│ │ │ │ - }
│ │ │ │ - };
│ │ │ │ -
│ │ │ │ - tile.onLoadEnd = function(evt) {
│ │ │ │ - this.numLoadingTiles--;
│ │ │ │ - var aborted = evt.type === 'unload';
│ │ │ │ - this.events.triggerEvent("tileloaded", {
│ │ │ │ - tile: tile,
│ │ │ │ - aborted: aborted
│ │ │ │ - });
│ │ │ │ - if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {
│ │ │ │ - var tileDiv = tile.getTile();
│ │ │ │ - if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {
│ │ │ │ - var bufferTile = document.getElementById(tile.id + '_bb');
│ │ │ │ - if (bufferTile) {
│ │ │ │ - bufferTile.parentNode.removeChild(bufferTile);
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - OpenLayers.Element.removeClass(tileDiv, replacingCls);
│ │ │ │ - }
│ │ │ │ - //if that was the last tile, then trigger a 'loadend' on the layer
│ │ │ │ - if (this.numLoadingTiles === 0) {
│ │ │ │ - if (this.backBuffer) {
│ │ │ │ - if (this.backBuffer.childNodes.length === 0) {
│ │ │ │ - // no tiles transitioning, remove immediately
│ │ │ │ - this.removeBackBuffer();
│ │ │ │ - } else {
│ │ │ │ - // wait until transition has ended or delay has passed
│ │ │ │ - this._transitionElement = aborted ?
│ │ │ │ - this.div.lastChild : tile.imgDiv;
│ │ │ │ - var transitionendEvents = this.transitionendEvents;
│ │ │ │ - for (var i = transitionendEvents.length - 1; i >= 0; --i) {
│ │ │ │ - OpenLayers.Event.observe(this._transitionElement,
│ │ │ │ - transitionendEvents[i],
│ │ │ │ - this._removeBackBuffer);
│ │ │ │ - }
│ │ │ │ - // the removal of the back buffer is delayed to prevent
│ │ │ │ - // flash effects due to the animation of tile displaying
│ │ │ │ - this.backBufferTimerId = window.setTimeout(
│ │ │ │ - this._removeBackBuffer, this.removeBackBufferDelay
│ │ │ │ - );
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - this.loading = false;
│ │ │ │ - this.events.triggerEvent("loadend");
│ │ │ │ - }
│ │ │ │ - };
│ │ │ │ -
│ │ │ │ - tile.onLoadError = function() {
│ │ │ │ - this.events.triggerEvent("tileerror", {
│ │ │ │ - tile: tile
│ │ │ │ - });
│ │ │ │ - };
│ │ │ │ -
│ │ │ │ - tile.events.on({
│ │ │ │ - "loadstart": tile.onLoadStart,
│ │ │ │ - "loadend": tile.onLoadEnd,
│ │ │ │ - "unload": tile.onLoadEnd,
│ │ │ │ - "loaderror": tile.onLoadError,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ + getVisibility: function() {
│ │ │ │ + return !(this.style && this.style.display == 'none' ||
│ │ │ │ + !this.layer ||
│ │ │ │ + this.layer && this.layer.styleMap &&
│ │ │ │ + this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
│ │ │ │ + this.layer && !this.layer.getVisibility());
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: removeTileMonitoringHooks
│ │ │ │ - * This function takes a tile as input and removes the tile hooks
│ │ │ │ - * that were added in addTileMonitoringHooks()
│ │ │ │ + /**
│ │ │ │ + * Method: createMarker
│ │ │ │ + * HACK - we need to decide if all vector features should be able to
│ │ │ │ + * create markers
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * tile - {}
│ │ │ │ + * Returns:
│ │ │ │ + * {} For now just returns null
│ │ │ │ */
│ │ │ │ - removeTileMonitoringHooks: function(tile) {
│ │ │ │ - tile.unload();
│ │ │ │ - tile.events.un({
│ │ │ │ - "loadstart": tile.onLoadStart,
│ │ │ │ - "loadend": tile.onLoadEnd,
│ │ │ │ - "unload": tile.onLoadEnd,
│ │ │ │ - "loaderror": tile.onLoadError,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ + createMarker: function() {
│ │ │ │ + return null;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: moveGriddedTiles
│ │ │ │ + * 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
│ │ │ │ */
│ │ │ │ - moveGriddedTiles: function() {
│ │ │ │ - var buffer = this.buffer + 1;
│ │ │ │ - while (true) {
│ │ │ │ - var tlTile = this.grid[0][0];
│ │ │ │ - var tlViewPort = {
│ │ │ │ - x: tlTile.position.x +
│ │ │ │ - this.map.layerContainerOriginPx.x,
│ │ │ │ - y: tlTile.position.y +
│ │ │ │ - this.map.layerContainerOriginPx.y
│ │ │ │ - };
│ │ │ │ - var ratio = this.getServerResolution() / this.map.getResolution();
│ │ │ │ - var tileSize = {
│ │ │ │ - w: Math.round(this.tileSize.w * ratio),
│ │ │ │ - h: Math.round(this.tileSize.h * ratio)
│ │ │ │ - };
│ │ │ │ - if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
│ │ │ │ - this.shiftColumn(true, tileSize);
│ │ │ │ - } else if (tlViewPort.x < -tileSize.w * buffer) {
│ │ │ │ - this.shiftColumn(false, tileSize);
│ │ │ │ - } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
│ │ │ │ - this.shiftRow(true, tileSize);
│ │ │ │ - } else if (tlViewPort.y < -tileSize.h * buffer) {
│ │ │ │ - this.shiftRow(false, tileSize);
│ │ │ │ - } else {
│ │ │ │ - break;
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ + destroyMarker: function() {
│ │ │ │ + // pass
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: shiftRow
│ │ │ │ - * Shifty grid work
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * prepend - {Boolean} if true, prepend to beginning.
│ │ │ │ - * if false, then append to end
│ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties
│ │ │ │ + * Method: createPopup
│ │ │ │ + * HACK - we need to decide if all vector features should be able to
│ │ │ │ + * create popups
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {} For now just returns null
│ │ │ │ */
│ │ │ │ - shiftRow: function(prepend, tileSize) {
│ │ │ │ - var grid = this.grid;
│ │ │ │ - var rowIndex = prepend ? 0 : (grid.length - 1);
│ │ │ │ - var sign = prepend ? -1 : 1;
│ │ │ │ - var rowSign = this.rowSign;
│ │ │ │ - var tileLayout = this.gridLayout;
│ │ │ │ - tileLayout.startrow += sign * rowSign;
│ │ │ │ + createPopup: function() {
│ │ │ │ + return null;
│ │ │ │ + },
│ │ │ │
│ │ │ │ - var modelRow = grid[rowIndex];
│ │ │ │ - var row = grid[prepend ? 'pop' : 'shift']();
│ │ │ │ - for (var i = 0, len = row.length; i < len; i++) {
│ │ │ │ - var tile = row[i];
│ │ │ │ - var position = modelRow[i].position.clone();
│ │ │ │ - position.y += tileSize.h * sign;
│ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);
│ │ │ │ + /**
│ │ │ │ + * Method: atPoint
│ │ │ │ + * Determins whether the feature intersects with the specified location.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * lonlat - {|Object} OpenLayers.LonLat or an
│ │ │ │ + * object with a 'lon' and 'lat' properties.
│ │ │ │ + * 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);
│ │ │ │ }
│ │ │ │ - grid[prepend ? 'unshift' : 'push'](row);
│ │ │ │ + return atPoint;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: shiftColumn
│ │ │ │ - * Shift grid work in the other dimension
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * prepend - {Boolean} if true, prepend to beginning.
│ │ │ │ - * if false, then append to end
│ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties
│ │ │ │ + * Method: destroyPopup
│ │ │ │ + * HACK - we need to decide if all vector features should be able to
│ │ │ │ + * delete popups
│ │ │ │ */
│ │ │ │ - shiftColumn: function(prepend, tileSize) {
│ │ │ │ - var grid = this.grid;
│ │ │ │ - var colIndex = prepend ? 0 : (grid[0].length - 1);
│ │ │ │ - var sign = prepend ? -1 : 1;
│ │ │ │ - var tileLayout = this.gridLayout;
│ │ │ │ - tileLayout.startcol += sign;
│ │ │ │ -
│ │ │ │ - for (var i = 0, len = grid.length; i < len; i++) {
│ │ │ │ - var row = grid[i];
│ │ │ │ - var position = row[colIndex].position.clone();
│ │ │ │ - var tile = row[prepend ? 'pop' : 'shift']();
│ │ │ │ - position.x += tileSize.w * sign;
│ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);
│ │ │ │ - row[prepend ? 'unshift' : 'push'](tile);
│ │ │ │ - }
│ │ │ │ + destroyPopup: function() {
│ │ │ │ + // pass
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: removeExcessTiles
│ │ │ │ - * When the size of the map or the buffer changes, we may need to
│ │ │ │ - * remove some excess rows and columns.
│ │ │ │ - *
│ │ │ │ + * Method: move
│ │ │ │ + * Moves the feature and redraws it at its new location
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * rows - {Integer} Maximum number of rows we want our grid to have.
│ │ │ │ - * columns - {Integer} Maximum number of columns we want our grid to have.
│ │ │ │ + * location - { or } the
│ │ │ │ + * location to which to move the feature.
│ │ │ │ */
│ │ │ │ - removeExcessTiles: function(rows, columns) {
│ │ │ │ - var i, l;
│ │ │ │ + move: function(location) {
│ │ │ │
│ │ │ │ - // remove extra rows
│ │ │ │ - while (this.grid.length > rows) {
│ │ │ │ - var row = this.grid.pop();
│ │ │ │ - for (i = 0, l = row.length; i < l; i++) {
│ │ │ │ - var tile = row[i];
│ │ │ │ - this.destroyTile(tile);
│ │ │ │ - }
│ │ │ │ + if (!this.layer || !this.geometry.move) {
│ │ │ │ + //do nothing if no layer or immoveable geometry
│ │ │ │ + return undefined;
│ │ │ │ }
│ │ │ │
│ │ │ │ - // remove extra columns
│ │ │ │ - for (i = 0, l = this.grid.length; i < l; i++) {
│ │ │ │ - while (this.grid[i].length > columns) {
│ │ │ │ - var row = this.grid[i];
│ │ │ │ - var tile = row.pop();
│ │ │ │ - this.destroyTile(tile);
│ │ │ │ - }
│ │ │ │ + var pixel;
│ │ │ │ + if (location.CLASS_NAME == "OpenLayers.LonLat") {
│ │ │ │ + pixel = this.layer.getViewPortPxFromLonLat(location);
│ │ │ │ + } else {
│ │ │ │ + pixel = location;
│ │ │ │ }
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: onMapResize
│ │ │ │ - * For singleTile layers, this will set a new tile size according to the
│ │ │ │ - * dimensions of the map pane.
│ │ │ │ - */
│ │ │ │ - onMapResize: function() {
│ │ │ │ - if (this.singleTile) {
│ │ │ │ - this.clearGrid();
│ │ │ │ - this.setTileSize();
│ │ │ │ - }
│ │ │ │ + var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
│ │ │ │ + var res = this.layer.map.getResolution();
│ │ │ │ + this.geometry.move(res * (pixel.x - lastPixel.x),
│ │ │ │ + res * (lastPixel.y - pixel.y));
│ │ │ │ + this.layer.drawFeature(this);
│ │ │ │ + return lastPixel;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIMethod: getTileBounds
│ │ │ │ - * Returns The tile bounds for a layer given a pixel location.
│ │ │ │ + * Method: toState
│ │ │ │ + * Sets the new state
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * viewPortPx - {} The location in the viewport.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {} Bounds of the tile at the given pixel location.
│ │ │ │ + * state - {String}
│ │ │ │ */
│ │ │ │ - getTileBounds: function(viewPortPx) {
│ │ │ │ - var maxExtent = this.maxExtent;
│ │ │ │ - var resolution = this.getResolution();
│ │ │ │ - var tileMapWidth = resolution * this.tileSize.w;
│ │ │ │ - var tileMapHeight = resolution * this.tileSize.h;
│ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
│ │ │ │ - var tileLeft = maxExtent.left + (tileMapWidth *
│ │ │ │ - Math.floor((mapPoint.lon -
│ │ │ │ - maxExtent.left) /
│ │ │ │ - tileMapWidth));
│ │ │ │ - var tileBottom = maxExtent.bottom + (tileMapHeight *
│ │ │ │ - Math.floor((mapPoint.lat -
│ │ │ │ - maxExtent.bottom) /
│ │ │ │ - tileMapHeight));
│ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom,
│ │ │ │ - tileLeft + tileMapWidth,
│ │ │ │ - tileBottom + tileMapHeight);
│ │ │ │ + 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.Layer.Grid"
│ │ │ │ + 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. These
│ │ │ │ + * styles correspond for the most part, to the styling properties defined
│ │ │ │ + * by the SVG standard.
│ │ │ │ + * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
│ │ │ │ + * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
│ │ │ │ + *
│ │ │ │ + * Symbolizer properties:
│ │ │ │ + * fill - {Boolean} Set to false if no fill is desired.
│ │ │ │ + * fillColor - {String} Hex fill color. Default is "#ee9900".
│ │ │ │ + * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4
│ │ │ │ + * stroke - {Boolean} Set to false if no stroke is desired.
│ │ │ │ + * strokeColor - {String} Hex stroke color. Default is "#ee9900".
│ │ │ │ + * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.
│ │ │ │ + * strokeWidth - {Number} Pixel stroke width. Default is 1.
│ │ │ │ + * strokeLinecap - {String} Stroke cap type. Default is "round". [butt | round | square]
│ │ │ │ + * strokeDashstyle - {String} Stroke dash style. Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
│ │ │ │ + * graphic - {Boolean} Set to false if no graphic is desired.
│ │ │ │ + * pointRadius - {Number} Pixel point radius. Default is 6.
│ │ │ │ + * pointerEvents - {String} Default is "visiblePainted".
│ │ │ │ + * cursor - {String} Default is "".
│ │ │ │ + * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
│ │ │ │ + * graphicWidth - {Number} Pixel width for sizing an external graphic.
│ │ │ │ + * graphicHeight - {Number} Pixel height for sizing an external graphic.
│ │ │ │ + * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
│ │ │ │ + * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
│ │ │ │ + * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
│ │ │ │ + * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
│ │ │ │ + * graphicZIndex - {Number} The integer z-index value to use in rendering.
│ │ │ │ + * graphicName - {String} Named graphic to use when rendering points. Supported values include "circle" (default),
│ │ │ │ + * "square", "star", "x", "cross", "triangle".
│ │ │ │ + * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
│ │ │ │ + * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
│ │ │ │ + * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
│ │ │ │ + * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
│ │ │ │ + * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
│ │ │ │ + * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
│ │ │ │ + * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.
│ │ │ │ + * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.
│ │ │ │ + * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
│ │ │ │ + * fillText or mozDrawText to be available.
│ │ │ │ + * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
│ │ │ │ + * composed of two characters. The first character is for the horizontal alignment, the second for the vertical
│ │ │ │ + * alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
│ │ │ │ + * alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
│ │ │ │ + * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
│ │ │ │ + * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
│ │ │ │ + * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
│ │ │ │ + * Default is false.
│ │ │ │ + * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
│ │ │ │ + * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.
│ │ │ │ + * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
│ │ │ │ + * fontColor - {String} The font color for the label, to be provided like CSS.
│ │ │ │ + * fontOpacity - {Number} Opacity (0-1) for the label
│ │ │ │ + * fontFamily - {String} The font family for the label, to be provided like in CSS.
│ │ │ │ + * fontSize - {String} The font size for the label, to be provided like in CSS.
│ │ │ │ + * fontStyle - {String} The font style for the label, to be provided like in CSS.
│ │ │ │ + * fontWeight - {String} The font weight for the label, to be provided like in CSS.
│ │ │ │ + * display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
│ │ │ │ + */
│ │ │ │ +OpenLayers.Feature.Vector.style = {
│ │ │ │ + 'default': {
│ │ │ │ + fillColor: "#ee9900",
│ │ │ │ + fillOpacity: 0.4,
│ │ │ │ + hoverFillColor: "white",
│ │ │ │ + hoverFillOpacity: 0.8,
│ │ │ │ + strokeColor: "#ee9900",
│ │ │ │ + strokeOpacity: 1,
│ │ │ │ + strokeWidth: 1,
│ │ │ │ + strokeLinecap: "round",
│ │ │ │ + strokeDashstyle: "solid",
│ │ │ │ + hoverStrokeColor: "red",
│ │ │ │ + hoverStrokeOpacity: 1,
│ │ │ │ + hoverStrokeWidth: 0.2,
│ │ │ │ + pointRadius: 6,
│ │ │ │ + hoverPointRadius: 1,
│ │ │ │ + hoverPointUnit: "%",
│ │ │ │ + pointerEvents: "visiblePainted",
│ │ │ │ + cursor: "inherit",
│ │ │ │ + fontColor: "#000000",
│ │ │ │ + labelAlign: "cm",
│ │ │ │ + labelOutlineColor: "white",
│ │ │ │ + labelOutlineWidth: 3
│ │ │ │ + },
│ │ │ │ + 'select': {
│ │ │ │ + fillColor: "blue",
│ │ │ │ + fillOpacity: 0.4,
│ │ │ │ + hoverFillColor: "white",
│ │ │ │ + hoverFillOpacity: 0.8,
│ │ │ │ + strokeColor: "blue",
│ │ │ │ + strokeOpacity: 1,
│ │ │ │ + strokeWidth: 2,
│ │ │ │ + strokeLinecap: "round",
│ │ │ │ + strokeDashstyle: "solid",
│ │ │ │ + hoverStrokeColor: "red",
│ │ │ │ + hoverStrokeOpacity: 1,
│ │ │ │ + hoverStrokeWidth: 0.2,
│ │ │ │ + pointRadius: 6,
│ │ │ │ + hoverPointRadius: 1,
│ │ │ │ + hoverPointUnit: "%",
│ │ │ │ + pointerEvents: "visiblePainted",
│ │ │ │ + cursor: "pointer",
│ │ │ │ + fontColor: "#000000",
│ │ │ │ + labelAlign: "cm",
│ │ │ │ + labelOutlineColor: "white",
│ │ │ │ + labelOutlineWidth: 3
│ │ │ │ +
│ │ │ │ + },
│ │ │ │ + 'temporary': {
│ │ │ │ + fillColor: "#66cccc",
│ │ │ │ + fillOpacity: 0.2,
│ │ │ │ + hoverFillColor: "white",
│ │ │ │ + hoverFillOpacity: 0.8,
│ │ │ │ + strokeColor: "#66cccc",
│ │ │ │ + strokeOpacity: 1,
│ │ │ │ + strokeLinecap: "round",
│ │ │ │ + strokeWidth: 2,
│ │ │ │ + strokeDashstyle: "solid",
│ │ │ │ + hoverStrokeColor: "red",
│ │ │ │ + hoverStrokeOpacity: 1,
│ │ │ │ + hoverStrokeWidth: 0.2,
│ │ │ │ + pointRadius: 6,
│ │ │ │ + hoverPointRadius: 1,
│ │ │ │ + hoverPointUnit: "%",
│ │ │ │ + pointerEvents: "visiblePainted",
│ │ │ │ + cursor: "inherit",
│ │ │ │ + fontColor: "#000000",
│ │ │ │ + labelAlign: "cm",
│ │ │ │ + labelOutlineColor: "white",
│ │ │ │ + labelOutlineWidth: 3
│ │ │ │ +
│ │ │ │ + },
│ │ │ │ + 'delete': {
│ │ │ │ + display: "none"
│ │ │ │ + }
│ │ │ │ +};
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/TileManager.js
│ │ │ │ + OpenLayers/Style.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │
│ │ │ │ /**
│ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ * @requires OpenLayers/Util.js
│ │ │ │ - * @requires OpenLayers/BaseTypes.js
│ │ │ │ - * @requires OpenLayers/BaseTypes/Element.js
│ │ │ │ - * @requires OpenLayers/Layer/Grid.js
│ │ │ │ - * @requires OpenLayers/Tile/Image.js
│ │ │ │ + * @requires OpenLayers/Feature/Vector.js
│ │ │ │ */
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Class: OpenLayers.TileManager
│ │ │ │ - * Provides queueing of image requests and caching of image elements.
│ │ │ │ - *
│ │ │ │ - * Queueing avoids unnecessary image requests while changing zoom levels
│ │ │ │ - * quickly, and helps improve dragging performance on mobile devices that show
│ │ │ │ - * a lag in dragging when loading of new images starts. and
│ │ │ │ - * are the configuration options to control this behavior.
│ │ │ │ - *
│ │ │ │ - * Caching avoids setting the src on image elements for images that have already
│ │ │ │ - * been used. Several maps can share a TileManager instance, in which case each
│ │ │ │ - * map gets its own tile queue, but all maps share the same tile cache.
│ │ │ │ + * Class: OpenLayers.Style
│ │ │ │ + * This class represents a UserStyle obtained
│ │ │ │ + * from a SLD, containing styling rules.
│ │ │ │ */
│ │ │ │ -OpenLayers.TileManager = OpenLayers.Class({
│ │ │ │ +OpenLayers.Style = OpenLayers.Class({
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: cacheSize
│ │ │ │ - * {Number} Number of image elements to keep referenced in this instance's
│ │ │ │ - * cache for fast reuse. Default is 256.
│ │ │ │ + * Property: id
│ │ │ │ + * {String} A unique id for this session.
│ │ │ │ */
│ │ │ │ - cacheSize: 256,
│ │ │ │ + id: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: tilesPerFrame
│ │ │ │ - * {Number} Number of queued tiles to load per frame (see ).
│ │ │ │ - * Default is 2.
│ │ │ │ + * APIProperty: name
│ │ │ │ + * {String}
│ │ │ │ */
│ │ │ │ - tilesPerFrame: 2,
│ │ │ │ + name: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: frameDelay
│ │ │ │ - * {Number} Delay between tile loading frames (see ) in
│ │ │ │ - * milliseconds. Default is 16.
│ │ │ │ + * Property: title
│ │ │ │ + * {String} Title of this style (set if included in SLD)
│ │ │ │ */
│ │ │ │ - frameDelay: 16,
│ │ │ │ + title: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: moveDelay
│ │ │ │ - * {Number} Delay in milliseconds after a map's move event before loading
│ │ │ │ - * tiles. Default is 100.
│ │ │ │ + * Property: description
│ │ │ │ + * {String} Description of this style (set if abstract is included in SLD)
│ │ │ │ */
│ │ │ │ - moveDelay: 100,
│ │ │ │ + description: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: zoomDelay
│ │ │ │ - * {Number} Delay in milliseconds after a map's zoomend event before loading
│ │ │ │ - * tiles. Default is 200.
│ │ │ │ + * APIProperty: layerName
│ │ │ │ + * {} name of the layer that this style belongs to, usually
│ │ │ │ + * according to the NamedLayer attribute of an SLD document.
│ │ │ │ */
│ │ │ │ - zoomDelay: 200,
│ │ │ │ + layerName: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: maps
│ │ │ │ - * {Array()} The maps to manage tiles on.
│ │ │ │ + * APIProperty: isDefault
│ │ │ │ + * {Boolean}
│ │ │ │ */
│ │ │ │ - maps: null,
│ │ │ │ + isDefault: false,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: rules
│ │ │ │ + * {Array()}
│ │ │ │ + */
│ │ │ │ + rules: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: tileQueueId
│ │ │ │ - * {Object} The ids of the loop, keyed by map id.
│ │ │ │ + * APIProperty: context
│ │ │ │ + * {Object} An optional object with properties that symbolizers' property
│ │ │ │ + * values should be evaluated against. If no context is specified,
│ │ │ │ + * feature.attributes will be used
│ │ │ │ */
│ │ │ │ - tileQueueId: null,
│ │ │ │ + context: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: tileQueue
│ │ │ │ - * {Object(Array())} Tiles queued for drawing, keyed by
│ │ │ │ - * map id.
│ │ │ │ + * Property: defaultStyle
│ │ │ │ + * {Object} hash of style properties to use as default for merging
│ │ │ │ + * rule-based style symbolizers onto. If no rules are defined,
│ │ │ │ + * createSymbolizer will return this style. If is set to
│ │ │ │ + * true, the defaultStyle will only be taken into account if there are
│ │ │ │ + * rules defined.
│ │ │ │ */
│ │ │ │ - tileQueue: null,
│ │ │ │ + defaultStyle: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: tileCache
│ │ │ │ - * {Object} Cached image elements, keyed by URL.
│ │ │ │ + * Property: defaultsPerSymbolizer
│ │ │ │ + * {Boolean} If set to true, the will extend the symbolizer
│ │ │ │ + * of every rule. Properties of the will also be used to set
│ │ │ │ + * missing symbolizer properties if the symbolizer has stroke, fill or
│ │ │ │ + * graphic set to true. Default is false.
│ │ │ │ */
│ │ │ │ - tileCache: null,
│ │ │ │ + defaultsPerSymbolizer: false,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: tileCacheIndex
│ │ │ │ - * {Array(String)} URLs of cached tiles. First entry is the least recently
│ │ │ │ - * used.
│ │ │ │ + * Property: propertyStyles
│ │ │ │ + * {Hash of Boolean} cache of style properties that need to be parsed for
│ │ │ │ + * propertyNames. Property names are keys, values won't be used.
│ │ │ │ */
│ │ │ │ - tileCacheIndex: null,
│ │ │ │ + propertyStyles: null,
│ │ │ │ +
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Constructor: OpenLayers.TileManager
│ │ │ │ - * Constructor for a new instance.
│ │ │ │ - *
│ │ │ │ + * Constructor: OpenLayers.Style
│ │ │ │ + * Creates a UserStyle.
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * options - {Object} Configuration for this instance.
│ │ │ │ + * style - {Object} Optional hash of style properties that will be
│ │ │ │ + * used as default style for this style object. This style
│ │ │ │ + * applies if no rules are specified. Symbolizers defined in
│ │ │ │ + * rules will extend this default style.
│ │ │ │ + * options - {Object} An optional object with properties to set on the
│ │ │ │ + * style.
│ │ │ │ + *
│ │ │ │ + * Valid options:
│ │ │ │ + * rules - {Array()} List of rules to be added to the
│ │ │ │ + * style.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ - initialize: function(options) {
│ │ │ │ + initialize: function(style, options) {
│ │ │ │ +
│ │ │ │ OpenLayers.Util.extend(this, options);
│ │ │ │ - this.maps = [];
│ │ │ │ - this.tileQueueId = {};
│ │ │ │ - this.tileQueue = {};
│ │ │ │ - this.tileCache = {};
│ │ │ │ - this.tileCacheIndex = [];
│ │ │ │ + this.rules = [];
│ │ │ │ + if (options && options.rules) {
│ │ │ │ + this.addRules(options.rules);
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + // use the default style from OpenLayers.Feature.Vector if no style
│ │ │ │ + // was given in the constructor
│ │ │ │ + this.setDefaultStyle(style ||
│ │ │ │ + OpenLayers.Feature.Vector.style["default"]);
│ │ │ │ +
│ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: addMap
│ │ │ │ - * Binds this instance to a map
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * map - {}
│ │ │ │ + /**
│ │ │ │ + * APIMethod: destroy
│ │ │ │ + * nullify references to prevent circular references and memory leaks
│ │ │ │ */
│ │ │ │ - addMap: function(map) {
│ │ │ │ - if (this._destroyed || !OpenLayers.Layer.Grid) {
│ │ │ │ - return;
│ │ │ │ - }
│ │ │ │ - this.maps.push(map);
│ │ │ │ - this.tileQueue[map.id] = [];
│ │ │ │ - for (var i = 0, ii = map.layers.length; i < ii; ++i) {
│ │ │ │ - this.addLayer({
│ │ │ │ - layer: map.layers[i]
│ │ │ │ - });
│ │ │ │ + destroy: function() {
│ │ │ │ + for (var i = 0, len = this.rules.length; i < len; i++) {
│ │ │ │ + this.rules[i].destroy();
│ │ │ │ + this.rules[i] = null;
│ │ │ │ }
│ │ │ │ - map.events.on({
│ │ │ │ - move: this.move,
│ │ │ │ - zoomend: this.zoomEnd,
│ │ │ │ - changelayer: this.changeLayer,
│ │ │ │ - addlayer: this.addLayer,
│ │ │ │ - preremovelayer: this.removeLayer,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ + this.rules = null;
│ │ │ │ + this.defaultStyle = null;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: removeMap
│ │ │ │ - * Unbinds this instance from a map
│ │ │ │ - *
│ │ │ │ + * Method: createSymbolizer
│ │ │ │ + * creates a style by applying all feature-dependent rules to the base
│ │ │ │ + * style.
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * map - {}
│ │ │ │ + * feature - {} feature to evaluate rules for
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} symbolizer hash
│ │ │ │ */
│ │ │ │ - removeMap: function(map) {
│ │ │ │ - if (this._destroyed || !OpenLayers.Layer.Grid) {
│ │ │ │ - return;
│ │ │ │ + createSymbolizer: function(feature) {
│ │ │ │ + var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
│ │ │ │ + OpenLayers.Util.extend({}, this.defaultStyle), feature);
│ │ │ │ +
│ │ │ │ + var rules = this.rules;
│ │ │ │ +
│ │ │ │ + var rule, context;
│ │ │ │ + var elseRules = [];
│ │ │ │ + var appliedRules = false;
│ │ │ │ + for (var i = 0, len = rules.length; i < len; i++) {
│ │ │ │ + rule = rules[i];
│ │ │ │ + // does the rule apply?
│ │ │ │ + var applies = rule.evaluate(feature);
│ │ │ │ +
│ │ │ │ + if (applies) {
│ │ │ │ + if (rule instanceof OpenLayers.Rule && rule.elseFilter) {
│ │ │ │ + elseRules.push(rule);
│ │ │ │ + } else {
│ │ │ │ + appliedRules = true;
│ │ │ │ + this.applySymbolizer(rule, style, feature);
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ }
│ │ │ │ - window.clearTimeout(this.tileQueueId[map.id]);
│ │ │ │ - if (map.layers) {
│ │ │ │ - for (var i = 0, ii = map.layers.length; i < ii; ++i) {
│ │ │ │ - this.removeLayer({
│ │ │ │ - layer: map.layers[i]
│ │ │ │ - });
│ │ │ │ +
│ │ │ │ + // if no other rules apply, apply the rules with else filters
│ │ │ │ + if (appliedRules == false && elseRules.length > 0) {
│ │ │ │ + appliedRules = true;
│ │ │ │ + for (var i = 0, len = elseRules.length; i < len; i++) {
│ │ │ │ + this.applySymbolizer(elseRules[i], style, feature);
│ │ │ │ }
│ │ │ │ }
│ │ │ │ - if (map.events) {
│ │ │ │ - map.events.un({
│ │ │ │ - move: this.move,
│ │ │ │ - zoomend: this.zoomEnd,
│ │ │ │ - changelayer: this.changeLayer,
│ │ │ │ - addlayer: this.addLayer,
│ │ │ │ - preremovelayer: this.removeLayer,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ +
│ │ │ │ + // don't display if there were rules but none applied
│ │ │ │ + if (rules.length > 0 && appliedRules == false) {
│ │ │ │ + style.display = "none";
│ │ │ │ }
│ │ │ │ - delete this.tileQueue[map.id];
│ │ │ │ - delete this.tileQueueId[map.id];
│ │ │ │ - OpenLayers.Util.removeItem(this.maps, map);
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: move
│ │ │ │ - * Handles the map's move event
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} Listener argument
│ │ │ │ - */
│ │ │ │ - move: function(evt) {
│ │ │ │ - this.updateTimeout(evt.object, this.moveDelay, true);
│ │ │ │ - },
│ │ │ │ + if (style.label != null && typeof style.label !== "string") {
│ │ │ │ + style.label = String(style.label);
│ │ │ │ + }
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: zoomEnd
│ │ │ │ - * Handles the map's zoomEnd event
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} Listener argument
│ │ │ │ - */
│ │ │ │ - zoomEnd: function(evt) {
│ │ │ │ - this.updateTimeout(evt.object, this.zoomDelay);
│ │ │ │ + return style;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: changeLayer
│ │ │ │ - * Handles the map's changeLayer event
│ │ │ │ + * Method: applySymbolizer
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * evt - {Object} Listener argument
│ │ │ │ - */
│ │ │ │ - changeLayer: function(evt) {
│ │ │ │ - if (evt.property === 'visibility' || evt.property === 'params') {
│ │ │ │ - this.updateTimeout(evt.object, 0);
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ -
│ │ │ │ - /**
│ │ │ │ - * Method: addLayer
│ │ │ │ - * Handles the map's addlayer event
│ │ │ │ + * rule - {}
│ │ │ │ + * style - {Object}
│ │ │ │ + * feature - {}
│ │ │ │ *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} The listener argument
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} A style with new symbolizer applied.
│ │ │ │ */
│ │ │ │ - addLayer: function(evt) {
│ │ │ │ - var layer = evt.layer;
│ │ │ │ - if (layer instanceof OpenLayers.Layer.Grid) {
│ │ │ │ - layer.events.on({
│ │ │ │ - addtile: this.addTile,
│ │ │ │ - retile: this.clearTileQueue,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ - var i, j, tile;
│ │ │ │ - for (i = layer.grid.length - 1; i >= 0; --i) {
│ │ │ │ - for (j = layer.grid[i].length - 1; j >= 0; --j) {
│ │ │ │ - tile = layer.grid[i][j];
│ │ │ │ - this.addTile({
│ │ │ │ - tile: tile
│ │ │ │ - });
│ │ │ │ - if (tile.url && !tile.imgDiv) {
│ │ │ │ - this.manageTileCache({
│ │ │ │ - object: tile
│ │ │ │ - });
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ - },
│ │ │ │ + applySymbolizer: function(rule, style, feature) {
│ │ │ │ + var symbolizerPrefix = feature.geometry ?
│ │ │ │ + this.getSymbolizerPrefix(feature.geometry) :
│ │ │ │ + OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: removeLayer
│ │ │ │ - * Handles the map's preremovelayer event
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} The listener argument
│ │ │ │ - */
│ │ │ │ - removeLayer: function(evt) {
│ │ │ │ - var layer = evt.layer;
│ │ │ │ - if (layer instanceof OpenLayers.Layer.Grid) {
│ │ │ │ - this.clearTileQueue({
│ │ │ │ - object: layer
│ │ │ │ + var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
│ │ │ │ +
│ │ │ │ + if (this.defaultsPerSymbolizer === true) {
│ │ │ │ + var defaults = this.defaultStyle;
│ │ │ │ + OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ + pointRadius: defaults.pointRadius
│ │ │ │ });
│ │ │ │ - if (layer.events) {
│ │ │ │ - layer.events.un({
│ │ │ │ - addtile: this.addTile,
│ │ │ │ - retile: this.clearTileQueue,
│ │ │ │ - scope: this
│ │ │ │ + if (symbolizer.stroke === true || symbolizer.graphic === true) {
│ │ │ │ + OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ + strokeWidth: defaults.strokeWidth,
│ │ │ │ + strokeColor: defaults.strokeColor,
│ │ │ │ + strokeOpacity: defaults.strokeOpacity,
│ │ │ │ + strokeDashstyle: defaults.strokeDashstyle,
│ │ │ │ + strokeLinecap: defaults.strokeLinecap
│ │ │ │ });
│ │ │ │ }
│ │ │ │ - if (layer.grid) {
│ │ │ │ - var i, j, tile;
│ │ │ │ - for (i = layer.grid.length - 1; i >= 0; --i) {
│ │ │ │ - for (j = layer.grid[i].length - 1; j >= 0; --j) {
│ │ │ │ - tile = layer.grid[i][j];
│ │ │ │ - this.unloadTile({
│ │ │ │ - object: tile
│ │ │ │ - });
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ + if (symbolizer.fill === true || symbolizer.graphic === true) {
│ │ │ │ + OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ + fillColor: defaults.fillColor,
│ │ │ │ + fillOpacity: defaults.fillOpacity
│ │ │ │ + });
│ │ │ │ + }
│ │ │ │ + if (symbolizer.graphic === true) {
│ │ │ │ + OpenLayers.Util.applyDefaults(symbolizer, {
│ │ │ │ + pointRadius: this.defaultStyle.pointRadius,
│ │ │ │ + externalGraphic: this.defaultStyle.externalGraphic,
│ │ │ │ + graphicName: this.defaultStyle.graphicName,
│ │ │ │ + graphicOpacity: this.defaultStyle.graphicOpacity,
│ │ │ │ + graphicWidth: this.defaultStyle.graphicWidth,
│ │ │ │ + graphicHeight: this.defaultStyle.graphicHeight,
│ │ │ │ + graphicXOffset: this.defaultStyle.graphicXOffset,
│ │ │ │ + graphicYOffset: this.defaultStyle.graphicYOffset
│ │ │ │ + });
│ │ │ │ }
│ │ │ │ }
│ │ │ │ - },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: updateTimeout
│ │ │ │ - * Applies the or to the loop,
│ │ │ │ - * and schedules more queue processing after if there are still
│ │ │ │ - * tiles in the queue.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * map - {} The map to update the timeout for
│ │ │ │ - * delay - {Number} The delay to apply
│ │ │ │ - * nice - {Boolean} If true, the timeout function will only be created if
│ │ │ │ - * the tilequeue is not empty. This is used by the move handler to
│ │ │ │ - * avoid impacts on dragging performance. For other events, the tile
│ │ │ │ - * queue may not be populated yet, so we need to set the timer
│ │ │ │ - * regardless of the queue size.
│ │ │ │ - */
│ │ │ │ - updateTimeout: function(map, delay, nice) {
│ │ │ │ - window.clearTimeout(this.tileQueueId[map.id]);
│ │ │ │ - var tileQueue = this.tileQueue[map.id];
│ │ │ │ - if (!nice || tileQueue.length) {
│ │ │ │ - this.tileQueueId[map.id] = window.setTimeout(
│ │ │ │ - OpenLayers.Function.bind(function() {
│ │ │ │ - this.drawTilesFromQueue(map);
│ │ │ │ - if (tileQueue.length) {
│ │ │ │ - this.updateTimeout(map, this.frameDelay);
│ │ │ │ - }
│ │ │ │ - }, this), delay
│ │ │ │ - );
│ │ │ │ - }
│ │ │ │ + // merge the style with the current style
│ │ │ │ + return this.createLiterals(
│ │ │ │ + OpenLayers.Util.extend(style, symbolizer), feature);
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: addTile
│ │ │ │ - * Listener for the layer's addtile event
│ │ │ │ - *
│ │ │ │ + * Method: createLiterals
│ │ │ │ + * creates literals for all style properties that have an entry in
│ │ │ │ + * .
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * evt - {Object} The listener argument
│ │ │ │ + * style - {Object} style to create literals for. Will be modified
│ │ │ │ + * inline.
│ │ │ │ + * feature - {Object}
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} the modified style
│ │ │ │ */
│ │ │ │ - addTile: function(evt) {
│ │ │ │ - if (evt.tile instanceof OpenLayers.Tile.Image) {
│ │ │ │ - evt.tile.events.on({
│ │ │ │ - beforedraw: this.queueTileDraw,
│ │ │ │ - beforeload: this.manageTileCache,
│ │ │ │ - loadend: this.addToCache,
│ │ │ │ - unload: this.unloadTile,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ - } else {
│ │ │ │ - // Layer has the wrong tile type, so don't handle it any longer
│ │ │ │ - this.removeLayer({
│ │ │ │ - layer: evt.tile.layer
│ │ │ │ - });
│ │ │ │ + createLiterals: function(style, feature) {
│ │ │ │ + var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
│ │ │ │ + OpenLayers.Util.extend(context, this.context);
│ │ │ │ +
│ │ │ │ + for (var i in this.propertyStyles) {
│ │ │ │ + style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
│ │ │ │ }
│ │ │ │ + return style;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: unloadTile
│ │ │ │ - * Listener for the tile's unload event
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} The listener argument
│ │ │ │ + * Method: findPropertyStyles
│ │ │ │ + * Looks into all rules for this style and the defaultStyle to collect
│ │ │ │ + * all the style hash property names containing ${...} strings that have
│ │ │ │ + * to be replaced using the createLiteral method before returning them.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} hash of property names that need createLiteral parsing. The
│ │ │ │ + * name of the property is the key, and the value is true;
│ │ │ │ */
│ │ │ │ - unloadTile: function(evt) {
│ │ │ │ - var tile = evt.object;
│ │ │ │ - tile.events.un({
│ │ │ │ - beforedraw: this.queueTileDraw,
│ │ │ │ - beforeload: this.manageTileCache,
│ │ │ │ - loadend: this.addToCache,
│ │ │ │ - unload: this.unloadTile,
│ │ │ │ - scope: this
│ │ │ │ - });
│ │ │ │ - OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);
│ │ │ │ + findPropertyStyles: function() {
│ │ │ │ + var propertyStyles = {};
│ │ │ │ +
│ │ │ │ + // check the default style
│ │ │ │ + var style = this.defaultStyle;
│ │ │ │ + this.addPropertyStyles(propertyStyles, style);
│ │ │ │ +
│ │ │ │ + // walk through all rules to check for properties in their symbolizer
│ │ │ │ + var rules = this.rules;
│ │ │ │ + var symbolizer, value;
│ │ │ │ + for (var i = 0, len = rules.length; i < len; i++) {
│ │ │ │ + symbolizer = rules[i].symbolizer;
│ │ │ │ + for (var key in symbolizer) {
│ │ │ │ + value = symbolizer[key];
│ │ │ │ + if (typeof value == "object") {
│ │ │ │ + // symbolizer key is "Point", "Line" or "Polygon"
│ │ │ │ + this.addPropertyStyles(propertyStyles, value);
│ │ │ │ + } else {
│ │ │ │ + // symbolizer is a hash of style properties
│ │ │ │ + this.addPropertyStyles(propertyStyles, symbolizer);
│ │ │ │ + break;
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ + return propertyStyles;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: queueTileDraw
│ │ │ │ - * Adds a tile to the queue that will draw it.
│ │ │ │ - *
│ │ │ │ + * Method: addPropertyStyles
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * evt - {Object} Listener argument of the tile's beforedraw event
│ │ │ │ + * propertyStyles - {Object} hash to add new property styles to. Will be
│ │ │ │ + * modified inline
│ │ │ │ + * symbolizer - {Object} search this symbolizer for property styles
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Object} propertyStyles hash
│ │ │ │ */
│ │ │ │ - queueTileDraw: function(evt) {
│ │ │ │ - var tile = evt.object;
│ │ │ │ - var queued = false;
│ │ │ │ - var layer = tile.layer;
│ │ │ │ - var url = layer.getURL(tile.bounds);
│ │ │ │ - var img = this.tileCache[url];
│ │ │ │ - if (img && img.className !== 'olTileImage') {
│ │ │ │ - // cached image no longer valid, e.g. because we're olTileReplacing
│ │ │ │ - delete this.tileCache[url];
│ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, url);
│ │ │ │ - img = null;
│ │ │ │ - }
│ │ │ │ - // queue only if image with same url not cached already
│ │ │ │ - if (layer.url && (layer.async || !img)) {
│ │ │ │ - // add to queue only if not in queue already
│ │ │ │ - var tileQueue = this.tileQueue[layer.map.id];
│ │ │ │ - if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {
│ │ │ │ - tileQueue.push(tile);
│ │ │ │ + addPropertyStyles: function(propertyStyles, symbolizer) {
│ │ │ │ + var property;
│ │ │ │ + for (var key in symbolizer) {
│ │ │ │ + property = symbolizer[key];
│ │ │ │ + if (typeof property == "string" &&
│ │ │ │ + property.match(/\$\{\w+\}/)) {
│ │ │ │ + propertyStyles[key] = true;
│ │ │ │ }
│ │ │ │ - queued = true;
│ │ │ │ }
│ │ │ │ - return !queued;
│ │ │ │ + return propertyStyles;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: drawTilesFromQueue
│ │ │ │ - * Draws tiles from the tileQueue, and unqueues the tiles
│ │ │ │ + * APIMethod: addRules
│ │ │ │ + * Adds rules to this style.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * rules - {Array()}
│ │ │ │ */
│ │ │ │ - drawTilesFromQueue: function(map) {
│ │ │ │ - var tileQueue = this.tileQueue[map.id];
│ │ │ │ - var limit = this.tilesPerFrame;
│ │ │ │ - var animating = map.zoomTween && map.zoomTween.playing;
│ │ │ │ - while (!animating && tileQueue.length && limit) {
│ │ │ │ - tileQueue.shift().draw(true);
│ │ │ │ - --limit;
│ │ │ │ - }
│ │ │ │ + addRules: function(rules) {
│ │ │ │ + Array.prototype.push.apply(this.rules, rules);
│ │ │ │ + this.propertyStyles = this.findPropertyStyles();
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: manageTileCache
│ │ │ │ - * Adds, updates, removes and fetches cache entries.
│ │ │ │ - *
│ │ │ │ + * APIMethod: setDefaultStyle
│ │ │ │ + * Sets the default style for this style object.
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * evt - {Object} Listener argument of the tile's beforeload event
│ │ │ │ + * style - {Object} Hash of style properties
│ │ │ │ */
│ │ │ │ - manageTileCache: function(evt) {
│ │ │ │ - var tile = evt.object;
│ │ │ │ - var img = this.tileCache[tile.url];
│ │ │ │ - if (img) {
│ │ │ │ - // if image is on its layer's backbuffer, remove it from backbuffer
│ │ │ │ - if (img.parentNode &&
│ │ │ │ - OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {
│ │ │ │ - img.parentNode.removeChild(img);
│ │ │ │ - img.id = null;
│ │ │ │ - }
│ │ │ │ - // only use image from cache if it is not on a layer already
│ │ │ │ - if (!img.parentNode) {
│ │ │ │ - img.style.visibility = 'hidden';
│ │ │ │ - img.style.opacity = 0;
│ │ │ │ - tile.setImage(img);
│ │ │ │ - // LRU - move tile to the end of the array to mark it as the most
│ │ │ │ - // recently used
│ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);
│ │ │ │ - this.tileCacheIndex.push(tile.url);
│ │ │ │ - }
│ │ │ │ - }
│ │ │ │ + setDefaultStyle: function(style) {
│ │ │ │ + this.defaultStyle = style;
│ │ │ │ + this.propertyStyles = this.findPropertyStyles();
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: addToCache
│ │ │ │ - *
│ │ │ │ + * Method: getSymbolizerPrefix
│ │ │ │ + * Returns the correct symbolizer prefix according to the
│ │ │ │ + * geometry type of the passed geometry
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * evt - {Object} Listener argument for the tile's loadend event
│ │ │ │ + * geometry - {}
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {String} key of the according symbolizer
│ │ │ │ */
│ │ │ │ - addToCache: function(evt) {
│ │ │ │ - var tile = evt.object;
│ │ │ │ - if (!this.tileCache[tile.url]) {
│ │ │ │ - if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {
│ │ │ │ - if (this.tileCacheIndex.length >= this.cacheSize) {
│ │ │ │ - delete this.tileCache[this.tileCacheIndex[0]];
│ │ │ │ - this.tileCacheIndex.shift();
│ │ │ │ - }
│ │ │ │ - this.tileCache[tile.url] = tile.imgDiv;
│ │ │ │ - this.tileCacheIndex.push(tile.url);
│ │ │ │ + getSymbolizerPrefix: function(geometry) {
│ │ │ │ + var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
│ │ │ │ + for (var i = 0, len = prefixes.length; i < len; i++) {
│ │ │ │ + if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
│ │ │ │ + return prefixes[i];
│ │ │ │ }
│ │ │ │ }
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: clearTileQueue
│ │ │ │ - * Clears the tile queue from tiles of a specific layer
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * evt - {Object} Listener argument of the layer's retile event
│ │ │ │ + * APIMethod: clone
│ │ │ │ + * Clones this style.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {} Clone of this style.
│ │ │ │ */
│ │ │ │ - clearTileQueue: function(evt) {
│ │ │ │ - var layer = evt.object;
│ │ │ │ - var tileQueue = this.tileQueue[layer.map.id];
│ │ │ │ - for (var i = tileQueue.length - 1; i >= 0; --i) {
│ │ │ │ - if (tileQueue[i].layer === layer) {
│ │ │ │ - tileQueue.splice(i, 1);
│ │ │ │ + clone: function() {
│ │ │ │ + var options = OpenLayers.Util.extend({}, this);
│ │ │ │ + // clone rules
│ │ │ │ + if (this.rules) {
│ │ │ │ + options.rules = [];
│ │ │ │ + for (var i = 0, len = this.rules.length; i < len; ++i) {
│ │ │ │ + options.rules.push(this.rules[i].clone());
│ │ │ │ }
│ │ │ │ }
│ │ │ │ + // clone context
│ │ │ │ + options.context = this.context && OpenLayers.Util.extend({}, this.context);
│ │ │ │ + //clone default style
│ │ │ │ + var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
│ │ │ │ + return new OpenLayers.Style(defaultStyle, options);
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Method: destroy
│ │ │ │ - */
│ │ │ │ - destroy: function() {
│ │ │ │ - for (var i = this.maps.length - 1; i >= 0; --i) {
│ │ │ │ - this.removeMap(this.maps[i]);
│ │ │ │ - }
│ │ │ │ - this.maps = null;
│ │ │ │ - this.tileQueue = null;
│ │ │ │ - this.tileQueueId = null;
│ │ │ │ - this.tileCache = null;
│ │ │ │ - this.tileCacheIndex = null;
│ │ │ │ - this._destroyed = true;
│ │ │ │ + CLASS_NAME: "OpenLayers.Style"
│ │ │ │ +});
│ │ │ │ +
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Function: createLiteral
│ │ │ │ + * converts a style value holding a combination of PropertyName and Literal
│ │ │ │ + * into a Literal, taking the property values from the passed features.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * value - {String} value to parse. If this string contains a construct like
│ │ │ │ + * "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
│ │ │ │ + * will be replaced by the value of the "bar" attribute of the passed
│ │ │ │ + * feature.
│ │ │ │ + * context - {Object} context to take attribute values from
│ │ │ │ + * feature - {} optional feature to pass to
│ │ │ │ + * for evaluating functions in the
│ │ │ │ + * context.
│ │ │ │ + * property - {String} optional, name of the property for which the literal is
│ │ │ │ + * being created for evaluating functions in the context.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {String} the parsed value. In the example of the value parameter above, the
│ │ │ │ + * result would be "foo valueOfBar", assuming that the passed feature has an
│ │ │ │ + * attribute named "bar" with the value "valueOfBar".
│ │ │ │ + */
│ │ │ │ +OpenLayers.Style.createLiteral = function(value, context, feature, property) {
│ │ │ │ + if (typeof value == "string" && value.indexOf("${") != -1) {
│ │ │ │ + value = OpenLayers.String.format(value, context, [feature, property]);
│ │ │ │ + value = (isNaN(value) || !value) ? value : parseFloat(value);
│ │ │ │ }
│ │ │ │ + return value;
│ │ │ │ +};
│ │ │ │
│ │ │ │ -});
│ │ │ │ +/**
│ │ │ │ + * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
│ │ │ │ + * {Array} prefixes of the sld symbolizers. These are the
│ │ │ │ + * same as the main geometry types
│ │ │ │ + */
│ │ │ │ +OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
│ │ │ │ + 'Raster'
│ │ │ │ +];
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Strategy.js
│ │ │ │ + OpenLayers/Rule.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │ +
│ │ │ │ /**
│ │ │ │ * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ + * @requires OpenLayers/Util.js
│ │ │ │ + * @requires OpenLayers/Style.js
│ │ │ │ */
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Class: OpenLayers.Strategy
│ │ │ │ - * Abstract vector layer strategy class. Not to be instantiated directly. Use
│ │ │ │ - * one of the strategy subclasses instead.
│ │ │ │ + * Class: OpenLayers.Rule
│ │ │ │ + * This class represents an SLD Rule, as being used for rule-based SLD styling.
│ │ │ │ */
│ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({
│ │ │ │ +OpenLayers.Rule = OpenLayers.Class({
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: layer
│ │ │ │ - * {} The layer this strategy belongs to.
│ │ │ │ + * Property: id
│ │ │ │ + * {String} A unique id for this session.
│ │ │ │ */
│ │ │ │ - layer: null,
│ │ │ │ + id: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: options
│ │ │ │ - * {Object} Any options sent to the constructor.
│ │ │ │ + * APIProperty: name
│ │ │ │ + * {String} name of this rule
│ │ │ │ */
│ │ │ │ - options: null,
│ │ │ │ + name: null,
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: active
│ │ │ │ - * {Boolean} The control is active.
│ │ │ │ + /**
│ │ │ │ + * Property: title
│ │ │ │ + * {String} Title of this rule (set if included in SLD)
│ │ │ │ */
│ │ │ │ - active: null,
│ │ │ │ + title: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: autoActivate
│ │ │ │ - * {Boolean} The creator of the strategy can set autoActivate to false
│ │ │ │ - * to fully control when the protocol is activated and deactivated.
│ │ │ │ - * Defaults to true.
│ │ │ │ + * Property: description
│ │ │ │ + * {String} Description of this rule (set if abstract is included in SLD)
│ │ │ │ */
│ │ │ │ - autoActivate: true,
│ │ │ │ + description: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: autoDestroy
│ │ │ │ - * {Boolean} The creator of the strategy can set autoDestroy to false
│ │ │ │ - * to fully control when the strategy is destroyed. Defaults to
│ │ │ │ - * true.
│ │ │ │ + * Property: context
│ │ │ │ + * {Object} An optional object with properties that the rule should be
│ │ │ │ + * evaluated against. If no context is specified, feature.attributes will
│ │ │ │ + * be used.
│ │ │ │ */
│ │ │ │ - autoDestroy: true,
│ │ │ │ + context: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Constructor: OpenLayers.Strategy
│ │ │ │ - * Abstract class for vector strategies. Create instances of a subclass.
│ │ │ │ + * Property: filter
│ │ │ │ + * {} Optional filter for the rule.
│ │ │ │ + */
│ │ │ │ + filter: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: elseFilter
│ │ │ │ + * {Boolean} Determines whether this rule is only to be applied only if
│ │ │ │ + * no other rules match (ElseFilter according to the SLD specification).
│ │ │ │ + * Default is false. For instances of OpenLayers.Rule, if elseFilter is
│ │ │ │ + * false, the rule will always apply. For subclasses, the else property is
│ │ │ │ + * ignored.
│ │ │ │ + */
│ │ │ │ + elseFilter: false,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: symbolizer
│ │ │ │ + * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
│ │ │ │ + * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
│ │ │ │ + * latter if useful if it is required to style e.g. vertices of a line
│ │ │ │ + * with a point symbolizer. Note, however, that this is not implemented
│ │ │ │ + * yet in OpenLayers, but it is the way how symbolizers are defined in
│ │ │ │ + * SLD.
│ │ │ │ + */
│ │ │ │ + symbolizer: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Property: symbolizers
│ │ │ │ + * {Array} Collection of symbolizers associated with this rule. If
│ │ │ │ + * provided at construction, the symbolizers array has precedence
│ │ │ │ + * over the deprecated symbolizer property. Note that multiple
│ │ │ │ + * symbolizers are not currently supported by the vector renderers.
│ │ │ │ + * Rules with multiple symbolizers are currently only useful for
│ │ │ │ + * maintaining elements in an SLD document.
│ │ │ │ + */
│ │ │ │ + symbolizers: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: minScaleDenominator
│ │ │ │ + * {Number} or {String} minimum scale at which to draw the feature.
│ │ │ │ + * In the case of a String, this can be a combination of text and
│ │ │ │ + * propertyNames in the form "literal ${propertyName}"
│ │ │ │ + */
│ │ │ │ + minScaleDenominator: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * APIProperty: maxScaleDenominator
│ │ │ │ + * {Number} or {String} maximum scale at which to draw the feature.
│ │ │ │ + * In the case of a String, this can be a combination of text and
│ │ │ │ + * propertyNames in the form "literal ${propertyName}"
│ │ │ │ + */
│ │ │ │ + maxScaleDenominator: null,
│ │ │ │ +
│ │ │ │ + /**
│ │ │ │ + * Constructor: OpenLayers.Rule
│ │ │ │ + * Creates a Rule.
│ │ │ │ *
│ │ │ │ * Parameters:
│ │ │ │ - * options - {Object} Optional object whose properties will be set on the
│ │ │ │ - * instance.
│ │ │ │ + * options - {Object} An optional object with properties to set on the
│ │ │ │ + * rule
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {}
│ │ │ │ */
│ │ │ │ initialize: function(options) {
│ │ │ │ + this.symbolizer = {};
│ │ │ │ OpenLayers.Util.extend(this, options);
│ │ │ │ - this.options = options;
│ │ │ │ - // set the active property here, so that user cannot override it
│ │ │ │ - this.active = false;
│ │ │ │ + if (this.symbolizers) {
│ │ │ │ + delete this.symbolizer;
│ │ │ │ + }
│ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ + /**
│ │ │ │ * APIMethod: destroy
│ │ │ │ - * Clean up the strategy.
│ │ │ │ + * nullify references to prevent circular references and memory leaks
│ │ │ │ */
│ │ │ │ destroy: function() {
│ │ │ │ - this.deactivate();
│ │ │ │ - this.layer = null;
│ │ │ │ - this.options = null;
│ │ │ │ + for (var i in this.symbolizer) {
│ │ │ │ + this.symbolizer[i] = null;
│ │ │ │ + }
│ │ │ │ + this.symbolizer = null;
│ │ │ │ + delete this.symbolizers;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: setLayer
│ │ │ │ - * Called to set the property.
│ │ │ │ - *
│ │ │ │ + * APIMethod: evaluate
│ │ │ │ + * evaluates this rule for a specific feature
│ │ │ │ + *
│ │ │ │ * Parameters:
│ │ │ │ - * layer - {}
│ │ │ │ + * feature - {} feature to apply the rule to.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * {Boolean} true if the rule applies, false if it does not.
│ │ │ │ + * This rule is the default rule and always returns true.
│ │ │ │ */
│ │ │ │ - setLayer: function(layer) {
│ │ │ │ - this.layer = layer;
│ │ │ │ + evaluate: function(feature) {
│ │ │ │ + var context = this.getContext(feature);
│ │ │ │ + var applies = true;
│ │ │ │ +
│ │ │ │ + if (this.minScaleDenominator || this.maxScaleDenominator) {
│ │ │ │ + var scale = feature.layer.map.getScale();
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + // check if within minScale/maxScale bounds
│ │ │ │ + if (this.minScaleDenominator) {
│ │ │ │ + applies = scale >= OpenLayers.Style.createLiteral(
│ │ │ │ + this.minScaleDenominator, context);
│ │ │ │ + }
│ │ │ │ + if (applies && this.maxScaleDenominator) {
│ │ │ │ + applies = scale < OpenLayers.Style.createLiteral(
│ │ │ │ + this.maxScaleDenominator, context);
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + // check if optional filter applies
│ │ │ │ + if (applies && this.filter) {
│ │ │ │ + // feature id filters get the feature, others get the context
│ │ │ │ + if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
│ │ │ │ + applies = this.filter.evaluate(feature);
│ │ │ │ + } else {
│ │ │ │ + applies = this.filter.evaluate(context);
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ +
│ │ │ │ + return applies;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: activate
│ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup.
│ │ │ │ - *
│ │ │ │ - * Returns:
│ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if
│ │ │ │ - * the strategy was already active.
│ │ │ │ + * Method: getContext
│ │ │ │ + * Gets the context for evaluating this rule
│ │ │ │ + *
│ │ │ │ + * Paramters:
│ │ │ │ + * feature - {} feature to take the context from if
│ │ │ │ + * none is specified.
│ │ │ │ */
│ │ │ │ - activate: function() {
│ │ │ │ - if (!this.active) {
│ │ │ │ - this.active = true;
│ │ │ │ - return true;
│ │ │ │ + getContext: function(feature) {
│ │ │ │ + var context = this.context;
│ │ │ │ + if (!context) {
│ │ │ │ + context = feature.attributes || feature.data;
│ │ │ │ }
│ │ │ │ - return false;
│ │ │ │ + if (typeof this.context == "function") {
│ │ │ │ + context = this.context(feature);
│ │ │ │ + }
│ │ │ │ + return context;
│ │ │ │ },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: deactivate
│ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate
│ │ │ │ - * tear-down.
│ │ │ │ - *
│ │ │ │ + * APIMethod: clone
│ │ │ │ + * Clones this rule.
│ │ │ │ + *
│ │ │ │ * Returns:
│ │ │ │ - * {Boolean} True if the strategy was successfully deactivated or false if
│ │ │ │ - * the strategy was already inactive.
│ │ │ │ + * {} Clone of this rule.
│ │ │ │ */
│ │ │ │ - deactivate: function() {
│ │ │ │ - if (this.active) {
│ │ │ │ - this.active = false;
│ │ │ │ - return true;
│ │ │ │ + clone: function() {
│ │ │ │ + var options = OpenLayers.Util.extend({}, this);
│ │ │ │ + if (this.symbolizers) {
│ │ │ │ + // clone symbolizers
│ │ │ │ + var len = this.symbolizers.length;
│ │ │ │ + options.symbolizers = new Array(len);
│ │ │ │ + for (var i = 0; i < len; ++i) {
│ │ │ │ + options.symbolizers[i] = this.symbolizers[i].clone();
│ │ │ │ + }
│ │ │ │ + } else {
│ │ │ │ + // clone symbolizer
│ │ │ │ + options.symbolizer = {};
│ │ │ │ + var value, type;
│ │ │ │ + for (var key in this.symbolizer) {
│ │ │ │ + value = this.symbolizer[key];
│ │ │ │ + type = typeof value;
│ │ │ │ + if (type === "object") {
│ │ │ │ + options.symbolizer[key] = OpenLayers.Util.extend({}, value);
│ │ │ │ + } else if (type === "string") {
│ │ │ │ + options.symbolizer[key] = value;
│ │ │ │ + }
│ │ │ │ + }
│ │ │ │ }
│ │ │ │ - return false;
│ │ │ │ + // clone filter
│ │ │ │ + options.filter = this.filter && this.filter.clone();
│ │ │ │ + // clone context
│ │ │ │ + options.context = this.context && OpenLayers.Util.extend({}, this.context);
│ │ │ │ + return new OpenLayers.Rule(options);
│ │ │ │ },
│ │ │ │
│ │ │ │ - CLASS_NAME: "OpenLayers.Strategy"
│ │ │ │ + CLASS_NAME: "OpenLayers.Rule"
│ │ │ │ });
│ │ │ │ /* ======================================================================
│ │ │ │ - OpenLayers/Handler.js
│ │ │ │ + OpenLayers/Symbolizer.js
│ │ │ │ ====================================================================== */
│ │ │ │
│ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ * full text of the license. */
│ │ │ │
│ │ │ │ /**
│ │ │ │ * @requires OpenLayers/BaseTypes/Class.js
│ │ │ │ - * @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.
│ │ │ │ + * Class: OpenLayers.Symbolizer
│ │ │ │ + * Base class representing a symbolizer used for feature rendering.
│ │ │ │ */
│ │ │ │ -OpenLayers.Handler = OpenLayers.Class({
│ │ │ │ +OpenLayers.Symbolizer = OpenLayers.Class({
│ │ │ │ +
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: id
│ │ │ │ - * {String}
│ │ │ │ + * APIProperty: zIndex
│ │ │ │ + * {Number} The zIndex determines the rendering order for a symbolizer.
│ │ │ │ + * Symbolizers with larger zIndex values are rendered over symbolizers
│ │ │ │ + * with smaller zIndex values. Default is 0.
│ │ │ │ */
│ │ │ │ - id: null,
│ │ │ │ + zIndex: 0,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: 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.
│ │ │ │ + * Constructor: OpenLayers.Symbolizer
│ │ │ │ + * Instances of this class are not useful. See one of the subclasses.
│ │ │ │ + *
│ │ │ │ + * Parameters:
│ │ │ │ + * config - {Object} An object containing properties to be set on the
│ │ │ │ + * symbolizer. Any documented symbolizer property can be set at
│ │ │ │ + * construction.
│ │ │ │ + *
│ │ │ │ + * Returns:
│ │ │ │ + * A new symbolizer.
│ │ │ │ */
│ │ │ │ - control: null,
│ │ │ │ + initialize: function(config) {
│ │ │ │ + OpenLayers.Util.extend(this, config);
│ │ │ │ + },
│ │ │ │
│ │ │ │ - /**
│ │ │ │ - * Property: map
│ │ │ │ - * {}
│ │ │ │ + /**
│ │ │ │ + * APIMethod: clone
│ │ │ │ + * Create a copy of this symbolizer.
│ │ │ │ + *
│ │ │ │ + * Returns a symbolizer of the same type with the same properties.
│ │ │ │ */
│ │ │ │ - map: null,
│ │ │ │ + clone: function() {
│ │ │ │ + var Type = eval(this.CLASS_NAME);
│ │ │ │ + return new Type(OpenLayers.Util.extend({}, this));
│ │ │ │ + },
│ │ │ │ +
│ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer"
│ │ │ │ +
│ │ │ │ +});
│ │ │ │ +
│ │ │ │ +/* ======================================================================
│ │ │ │ + OpenLayers/Symbolizer/Point.js
│ │ │ │ + ====================================================================== */
│ │ │ │ +
│ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
│ │ │ │ + * full list of contributors). Published under the 2-clause BSD license.
│ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the
│ │ │ │ + * full text of the license. */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * @requires OpenLayers/Symbolizer.js
│ │ │ │ + */
│ │ │ │ +
│ │ │ │ +/**
│ │ │ │ + * Class: OpenLayers.Symbolizer.Point
│ │ │ │ + * A symbolizer used to render point features.
│ │ │ │ + */
│ │ │ │ +OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * APIProperty: keyMask
│ │ │ │ - * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
│ │ │ │ - * constants to construct a keyMask. The keyMask is used by
│ │ │ │ - * . 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)
│ │ │ │ + * APIProperty: strokeColor
│ │ │ │ + * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000"
│ │ │ │ + * for red).
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - keyMask: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: active
│ │ │ │ - * {Boolean}
│ │ │ │ + * APIProperty: strokeOpacity
│ │ │ │ + * {Number} Stroke opacity (0-1).
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - 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.
│ │ │ │ + * APIProperty: strokeWidth
│ │ │ │ + * {Number} Pixel stroke width.
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - evt: null,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Property: touch
│ │ │ │ - * {Boolean} Indicates the support of touch events. When touch events are
│ │ │ │ - * started touch will be true and all mouse related listeners will do
│ │ │ │ - * nothing.
│ │ │ │ + * APIProperty: strokeLinecap
│ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square").
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - touch: false,
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Constructor: OpenLayers.Handler
│ │ │ │ - * Construct a handler.
│ │ │ │ - *
│ │ │ │ - * Parameters:
│ │ │ │ - * 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. If a map property
│ │ │ │ - * is present in the options argument it will be used instead.
│ │ │ │ - * 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.
│ │ │ │ + * Property: strokeDashstyle
│ │ │ │ + * {String} Stroke dash style according to the SLD spec. Note that the
│ │ │ │ + * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
│ │ │ │ + * "longdash", "longdashdot", or "solid") will not work in SLD, but
│ │ │ │ + * most SLD patterns will render correctly in OpenLayers.
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - initialize: function(control, callbacks, options) {
│ │ │ │ - OpenLayers.Util.extend(this, options);
│ │ │ │ - this.control = control;
│ │ │ │ - this.callbacks = callbacks;
│ │ │ │
│ │ │ │ - var map = this.map || control.map;
│ │ │ │ - if (map) {
│ │ │ │ - this.setMap(map);
│ │ │ │ - }
│ │ │ │ + /**
│ │ │ │ + * APIProperty: fillColor
│ │ │ │ + * {String} RGB hex fill color (e.g. "#ff0000" for red).
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ + */
│ │ │ │
│ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
│ │ │ │ - },
│ │ │ │ + /**
│ │ │ │ + * APIProperty: fillOpacity
│ │ │ │ + * {Number} Fill opacity (0-1).
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ + */
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: setMap
│ │ │ │ + * APIProperty: pointRadius
│ │ │ │ + * {Number} Pixel point radius.
│ │ │ │ + *
│ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults.
│ │ │ │ */
│ │ │ │ - setMap: function(map) {
│ │ │ │ - this.map = map;
│ │ │ │ - },
│ │ │ │
│ │ │ │ /**
│ │ │ │ - * Method: checkModifiers
│ │ │ │ - * Check the keyMask on the handler. If no is set, this always
│ │ │ │ - * returns true. If a