From 8bf71ebb3ad99214680d56dee550eda11c28620c Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Mon, 11 Mar 2013 16:10:10 +0100 Subject: [PATCH 01/11] Add a zoomslider control. --- css/ol.css | 28 ++ examples/zoomslider.html | 115 ++++++++ examples/zoomslider.js | 39 +++ src/objectliterals.exports | 5 + src/ol/control/zoomslider.exports | 1 + src/ol/control/zoomslidercontrol.js | 351 ++++++++++++++++++++++++ test/spec/ol/control/zoomslider.test.js | 107 ++++++++ 7 files changed, 646 insertions(+) create mode 100644 examples/zoomslider.html create mode 100644 examples/zoomslider.js create mode 100644 src/ol/control/zoomslider.exports create mode 100644 src/ol/control/zoomslidercontrol.js create mode 100644 test/spec/ol/control/zoomslider.test.js diff --git a/css/ol.css b/css/ol.css index 1a2ede8923..d6a24690d6 100644 --- a/css/ol.css +++ b/css/ol.css @@ -95,3 +95,31 @@ .ol-zoom-out:before { content: "\2212"; } + +.ol-zoomslider { + position: absolute; + top: 67px; + left: 8px; + background: rgba(255, 255, 255, 0.4); + border-radius: 4px; + width: 28px; + height: 200px; + outline: none; + overflow: hidden; + padding: 0; + margin: 0; +} +.ol-zoomslider-thumb { + position: absolute; + display: block; + padding: 0; + margin: 2px; + background: #130085; /* @alternate */ + background: rgba(0,60,136,0.5); + filter: alpha(opacity=80); + border-radius: 2px; + outline: none; + overflow: hidden; + height: 20px; + width: 24px; +} diff --git a/examples/zoomslider.html b/examples/zoomslider.html new file mode 100644 index 0000000000..437c5812b3 --- /dev/null +++ b/examples/zoomslider.html @@ -0,0 +1,115 @@ + + + + + + + + + + + ol3 ZoomSlider demo + + + + + +
+ +
+
+

Default style

+
+
+
+

Placed between zoom controls

+
+
+
+

Horizontal and completely re-styled

+
+
+
+ +
+ +
+

ZoomSlider control

+

Example of various ZoomSlider controls.

+
+

+ See the zoomslider.js + source to see how this is done. +

+
+
+ zoom, zoomslider, slider, style, styling, css, control +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/zoomslider.js b/examples/zoomslider.js new file mode 100644 index 0000000000..56cd02c210 --- /dev/null +++ b/examples/zoomslider.js @@ -0,0 +1,39 @@ +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); +goog.require('ol.Map'); +goog.require('ol.View2D'); +goog.require('ol.control.ZoomSlider'); +goog.require('ol.layer.TileLayer'); +goog.require('ol.source.MapQuestOpenAerial'); + + +/** + * Helper method for map-creation. + * + * @param {string} divId The id of the div for the map. + * @return {ol.Map} The ol.Map instance. + */ +var createMap = function(divId) { + var layer, map, zoomslider; + layer = new ol.layer.TileLayer({ + source: new ol.source.MapQuestOpenAerial() + }); + map = new ol.Map({ + layers: new ol.Collection([layer]), + target: divId, + view: new ol.View2D({ + center: new ol.Coordinate(0, 0), + zoom: 2 + }) + }); + zoomslider = new ol.control.ZoomSlider({ + minResolution: 500, + maxResolution: 100000, + map: map + }); + return map; +}; + +var map1 = createMap('map1'); +var map2 = createMap('map2'); +var map3 = createMap('map3'); diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 09a9e5fe10..0e5d215c37 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -70,6 +70,11 @@ @exportObjectLiteralProperty ol.control.ZoomOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.ZoomOptions.target Element|undefined +@exportObjectLiteral ol.control.ZoomSliderOptions +@exportObjectLiteralProperty ol.control.ZoomSliderOptions.map ol.Map|undefined +@exportObjectLiteralProperty ol.control.ZoomSliderOptions.maxResolution number|undefined +@exportObjectLiteralProperty ol.control.ZoomSliderOptions.minResolution number|undefined + @exportObjectLiteral ol.interaction.DefaultOptions @exportObjectLiteralProperty ol.interaction.DefaultOptions.doubleClickZoom boolean|undefined @exportObjectLiteralProperty ol.interaction.DefaultOptions.dragPan boolean|undefined diff --git a/src/ol/control/zoomslider.exports b/src/ol/control/zoomslider.exports new file mode 100644 index 0000000000..cbc760930a --- /dev/null +++ b/src/ol/control/zoomslider.exports @@ -0,0 +1 @@ +@exportClass ol.control.ZoomSlider ol.control.ZoomSliderOptions \ No newline at end of file diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js new file mode 100644 index 0000000000..2ecb78f55c --- /dev/null +++ b/src/ol/control/zoomslidercontrol.js @@ -0,0 +1,351 @@ +// FIXME works for View2D only +// FIXME should possibly show tooltip when dragging? +// FIXME should possibly be adjustable by clicking on container + +goog.provide('ol.control.ZoomSlider'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.fx.Dragger'); +goog.require('goog.style'); +goog.require('ol.FrameState'); +goog.require('ol.MapEventType'); +goog.require('ol.control.Control'); + + + +/** + * @constructor + * @extends {ol.control.Control} + * @param {ol.control.ZoomSliderOptions} zoomSliderOptions Zoom options. + */ +ol.control.ZoomSlider = function(zoomSliderOptions) { + // FIXME these should be read out from a map if not given, and only then + // fallback to the constants if they weren't defined on the map. + /** + * The minimum resolution that this control that one can set with this + * control. + * + * @type {number} + * @private + */ + this.maxResolution_ = goog.isDef(zoomSliderOptions.maxResolution) ? + zoomSliderOptions.maxResolution : + ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION; + + /** + * The maximum resolution that this control that one can set with this + * control. + * + * @type {number} + * @private + */ + this.minResolution_ = goog.isDef(zoomSliderOptions.minResolution) ? + zoomSliderOptions.minResolution : + ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION; + + goog.asserts.assert( + this.minResolution_ < this.maxResolution_, + 'minResolution must be smaller than maxResolution.' + ); + + /** + * The range of resolutions we are handling in this slider. + * + * @type {number} + * @private + */ + this.range_ = this.maxResolution_ - this.minResolution_; + + /** + * Will hold the current resolution of the view. + * + * @type {number} + * @private + */ + this.currentResolution_; + + /** + * The direction of the slider. Will be determined from actual display of the + * container and defaults to ol.control.ZoomSlider.direction.VERTICAL. + * + * @type {ol.control.ZoomSlider.direction} + * @private + */ + this.direction_ = ol.control.ZoomSlider.direction.VERTICAL; + + var elem = this.createDom_(); + this.dragger_ = this.createDraggable_(elem); + + // FIXME currently only a do nothing function is bound. + goog.events.listen(elem, [ + goog.events.EventType.TOUCHEND, + goog.events.EventType.CLICK + ], this.handleContainerClick_, false, this); + + goog.base(this, { + element: elem, + map: zoomSliderOptions.map + }); +}; +goog.inherits(ol.control.ZoomSlider, ol.control.Control); + + +/** + * The enum for available directions. + * + * @enum {number} + */ +ol.control.ZoomSlider.direction = { + VERTICAL: 0, + HORIZONTAL: 1 +}; + + +/** + * The CSS class that we'll give the zoomslider container. + * + * @const {string} + */ +ol.control.ZoomSlider.CSS_CLASS_CONTAINER = 'ol-zoomslider'; + + +/** + * The CSS class that we'll give the zoomslider thumb. + * + * @const {string} + */ +ol.control.ZoomSlider.CSS_CLASS_THUMB = + ol.control.ZoomSlider.CSS_CLASS_CONTAINER + '-thumb'; + + +/** + * The default value for minResolution_ when the control isn't instanciated with + * an explicit value. + * + * @const {number} + */ +ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION = 500; + + +/** + * The default value for maxResolution_ when the control isn't instanciated with + * an explicit value. + * + * @const {number} + */ +ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION = 1000000; + + +/** + * @inheritDoc + */ +ol.control.ZoomSlider.prototype.setMap = function(map) { + goog.base(this, 'setMap', map); + this.currentResolution_ = map.getView().getResolution(); + this.initMapEventListeners_(); + this.initSlider_(); + this.positionThumbForResolution_(this.currentResolution_); +}; + + +/** + * Initializes the event listeners for map events. + * + * @private + */ +ol.control.ZoomSlider.prototype.initMapEventListeners_ = function() { + goog.events.listen(this.getMap(), ol.MapEventType.POSTRENDER, + this.handleMapPostRender_, undefined, this); +}; + + +/** + * Initializes the slider element. This will determine and set this controls + * direction_ and also constrain the dragging of the thumb to always be within + * the bounds of the container. + * + * @private + */ +ol.control.ZoomSlider.prototype.initSlider_ = function() { + var container = this.element, + thumb = goog.dom.getFirstElementChild(container), + elemBounds = goog.style.getBounds(container), + elemPaddings = goog.style.getPaddingBox(container), + elemBorderBox = goog.style.getBorderBox(container), + thumbBounds = goog.style.getBounds(thumb), + thumbMargins = goog.style.getMarginBox(thumb), + thumbBorderBox = goog.style.getBorderBox(thumb), + w = elemBounds.width - + elemPaddings.left - elemPaddings.right - + thumbMargins.left - thumbMargins.right - + thumbBorderBox.left - thumbBorderBox.right - + thumbBounds.width, + h = elemBounds.height - + elemPaddings.top - elemPaddings.bottom - + thumbMargins.top - thumbMargins.bottom - + thumbBorderBox.top - thumbBorderBox.bottom - + thumbBounds.height, + limits; + // set the direction_ of the zoomslider and the allowed bounds for dragging + if (elemBounds.width > elemBounds.height) { + this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL; + limits = new goog.math.Rect(0, 0, w, 0); + } else { + this.direction_ = ol.control.ZoomSlider.direction.VERTICAL; + limits = new goog.math.Rect(0, 0, 0, h); + } + this.dragger_.setLimits(limits); +}; + + +/** + * @param {{frameState:ol.FrameState}} evtObj The evtObj. + * @private + */ +ol.control.ZoomSlider.prototype.handleMapPostRender_ = function(evtObj) { + var res = evtObj.frameState.view2DState.resolution; + if (res !== this.currentResolution_) { + this.currentResolution_ = res; + this.positionThumbForResolution_(res); + } +}; + + +/** + * @param {goog.events.BrowserEvent} browserEvent The browser event to handle. + * @private + */ +ol.control.ZoomSlider.prototype.handleContainerClick_ = function(browserEvent) { + // TODO implement proper resolution calculation according to browserEvent +}; + + +/** + * Positions the thumb inside its container according to the given resolution. + * + * @param {number} res The res. + * @private + */ +ol.control.ZoomSlider.prototype.positionThumbForResolution_ = function(res) { + var amount = this.amountForResolution_(res), + dragger = this.dragger_, + thumb = goog.dom.getFirstElementChild(this.element); + + if (this.direction_ == ol.control.ZoomSlider.direction.HORIZONTAL) { + var left = dragger.limits.left + dragger.limits.width * amount; + goog.style.setPosition(thumb, left); + } else { + var top = dragger.limits.top + dragger.limits.height * amount; + goog.style.setPosition(thumb, dragger.limits.left, top); + } +}; + + +/** + * Calculates the amount the thumb has been dragged to allow for calculation + * of the corresponding resolution. + * + * @param {goog.fx.DragDropEvent} e The dragdropevent. + * @return {number} The amount the thumb has been dragged. + * @private + */ +ol.control.ZoomSlider.prototype.amountDragged_ = function(e) { + var draggerLimits = this.dragger_.limits, + amount = 0; + if (this.direction_ === ol.control.ZoomSlider.direction.HORIZONTAL) { + amount = (e.left - draggerLimits.left) / draggerLimits.width; + } else { + amount = (e.top - draggerLimits.top) / draggerLimits.height; + } + return amount; +}; + + +/** + * Calculates the corresponding resolution of the thumb by the amount it has + * been dragged from its minimum. + * + * @param {number} amount The amount the thumb has been dragged. + * @return {number} a resolution between this.minResolution_ and + * this.maxResolution_. + * @private + */ +ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) { + var saneAmount = goog.math.clamp(amount, 0, 1); + return this.minResolution_ + this.range_ * saneAmount; +}; + + +/** + * Determines an amount of dragging relative to this minimum position by the + * given resolution. + * + * @param {number} res The resolution to get the amount for. + * @return {number} an amount between 0 and 1. + * @private + */ +ol.control.ZoomSlider.prototype.amountForResolution_ = function(res) { + var saneRes = goog.math.clamp(res, this.minResolution_, this.maxResolution_); + return (saneRes - this.minResolution_) / this.range_; +}; + + +/** + * Handles the user caused changes of the slider thumb and adjusts the + * resolution of our map accordingly. Will be called both while dragging and + * when dragging ends. + * + * @param {goog.fx.DragDropEvent} e The dragdropevent. + * @private + */ +ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { + var map = this.getMap(), + amountDragged = this.amountDragged_(e), + res = this.resolutionForAmount_(amountDragged); + if (res !== this.currentResolution_) { + this.currentResolution_ = res; + map.getView().setResolution(res); + } +}; + + +/** + * Actually enable draggable behaviour for the thumb of the zoomslider and bind + * relvant event listeners. + * + * @param {Element} elem The element for the slider. + * @return {goog.fx.Dragger} The actual goog.fx.Dragger instance. + * @private + */ +ol.control.ZoomSlider.prototype.createDraggable_ = function(elem) { + var dragger = new goog.fx.Dragger(elem.childNodes[0]); + dragger.addEventListener(goog.fx.Dragger.EventType.DRAG, + this.handleSliderChange_, undefined, this); + dragger.addEventListener(goog.fx.Dragger.EventType.END, + this.handleSliderChange_, undefined, this); + return dragger; +}; + + +/** + * Setup the DOM-structure we need for the zoomslider. + * + * @param {Element=} opt_elem The element for the slider. + * @return {Element} The correctly set up DOMElement. + * @private + */ +ol.control.ZoomSlider.prototype.createDom_ = function(opt_elem) { + var elem, + sliderCssCls = ol.control.ZoomSlider.CSS_CLASS_CONTAINER + + ' ol-unselectable', + thumbCssCls = ol.control.ZoomSlider.CSS_CLASS_THUMB + + ' ol-unselectable'; + + elem = goog.dom.createDom(goog.dom.TagName.DIV, sliderCssCls, + goog.dom.createDom(goog.dom.TagName.DIV, thumbCssCls)); + + return elem; +}; diff --git a/test/spec/ol/control/zoomslider.test.js b/test/spec/ol/control/zoomslider.test.js new file mode 100644 index 0000000000..da5010da60 --- /dev/null +++ b/test/spec/ol/control/zoomslider.test.js @@ -0,0 +1,107 @@ +goog.provide('ol.test.control.ZoomSlider'); + +describe('ol.control.ZoomSlider', function() { + var map, zoomslider; + + beforeEach(function() { + map = new ol.Map({ + target: document.getElementById('map') + }); + zoomslider = new ol.control.ZoomSlider({ + minResolution: 5000, + maxResolution: 100000, + map: map + }); + }); + + afterEach(function() { + zoomslider.dispose(); + map.dispose(); + }); + + describe('configuration & defaults', function() { + + it('has valid defaults for min and maxresolution', function() { + expect(function() { + zoomslider = new ol.control.ZoomSlider({}); + }).not.toThrow(); + expect(zoomslider.minResolution_).toBe(500); + expect(zoomslider.maxResolution_).toBe(1000000); + expect(zoomslider.range_).toBe(999500); + }); + + it('throws exception when configured with wrong resolutions', function() { + expect(function() { + zoomslider = new ol.control.ZoomSlider({ + minResolution: 50, + maxResolution: 0 + }); + }).toThrow(); + }); + + it('can be configured with valid resolutions', function() { + expect(function() { + zoomslider = new ol.control.ZoomSlider({ + minResolution: 790, + maxResolution: 91000 + }); + }).not.toThrow(); + expect(zoomslider.minResolution_).toBe(790); + expect(zoomslider.maxResolution_).toBe(91000); + expect(zoomslider.range_).toBe(90210); + }); + }); + + describe('DOM creation', function() { + it('creates the expected DOM elements', function() { + var zoomSliderContainers = goog.dom.getElementsByClass('ol-zoomslider'), + zoomSliderContainer, + zoomSliderThumbs, + zoomSliderThumb, + hasUnselectableCls; + + expect(zoomSliderContainers.length).toBe(1); + + zoomSliderContainer = zoomSliderContainers[0]; + expect(zoomSliderContainer instanceof HTMLDivElement).toBe(true); + + hasUnselectableCls = goog.dom.classes.has(zoomSliderContainer, + 'ol-unselectable'); + expect(hasUnselectableCls).toBe(true); + + zoomSliderThumbs = goog.dom.getElementsByClass('ol-zoomslider-thumb', + zoomSliderContainer); + expect(zoomSliderThumbs.length).toBe(1); + + zoomSliderThumb = zoomSliderThumbs[0]; + expect(zoomSliderThumb instanceof HTMLDivElement).toBe(true); + + hasUnselectableCls = goog.dom.classes.has(zoomSliderThumb, + 'ol-unselectable'); + expect(hasUnselectableCls).toBe(true); + }); + + }); + + describe('dragger setup', function() { + it('creates a goog.fx.Dragger', function() { + expect(zoomslider.dragger_).toBeDefined(); + expect(zoomslider.dragger_).toBeA(goog.fx.Dragger); + + expect(zoomslider.dragger_.limits).toBeDefined(); + expect(zoomslider.dragger_.limits).toBeA(goog.math.Rect); + + expect(zoomslider.direction_).toBeDefined(); + expect(zoomslider.direction_).toBe(1); // vertical + }); + }); + +}); + +goog.require('goog.dom'); +goog.require('goog.dom.classes'); +goog.require('goog.fx.Dragger'); +goog.require('goog.math.Rect'); +goog.require('goog.style'); +goog.require('ol.Map'); +goog.require('ol.control.ZoomSlider'); From f496471273528ce51c1c1e61d6d389954bd30904 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 15:52:38 +0100 Subject: [PATCH 02/11] Simplify example and let it use real resolutions. Thanks to @twpayne for the suggestion. --- examples/zoomslider.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/zoomslider.js b/examples/zoomslider.js index 56cd02c210..984ca24a1c 100644 --- a/examples/zoomslider.js +++ b/examples/zoomslider.js @@ -1,4 +1,3 @@ -goog.require('ol.Collection'); goog.require('ol.Coordinate'); goog.require('ol.Map'); goog.require('ol.View2D'); @@ -14,12 +13,18 @@ goog.require('ol.source.MapQuestOpenAerial'); * @return {ol.Map} The ol.Map instance. */ var createMap = function(divId) { - var layer, map, zoomslider; + var source, layer, map, zoomslider, resolutions, minRes, maxRes; + + source = new ol.source.MapQuestOpenAerial(); + resolutions = source.getResolutions(); + minRes = resolutions[resolutions.length - 1]; + maxRes = resolutions[0]; + layer = new ol.layer.TileLayer({ - source: new ol.source.MapQuestOpenAerial() + source: source }); map = new ol.Map({ - layers: new ol.Collection([layer]), + layers: [layer], target: divId, view: new ol.View2D({ center: new ol.Coordinate(0, 0), @@ -27,8 +32,8 @@ var createMap = function(divId) { }) }); zoomslider = new ol.control.ZoomSlider({ - minResolution: 500, - maxResolution: 100000, + minResolution: minRes, + maxResolution: maxRes, map: map }); return map; From ce67625b20804f957359fbbe23d42adb0f93b270 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 16:08:18 +0100 Subject: [PATCH 03/11] Use OSM resolutions as fallback resolutions. Hat tip to @twpayne for suggesting this change in the defaults. --- src/ol/control/zoomslidercontrol.js | 10 ++++++---- test/spec/ol/control/zoomslider.test.js | 9 ++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 2ecb78f55c..fbaaac77f6 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -123,20 +123,22 @@ ol.control.ZoomSlider.CSS_CLASS_THUMB = /** * The default value for minResolution_ when the control isn't instanciated with - * an explicit value. + * an explicit value. The default value is the resolution of the standard OSM + * tiles at zoomlevel 18. * * @const {number} */ -ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION = 500; +ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION = 0.5971642833948135; /** * The default value for maxResolution_ when the control isn't instanciated with - * an explicit value. + * an explicit value. The default value is the resolution of the standard OSM + * tiles at zoomlevel 0. * * @const {number} */ -ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION = 1000000; +ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION = 156543.0339; /** diff --git a/test/spec/ol/control/zoomslider.test.js b/test/spec/ol/control/zoomslider.test.js index da5010da60..1d52d5a522 100644 --- a/test/spec/ol/control/zoomslider.test.js +++ b/test/spec/ol/control/zoomslider.test.js @@ -22,12 +22,15 @@ describe('ol.control.ZoomSlider', function() { describe('configuration & defaults', function() { it('has valid defaults for min and maxresolution', function() { + var expectedMin = 0.5971642833948135, + expectedMax = 156543.0339, + expectedRange = expectedMax - expectedMin; expect(function() { zoomslider = new ol.control.ZoomSlider({}); }).not.toThrow(); - expect(zoomslider.minResolution_).toBe(500); - expect(zoomslider.maxResolution_).toBe(1000000); - expect(zoomslider.range_).toBe(999500); + expect(zoomslider.minResolution_).toBe(expectedMin); + expect(zoomslider.maxResolution_).toBe(expectedMax); + expect(zoomslider.range_).toBe(expectedRange); }); it('throws exception when configured with wrong resolutions', function() { From d77a8b0a1d2a9421c84c6cd36ad9b3703a6908dc Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 16:09:58 +0100 Subject: [PATCH 04/11] Add an assertion to the dragchange handler. This ensures that the result of resolution calculation stays inside of the allowed bounds. --- src/ol/control/zoomslidercontrol.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index fbaaac77f6..3e9a151fdb 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -307,6 +307,8 @@ ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { var map = this.getMap(), amountDragged = this.amountDragged_(e), res = this.resolutionForAmount_(amountDragged); + goog.asserts.assert(res >= this.minResolution_ && res <= this.maxResolution_, + 'calculated new resolution is in allowed bounds.'); if (res !== this.currentResolution_) { this.currentResolution_ = res; map.getView().setResolution(res); From bc0772281628af8036fee01b29fb9198b8a2fcf8 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 16:18:25 +0100 Subject: [PATCH 05/11] Unregister previous event listeners, if any. Thanks for spotting this, @twpayne. --- src/ol/control/zoomslidercontrol.js | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 3e9a151fdb..74074d238e 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -76,6 +76,18 @@ ol.control.ZoomSlider = function(zoomSliderOptions) { */ this.direction_ = ol.control.ZoomSlider.direction.VERTICAL; + /** + * @private + * @type {Array.} + */ + this.mapListenerKeys_ = null; + + /** + * @private + * @type {Array.} + */ + this.draggerListenerKeys_ = null; + var elem = this.createDom_(); this.dragger_ = this.createDraggable_(elem); @@ -159,8 +171,16 @@ ol.control.ZoomSlider.prototype.setMap = function(map) { * @private */ ol.control.ZoomSlider.prototype.initMapEventListeners_ = function() { - goog.events.listen(this.getMap(), ol.MapEventType.POSTRENDER, - this.handleMapPostRender_, undefined, this); + if (!goog.isNull(this.mapListenerKeys_)) { + goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); + this.mapListenerKeys_ = null; + } + if (!goog.isNull(this.getMap())) { + this.mapListenerKeys_ = [ + goog.events.listen(this.getMap(), ol.MapEventType.POSTRENDER, + this.handleMapPostRender_, undefined, this) + ]; + } }; @@ -325,11 +345,17 @@ ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { * @private */ ol.control.ZoomSlider.prototype.createDraggable_ = function(elem) { + if (!goog.isNull(this.draggerListenerKeys_)) { + goog.array.forEach(this.draggerListenerKeys_, goog.events.unlistenByKey); + this.draggerListenerKeys_ = null; + } var dragger = new goog.fx.Dragger(elem.childNodes[0]); - dragger.addEventListener(goog.fx.Dragger.EventType.DRAG, - this.handleSliderChange_, undefined, this); - dragger.addEventListener(goog.fx.Dragger.EventType.END, - this.handleSliderChange_, undefined, this); + this.draggerListenerKeys_ = [ + goog.events.listen(dragger, [ + goog.fx.Dragger.EventType.DRAG, + goog.fx.Dragger.EventType.END + ], this.handleSliderChange_, undefined, this) + ]; return dragger; }; From 220c7bd6f8dc5b36a0ec7799b2f97b5212c4b72f Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 16:21:59 +0100 Subject: [PATCH 06/11] More specific argument type of callback method. --- src/ol/control/zoomslidercontrol.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 74074d238e..c205700655 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -224,11 +224,11 @@ ol.control.ZoomSlider.prototype.initSlider_ = function() { /** - * @param {{frameState:ol.FrameState}} evtObj The evtObj. + * @param {ol.MapEvent} mapEvtObj The ol.MapEvent object. * @private */ -ol.control.ZoomSlider.prototype.handleMapPostRender_ = function(evtObj) { - var res = evtObj.frameState.view2DState.resolution; +ol.control.ZoomSlider.prototype.handleMapPostRender_ = function(mapEvtObj) { + var res = mapEvtObj.frameState.view2DState.resolution; if (res !== this.currentResolution_) { this.currentResolution_ = res; this.positionThumbForResolution_(res); From 94d7379ba387a099330b0c91c05a27fb5851c8e3 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Tue, 12 Mar 2013 16:59:35 +0100 Subject: [PATCH 07/11] Correct API documentation. --- src/ol/control/zoomslidercontrol.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index c205700655..cbbcbcc7b8 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -25,8 +25,7 @@ ol.control.ZoomSlider = function(zoomSliderOptions) { // FIXME these should be read out from a map if not given, and only then // fallback to the constants if they weren't defined on the map. /** - * The minimum resolution that this control that one can set with this - * control. + * The minimum resolution that one can set with this control. * * @type {number} * @private @@ -36,8 +35,7 @@ ol.control.ZoomSlider = function(zoomSliderOptions) { ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION; /** - * The maximum resolution that this control that one can set with this - * control. + * The maximum resolution that one can set with this control. * * @type {number} * @private From e872a49bebddad9f6f4eaffa96dd9d0be823e4de Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Wed, 13 Mar 2013 10:12:13 +0100 Subject: [PATCH 08/11] Use getContentBoxSize() for slider initialisation. Initial note to revisit this came from @twpayne; thanks. --- src/ol/control/zoomslidercontrol.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index cbbcbcc7b8..bc9a3e2fad 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -192,25 +192,20 @@ ol.control.ZoomSlider.prototype.initMapEventListeners_ = function() { ol.control.ZoomSlider.prototype.initSlider_ = function() { var container = this.element, thumb = goog.dom.getFirstElementChild(container), - elemBounds = goog.style.getBounds(container), - elemPaddings = goog.style.getPaddingBox(container), - elemBorderBox = goog.style.getBorderBox(container), + elemSize = goog.style.getContentBoxSize(container), thumbBounds = goog.style.getBounds(thumb), thumbMargins = goog.style.getMarginBox(thumb), thumbBorderBox = goog.style.getBorderBox(thumb), - w = elemBounds.width - - elemPaddings.left - elemPaddings.right - + w = elemSize.width - thumbMargins.left - thumbMargins.right - thumbBorderBox.left - thumbBorderBox.right - thumbBounds.width, - h = elemBounds.height - - elemPaddings.top - elemPaddings.bottom - + h = elemSize.height - thumbMargins.top - thumbMargins.bottom - thumbBorderBox.top - thumbBorderBox.bottom - thumbBounds.height, limits; - // set the direction_ of the zoomslider and the allowed bounds for dragging - if (elemBounds.width > elemBounds.height) { + if (elemSize.width > elemSize.height) { this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL; limits = new goog.math.Rect(0, 0, w, 0); } else { From 9798eb5fa804391f0c2bf5507fa8b1f52c4abbc7 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Wed, 13 Mar 2013 16:52:37 +0100 Subject: [PATCH 09/11] Don't use non-exported method in example. --- examples/zoomslider.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/zoomslider.js b/examples/zoomslider.js index 984ca24a1c..f2831b0719 100644 --- a/examples/zoomslider.js +++ b/examples/zoomslider.js @@ -16,10 +16,9 @@ var createMap = function(divId) { var source, layer, map, zoomslider, resolutions, minRes, maxRes; source = new ol.source.MapQuestOpenAerial(); - resolutions = source.getResolutions(); - minRes = resolutions[resolutions.length - 1]; - maxRes = resolutions[0]; - + // These are the min and max resolutions of MapQuestOpenAerial + minRes = 0.5971642834779395; + maxRes = 156543.03392804097; layer = new ol.layer.TileLayer({ source: source }); From 1040a082c13473414300b59a194d0fdefa5abb64 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Thu, 14 Mar 2013 09:28:48 +0100 Subject: [PATCH 10/11] Remove CSS workarounds as suggested by @fredj. --- css/ol.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/css/ol.css b/css/ol.css index d6a24690d6..09e6c23dbf 100644 --- a/css/ol.css +++ b/css/ol.css @@ -114,9 +114,7 @@ display: block; padding: 0; margin: 2px; - background: #130085; /* @alternate */ background: rgba(0,60,136,0.5); - filter: alpha(opacity=80); border-radius: 2px; outline: none; overflow: hidden; From 5059fe0be78ad632d34d79bc493dddbe4180033e Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Thu, 14 Mar 2013 11:29:22 +0100 Subject: [PATCH 11/11] Adjust test syntax. --- test/spec/ol/control/zoomslider.test.js | 41 +++++++++++-------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/test/spec/ol/control/zoomslider.test.js b/test/spec/ol/control/zoomslider.test.js index 1d52d5a522..fed6a111ab 100644 --- a/test/spec/ol/control/zoomslider.test.js +++ b/test/spec/ol/control/zoomslider.test.js @@ -27,10 +27,10 @@ describe('ol.control.ZoomSlider', function() { expectedRange = expectedMax - expectedMin; expect(function() { zoomslider = new ol.control.ZoomSlider({}); - }).not.toThrow(); - expect(zoomslider.minResolution_).toBe(expectedMin); - expect(zoomslider.maxResolution_).toBe(expectedMax); - expect(zoomslider.range_).toBe(expectedRange); + }).not.to.throwException(); + expect(zoomslider.minResolution_).to.be(expectedMin); + expect(zoomslider.maxResolution_).to.be(expectedMax); + expect(zoomslider.range_).to.be(expectedRange); }); it('throws exception when configured with wrong resolutions', function() { @@ -39,7 +39,7 @@ describe('ol.control.ZoomSlider', function() { minResolution: 50, maxResolution: 0 }); - }).toThrow(); + }).to.throwException(); }); it('can be configured with valid resolutions', function() { @@ -48,10 +48,10 @@ describe('ol.control.ZoomSlider', function() { minResolution: 790, maxResolution: 91000 }); - }).not.toThrow(); - expect(zoomslider.minResolution_).toBe(790); - expect(zoomslider.maxResolution_).toBe(91000); - expect(zoomslider.range_).toBe(90210); + }).not.to.throwException(); + expect(zoomslider.minResolution_).to.be(790); + expect(zoomslider.maxResolution_).to.be(91000); + expect(zoomslider.range_).to.be(90210); }); }); @@ -63,39 +63,34 @@ describe('ol.control.ZoomSlider', function() { zoomSliderThumb, hasUnselectableCls; - expect(zoomSliderContainers.length).toBe(1); + expect(zoomSliderContainers.length).to.be(1); zoomSliderContainer = zoomSliderContainers[0]; - expect(zoomSliderContainer instanceof HTMLDivElement).toBe(true); + expect(zoomSliderContainer instanceof HTMLDivElement).to.be(true); hasUnselectableCls = goog.dom.classes.has(zoomSliderContainer, 'ol-unselectable'); - expect(hasUnselectableCls).toBe(true); + expect(hasUnselectableCls).to.be(true); zoomSliderThumbs = goog.dom.getElementsByClass('ol-zoomslider-thumb', zoomSliderContainer); - expect(zoomSliderThumbs.length).toBe(1); + expect(zoomSliderThumbs.length).to.be(1); zoomSliderThumb = zoomSliderThumbs[0]; - expect(zoomSliderThumb instanceof HTMLDivElement).toBe(true); + expect(zoomSliderThumb instanceof HTMLDivElement).to.be(true); hasUnselectableCls = goog.dom.classes.has(zoomSliderThumb, 'ol-unselectable'); - expect(hasUnselectableCls).toBe(true); + expect(hasUnselectableCls).to.be(true); }); }); describe('dragger setup', function() { it('creates a goog.fx.Dragger', function() { - expect(zoomslider.dragger_).toBeDefined(); - expect(zoomslider.dragger_).toBeA(goog.fx.Dragger); - - expect(zoomslider.dragger_.limits).toBeDefined(); - expect(zoomslider.dragger_.limits).toBeA(goog.math.Rect); - - expect(zoomslider.direction_).toBeDefined(); - expect(zoomslider.direction_).toBe(1); // vertical + expect(zoomslider.dragger_ instanceof goog.fx.Dragger).to.be(true); + expect(zoomslider.dragger_.limits instanceof goog.math.Rect).to.be(true); + expect(zoomslider.direction_).to.be(1); // vertical }); });