diff --git a/css/ol.css b/css/ol.css index b5c2bd97fb..895ce60c8a 100644 --- a/css/ol.css +++ b/css/ol.css @@ -1,4 +1,4 @@ -.ol-viewport { +.ol-viewport .ol-unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; diff --git a/demos/full-screen/full-screen.js b/demos/full-screen/full-screen.js index c7ce3c6796..979290ea66 100644 --- a/demos/full-screen/full-screen.js +++ b/demos/full-screen/full-screen.js @@ -5,6 +5,7 @@ goog.require('ol.Collection'); goog.require('ol.Coordinate'); goog.require('ol.Map'); goog.require('ol.MapOptions'); // FIXME this should not be required +goog.require('ol.overlay.Overlay'); goog.require('ol.source.MapQuestOpenAerial'); @@ -22,3 +23,9 @@ var map = new ol.Map(document.getElementById('map'), { layers: new ol.Collection([layer]), zoom: 2 }); +var vienna = new ol.overlay.Overlay({ + map: map, + coordinate: ol.Projection.transformWithCodes( + new ol.Coordinate(16, 48), 'EPSG:4326', 'EPSG:3857'), + element: document.getElementById('vienna') +}); diff --git a/src/ol/control/control.js b/src/ol/control/control.js index b974259b5d..e4f239c536 100644 --- a/src/ol/control/control.js +++ b/src/ol/control/control.js @@ -67,7 +67,8 @@ ol.control.Control.prototype.setMap = function(map) { } this.map_ = map; if (!goog.isNull(this.map_)) { - var target = goog.isDef(this.target_) ? this.target_ : map.getViewport(); + var target = goog.isDef(this.target_) ? + this.target_ : map.getOverlayContainer(); goog.dom.appendChild(target, this.element); } }; diff --git a/src/ol/control/zoom.js b/src/ol/control/zoom.js index 754bae35f8..583532e567 100644 --- a/src/ol/control/zoom.js +++ b/src/ol/control/zoom.js @@ -42,10 +42,7 @@ ol.control.Zoom = function(zoomOptions) { goog.events.listen(outElement, eventType, this.handleOut_, false, this); var element = goog.dom.createDom( - goog.dom.TagName.DIV, 'ol-zoom', inElement, outElement); - goog.events.listen( - element, ol.BrowserFeature.HAS_TOUCH ? goog.events.EventType.TOUCHSTART : - goog.events.EventType.MOUSEDOWN, goog.events.Event.stopPropagation); + goog.dom.TagName.DIV, 'ol-zoom ol-unselectable', inElement, outElement); goog.base(this, { element: element, diff --git a/src/ol/map.js b/src/ol/map.js index 10a7c50456..b667bf470f 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -24,6 +24,7 @@ goog.require('goog.functions'); goog.require('goog.fx.anim'); goog.require('goog.fx.anim.Animated'); goog.require('goog.object'); +goog.require('ol.BrowserFeature'); goog.require('ol.Collection'); goog.require('ol.Color'); goog.require('ol.Constraints'); @@ -63,14 +64,6 @@ ol.MapProperty = { }; -/** - * @enum {number} - */ -ol.MapPaneZIndex = { - VIEWPORT: 1000 -}; - - /** * @constructor @@ -145,15 +138,25 @@ ol.Map = function(container, mapOptionsLiteral) { * @private * @type {Element} */ - this.viewport_ = goog.dom.createElement(goog.dom.TagName.DIV); - this.viewport_.className = 'ol-viewport'; + this.viewport_ = goog.dom.createDom(goog.dom.TagName.DIV, 'ol-viewport'); this.viewport_.style.position = 'relative'; this.viewport_.style.overflow = 'hidden'; this.viewport_.style.width = '100%'; this.viewport_.style.height = '100%'; - this.viewport_.style.zIndex = ol.MapPaneZIndex.VIEWPORT; goog.dom.appendChild(container, this.viewport_); + /** + * @private + * @type {Element} + */ + this.overlayContainer_ = goog.dom.createDom(goog.dom.TagName.DIV, + 'ol-overlaycontainer'); + goog.events.listen(this.overlayContainer_, + ol.BrowserFeature.HAS_TOUCH ? + goog.events.EventType.TOUCHSTART : goog.events.EventType.MOUSEDOWN, + goog.events.Event.stopPropagation); + goog.dom.appendChild(this.viewport_, this.overlayContainer_); + var mapBrowserEventHandler = new ol.MapBrowserEventHandler(this); goog.events.listen(mapBrowserEventHandler, [ ol.MapBrowserEvent.EventType.CLICK, @@ -504,6 +507,16 @@ ol.Map.prototype.getViewport = function() { }; +/** + * @return {Element} The map's overlay container. Elements added to this + * container won't let mousedown and touchstart events through to the map, so + * clicks and gestures on an overlay don't trigger any MapBrowserEvent. + */ +ol.Map.prototype.getOverlayContainer = function() { + return this.overlayContainer_; +}; + + /** * @param {goog.events.BrowserEvent} browserEvent Browser event. * @param {string=} opt_type Type. diff --git a/src/ol/mapbrowserevent.js b/src/ol/mapbrowserevent.js index 21561f54d9..fdfe8668ce 100644 --- a/src/ol/mapbrowserevent.js +++ b/src/ol/mapbrowserevent.js @@ -170,7 +170,6 @@ ol.MapBrowserEventHandler.prototype.click_ = function(browserEvent) { this.touchEnableBrowserEvent_(browserEvent); var newEvent = new ol.MapBrowserEvent( ol.MapBrowserEvent.EventType.CLICK, this.map_, browserEvent); - this.down_ = null; this.dispatchEvent(newEvent); } }; @@ -181,7 +180,7 @@ ol.MapBrowserEventHandler.prototype.click_ = function(browserEvent) { * @private */ ol.MapBrowserEventHandler.prototype.dblclick_ = function(browserEvent) { - if (!this.dragged_) { + if (!this.dragged_ && this.down_) { var now = new Date().getTime(); if (!this.timestamp_ || now - this.timestamp_ > 250) { this.timestamp_ = now; diff --git a/src/ol/overlay/overlay.js b/src/ol/overlay/overlay.js new file mode 100644 index 0000000000..6b501b184f --- /dev/null +++ b/src/ol/overlay/overlay.js @@ -0,0 +1,185 @@ +goog.provide('ol.overlay.Overlay'); +goog.provide('ol.overlay.OverlayOptions'); +goog.provide('ol.overlay.OverlayPositioning'); + +goog.require('goog.events'); +goog.require('goog.style'); +goog.require('ol.Map'); +goog.require('ol.MapProperty'); + + +/** + * @typedef {{coordinate: (ol.Coordinate|undefined), + * element: (Element|undefined), + * map: (ol.Map|undefined), + * positioning: (Array.|undefined)}} + */ +ol.overlay.OverlayOptions; + + + +/** + * @constructor + * @param {ol.overlay.OverlayOptions} overlayOptions Overlay options. + */ +ol.overlay.Overlay = function(overlayOptions) { + + /** + * @type {ol.Coordinate} + * @private + */ + this.coordinate_ = null; + + /** + * @type {Element} + * @private + */ + this.element_ = null; + + /** + * @private + * @type {ol.Map} + */ + this.map_ = null; + + /** + * @type {Array.} + * @private + */ + this.positioning_ = [ + ol.overlay.OverlayPositioning.LEFT, + ol.overlay.OverlayPositioning.BOTTOM + ]; + + /** + * @private + * @type {Array.} + */ + this.mapListenerKeys_ = []; + + if (goog.isDef(overlayOptions.coordinate)) { + this.setCoordinate(overlayOptions.coordinate); + } + if (goog.isDef(overlayOptions.element)) { + this.setElement(overlayOptions.element); + } + if (goog.isDef(overlayOptions.map)) { + this.setMap(overlayOptions.map); + } + if (goog.isDef(overlayOptions.positioning)) { + this.setPositioning(overlayOptions.positioning); + } +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate for the overlay's position on + * the map. + */ +ol.overlay.Overlay.prototype.setCoordinate = function(coordinate) { + this.coordinate_ = coordinate; + this.updatePixelPosition_(); +}; + + +/** + * @param {Element} element The DOM element for the overlay. + */ +ol.overlay.Overlay.prototype.setElement = function(element) { + if (this.element_) { + goog.dom.removeNode(this.element_); + } + this.element_ = element; + if (this.map_) { + goog.dom.append(/** @type {!Node} */ (this.map_.getOverlayContainer()), + this.element_); + } + this.updatePixelPosition_(); +}; + + +/** + * @return {Element} The DOM element for the overlay. + */ +ol.overlay.Overlay.prototype.getElement = function() { + return this.element_; +}; + + +/** + * @param {ol.Map} map Map. + */ +ol.overlay.Overlay.prototype.setMap = function(map) { + this.map_ = map; + goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); + if (this.element_) { + this.setElement(this.element_); + } + this.mapListenerKeys_ = map ? [ + goog.events.listen( + map, ol.Object.getChangedEventType(ol.MapProperty.CENTER), + this.updatePixelPosition_, false, this), + goog.events.listen( + map, ol.Object.getChangedEventType(ol.MapProperty.RESOLUTION), + this.updatePixelPosition_, false, this), + goog.events.listen( + map, ol.Object.getChangedEventType(ol.MapProperty.ROTATION), + this.updatePixelPosition_, false, this), + goog.events.listen( + map, ol.Object.getChangedEventType(ol.MapProperty.SIZE), + this.updatePixelPosition_, false, this) + ] : []; + this.updatePixelPosition_(); +}; + + +/** + * @return {ol.Map} Map. + */ +ol.overlay.Overlay.prototype.getMap = function() { + return this.map_; +}; + + +/** + * Set the CSS properties to use for x- and y-positioning of the element. If + * not set, the default is {@code ['left', 'bottom']}. + * @param {Array.} positioning The positioning. + */ +ol.overlay.Overlay.prototype.setPositioning = function(positioning) { + this.positioning_ = positioning; + this.updatePixelPosition_(); +}; + + +/** + * @private + */ +ol.overlay.Overlay.prototype.updatePixelPosition_ = function() { + if (!goog.isNull(this.map_) && !goog.isNull(this.coordinate_) && + !goog.isNull(this.element_)) { + var pixel = this.map_.getPixelFromCoordinate(this.coordinate_); + var mapSize = this.map_.get(ol.MapProperty.SIZE); + var x = Math.round(pixel.x); + if (this.positioning_[0] === ol.overlay.OverlayPositioning.RIGHT) { + x = mapSize.width - x; + } + var y = Math.round(pixel.y); + if (this.positioning_[1] === ol.overlay.OverlayPositioning.BOTTOM) { + y = mapSize.height - y; + } + goog.style.setStyle(this.element_, this.positioning_[0], x + 'px'); + goog.style.setStyle(this.element_, this.positioning_[1], y + 'px'); + } +}; + + +/** + * @enum {string} + */ +ol.overlay.OverlayPositioning = { + LEFT: 'left', + RIGHT: 'right', + TOP: 'top', + BOTTOM: 'bottom' +}; diff --git a/src/ol/renderer/dom/map.js b/src/ol/renderer/dom/map.js index 407c13ebc7..1ba0ce0d7d 100644 --- a/src/ol/renderer/dom/map.js +++ b/src/ol/renderer/dom/map.js @@ -30,13 +30,13 @@ ol.renderer.dom.Map = function(container, map) { * @private */ this.layersPane_ = goog.dom.createElement(goog.dom.TagName.DIV); - this.layersPane_.className = 'ol-layers-pane'; + this.layersPane_.className = 'ol-layers-pane ol-unselectable'; var style = this.layersPane_.style; style.position = 'absolute'; style.width = '100%'; style.height = '100%'; - goog.dom.appendChild(container, this.layersPane_); + goog.dom.insertChildAt(container, this.layersPane_, 0); /** * @type {Object} diff --git a/src/ol/renderer/webgl/map.js b/src/ol/renderer/webgl/map.js index ddb11d00f6..e41e79a838 100644 --- a/src/ol/renderer/webgl/map.js +++ b/src/ol/renderer/webgl/map.js @@ -151,7 +151,8 @@ ol.renderer.webgl.Map = function(container, map) { this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS); this.canvas_.height = container.clientHeight; this.canvas_.width = container.clientWidth; - goog.dom.appendChild(container, this.canvas_); + this.canvas_.className = 'ol-unselectable'; + goog.dom.insertChildAt(container, this.canvas_, 0); /** * @private