From 3a5d45d62e48992523497849c7d02c1d2fd45107 Mon Sep 17 00:00:00 2001 From: Alexander Parshin Date: Tue, 30 Sep 2014 09:57:53 +0400 Subject: [PATCH 01/89] Add optional parameter for result rounding in L.PolyUtil.clipPolygon and L.LineUtil.clipSegment --- spec/suites/geometry/LineUtilSpec.js | 15 +++++++++++++++ spec/suites/geometry/PolyUtilSpec.js | 15 +++++++++++++++ src/geometry/LineUtil.js | 8 ++++---- src/geometry/PolyUtil.js | 6 +++--- src/layer/vector/Polygon.js | 2 +- src/layer/vector/Polyline.js | 2 +- 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/spec/suites/geometry/LineUtilSpec.js b/spec/suites/geometry/LineUtilSpec.js index b7251a0f..c8873e6a 100644 --- a/spec/suites/geometry/LineUtilSpec.js +++ b/spec/suites/geometry/LineUtilSpec.js @@ -33,6 +33,21 @@ describe('LineUtil', function () { expect(segment).to.be(false); }); + + it('can round numbers in clipped bounds', function () { + var a = new L.Point(4, 5); + var b = new L.Point(8, 6); + + var segment1 = L.LineUtil.clipSegment(a, b, bounds); + + expect(segment1[0]).to.eql(new L.Point(5, 5.25)); + expect(segment1[1]).to.eql(b); + + var segment2 = L.LineUtil.clipSegment(a, b, bounds, false, true); + + expect(segment2[0]).to.eql(new L.Point(5, 5)); + expect(segment2[1]).to.eql(b); + }); }); describe('#pointToSegmentDistance & #closestPointOnSegment', function () { diff --git a/spec/suites/geometry/PolyUtilSpec.js b/spec/suites/geometry/PolyUtilSpec.js index 0cd382ee..6dcf7839 100644 --- a/spec/suites/geometry/PolyUtilSpec.js +++ b/spec/suites/geometry/PolyUtilSpec.js @@ -10,6 +10,7 @@ describe('PolyUtil', function () { new L.Point(10, 15) ]; + //check clip without rounding var clipped = L.PolyUtil.clipPolygon(points, bounds); for (var i = 0, len = clipped.length; i < len; i++) { @@ -17,6 +18,20 @@ describe('PolyUtil', function () { } expect(clipped).to.eql([ + new L.Point(7.5, 10), + new L.Point(5, 5), + new L.Point(10, 7.5), + new L.Point(10, 10) + ]); + + //check clip with rounding + var clippedRounded = L.PolyUtil.clipPolygon(points, bounds, true); + + for (i = 0, len = clippedRounded.length; i < len; i++) { + delete clippedRounded[i]._code; + } + + expect(clippedRounded).to.eql([ new L.Point(8, 10), new L.Point(5, 5), new L.Point(10, 8), diff --git a/src/geometry/LineUtil.js b/src/geometry/LineUtil.js index ab6a7b6c..28f44475 100644 --- a/src/geometry/LineUtil.js +++ b/src/geometry/LineUtil.js @@ -99,7 +99,7 @@ L.LineUtil = { // Cohen-Sutherland line clipping algorithm. // Used to avoid rendering parts of a polyline that are not currently visible. - clipSegment: function (a, b, bounds, useLastCode) { + clipSegment: function (a, b, bounds, useLastCode, round) { var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), codeB = this._getBitCode(b, bounds), @@ -118,7 +118,7 @@ L.LineUtil = { // other cases } else { codeOut = codeA || codeB; - p = this._getEdgeIntersection(a, b, codeOut, bounds); + p = this._getEdgeIntersection(a, b, codeOut, bounds, round); newCode = this._getBitCode(p, bounds); if (codeOut === codeA) { @@ -132,7 +132,7 @@ L.LineUtil = { } }, - _getEdgeIntersection: function (a, b, code, bounds) { + _getEdgeIntersection: function (a, b, code, bounds, round) { var dx = b.x - a.x, dy = b.y - a.y, min = bounds.min, @@ -156,7 +156,7 @@ L.LineUtil = { y = a.y + dy * (min.x - a.x) / dx; } - return new L.Point(x, y, true); + return new L.Point(x, y, round); }, _getBitCode: function (/*Point*/ p, bounds) { diff --git a/src/geometry/PolyUtil.js b/src/geometry/PolyUtil.js index 68aa36d8..9b3b24e0 100644 --- a/src/geometry/PolyUtil.js +++ b/src/geometry/PolyUtil.js @@ -10,7 +10,7 @@ L.PolyUtil = {}; * Sutherland-Hodgeman polygon clipping algorithm. * Used to avoid rendering parts of a polygon that are not currently visible. */ -L.PolyUtil.clipPolygon = function (points, bounds) { +L.PolyUtil.clipPolygon = function (points, bounds, round) { var clippedPoints, edges = [1, 4, 2, 8], i, j, k, @@ -35,7 +35,7 @@ L.PolyUtil.clipPolygon = function (points, bounds) { if (!(a._code & edge)) { // if b is outside the clip window (a->b goes out of screen) if (b._code & edge) { - p = lu._getEdgeIntersection(b, a, edge, bounds); + p = lu._getEdgeIntersection(b, a, edge, bounds, round); p._code = lu._getBitCode(p, bounds); clippedPoints.push(p); } @@ -43,7 +43,7 @@ L.PolyUtil.clipPolygon = function (points, bounds) { // else if b is inside the clip window (a->b enters the screen) } else if (!(b._code & edge)) { - p = lu._getEdgeIntersection(b, a, edge, bounds); + p = lu._getEdgeIntersection(b, a, edge, bounds, round); p._code = lu._getBitCode(p, bounds); clippedPoints.push(p); } diff --git a/src/layer/vector/Polygon.js b/src/layer/vector/Polygon.js index 03438887..f35bc6df 100644 --- a/src/layer/vector/Polygon.js +++ b/src/layer/vector/Polygon.js @@ -58,7 +58,7 @@ L.Polygon = L.Polyline.extend({ this._parts = []; for (var i = 0, len = this._rings.length, clipped; i < len; i++) { - clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds); + clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true); if (clipped.length) { this._parts.push(clipped); } diff --git a/src/layer/vector/Polyline.js b/src/layer/vector/Polyline.js index a045b28a..9f2209fc 100644 --- a/src/layer/vector/Polyline.js +++ b/src/layer/vector/Polyline.js @@ -179,7 +179,7 @@ L.Polyline = L.Path.extend({ points = this._rings[i]; for (j = 0, len2 = points.length; j < len2 - 1; j++) { - segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j); + segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true); if (!segment) { continue; } From 08d828f519648789b77da77105186ae87fb27ffb Mon Sep 17 00:00:00 2001 From: Rick Mohr Date: Tue, 25 Nov 2014 15:32:53 -0500 Subject: [PATCH 02/89] Improve inertial scrolling [This post](http://stackoverflow.com/a/3628949/362702) claims that in Apple's kinetic scrolling, "The velocity of the touch is measured at the exact moment that the finger is lifted." I tried both this "final velocity" approach and the "max velocity" approach proposed in #2987. Both allow a stronger "fling" than the current "average velocity" approach, but "max velocity" can feel wrong if you slow down at the end of your swipe. But because the "final velocity" approach uses just one data point it can be unstable, occasionally giving a too-large velocity from a small time delta. Best is to stabilize that by averaging a few data points, so we're back to "average velocity" but using a shorter time period. Averaging over 50 ms instead of 100 ms gives good results, usually 4 data points on both my iPhone 4s and Chrome on my Windows laptop. Another reason the current code has a weak fling is that the velocity was being calculated incorrectly. Because `delay` was added to the time delta, time was computed from n+1 points but distance from n points. I also changed the default value of `inertiaThreshold` (intended to prevent unwanted additional movement if you stop dragging and then lift your finger) to `Infinity`, for two reasons: 1) A fling gesture often failed on my iPhone because the measured `delay` was higher than `inertiaThreshold` (current default 32), sometimes by hundreds of ms. 2) With the updated velocity code I don't see unwanted additional movement when I stop dragging and then lift my finger. There is a remaining issue (with both the original and updated code). If you fling the map and try to fling it again before it stops moving, no drag events are generated for the second fling so it has no effect. I don't see this problem with e.g. Google or Apple maps. Entered as #3062. Fixes #2987 --- src/map/handler/Map.Drag.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index 1df7d872..1262bfec 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -8,7 +8,7 @@ L.Map.mergeOptions({ inertia: !L.Browser.android23, inertiaDeceleration: 3400, // px/s^2 inertiaMaxSpeed: Infinity, // px/s - inertiaThreshold: L.Browser.touch ? 32 : 18, // ms + inertiaThreshold: Infinity, easeLinearity: 0.2, // TODO refactor, move to CRS @@ -72,7 +72,7 @@ L.Map.Drag = L.Handler.extend({ this._positions.push(pos); this._times.push(time); - if (time - this._times[0] > 100) { + if (time - this._times[0] > 50) { this._positions.shift(); this._times.shift(); } @@ -110,7 +110,7 @@ L.Map.Drag = L.Handler.extend({ options = map.options, delay = +new Date() - this._lastTime, - noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; + noInertia = !options.inertia || delay > options.inertiaThreshold || this._times.length < 2; map.fire('dragend', e); @@ -120,7 +120,7 @@ L.Map.Drag = L.Handler.extend({ } else { var direction = this._lastPos.subtract(this._positions[0]), - duration = (this._lastTime + delay - this._times[0]) / 1000, + duration = (this._lastTime - this._times[0]) / 1000, ease = options.easeLinearity, speedVector = direction.multiplyBy(ease / duration), From b7dbdade9e1ee46d42193b5da18d4c03bbc9e339 Mon Sep 17 00:00:00 2001 From: batphil Date: Fri, 2 Jan 2015 01:23:44 +0100 Subject: [PATCH 03/89] Happy new year :o) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d6258ad4..13f6bcf2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2014, Vladimir Agafonkin +Copyright (c) 2010-2015, Vladimir Agafonkin Copyright (c) 2010-2011, CloudMade All rights reserved. From 1d174cd5028043c5cff9a8d06d7c38d0481b9ca1 Mon Sep 17 00:00:00 2001 From: Steve Kashishian Date: Tue, 6 Jan 2015 16:34:35 -0600 Subject: [PATCH 04/89] provide a default popup text color of #333 --- dist/leaflet.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index 5f3d8abc..0d6f2dff 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -440,7 +440,7 @@ .leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white; - + color: #333; box-shadow: 0 3px 14px rgba(0,0,0,0.4); } .leaflet-container a.leaflet-popup-close-button { From 2ee1aa2c2365e0cf4404817b4d268d5ed788a653 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 12 Jan 2015 11:03:42 -0800 Subject: [PATCH 05/89] jshint --- spec/suites/geo/LatLngSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/suites/geo/LatLngSpec.js b/spec/suites/geo/LatLngSpec.js index 9da63f8e..c7de51db 100644 --- a/spec/suites/geo/LatLngSpec.js +++ b/spec/suites/geo/LatLngSpec.js @@ -112,8 +112,8 @@ describe('LatLng', function () { }); it('accepts an object with alt', function () { - expect(L.latLng({lat: 50, lng: 30, alt:100})).to.eql(new L.LatLng(50, 30, 100)); - expect(L.latLng({lat: 50, lon: 30, alt:100})).to.eql(new L.LatLng(50, 30, 100)); + expect(L.latLng({lat: 50, lng: 30, alt: 100})).to.eql(new L.LatLng(50, 30, 100)); + expect(L.latLng({lat: 50, lon: 30, alt: 100})).to.eql(new L.LatLng(50, 30, 100)); }); }); }); From c8fca59b51086fcaa024baddb49529426d8efeb9 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 12 Jan 2015 13:03:51 -0800 Subject: [PATCH 06/89] Move _pruneTiles up to _update _update is the only place _addTiles is called from, makes more sense to call _add then _prune than for _add to call _prune itself. --- src/layer/tile/GridLayer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 6203929e..a0ac8ca7 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -360,6 +360,7 @@ L.GridLayer = L.Layer.extend({ } this._addTiles(bounds); + this._pruneTiles(); }, // tile coordinates range for particular geo bounds and zoom @@ -415,8 +416,6 @@ L.GridLayer = L.Layer.extend({ this._level.el.appendChild(fragment); } - - this._pruneTiles(); }, _isValidTile: function (coords) { From 99a21117a08380d6c11ef1ee1e27a3799e942c95 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 12 Jan 2015 14:40:05 -0800 Subject: [PATCH 07/89] Merge _tiles, _loaded, and _retain into a single map --- src/layer/tile/GridLayer.js | 58 +++++++++++++++++++++---------------- src/layer/tile/TileLayer.js | 2 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index a0ac8ca7..5d4569d6 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -34,8 +34,6 @@ L.GridLayer = L.Layer.extend({ this._levels = {}; this._tiles = {}; - this._loaded = {}; - this._retain = {}; this._tilesToLoad = 0; this._reset(); @@ -156,7 +154,7 @@ L.GridLayer = L.Layer.extend({ if (L.Browser.ielt9) { // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles for (var i in this._tiles) { - L.DomUtil.setOpacity(this._tiles[i], opacity); + L.DomUtil.setOpacity(this._tiles[i].el, opacity); } } else { L.DomUtil.setOpacity(this._container, opacity); @@ -205,29 +203,34 @@ L.GridLayer = L.Layer.extend({ if (!this._map) { return; } - this._retain = {}; - var bounds = this._map.getBounds(), z = this._tileZoom, range = this._getTileRange(bounds, z), - i, j, key, found; + i, j, key, tile, found; + + for (key in this._tiles) { + this._tiles[key].retain = false; + } for (i = range.min.x; i <= range.max.x; i++) { for (j = range.min.y; j <= range.max.y; j++) { key = i + ':' + j + ':' + z; + tile = this._tiles[key]; - this._retain[key] = true; + if (!tile) { continue; } + tile.retain = true; - if (!this._loaded[key]) { + if (!tile.loaded) { found = this._retainParent(i, j, z, z - 5) || this._retainChildren(i, j, z, z + 2); } } } for (key in this._tiles) { - if (!this._retain[key]) { - if (!this._loaded[key]) { + tile = this._tiles[key]; + if (!tile.retain) { + if (!tile.loaded) { this._removeTile(key); this._tilesToLoad--; } else if (this._map._fadeAnimated) { @@ -247,7 +250,8 @@ L.GridLayer = L.Layer.extend({ }, _deferRemove: function (key) { - if (!this._retain[key]) { + var tile = this._tiles[key]; + if (tile && !tile.retain) { this._removeTile(key); } }, @@ -257,10 +261,11 @@ L.GridLayer = L.Layer.extend({ y2 = Math.floor(y / 2), z2 = z - 1; - var key = x2 + ':' + y2 + ':' + z2; + var key = x2 + ':' + y2 + ':' + z2, + tile = this._tiles[key]; - if (this._loaded[key]) { - this._retain[key] = true; + if (tile && tile.loaded) { + tile.retain = true; return true; } else if (z2 > minZoom) { @@ -275,10 +280,11 @@ L.GridLayer = L.Layer.extend({ for (var i = 2 * x; i < 2 * x + 2; i++) { for (var j = 2 * y; j < 2 * y + 2; j++) { - var key = i + ':' + j + ':' + (z + 1); + var key = i + ':' + j + ':' + (z + 1), + tile = this._tiles[key]; - if (this._loaded[key]) { - this._retain[key] = true; + if (tile && tile.loaded) { + tile.retain = true; } else if (z + 1 < maxZoom) { this._retainChildren(i, j, z + 1, maxZoom); @@ -481,13 +487,12 @@ L.GridLayer = L.Layer.extend({ var tile = this._tiles[key]; if (!tile) { return; } - L.DomUtil.remove(tile); + L.DomUtil.remove(tile.el); delete this._tiles[key]; - delete this._loaded[key]; this.fire('tileunload', { - tile: tile, + tile: tile.el, coords: this._keyToTileCoords(key) }); }, @@ -533,7 +538,9 @@ L.GridLayer = L.Layer.extend({ L.DomUtil.setPosition(tile, tilePos, true); // save tile in cache - this._tiles[key] = tile; + this._tiles[key] = { + el: tile + }; container.appendChild(tile); this.fire('tileloadstart', { @@ -553,15 +560,16 @@ L.GridLayer = L.Layer.extend({ var key = this._tileCoordsToKey(coords); - if (!this._tiles[key]) { return; } + tile = this._tiles[key]; + if (!tile) { return; } - this._loaded[key] = true; + tile.loaded = true; this._pruneTiles(); - L.DomUtil.addClass(tile, 'leaflet-tile-loaded'); + L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded'); this.fire('tileload', { - tile: tile, + tile: tile.el, coords: coords }); diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index 94f1f21a..9b0d9032 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -136,7 +136,7 @@ L.TileLayer = L.GridLayer.extend({ _abortLoading: function () { var i, tile; for (i in this._tiles) { - tile = this._tiles[i]; + tile = this._tiles[i].el; tile.onload = L.Util.falseFn; tile.onerror = L.Util.falseFn; From d08bdc8e78c1420910d834dc7735de0aa1d5463d Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 12 Jan 2015 14:49:18 -0800 Subject: [PATCH 08/89] Calculate _tilesToLoad Avoids cached _tilesToLoad value getting out of sync with actual loading tiles, e.g. if _removeOtherTiles or _abortLoading removes tiles that are loading. --- src/layer/tile/GridLayer.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 5d4569d6..dde3563b 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -32,9 +32,7 @@ L.GridLayer = L.Layer.extend({ this._pruneTiles = L.Util.throttle(this._pruneTiles, 200, this); this._levels = {}; - this._tiles = {}; - this._tilesToLoad = 0; this._reset(); this._update(); @@ -232,7 +230,6 @@ L.GridLayer = L.Layer.extend({ if (!tile.retain) { if (!tile.loaded) { this._removeTile(key); - this._tilesToLoad--; } else if (this._map._fadeAnimated) { setTimeout(L.bind(this._deferRemove, this, key), 250); } else { @@ -246,7 +243,6 @@ L.GridLayer = L.Layer.extend({ for (var key in this._tiles) { this._removeTile(key); } - this._tilesToLoad = 0; }, _deferRemove: function (key) { @@ -402,21 +398,16 @@ L.GridLayer = L.Layer.extend({ return a.distanceTo(center) - b.distanceTo(center); }); - var tilesToLoad = queue.length; - - - if (tilesToLoad !== 0) { + if (queue.length !== 0) { // if its the first batch of tiles to load - if (!this._tilesToLoad) { + if (this._tilesToLoad() === 0) { this.fire('loading'); } - this._tilesToLoad += tilesToLoad; - // create DOM fragment to append tiles in one batch var fragment = document.createDocumentFragment(); - for (i = 0; i < tilesToLoad; i++) { + for (i = 0; i < queue.length; i++) { this._addTile(queue[i], fragment); } @@ -573,9 +564,7 @@ L.GridLayer = L.Layer.extend({ coords: coords }); - this._tilesToLoad--; - - if (this._tilesToLoad === 0) { + if (this._tilesToLoad() === 0) { this.fire('load'); } }, @@ -600,6 +589,14 @@ L.GridLayer = L.Layer.extend({ _animateZoom: function (e) { this._setZoomTransforms(e.center, e.zoom); + }, + + _tilesToLoad: function () { + var result = 0; + for (var key in this._tiles) { + if (this._tiles[key].loading) { result++; } + } + return result; } }); From b76caf0f3c358b601f19837b15b307baf0a09b49 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 13 Jan 2015 13:22:19 +0200 Subject: [PATCH 09/89] don't count how many tiles to load, ref #3129 --- src/layer/tile/GridLayer.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index dde3563b..52294704 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -400,7 +400,7 @@ L.GridLayer = L.Layer.extend({ if (queue.length !== 0) { // if its the first batch of tiles to load - if (this._tilesToLoad() === 0) { + if (this._noTilesToLoad()) { this.fire('loading'); } @@ -564,7 +564,7 @@ L.GridLayer = L.Layer.extend({ coords: coords }); - if (this._tilesToLoad() === 0) { + if (this._noTilesToLoad()) { this.fire('load'); } }, @@ -591,12 +591,11 @@ L.GridLayer = L.Layer.extend({ this._setZoomTransforms(e.center, e.zoom); }, - _tilesToLoad: function () { - var result = 0; + _noTilesToLoad: function () { for (var key in this._tiles) { - if (this._tiles[key].loading) { result++; } + if (!this._tiles[key].loaded) { return false; } } - return result; + return true; } }); From 4dfbcc46ddd4fe6d67b61e5f90bbc62da8d2f219 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 13 Jan 2015 13:31:11 +0200 Subject: [PATCH 10/89] remove inertiaThreshold option, ref #3063 --- src/map/handler/Map.Drag.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index 1262bfec..92476de0 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -8,7 +8,6 @@ L.Map.mergeOptions({ inertia: !L.Browser.android23, inertiaDeceleration: 3400, // px/s^2 inertiaMaxSpeed: Infinity, // px/s - inertiaThreshold: Infinity, easeLinearity: 0.2, // TODO refactor, move to CRS @@ -110,7 +109,7 @@ L.Map.Drag = L.Handler.extend({ options = map.options, delay = +new Date() - this._lastTime, - noInertia = !options.inertia || delay > options.inertiaThreshold || this._times.length < 2; + noInertia = !options.inertia || this._times.length < 2; map.fire('dragend', e); From c3a39f50cf7e36278749deebbd6855e5d83b4f98 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 13 Jan 2015 13:31:46 +0200 Subject: [PATCH 11/89] fix accidentally broken build --- src/map/handler/Map.Drag.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index 92476de0..9980d4d3 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -107,7 +107,6 @@ L.Map.Drag = L.Handler.extend({ _onDragEnd: function (e) { var map = this._map, options = map.options, - delay = +new Date() - this._lastTime, noInertia = !options.inertia || this._times.length < 2; From c656e3f993be1f26c3e9064b8da2faa07c4176bd Mon Sep 17 00:00:00 2001 From: Brett Camper Date: Wed, 14 Jan 2015 19:47:46 -0500 Subject: [PATCH 12/89] flyTo: convert targetCenter to latLng see #3139 --- src/map/anim/Map.FlyTo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/anim/Map.FlyTo.js b/src/map/anim/Map.FlyTo.js index d9af494e..81128f1a 100644 --- a/src/map/anim/Map.FlyTo.js +++ b/src/map/anim/Map.FlyTo.js @@ -9,6 +9,7 @@ L.Map.include({ size = this.getSize(), startZoom = this._zoom; + targetCenter = L.latLng(targetCenter); targetZoom = targetZoom === undefined ? startZoom : targetZoom; var w0 = Math.max(size.x, size.y), From 48a374db059febbb9a1a510cc4fc8bed837f442c Mon Sep 17 00:00:00 2001 From: Chris Laidlaw Date: Thu, 15 Jan 2015 15:42:54 -0800 Subject: [PATCH 13/89] BUG: _tryAnimatedPan lies to setView about whether or not an animation was initiated. --- src/map/anim/Map.PanAnimation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/anim/Map.PanAnimation.js b/src/map/anim/Map.PanAnimation.js index 9b20aef6..370054aa 100644 --- a/src/map/anim/Map.PanAnimation.js +++ b/src/map/anim/Map.PanAnimation.js @@ -96,6 +96,6 @@ L.Map.include({ this.panBy(offset, options); - return true; + return options.animate !== false; } }); From cc750d43ae5a79bd4c1f5cc4cba98d992a67713c Mon Sep 17 00:00:00 2001 From: Bernhard Eder Date: Sun, 18 Jan 2015 23:45:38 +0100 Subject: [PATCH 14/89] fixed error in _tryAnimatedPan when options is undefined --- src/map/anim/Map.PanAnimation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/anim/Map.PanAnimation.js b/src/map/anim/Map.PanAnimation.js index 370054aa..9dddff5f 100644 --- a/src/map/anim/Map.PanAnimation.js +++ b/src/map/anim/Map.PanAnimation.js @@ -96,6 +96,6 @@ L.Map.include({ this.panBy(offset, options); - return options.animate !== false; + return (options && options.animate) !== false; } }); From 4c46abe7810272704cc1a7ce0ccfb84c45456791 Mon Sep 17 00:00:00 2001 From: Stefan Sydow Date: Mon, 19 Jan 2015 19:07:04 +0100 Subject: [PATCH 15/89] preserve dragable state on layer deactivation --- spec/suites/layer/marker/MarkerSpec.js | 5 +++++ src/layer/marker/Marker.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/suites/layer/marker/MarkerSpec.js b/spec/suites/layer/marker/MarkerSpec.js index fc425850..a67e3343 100644 --- a/spec/suites/layer/marker/MarkerSpec.js +++ b/spec/suites/layer/marker/MarkerSpec.js @@ -40,6 +40,11 @@ describe("Marker", function () { marker.setIcon(icon1); expect(marker.dragging.enabled()).to.be(true); + + map.removeLayer(marker); + map.addLayer(marker); + + expect(marker.dragging.enabled()).to.be(true); }); it("changes the icon to another DivIcon", function () { diff --git a/src/layer/marker/Marker.js b/src/layer/marker/Marker.js index d8b001da..6d40888a 100644 --- a/src/layer/marker/Marker.js +++ b/src/layer/marker/Marker.js @@ -32,8 +32,8 @@ L.Marker = L.Layer.extend({ }, onRemove: function () { - if (this.dragging) { - this.dragging.disable(); + if (this.dragging && this.dragging.enabled()) { + this.dragging.removeHooks(); } this._removeIcon(); From 73147bc9e691f200d5728a39d93931192f168e62 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Sat, 24 Jan 2015 11:20:01 +0100 Subject: [PATCH 16/89] Update year in copyright.js --- src/copyright.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/copyright.js b/src/copyright.js index cc6889d2..63397223 100644 --- a/src/copyright.js +++ b/src/copyright.js @@ -1,4 +1,4 @@ /* Leaflet {VERSION}, a JS library for interactive maps. http://leafletjs.com - (c) 2010-2014 Vladimir Agafonkin, (c) 2010-2011 CloudMade + (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ From 46d5992279f1e7cc1fb32e695adcf4bd4874d2a1 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Sat, 24 Jan 2015 11:23:23 +0100 Subject: [PATCH 17/89] Use SVG version of Travis build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 220b74ec..15e55b74 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you want to **get involved** with Leaflet development, check out the [contrib Let's make the best mapping library that will ever exist, and push the limits of what's possible with online maps! -[![Build Status](https://travis-ci.org/Leaflet/Leaflet.png?branch=master)](https://travis-ci.org/Leaflet/Leaflet) +[![Build Status](https://travis-ci.org/Leaflet/Leaflet.svg?branch=master)](https://travis-ci.org/Leaflet/Leaflet) [Vladimir Agafonkin]: http://agafonkin.com/en [contributors]: https://github.com/Leaflet/Leaflet/graphs/contributors From a28c9508be931820e4c22253dea552d89cefa74b Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Sun, 25 Jan 2015 19:27:24 -0500 Subject: [PATCH 18/89] Don't mutate this._defaultLocateOptions. --- src/map/ext/Map.Geolocation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/ext/Map.Geolocation.js b/src/map/ext/Map.Geolocation.js index 38235516..05fe552a 100644 --- a/src/map/ext/Map.Geolocation.js +++ b/src/map/ext/Map.Geolocation.js @@ -14,7 +14,7 @@ L.Map.include({ locate: function (/*Object*/ options) { - options = this._locateOptions = L.extend(this._defaultLocateOptions, options); + options = this._locateOptions = L.extend({}, this._defaultLocateOptions, options); if (!navigator.geolocation) { this._handleGeolocationError({ From aace5b278e3584da2c2aa477964d7740c7870e4f Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 27 Jan 2015 19:09:49 +0200 Subject: [PATCH 19/89] add Control.Zoom disable/enable methods, close #3172 --- src/control/Control.Zoom.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/control/Control.Zoom.js b/src/control/Control.Zoom.js index 980cf49b..08c6505b 100644 --- a/src/control/Control.Zoom.js +++ b/src/control/Control.Zoom.js @@ -31,12 +31,28 @@ L.Control.Zoom = L.Control.extend({ map.off('zoomend zoomlevelschange', this._updateDisabled, this); }, + disable: function () { + this._enabled = false; + this._updateDisabled(); + return this; + }, + + enable: function () { + this._enabled = true; + this._updateDisabled(); + return this; + }, + _zoomIn: function (e) { - this._map.zoomIn(e.shiftKey ? 3 : 1); + if (this._enabled) { + this._map.zoomIn(e.shiftKey ? 3 : 1); + } }, _zoomOut: function (e) { - this._map.zoomOut(e.shiftKey ? 3 : 1); + if (this._enabled) { + this._map.zoomOut(e.shiftKey ? 3 : 1); + } }, _createButton: function (html, title, className, container, fn) { @@ -61,10 +77,10 @@ L.Control.Zoom = L.Control.extend({ L.DomUtil.removeClass(this._zoomInButton, className); L.DomUtil.removeClass(this._zoomOutButton, className); - if (map._zoom === map.getMinZoom()) { + if (!this._enabled || map._zoom === map.getMinZoom()) { L.DomUtil.addClass(this._zoomOutButton, className); } - if (map._zoom === map.getMaxZoom()) { + if (!this._enabled || map._zoom === map.getMaxZoom()) { L.DomUtil.addClass(this._zoomInButton, className); } } From 93f3304f22ee99beaa5adc5ae8bb16fa1739ce18 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 27 Jan 2015 23:15:07 +0200 Subject: [PATCH 20/89] fix zoom control regression --- src/control/Control.Zoom.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/control/Control.Zoom.js b/src/control/Control.Zoom.js index 08c6505b..86a82b02 100644 --- a/src/control/Control.Zoom.js +++ b/src/control/Control.Zoom.js @@ -32,25 +32,25 @@ L.Control.Zoom = L.Control.extend({ }, disable: function () { - this._enabled = false; + this._disabled = true; this._updateDisabled(); return this; }, enable: function () { - this._enabled = true; + this._disabled = false; this._updateDisabled(); return this; }, _zoomIn: function (e) { - if (this._enabled) { + if (!this._disabled) { this._map.zoomIn(e.shiftKey ? 3 : 1); } }, _zoomOut: function (e) { - if (this._enabled) { + if (!this._disabled) { this._map.zoomOut(e.shiftKey ? 3 : 1); } }, @@ -77,10 +77,10 @@ L.Control.Zoom = L.Control.extend({ L.DomUtil.removeClass(this._zoomInButton, className); L.DomUtil.removeClass(this._zoomOutButton, className); - if (!this._enabled || map._zoom === map.getMinZoom()) { + if (this._disabled || map._zoom === map.getMinZoom()) { L.DomUtil.addClass(this._zoomOutButton, className); } - if (!this._enabled || map._zoom === map.getMaxZoom()) { + if (this._disabled || map._zoom === map.getMaxZoom()) { L.DomUtil.addClass(this._zoomInButton, className); } } From 65efd30567683a61326e665813eacf3ebd2a0087 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 28 Jan 2015 17:17:26 +0200 Subject: [PATCH 21/89] minor chaining fixes --- src/control/Control.Attribution.js | 4 ++-- src/control/Control.Layers.js | 2 +- src/layer/GeoJSON.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/control/Control.Attribution.js b/src/control/Control.Attribution.js index 4803e04a..dea9fec0 100644 --- a/src/control/Control.Attribution.js +++ b/src/control/Control.Attribution.js @@ -39,7 +39,7 @@ L.Control.Attribution = L.Control.extend({ }, addAttribution: function (text) { - if (!text) { return; } + if (!text) { return this; } if (!this._attributions[text]) { this._attributions[text] = 0; @@ -52,7 +52,7 @@ L.Control.Attribution = L.Control.extend({ }, removeAttribution: function (text) { - if (!text) { return; } + if (!text) { return this; } if (this._attributions[text]) { this._attributions[text]--; diff --git a/src/control/Control.Layers.js b/src/control/Control.Layers.js index 5366dc87..2ed054b1 100644 --- a/src/control/Control.Layers.js +++ b/src/control/Control.Layers.js @@ -123,7 +123,7 @@ L.Control.Layers = L.Control.extend({ }, _update: function () { - if (!this._container) { return; } + if (!this._container) { return this; } L.DomUtil.empty(this._baseLayersList); L.DomUtil.empty(this._overlaysList); diff --git a/src/layer/GeoJSON.js b/src/layer/GeoJSON.js index d8700a8a..cb29d8de 100644 --- a/src/layer/GeoJSON.js +++ b/src/layer/GeoJSON.js @@ -31,7 +31,7 @@ L.GeoJSON = L.FeatureGroup.extend({ var options = this.options; - if (options.filter && !options.filter(geojson)) { return; } + if (options.filter && !options.filter(geojson)) { return this; } var layer = L.GeoJSON.geometryToLayer(geojson, options); layer.feature = L.GeoJSON.asFeature(geojson); @@ -146,7 +146,7 @@ L.extend(L.GeoJSON, { for (var i = 0, len = latlngs.length; i < len; i++) { coords.push(levelsDeep ? - L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed): + L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) : L.GeoJSON.latLngToCoords(latlngs[i])); } From e749d1915b5ecc179222359e687b0dd10d73fcc5 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 28 Jan 2015 17:27:31 +0200 Subject: [PATCH 22/89] add ESLint rules; various code style fixes --- package.json | 51 ++++++++++++++++++++++++++-------- src/control/Control.Zoom.js | 1 - src/core/Util.js | 3 +- src/dom/DomEvent.js | 9 +++--- src/dom/DomUtil.js | 3 +- src/geo/LatLng.js | 1 - src/geometry/LineUtil.js | 6 ++-- src/geometry/Point.js | 2 +- src/layer/Popup.js | 2 +- src/layer/marker/Marker.js | 2 +- src/layer/tile/GridLayer.js | 3 +- src/layer/vector/SVG.js | 2 +- src/map/handler/Map.BoxZoom.js | 2 +- src/map/handler/Map.Tap.js | 4 +-- 14 files changed, 61 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index c4ac13cf..7fd0e73d 100644 --- a/package.json +++ b/package.json @@ -3,20 +3,21 @@ "version": "0.8.0-dev", "description": "JavaScript library for mobile-friendly interactive maps", "devDependencies": { - "jake": "~8.0.10", - "jshint": "~2.5.7", - "uglify-js": "~2.4.15", - "mocha": "~2.0.1", + "copyfiles": "0.1.0", + "eslint": "^0.13.0", "happen": "~0.1.3", - "karma": "~0.12.24", - "karma-mocha": "~0.1.9", - "karma-coverage": "~0.2.6", + "jake": "~8.0.10", + "jshint": "~2.6.0", + "karma": "~0.12.31", + "karma-chrome-launcher": "^0.1.7", + "karma-coverage": "~0.2.7", + "karma-firefox-launcher": "~0.1.4", + "karma-mocha": "~0.1.10", "karma-phantomjs-launcher": "^0.1.4", - "karma-chrome-launcher": "^0.1.5", - "karma-firefox-launcher": "~0.1.3", "karma-safari-launcher": "~0.1.1", + "mocha": "~2.1.0", "tin": "^0.5.0", - "copyfiles": "0.1.0" + "uglify-js": "~2.4.16" }, "main": "dist/leaflet-src.js", "style": "dist/leaflet.css", @@ -38,5 +39,33 @@ "type": "BSD-2-Clause", "url": "https://github.com/Leaflet/Leaflet/blob/master/LICENSE" } - ] + ], + "eslintConfig": { + "rules": { + "camelcase": 2, + "quotes": [2, "single"], + "no-mixed-spaces-and-tabs": [2, "smart-tabs"], + "space-after-function-name": 2, + "space-in-parens": 2, + "space-in-brackets": 2, + "space-before-blocks": 2, + "space-after-keywords": 2, + "no-lonely-if": 2, + "comma-style": 2, + "no-underscore-dangle": 0, + "no-constant-condition": 0, + "no-multi-spaces": 0, + "strict": 0, + "key-spacing": 0, + "no-shadow": 0 + }, + "globals": { + "L": true, + "module": false, + "define": false + }, + "env": { + "browser": true + } + } } diff --git a/src/control/Control.Zoom.js b/src/control/Control.Zoom.js index 86a82b02..978d2579 100644 --- a/src/control/Control.Zoom.js +++ b/src/control/Control.Zoom.js @@ -100,4 +100,3 @@ L.Map.addInitHook(function () { L.control.zoom = function (options) { return new L.Control.Zoom(options); }; - diff --git a/src/core/Util.js b/src/core/Util.js index fc66a76e..f052bd9a 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -42,9 +42,10 @@ L.Util = { // return unique ID of an object stamp: function (obj) { - // jshint camelcase: false + /*eslint-disable */ obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId; return obj._leaflet_id; + /*eslint-enable */ }, lastId: 0, diff --git a/src/dom/DomEvent.js b/src/dom/DomEvent.js index 9a59441e..9d67761f 100644 --- a/src/dom/DomEvent.js +++ b/src/dom/DomEvent.js @@ -54,7 +54,7 @@ L.DomEvent = { if (L.Browser.pointer && type.indexOf('touch') === 0) { this.addPointerListener(obj, type, handler, id); - + } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { this.addDoubleTapListener(obj, handler, id); @@ -67,8 +67,9 @@ L.DomEvent = { } else if ((type === 'mouseenter') || (type === 'mouseleave')) { handler = function (e) { e = e || window.event; - if (!L.DomEvent._checkMouse(obj, e)) { return; } - return originalHandler(e); + if (L.DomEvent._checkMouse(obj, e)) { + originalHandler(e); + } }; obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); @@ -240,7 +241,7 @@ L.DomEvent = { } L.DomEvent._lastClick = timeStamp; - return handler(e); + handler(e); } }; diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index c13c41e7..af0600fd 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -145,8 +145,9 @@ L.DomUtil = { setPosition: function (el, point, no3d) { // (HTMLElement, Point[, Boolean]) - // jshint camelcase: false + /*eslint-disable */ el._leaflet_pos = point; + /*eslint-enable */ if (L.Browser.any3d && !no3d) { L.DomUtil.setTransform(el, point); diff --git a/src/geo/LatLng.js b/src/geo/LatLng.js index 27ec3195..f040cb62 100644 --- a/src/geo/LatLng.js +++ b/src/geo/LatLng.js @@ -80,4 +80,3 @@ L.latLng = function (a, b, c) { } return new L.LatLng(a, b, c); }; - diff --git a/src/geometry/LineUtil.js b/src/geometry/LineUtil.js index ab6a7b6c..6268f3fb 100644 --- a/src/geometry/LineUtil.js +++ b/src/geometry/LineUtil.js @@ -10,7 +10,7 @@ L.LineUtil = { // Simplify polyline with vertex reduction and Douglas-Peucker simplification. // Improves rendering performance dramatically by lessening the number of points to draw. - simplify: function (/*Point[]*/ points, /*Number*/ tolerance) { + simplify: function (points, tolerance) { if (!tolerance || !points.length) { return points.slice(); } @@ -27,11 +27,11 @@ L.LineUtil = { }, // distance from a point to a segment between two points - pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + pointToSegmentDistance: function (p, p1, p2) { return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); }, - closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + closestPointOnSegment: function (p, p1, p2) { return this._sqClosestPointOnSegment(p, p1, p2); }, diff --git a/src/geometry/Point.js b/src/geometry/Point.js index 20c5aa59..be4437c8 100644 --- a/src/geometry/Point.js +++ b/src/geometry/Point.js @@ -2,7 +2,7 @@ * L.Point represents a point with x and y coordinates. */ -L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { +L.Point = function (x, y, round) { this.x = (round ? Math.round(x) : x); this.y = (round ? Math.round(y) : y); }; diff --git a/src/layer/Popup.js b/src/layer/Popup.js index 3a702971..0391f845 100644 --- a/src/layer/Popup.js +++ b/src/layer/Popup.js @@ -132,7 +132,7 @@ L.Popup = L.Layer.extend({ } return events; }, - + isOpen: function () { return !!this._map && this._map.hasLayer(this); }, diff --git a/src/layer/marker/Marker.js b/src/layer/marker/Marker.js index 6d40888a..309c89aa 100644 --- a/src/layer/marker/Marker.js +++ b/src/layer/marker/Marker.js @@ -58,7 +58,7 @@ L.Marker = L.Layer.extend({ var oldLatLng = this._latlng; this._latlng = L.latLng(latlng); this.update(); - return this.fire('move', { oldLatLng: oldLatLng, latlng: this._latlng }); + return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); }, setZIndexOffset: function (offset) { diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 52294704..052c95b6 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -220,7 +220,8 @@ L.GridLayer = L.Layer.extend({ tile.retain = true; if (!tile.loaded) { - found = this._retainParent(i, j, z, z - 5) || this._retainChildren(i, j, z, z + 2); + found = this._retainParent(i, j, z, z - 5); + if (!found) { this._retainChildren(i, j, z, z + 2); } } } } diff --git a/src/layer/vector/SVG.js b/src/layer/vector/SVG.js index cb538b35..d230d697 100644 --- a/src/layer/vector/SVG.js +++ b/src/layer/vector/SVG.js @@ -122,7 +122,7 @@ L.SVG = L.Renderer.extend({ // drawing a circle with two half-arcs var d = layer._empty() ? 'M0 0' : 'M' + (p.x - r) + ',' + p.y + - arc + (r * 2) + ',0 ' + + arc + (r * 2) + ',0 ' + arc + (-r * 2) + ',0 '; this._setPath(layer, d); diff --git a/src/map/handler/Map.BoxZoom.js b/src/map/handler/Map.BoxZoom.js index fcca466c..dff50690 100644 --- a/src/map/handler/Map.BoxZoom.js +++ b/src/map/handler/Map.BoxZoom.js @@ -83,7 +83,7 @@ L.Map.BoxZoom = L.Handler.extend({ }, _onMouseUp: function (e) { - if ((e.which !== 1) && (e.button !== 1)) { return false; } + if ((e.which !== 1) && (e.button !== 1)) { return; } this._finish(); diff --git a/src/map/handler/Map.Tap.js b/src/map/handler/Map.Tap.js index 214696a3..bc0b27e6 100644 --- a/src/map/handler/Map.Tap.js +++ b/src/map/handler/Map.Tap.js @@ -48,7 +48,7 @@ L.Map.Tap = L.Handler.extend({ this._simulateEvent('contextmenu', first); } }, this), 1000); - + this._simulateEvent('mousedown', first); L.DomEvent.on(document, { @@ -73,7 +73,7 @@ L.Map.Tap = L.Handler.extend({ if (el && el.tagName && el.tagName.toLowerCase() === 'a') { L.DomUtil.removeClass(el, 'leaflet-active'); } - + this._simulateEvent('mouseup', first); // simulate click if the touch didn't move too much From d145b1fdba3de29200b1e8e0215eff71545c91eb Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 28 Jan 2015 19:32:27 +0200 Subject: [PATCH 23/89] fix a bunch of eslint warnings in the spec suite --- spec/suites/SpecHelper.js | 2 +- spec/suites/core/EventsSpec.js | 6 +++--- spec/suites/core/UtilSpec.js | 5 +++-- spec/suites/geo/LatLngSpec.js | 1 - spec/suites/layer/LayerGroupSpec.js | 2 +- spec/suites/layer/marker/MarkerSpec.js | 14 +++++++------- spec/suites/layer/tile/GridLayerSpec.js | 11 ++++++----- spec/suites/layer/vector/CircleMarkerSpec.js | 14 +++++++------- 8 files changed, 28 insertions(+), 27 deletions(-) diff --git a/spec/suites/SpecHelper.js b/spec/suites/SpecHelper.js index cabff4b2..1e5256b0 100644 --- a/spec/suites/SpecHelper.js +++ b/spec/suites/SpecHelper.js @@ -1,4 +1,5 @@ if (!Array.prototype.map) { + /*eslint no-extend-native:0*/ Array.prototype.map = function (fun /*, thisp */) { "use strict"; @@ -7,7 +8,6 @@ if (!Array.prototype.map) { } var t = Object(this); - // jshint bitwise: false var len = t.length >>> 0; if (typeof fun !== "function") { throw new TypeError(); diff --git a/spec/suites/core/EventsSpec.js b/spec/suites/core/EventsSpec.js index 8af595a1..72df55cc 100644 --- a/spec/suites/core/EventsSpec.js +++ b/spec/suites/core/EventsSpec.js @@ -14,7 +14,7 @@ describe('Events', function () { obj.addEventListener('test', spy1); obj.addEventListener('test', spy2); obj.addEventListener('other', spy3); - obj.addEventListener({ test: spy4, other: spy5 }); + obj.addEventListener({test: spy4, other: spy5}); // obj.addEventListener({'test other': spy6 }); expect(spy1.called).to.be(false); @@ -72,8 +72,8 @@ describe('Events', function () { obj.addEventListener('test', listener1); obj2.addEventListener('test', listener2, foo); - obj3.addEventListener({ test: listener3 }); - obj4.addEventListener({ test: listener4 }, foo); + obj3.addEventListener({test: listener3}); + obj4.addEventListener({test: listener4}, foo); obj.fireEvent('test', {baz: 1}); obj2.fireEvent('test', {baz: 2}); diff --git a/spec/suites/core/UtilSpec.js b/spec/suites/core/UtilSpec.js index a8e01991..ddd2e82d 100644 --- a/spec/suites/core/UtilSpec.js +++ b/spec/suites/core/UtilSpec.js @@ -40,9 +40,9 @@ describe('Util', function () { return this; }; - var fn2 = L.Util.bind(fn, { foo: 'bar' }); + var fn2 = L.Util.bind(fn, {foo: 'bar'}); - expect(fn2()).to.eql({ foo: 'bar' }); + expect(fn2()).to.eql({foo: 'bar'}); }); it('passes additional arguments to the bound function', function () { @@ -230,6 +230,7 @@ describe('Util', function () { describe('#isArray', function () { expect(L.Util.isArray([1, 2, 3])).to.be(true); + /*eslint no-array-constructor:0*/ expect(L.Util.isArray(new Array(1, 2, 3))).to.be(true); expect(L.Util.isArray('blabla')).to.be(false); expect(L.Util.isArray({0: 1, 1: 2})).to.be(false); diff --git a/spec/suites/geo/LatLngSpec.js b/spec/suites/geo/LatLngSpec.js index c7de51db..3b8bce0e 100644 --- a/spec/suites/geo/LatLngSpec.js +++ b/spec/suites/geo/LatLngSpec.js @@ -117,4 +117,3 @@ describe('LatLng', function () { }); }); }); - diff --git a/spec/suites/layer/LayerGroupSpec.js b/spec/suites/layer/LayerGroupSpec.js index 21220f8e..d157fafc 100644 --- a/spec/suites/layer/LayerGroupSpec.js +++ b/spec/suites/layer/LayerGroupSpec.js @@ -58,7 +58,7 @@ it('iterates over all layers', function () { var lg = L.layerGroup(), marker = L.marker([0, 0]), - ctx = { foo: 'bar' }; + ctx = {foo: 'bar'}; lg.addLayer(marker); diff --git a/spec/suites/layer/marker/MarkerSpec.js b/spec/suites/layer/marker/MarkerSpec.js index a67e3343..e709c6fc 100644 --- a/spec/suites/layer/marker/MarkerSpec.js +++ b/spec/suites/layer/marker/MarkerSpec.js @@ -48,11 +48,11 @@ describe("Marker", function () { }); it("changes the icon to another DivIcon", function () { - var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); + var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})}); map.addLayer(marker); var beforeIcon = marker._icon; - marker.setIcon(new L.DivIcon({html: 'Inner2Text' })); + marker.setIcon(new L.DivIcon({html: 'Inner2Text'})); var afterIcon = marker._icon; expect(beforeIcon).to.be(afterIcon); @@ -60,7 +60,7 @@ describe("Marker", function () { }); it("removes text when changing to a blank DivIcon", function () { - var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); + var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})}); map.addLayer(marker); marker.setIcon(new L.DivIcon()); @@ -70,7 +70,7 @@ describe("Marker", function () { }); it("changes a DivIcon to an image", function () { - var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text' }) }); + var marker = new L.Marker([0, 0], {icon: new L.DivIcon({html: 'Inner1Text'})}); map.addLayer(marker); var oldIcon = marker._icon; @@ -92,7 +92,7 @@ describe("Marker", function () { map.addLayer(marker); var oldIcon = marker._icon; - marker.setIcon(new L.DivIcon({html: 'Inner1Text' })); + marker.setIcon(new L.DivIcon({html: 'Inner1Text'})); expect(oldIcon).to.not.be(marker._icon); expect(oldIcon.parentNode).to.be(null); @@ -102,7 +102,7 @@ describe("Marker", function () { }); it("reuses the icon/shadow when changing icon", function () { - var marker = new L.Marker([0, 0], { icon: icon1}); + var marker = new L.Marker([0, 0], {icon: icon1}); map.addLayer(marker); var oldIcon = marker._icon; var oldShadow = marker._shadow; @@ -120,7 +120,7 @@ describe("Marker", function () { describe("#setLatLng", function () { it("fires a move event", function () { - var marker = new L.Marker([0, 0], { icon: icon1 }); + var marker = new L.Marker([0, 0], {icon: icon1}); map.addLayer(marker); var beforeLatLng = marker._latlng; diff --git a/spec/suites/layer/tile/GridLayerSpec.js b/spec/suites/layer/tile/GridLayerSpec.js index d5cfdfe8..06c5f066 100644 --- a/spec/suites/layer/tile/GridLayerSpec.js +++ b/spec/suites/layer/tile/GridLayerSpec.js @@ -202,11 +202,12 @@ describe('GridLayer', function () { }); describe("when a tilelayer is removed from a map", function () { it("has its zoomlevels updated to only fit the layers it currently has", function () { - var tiles = [ L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map), - L.gridLayer({minZoom: 5, maxZoom: 10}).addTo(map), - L.gridLayer({minZoom: 10, maxZoom: 20}).addTo(map), - L.gridLayer({minZoom: 0, maxZoom: 25}).addTo(map) - ]; + var tiles = [ + L.gridLayer({minZoom: 10, maxZoom: 15}).addTo(map), + L.gridLayer({minZoom: 5, maxZoom: 10}).addTo(map), + L.gridLayer({minZoom: 10, maxZoom: 20}).addTo(map), + L.gridLayer({minZoom: 0, maxZoom: 25}).addTo(map) + ]; map.whenReady(function () { expect(map.getMinZoom()).to.be(0); expect(map.getMaxZoom()).to.be(25); diff --git a/spec/suites/layer/vector/CircleMarkerSpec.js b/spec/suites/layer/vector/CircleMarkerSpec.js index 81a712ed..11094392 100644 --- a/spec/suites/layer/vector/CircleMarkerSpec.js +++ b/spec/suites/layer/vector/CircleMarkerSpec.js @@ -8,7 +8,7 @@ describe("when a CircleMarker is added to the map ", function () { describe("with a radius set as an option", function () { it("takes that radius", function () { - var marker = L.circleMarker([0, 0], { radius: 20 }).addTo(map); + var marker = L.circleMarker([0, 0], {radius: 20}).addTo(map); expect(marker._radius).to.be(20); }); @@ -16,7 +16,7 @@ describe("and radius is set before adding it", function () { it("takes that radius", function () { - var marker = L.circleMarker([0, 0], { radius: 20 }); + var marker = L.circleMarker([0, 0], {radius: 20}); marker.setRadius(15); marker.addTo(map); expect(marker._radius).to.be(15); @@ -25,7 +25,7 @@ describe("and radius is set after adding it", function () { it("takes that radius", function () { - var marker = L.circleMarker([0, 0], { radius: 20 }); + var marker = L.circleMarker([0, 0], {radius: 20}); marker.addTo(map); marker.setRadius(15); expect(marker._radius).to.be(15); @@ -34,16 +34,16 @@ describe("and setStyle is used to change the radius after adding", function () { it("takes the given radius", function () { - var marker = L.circleMarker([0, 0], { radius: 20 }); + var marker = L.circleMarker([0, 0], {radius: 20}); marker.addTo(map); - marker.setStyle({ radius: 15 }); + marker.setStyle({radius: 15}); expect(marker._radius).to.be(15); }); }); describe("and setStyle is used to change the radius before adding", function () { it("takes the given radius", function () { - var marker = L.circleMarker([0, 0], { radius: 20 }); - marker.setStyle({ radius: 15 }); + var marker = L.circleMarker([0, 0], {radius: 20}); + marker.setStyle({radius: 15}); marker.addTo(map); expect(marker._radius).to.be(15); }); From aeb508317065578eef21fb4a4ef43be3c63994c0 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 28 Jan 2015 19:33:45 +0200 Subject: [PATCH 24/89] complete the switch to ESLint --- Jakefile.js | 12 ++++++------ build/eslintrc.json | 28 ++++++++++++++++++++++++++++ build/hintrc.js | 40 ---------------------------------------- package.json | 31 +------------------------------ spec/eslintrc.json | 37 +++++++++++++++++++++++++++++++++++++ src/core/Class.js | 1 - src/dom/DomUtil.js | 1 - src/geometry/LineUtil.js | 2 -- src/geometry/PolyUtil.js | 2 -- 9 files changed, 72 insertions(+), 82 deletions(-) create mode 100644 build/eslintrc.json delete mode 100644 build/hintrc.js create mode 100644 spec/eslintrc.json diff --git a/Jakefile.js b/Jakefile.js index 1e3599c9..dcb73dfc 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -15,10 +15,10 @@ For a custom build, open build/build.html in the browser and follow the instruct var build = require('./build/build.js'), version = require('./src/Leaflet.js').version; -function hint(msg, paths) { +function hint(msg, args) { return function () { console.log(msg); - jake.exec('node node_modules/jshint/bin/jshint -c ' + paths, + jake.exec('node node_modules/eslint/bin/eslint.js ' + args, {printStdout: true}, function () { console.log('\tCheck passed.\n'); complete(); @@ -26,11 +26,11 @@ function hint(msg, paths) { }; } -desc('Check Leaflet source for errors with JSHint'); -task('lint', {async: true}, hint('Checking for JS errors...', 'build/hintrc.js src')); +desc('Check Leaflet source for errors with ESLint'); +task('lint', {async: true}, hint('Checking for JS errors...', 'src --config build/eslintrc.json')); -desc('Check Leaflet specs source for errors with JSHint'); -task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/spec.hintrc.js spec/suites')); +desc('Check Leaflet specs source for errors with ESLint'); +task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/eslintrc.json')); desc('Combine and compress Leaflet source files'); task('build', {async: true}, function (compsBase32, buildName) { diff --git a/build/eslintrc.json b/build/eslintrc.json new file mode 100644 index 00000000..19e75a8f --- /dev/null +++ b/build/eslintrc.json @@ -0,0 +1,28 @@ +{ + "rules": { + "camelcase": 2, + "quotes": [2, "single"], + "no-mixed-spaces-and-tabs": [2, "smart-tabs"], + "space-after-function-name": 2, + "space-in-parens": 2, + "space-in-brackets": 2, + "space-before-blocks": 2, + "space-after-keywords": 2, + "no-lonely-if": 2, + "comma-style": 2, + "no-underscore-dangle": 0, + "no-constant-condition": 0, + "no-multi-spaces": 0, + "strict": 0, + "key-spacing": 0, + "no-shadow": 0 + }, + "globals": { + "L": true, + "module": false, + "define": false + }, + "env": { + "browser": true + } +} diff --git a/build/hintrc.js b/build/hintrc.js deleted file mode 100644 index 596aeafe..00000000 --- a/build/hintrc.js +++ /dev/null @@ -1,40 +0,0 @@ -{ - // environment - "browser": true, - "node": true, - "globals": { - "L": true, - "define": true - }, - "strict": false, - "es3": true, - - // code style - "bitwise": true, - "camelcase": true, - "curly": true, - "eqeqeq": true, - "forin": false, - "immed": true, - "latedef": true, - "newcap": true, - "noarg": true, - "noempty": true, - "nonew": true, - "undef": true, - "unused": true, - "quotmark": "single", - - // whitespace - "indent": 4, - "trailing": true, - "white": true, - "smarttabs": true, - "maxlen": 120 - - // code simplicity - not enforced but nice to check from time to time - // "maxstatements": 20, - // "maxcomplexity": 5 - // "maxparams": 4, - // "maxdepth": 4 -} diff --git a/package.json b/package.json index 7fd0e73d..af41f9d8 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "eslint": "^0.13.0", "happen": "~0.1.3", "jake": "~8.0.10", - "jshint": "~2.6.0", "karma": "~0.12.31", "karma-chrome-launcher": "^0.1.7", "karma-coverage": "~0.2.7", @@ -39,33 +38,5 @@ "type": "BSD-2-Clause", "url": "https://github.com/Leaflet/Leaflet/blob/master/LICENSE" } - ], - "eslintConfig": { - "rules": { - "camelcase": 2, - "quotes": [2, "single"], - "no-mixed-spaces-and-tabs": [2, "smart-tabs"], - "space-after-function-name": 2, - "space-in-parens": 2, - "space-in-brackets": 2, - "space-before-blocks": 2, - "space-after-keywords": 2, - "no-lonely-if": 2, - "comma-style": 2, - "no-underscore-dangle": 0, - "no-constant-condition": 0, - "no-multi-spaces": 0, - "strict": 0, - "key-spacing": 0, - "no-shadow": 0 - }, - "globals": { - "L": true, - "module": false, - "define": false - }, - "env": { - "browser": true - } - } + ] } diff --git a/spec/eslintrc.json b/spec/eslintrc.json new file mode 100644 index 00000000..32b81cc5 --- /dev/null +++ b/spec/eslintrc.json @@ -0,0 +1,37 @@ +{ + "rules": { + "camelcase": 2, + "no-mixed-spaces-and-tabs": [2, "smart-tabs"], + "space-after-function-name": 2, + "space-in-parens": 2, + "space-in-brackets": 2, + "space-before-blocks": 2, + "space-after-keywords": 2, + "no-lonely-if": 2, + "comma-style": 2, + "no-unused-vars": 0, + "quotes": 0, + "no-underscore-dangle": 0, + "no-constant-condition": 0, + "no-multi-spaces": 0, + "strict": 0, + "key-spacing": 0, + "no-shadow": 0, + "no-irregular-whitespace": 0 + }, + "globals": { + "L": true, + "module": false, + "define": false, + "expect": false, + "it": false, + "describe": false, + "sinon": false, + "happen": false, + "beforeEach": false, + "afterEach": false + }, + "env": { + "browser": true + } +} diff --git a/src/core/Class.js b/src/core/Class.js index c2d74cb2..c7a18ab4 100644 --- a/src/core/Class.js +++ b/src/core/Class.js @@ -19,7 +19,6 @@ L.Class.extend = function (props) { this.callInitHooks(); }; - // jshint camelcase: false var parentProto = NewClass.__super__ = this.prototype; var proto = L.Util.create(parentProto); diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index af0600fd..a9c1ea14 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -161,7 +161,6 @@ L.DomUtil = { // this method is only used for elements previously positioned using setPosition, // so it's safe to cache the position for performance - // jshint camelcase: false return el._leaflet_pos; } }; diff --git a/src/geometry/LineUtil.js b/src/geometry/LineUtil.js index 6268f3fb..8948c471 100644 --- a/src/geometry/LineUtil.js +++ b/src/geometry/LineUtil.js @@ -3,8 +3,6 @@ * and polylines (clipping, simplification, distances, etc.) */ -/*jshint bitwise:false */ // allow bitwise operations for this file - L.LineUtil = { // Simplify polyline with vertex reduction and Douglas-Peucker simplification. diff --git a/src/geometry/PolyUtil.js b/src/geometry/PolyUtil.js index 68aa36d8..37316e4a 100644 --- a/src/geometry/PolyUtil.js +++ b/src/geometry/PolyUtil.js @@ -2,8 +2,6 @@ * L.PolyUtil contains utility functions for polygons (clipping, etc.). */ -/*jshint bitwise:false */ // allow bitwise operations here - L.PolyUtil = {}; /* From 22cfd12956f6dcf4e3f5d3e2ee34b8046bdf1fe4 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 12 Jan 2015 17:42:29 -0800 Subject: [PATCH 25/89] Fade tiles with requestAnimationFrame rather than CSS --- dist/leaflet.css | 2 - src/layer/tile/GridLayer.js | 81 ++++++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index 0d6f2dff..05e2bb2e 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -139,7 +139,6 @@ /* zoom and fade animations */ -.leaflet-fade-anim .leaflet-tile, .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; @@ -147,7 +146,6 @@ -o-transition: opacity 0.2s linear; transition: opacity 0.2s linear; } -.leaflet-fade-anim .leaflet-tile-loaded, .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 052c95b6..578e41de 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -149,13 +149,32 @@ L.GridLayer = L.Layer.extend({ _updateOpacity: function () { var opacity = this.options.opacity; - if (L.Browser.ielt9) { - // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles - for (var i in this._tiles) { - L.DomUtil.setOpacity(this._tiles[i].el, opacity); - } - } else { + // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles + if (!L.Browser.ielt9 && !this._map._fadeAnimated) { L.DomUtil.setOpacity(this._container, opacity); + return; + } + + var now = +new Date(), + nextFrame = false; + + for (var key in this._tiles) { + var tile = this._tiles[key]; + if (!tile.loaded || tile.active) { continue; } + + var fade = Math.min(1, (now - tile.loaded) / 200); + if (fade < 1) { + L.DomUtil.setOpacity(tile.el, opacity * fade); + nextFrame = true; + } else { + L.DomUtil.setOpacity(tile.el, opacity); + tile.active = true; + this._pruneTiles(); + } + } + + if (nextFrame) { + L.Util.requestAnimFrame(this._updateOpacity, this); } }, @@ -194,6 +213,18 @@ L.GridLayer = L.Layer.extend({ this._level = level; + if (this._map._fadeAnimated) { + var now = +new Date(); + for (var key in this._tiles) { + var tile = this._tiles[key]; + if (tile.loaded && this._keyToTileCoords(key).z === zoom) { + tile.active = false; + tile.loaded = now; + } + } + this._updateOpacity(); + } + return level; }, @@ -204,7 +235,7 @@ L.GridLayer = L.Layer.extend({ var bounds = this._map.getBounds(), z = this._tileZoom, range = this._getTileRange(bounds, z), - i, j, key, tile, found; + i, j, key, tile; for (key in this._tiles) { this._tiles[key].retain = false; @@ -216,26 +247,19 @@ L.GridLayer = L.Layer.extend({ key = i + ':' + j + ':' + z; tile = this._tiles[key]; - if (!tile) { continue; } - tile.retain = true; + if ((!tile || !tile.active) && !this._retainParent(i, j, z, z - 5)) { + this._retainChildren(i, j, z, z + 2); + } - if (!tile.loaded) { - found = this._retainParent(i, j, z, z - 5); - if (!found) { this._retainChildren(i, j, z, z + 2); } + if (tile) { + tile.retain = true; } } } for (key in this._tiles) { - tile = this._tiles[key]; - if (!tile.retain) { - if (!tile.loaded) { - this._removeTile(key); - } else if (this._map._fadeAnimated) { - setTimeout(L.bind(this._deferRemove, this, key), 250); - } else { - this._removeTile(key); - } + if (!this._tiles[key].retain) { + this._removeTile(key); } } }, @@ -246,13 +270,6 @@ L.GridLayer = L.Layer.extend({ } }, - _deferRemove: function (key) { - var tile = this._tiles[key]; - if (tile && !tile.retain) { - this._removeTile(key); - } - }, - _retainParent: function (x, y, z, minZoom) { var x2 = Math.floor(x / 2), y2 = Math.floor(y / 2), @@ -555,7 +572,13 @@ L.GridLayer = L.Layer.extend({ tile = this._tiles[key]; if (!tile) { return; } - tile.loaded = true; + tile.loaded = +new Date(); + if (this._map._fadeAnimated) { + L.Util.requestAnimFrame(this._updateOpacity, this); + } else { + tile.active = true; + } + this._pruneTiles(); L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded'); From bbf32ebfcdb0d24d187dbb54eecbea367fd7b72d Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 14 Jan 2015 15:17:55 -0800 Subject: [PATCH 26/89] Begin tile loading and fading during zoom animation --- src/layer/tile/GridLayer.js | 59 +++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 578e41de..328ecc3c 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -34,7 +34,7 @@ L.GridLayer = L.Layer.extend({ this._levels = {}; this._tiles = {}; - this._reset(); + this._viewReset(); this._update(); }, @@ -99,13 +99,13 @@ L.GridLayer = L.Layer.extend({ getEvents: function () { var events = { - viewreset: this._reset, - moveend: this._update + viewreset: this._viewReset, + moveend: this._move }; if (!this.options.updateWhenIdle) { // update tiles on move, but not more often than once per given interval - events.move = L.Util.throttle(this._update, this.options.updateInterval, this); + events.move = L.Util.throttle(this._move, this.options.updateInterval, this); } if (this._zoomAnimated) { @@ -192,7 +192,8 @@ L.GridLayer = L.Layer.extend({ }, _updateLevels: function () { - var zoom = this._tileZoom; + var zoom = this._tileZoom, + offset; for (var z in this._levels) { this._levels[z].el.style.zIndex = -Math.abs(zoom - z); @@ -209,6 +210,9 @@ L.GridLayer = L.Layer.extend({ level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); level.zoom = zoom; + + this._setZoomTransform(level, map.getCenter(), map.getZoom()); + offset = level.el.offsetWidth; // Force recalculation to trigger transitions. } this._level = level; @@ -307,22 +311,30 @@ L.GridLayer = L.Layer.extend({ } }, - _reset: function (e) { - var map = this._map, - zoom = map.getZoom(), - tileZoom = Math.round(zoom), - tileZoomChanged = this._tileZoom !== tileZoom; + _viewReset: function (e) { + var map = this._map; + this._reset(map.getCenter(), map.getZoom(), e && e.hard); + }, - if (tileZoomChanged || (e && e.hard)) { + _animateZoom: function (e) { + this._reset(e.center, e.zoom); + }, + + _reset: function (center, zoom, hard) { + var tileZoom = Math.round(zoom), + tileZoomChanged = this._tileZoom !== tileZoom; + + if (tileZoomChanged || hard) { if (this._abortLoading) { this._abortLoading(); } this._tileZoom = tileZoom; this._updateLevels(); this._resetGrid(); + this._update(center, zoom); } - this._setZoomTransforms(map.getCenter(), zoom); + this._setZoomTransforms(center, zoom); }, _setZoomTransforms: function (center, zoom) { @@ -364,8 +376,14 @@ L.GridLayer = L.Layer.extend({ return this.options.tileSize; }, - _update: function () { - if (!this._map) { return; } + _move: function () { + this._update(); + }, + + _update: function (center, zoom) { + + var map = this._map; + if (!map) { return; } // TODO move to reset // var zoom = this._map.getZoom(); @@ -373,7 +391,14 @@ L.GridLayer = L.Layer.extend({ // if (zoom > this.options.maxZoom || // zoom < this.options.minZoom) { return; } - var bounds = this._map.getBounds(); + if (center === undefined) { center = map.getCenter(); } + if (zoom === undefined) { zoom = map.getZoom(); } + + var topLeftPoint = map._getNewPixelOrigin(center, zoom).subtract(map._getMapPanePos()), + pixelBounds = new L.Bounds(topLeftPoint, topLeftPoint.add(map.getSize())), + sw = map.unproject(pixelBounds.getBottomLeft(), zoom), + ne = map.unproject(pixelBounds.getTopRight(), zoom), + bounds = new L.LatLngBounds(sw, ne); if (this.options.unloadInvisibleTiles) { this._removeOtherTiles(bounds); @@ -611,10 +636,6 @@ L.GridLayer = L.Layer.extend({ bounds.max.divideBy(this._tileSize).ceil().subtract([1, 1])); }, - _animateZoom: function (e) { - this._setZoomTransforms(e.center, e.zoom); - }, - _noTilesToLoad: function () { for (var key in this._tiles) { if (!this._tiles[key].loaded) { return false; } From e7a7bfc9eabf612934aa2a78b29f6a2928ca7000 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 28 Jan 2015 19:45:56 +0200 Subject: [PATCH 27/89] fix build --- src/layer/tile/GridLayer.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 328ecc3c..db92892f 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -192,8 +192,7 @@ L.GridLayer = L.Layer.extend({ }, _updateLevels: function () { - var zoom = this._tileZoom, - offset; + var zoom = this._tileZoom; for (var z in this._levels) { this._levels[z].el.style.zIndex = -Math.abs(zoom - z); @@ -212,7 +211,7 @@ L.GridLayer = L.Layer.extend({ level.zoom = zoom; this._setZoomTransform(level, map.getCenter(), map.getZoom()); - offset = level.el.offsetWidth; // Force recalculation to trigger transitions. + L.Util.falseFn(level.el.offsetWidth); // Force recalculation to trigger transitions. } this._level = level; From bb19fc1bf8a66740ccfb721396d9cf97c19196dc Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 29 Jan 2015 11:21:22 -0800 Subject: [PATCH 28/89] Deterministic tile pruning Do not throttle _pruneTiles. Instead, when adding new tiles, prune tiles that are no longer needed immediately, and when a new tile finishes its fade animation, immediately prune just its parents/children. --- src/layer/tile/GridLayer.js | 140 +++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index db92892f..bf4304a4 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -29,8 +29,6 @@ L.GridLayer = L.Layer.extend({ onAdd: function () { this._initContainer(); - this._pruneTiles = L.Util.throttle(this._pruneTiles, 200, this); - this._levels = {}; this._tiles = {}; @@ -169,7 +167,7 @@ L.GridLayer = L.Layer.extend({ } else { L.DomUtil.setOpacity(tile.el, opacity); tile.active = true; - this._pruneTiles(); + this._pruneTiles(this._keyToTileCoords(key)); } } @@ -231,39 +229,10 @@ L.GridLayer = L.Layer.extend({ return level; }, - _pruneTiles: function () { - - if (!this._map) { return; } - - var bounds = this._map.getBounds(), - z = this._tileZoom, - range = this._getTileRange(bounds, z), - i, j, key, tile; - - for (key in this._tiles) { - this._tiles[key].retain = false; - } - - for (i = range.min.x; i <= range.max.x; i++) { - for (j = range.min.y; j <= range.max.y; j++) { - - key = i + ':' + j + ':' + z; - tile = this._tiles[key]; - - if ((!tile || !tile.active) && !this._retainParent(i, j, z, z - 5)) { - this._retainChildren(i, j, z, z + 2); - } - - if (tile) { - tile.retain = true; - } - } - } - - for (key in this._tiles) { - if (!this._tiles[key].retain) { - this._removeTile(key); - } + _pruneTiles: function (coords) { + if (coords.z !== this._tileZoom) { return; } + if (!this._pruneParent(coords.x, coords.y, coords.z, coords.z - 5)) { + this._pruneChildren(coords.x, coords.y, coords.z, coords.z + 2); } }, @@ -292,6 +261,25 @@ L.GridLayer = L.Layer.extend({ return false; }, + _pruneParent: function (x, y, z, minZoom) { + var x2 = Math.floor(x / 2), + y2 = Math.floor(y / 2), + z2 = z - 1; + + var key = x2 + ':' + y2 + ':' + z2, + tile = this._tiles[key]; + + if (tile && tile.loaded) { + this._removeTile(key); + return true; + + } else if (z2 > minZoom) { + return this._pruneParent(x2, y2, z2, minZoom); + } + + return false; + }, + _retainChildren: function (x, y, z, maxZoom) { for (var i = 2 * x; i < 2 * x + 2; i++) { @@ -310,9 +298,30 @@ L.GridLayer = L.Layer.extend({ } }, + _pruneChildren: function (x, y, z, maxZoom) { + + for (var i = 2 * x; i < 2 * x + 2; i++) { + for (var j = 2 * y; j < 2 * y + 2; j++) { + + var key = i + ':' + j + ':' + (z + 1), + tile = this._tiles[key]; + + if (tile && tile.loaded) { + this._removeTile(key); + + } else if (z + 1 < maxZoom) { + this._pruneChildren(i, j, z + 1, maxZoom); + } + } + } + }, + _viewReset: function (e) { var map = this._map; this._reset(map.getCenter(), map.getZoom(), e && e.hard); + if (this.options.unloadInvisibleTiles) { + this._removeOtherTiles(map.getBounds()); + } }, _animateZoom: function (e) { @@ -395,32 +404,16 @@ L.GridLayer = L.Layer.extend({ var topLeftPoint = map._getNewPixelOrigin(center, zoom).subtract(map._getMapPanePos()), pixelBounds = new L.Bounds(topLeftPoint, topLeftPoint.add(map.getSize())), - sw = map.unproject(pixelBounds.getBottomLeft(), zoom), - ne = map.unproject(pixelBounds.getTopRight(), zoom), - bounds = new L.LatLngBounds(sw, ne); - - if (this.options.unloadInvisibleTiles) { - this._removeOtherTiles(bounds); - } - - this._addTiles(bounds); - this._pruneTiles(); - }, - - // tile coordinates range for particular geo bounds and zoom - _getTileRange: function (bounds, zoom) { - var pxBounds = new L.Bounds( - this._map.project(bounds.getNorthWest(), zoom), - this._map.project(bounds.getSouthEast(), zoom)); - return this._pxBoundsToTileRange(pxBounds); - }, - - _addTiles: function (bounds) { - var queue = [], - tileRange = this._getTileRange(bounds, this._tileZoom), - center = tileRange.getCenter(), + queue = [], + key, + tileRange = this._pxBoundsToTileRange(pixelBounds), + tileCenter = tileRange.getCenter(), j, i, coords; + for (key in this._tiles) { + this._tiles[key].retain = false; + } + // create a queue of coordinates to load tiles from for (j = tileRange.min.y; j <= tileRange.max.y; j++) { for (i = tileRange.min.x; i <= tileRange.max.x; i++) { @@ -428,16 +421,25 @@ L.GridLayer = L.Layer.extend({ coords = new L.Point(i, j); coords.z = this._tileZoom; - // add tile to queue if it's not in cache or out of bounds - if (!(this._tileCoordsToKey(coords) in this._tiles) && this._isValidTile(coords)) { + if (!this._isValidTile(coords)) { continue; } + + var tile = this._tiles[this._tileCoordsToKey(coords)]; + + if (tile) { + tile.retain = true; + } else { queue.push(coords); } + + if ((!tile || !tile.active) && !this._retainParent(i, j, coords.z, coords.z - 5)) { + this._retainChildren(i, j, coords.z, coords.z + 2); + } } } // sort tile queue to load tiles in order of their distance to center queue.sort(function (a, b) { - return a.distanceTo(center) - b.distanceTo(center); + return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); }); if (queue.length !== 0) { @@ -455,6 +457,12 @@ L.GridLayer = L.Layer.extend({ this._level.el.appendChild(fragment); } + + for (key in this._tiles) { + if (!this._tiles[key].retain) { + this._removeTile(key); + } + } }, _isValidTile: function (coords) { @@ -572,7 +580,8 @@ L.GridLayer = L.Layer.extend({ // save tile in cache this._tiles[key] = { - el: tile + el: tile, + retain: true }; container.appendChild(tile); @@ -601,10 +610,9 @@ L.GridLayer = L.Layer.extend({ L.Util.requestAnimFrame(this._updateOpacity, this); } else { tile.active = true; + this._pruneTiles(coords); } - this._pruneTiles(); - L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded'); this.fire('tileload', { From 7d90dc152e84f8d617ad348cad0c42e1d8f98e0c Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 29 Jan 2015 16:20:02 -0800 Subject: [PATCH 29/89] Clean up pixel bounds calculations --- src/layer/tile/GridLayer.js | 8 +++----- src/map/Map.js | 11 +++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index bf4304a4..67b42761 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -402,13 +402,11 @@ L.GridLayer = L.Layer.extend({ if (center === undefined) { center = map.getCenter(); } if (zoom === undefined) { zoom = map.getZoom(); } - var topLeftPoint = map._getNewPixelOrigin(center, zoom).subtract(map._getMapPanePos()), - pixelBounds = new L.Bounds(topLeftPoint, topLeftPoint.add(map.getSize())), - queue = [], - key, + var pixelBounds = map.getPixelBounds(center, zoom), tileRange = this._pxBoundsToTileRange(pixelBounds), tileCenter = tileRange.getCenter(), - j, i, coords; + queue = [], + key, j, i, coords; for (key in this._tiles) { this._tiles[key].retain = false; diff --git a/src/map/Map.js b/src/map/Map.js index 2165c39a..0d5166f2 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -328,8 +328,8 @@ L.Map = L.Evented.extend({ return this._size.clone(); }, - getPixelBounds: function () { - var topLeftPoint = this._getTopLeftPoint(); + getPixelBounds: function (center, zoom) { + var topLeftPoint = this._getTopLeftPoint(center, zoom); return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); }, @@ -636,8 +636,11 @@ L.Map = L.Evented.extend({ return pos && !pos.equals([0, 0]); }, - _getTopLeftPoint: function () { - return this.getPixelOrigin().subtract(this._getMapPanePos()); + _getTopLeftPoint: function (center, zoom) { + var pixelOrigin = center && zoom !== undefined ? + this._getNewPixelOrigin(center, zoom) : + this.getPixelOrigin(); + return pixelOrigin.subtract(this._getMapPanePos()); }, _getNewPixelOrigin: function (center, zoom) { From 465cfbad293ee7067f9f39ec3eb50639ebe693be Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 30 Jan 2015 10:44:48 -0800 Subject: [PATCH 30/89] Remove opacity resetting on zoom of pre-existing tiles Now that we are fading during the zoom animation, this is no longer necessary. The zoom animation itself provides enough of a transition, and resetting the opacity can lead to more flickering than it solves. --- src/layer/tile/GridLayer.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 67b42761..057de550 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -214,18 +214,6 @@ L.GridLayer = L.Layer.extend({ this._level = level; - if (this._map._fadeAnimated) { - var now = +new Date(); - for (var key in this._tiles) { - var tile = this._tiles[key]; - if (tile.loaded && this._keyToTileCoords(key).z === zoom) { - tile.active = false; - tile.loaded = now; - } - } - this._updateOpacity(); - } - return level; }, From fb6eda1fbe9b47bea0693c71ef13c69847da2e29 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Sat, 31 Jan 2015 10:10:38 +0200 Subject: [PATCH 31/89] don't request redundant fade in frames --- src/layer/tile/GridLayer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 057de550..6f4ec711 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -172,7 +172,8 @@ L.GridLayer = L.Layer.extend({ } if (nextFrame) { - L.Util.requestAnimFrame(this._updateOpacity, this); + L.Util.cancelAnimFrame(this._fadeFrame); + this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); } }, From cbfbc883700f15e61dd41885c73b494cba32ade4 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Sat, 31 Jan 2015 10:13:04 +0200 Subject: [PATCH 32/89] optimize DomUtil.setOpacity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V8 can’t optimize functions with try/catch inside --- src/dom/DomUtil.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index a9c1ea14..489e9bee 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -100,27 +100,30 @@ L.DomUtil = { el.style.opacity = value; } else if ('filter' in el.style) { + L.DomUtil._setOpacityIE(el, value); + } + }, - var filter = false, - filterName = 'DXImageTransform.Microsoft.Alpha'; + _setOpacityIE: function (el, value) { + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; - // filters collection throws an error if we try to retrieve a filter that doesn't exist - try { - filter = el.filters.item(filterName); - } catch (e) { - // don't set opacity to 1 if we haven't already set an opacity, - // it isn't needed and breaks transparent pngs. - if (value === 1) { return; } - } + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } - value = Math.round(value * 100); + value = Math.round(value * 100); - if (filter) { - filter.Enabled = (value !== 100); - filter.Opacity = value; - } else { - el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; - } + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; } }, From 16b1b26116fe6735ecb4902491299c07ac5ebb48 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 2 Feb 2015 12:15:10 -0800 Subject: [PATCH 33/89] Adjust retention logic Continue to retain loaded-but-still-fading-in tiles, but also continue to look for loaded-and-active tiles. This reduces flicker on multiple rapid zoom actions. --- src/layer/tile/GridLayer.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 6f4ec711..dd203bce 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -239,11 +239,15 @@ L.GridLayer = L.Layer.extend({ var key = x2 + ':' + y2 + ':' + z2, tile = this._tiles[key]; - if (tile && tile.loaded) { + if (tile && tile.active) { tile.retain = true; return true; - } else if (z2 > minZoom) { + } else if (tile && tile.loaded) { + tile.retain = true; + } + + if (z2 > minZoom) { return this._retainParent(x2, y2, z2, minZoom); } @@ -258,7 +262,7 @@ L.GridLayer = L.Layer.extend({ var key = x2 + ':' + y2 + ':' + z2, tile = this._tiles[key]; - if (tile && tile.loaded) { + if (tile && tile.active) { this._removeTile(key); return true; @@ -277,10 +281,15 @@ L.GridLayer = L.Layer.extend({ var key = i + ':' + j + ':' + (z + 1), tile = this._tiles[key]; - if (tile && tile.loaded) { + if (tile && tile.active) { tile.retain = true; + continue; - } else if (z + 1 < maxZoom) { + } else if (tile && tile.loaded) { + tile.retain = true; + } + + if (z + 1 < maxZoom) { this._retainChildren(i, j, z + 1, maxZoom); } } @@ -295,7 +304,7 @@ L.GridLayer = L.Layer.extend({ var key = i + ':' + j + ':' + (z + 1), tile = this._tiles[key]; - if (tile && tile.loaded) { + if (tile && tile.active) { this._removeTile(key); } else if (z + 1 < maxZoom) { From 4d119c9cdbb286733837c1d1b8efc0f1da5ca9fa Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 2 Feb 2015 12:31:46 -0800 Subject: [PATCH 34/89] Don't prune tiles during animated zoom Tiles around the border will not be necessary when the zoom completes, but need to be retained during the zoom. --- src/layer/tile/GridLayer.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index dd203bce..a021915a 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -323,10 +323,10 @@ L.GridLayer = L.Layer.extend({ }, _animateZoom: function (e) { - this._reset(e.center, e.zoom); + this._reset(e.center, e.zoom, false, true); }, - _reset: function (center, zoom, hard) { + _reset: function (center, zoom, hard, noPrune) { var tileZoom = Math.round(zoom), tileZoomChanged = this._tileZoom !== tileZoom; @@ -337,7 +337,7 @@ L.GridLayer = L.Layer.extend({ this._tileZoom = tileZoom; this._updateLevels(); this._resetGrid(); - this._update(center, zoom); + this._update(center, zoom, noPrune); } this._setZoomTransforms(center, zoom); @@ -386,8 +386,7 @@ L.GridLayer = L.Layer.extend({ this._update(); }, - _update: function (center, zoom) { - + _update: function (center, zoom, noPrune) { var map = this._map; if (!map) { return; } @@ -454,6 +453,8 @@ L.GridLayer = L.Layer.extend({ this._level.el.appendChild(fragment); } + if (noPrune) { return; } + for (key in this._tiles) { if (!this._tiles[key].retain) { this._removeTile(key); From 602648111fbcc939dbba81faa6e0c25c6775c260 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 2 Feb 2015 13:46:15 -0800 Subject: [PATCH 35/89] Use rounded zoom level when calculating tile bounds --- src/layer/tile/GridLayer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index a021915a..6e24c77c 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -337,7 +337,7 @@ L.GridLayer = L.Layer.extend({ this._tileZoom = tileZoom; this._updateLevels(); this._resetGrid(); - this._update(center, zoom, noPrune); + this._update(center, tileZoom, noPrune); } this._setZoomTransforms(center, zoom); @@ -397,7 +397,7 @@ L.GridLayer = L.Layer.extend({ // zoom < this.options.minZoom) { return; } if (center === undefined) { center = map.getCenter(); } - if (zoom === undefined) { zoom = map.getZoom(); } + if (zoom === undefined) { zoom = Math.round(map.getZoom()); } var pixelBounds = map.getPixelBounds(center, zoom), tileRange = this._pxBoundsToTileRange(pixelBounds), @@ -414,7 +414,7 @@ L.GridLayer = L.Layer.extend({ for (i = tileRange.min.x; i <= tileRange.max.x; i++) { coords = new L.Point(i, j); - coords.z = this._tileZoom; + coords.z = zoom; if (!this._isValidTile(coords)) { continue; } From ddcfa5265a13311e40286d9ff1f0f283c22c2fe4 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 3 Feb 2015 15:25:22 +0200 Subject: [PATCH 36/89] less redundant opacity-changing frames --- src/layer/tile/GridLayer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 6e24c77c..1f6482ac 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -604,7 +604,8 @@ L.GridLayer = L.Layer.extend({ tile.loaded = +new Date(); if (this._map._fadeAnimated) { - L.Util.requestAnimFrame(this._updateOpacity, this); + L.Util.cancelAnimFrame(this._fadeFrame); + this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); } else { tile.active = true; this._pruneTiles(coords); From fc70a21884961a3cc2d9b498638abe4862a86b28 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 3 Feb 2015 14:05:03 -0800 Subject: [PATCH 37/89] Go back to non-incremental tile pruning algorithm --- src/layer/tile/GridLayer.js | 107 +++++++++++++----------------------- 1 file changed, 39 insertions(+), 68 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 1f6482ac..c092c8b1 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -167,7 +167,7 @@ L.GridLayer = L.Layer.extend({ } else { L.DomUtil.setOpacity(tile.el, opacity); tile.active = true; - this._pruneTiles(this._keyToTileCoords(key)); + this._pruneTiles(); } } @@ -218,10 +218,28 @@ L.GridLayer = L.Layer.extend({ return level; }, - _pruneTiles: function (coords) { - if (coords.z !== this._tileZoom) { return; } - if (!this._pruneParent(coords.x, coords.y, coords.z, coords.z - 5)) { - this._pruneChildren(coords.x, coords.y, coords.z, coords.z + 2); + _pruneTiles: function () { + var key, tile; + + for (key in this._tiles) { + tile = this._tiles[key]; + tile.retain = tile.current; + } + + for (key in this._tiles) { + tile = this._tiles[key]; + if (tile.current && !tile.active) { + var coords = tile.coords; + if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { + this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); + } + } + } + + for (key in this._tiles) { + if (!this._tiles[key].retain) { + this._removeTile(key); + } } }, @@ -254,25 +272,6 @@ L.GridLayer = L.Layer.extend({ return false; }, - _pruneParent: function (x, y, z, minZoom) { - var x2 = Math.floor(x / 2), - y2 = Math.floor(y / 2), - z2 = z - 1; - - var key = x2 + ':' + y2 + ':' + z2, - tile = this._tiles[key]; - - if (tile && tile.active) { - this._removeTile(key); - return true; - - } else if (z2 > minZoom) { - return this._pruneParent(x2, y2, z2, minZoom); - } - - return false; - }, - _retainChildren: function (x, y, z, maxZoom) { for (var i = 2 * x; i < 2 * x + 2; i++) { @@ -296,24 +295,6 @@ L.GridLayer = L.Layer.extend({ } }, - _pruneChildren: function (x, y, z, maxZoom) { - - for (var i = 2 * x; i < 2 * x + 2; i++) { - for (var j = 2 * y; j < 2 * y + 2; j++) { - - var key = i + ':' + j + ':' + (z + 1), - tile = this._tiles[key]; - - if (tile && tile.active) { - this._removeTile(key); - - } else if (z + 1 < maxZoom) { - this._pruneChildren(i, j, z + 1, maxZoom); - } - } - } - }, - _viewReset: function (e) { var map = this._map; this._reset(map.getCenter(), map.getZoom(), e && e.hard); @@ -337,7 +318,10 @@ L.GridLayer = L.Layer.extend({ this._tileZoom = tileZoom; this._updateLevels(); this._resetGrid(); - this._update(center, tileZoom, noPrune); + this._update(center, tileZoom); + if (!noPrune) { + this._pruneTiles(); + } } this._setZoomTransforms(center, zoom); @@ -384,9 +368,10 @@ L.GridLayer = L.Layer.extend({ _move: function () { this._update(); + this._pruneTiles(); }, - _update: function (center, zoom, noPrune) { + _update: function (center, zoom) { var map = this._map; if (!map) { return; } @@ -402,33 +387,26 @@ L.GridLayer = L.Layer.extend({ var pixelBounds = map.getPixelBounds(center, zoom), tileRange = this._pxBoundsToTileRange(pixelBounds), tileCenter = tileRange.getCenter(), - queue = [], - key, j, i, coords; + queue = []; - for (key in this._tiles) { - this._tiles[key].retain = false; + for (var key in this._tiles) { + this._tiles[key].current = false; } // create a queue of coordinates to load tiles from - for (j = tileRange.min.y; j <= tileRange.max.y; j++) { - for (i = tileRange.min.x; i <= tileRange.max.x; i++) { - - coords = new L.Point(i, j); + for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { + for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { + var coords = new L.Point(i, j); coords.z = zoom; if (!this._isValidTile(coords)) { continue; } var tile = this._tiles[this._tileCoordsToKey(coords)]; - if (tile) { - tile.retain = true; + tile.current = true; } else { queue.push(coords); } - - if ((!tile || !tile.active) && !this._retainParent(i, j, coords.z, coords.z - 5)) { - this._retainChildren(i, j, coords.z, coords.z + 2); - } } } @@ -452,14 +430,6 @@ L.GridLayer = L.Layer.extend({ this._level.el.appendChild(fragment); } - - if (noPrune) { return; } - - for (key in this._tiles) { - if (!this._tiles[key].retain) { - this._removeTile(key); - } - } }, _isValidTile: function (coords) { @@ -578,7 +548,8 @@ L.GridLayer = L.Layer.extend({ // save tile in cache this._tiles[key] = { el: tile, - retain: true + coords: coords, + current: true }; container.appendChild(tile); @@ -608,7 +579,7 @@ L.GridLayer = L.Layer.extend({ this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); } else { tile.active = true; - this._pruneTiles(coords); + this._pruneTiles(); } L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded'); From ff679aa7dc7a6363fab5f867962d5e167f380b5f Mon Sep 17 00:00:00 2001 From: Jared Date: Fri, 6 Feb 2015 11:41:40 +1300 Subject: [PATCH 38/89] Do not render the stroke if the weight is explicitly set to 0 When rendering to SVG, if the weight is 0 the outline is not displayed. However, when rendering to canvas it will still display the outline. This change makes the behaviour consistent when rendering to either. --- src/layer/vector/Canvas.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/vector/Canvas.js b/src/layer/vector/Canvas.js index 3de15af9..ace5e00e 100644 --- a/src/layer/vector/Canvas.js +++ b/src/layer/vector/Canvas.js @@ -169,7 +169,7 @@ L.Canvas = L.Renderer.extend({ ctx.fill(options.fillRule || 'evenodd'); } - if (options.stroke) { + if (options.stroke && options.weight !== 0) { ctx.globalAlpha = clear ? 1 : options.opacity; // if clearing shape, do it with the previously drawn line width From a0ebbef0074001b8e8913bfa919a10788038ad53 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 5 Feb 2015 10:12:07 -0800 Subject: [PATCH 39/89] Don't adjust opacity for non-current tiles --- src/layer/tile/GridLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index c092c8b1..86b15ac5 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -158,7 +158,7 @@ L.GridLayer = L.Layer.extend({ for (var key in this._tiles) { var tile = this._tiles[key]; - if (!tile.loaded || tile.active) { continue; } + if (!tile.current || !tile.loaded || tile.active) { continue; } var fade = Math.min(1, (now - tile.loaded) / 200); if (fade < 1) { From 8d9650552708cdd44b841c3b3beb0d84b9b830a1 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 5 Feb 2015 10:13:23 -0800 Subject: [PATCH 40/89] Remove src reset on removed tiles This is a performance hit on Chrome; no evidence it ever solved a real issue. (Ref #107) --- src/layer/tile/TileLayer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index 9b0d9032..c4a2f2b9 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -110,7 +110,6 @@ L.TileLayer = L.GridLayer.extend({ _onTileRemove: function (e) { e.tile.onload = null; - e.tile.src = L.Util.emptyImageUrl; }, _getZoomForUrl: function () { From 6b67df023234186e70647334c381fd66e4838721 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 6 Feb 2015 03:12:36 +0200 Subject: [PATCH 41/89] make animations significantly smoother with will-change --- dist/leaflet.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index 05e2bb2e..3c6cb616 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -139,6 +139,9 @@ /* zoom and fade animations */ +.leaflet-tile { + will-change: opacity; + } .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; @@ -149,7 +152,9 @@ .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } - +.leaflet-zoom-animated { + will-change: transform; + } .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); From 1d7c492bdd220c405ebfcf68daf639090b8b1e2d Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 6 Feb 2015 12:17:26 +0200 Subject: [PATCH 42/89] discard unneeded tile buffers --- src/layer/tile/GridLayer.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 86b15ac5..f12336f9 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -191,10 +191,16 @@ L.GridLayer = L.Layer.extend({ }, _updateLevels: function () { - var zoom = this._tileZoom; + var zoom = this._tileZoom, + maxZoom = this.options.maxZoom; for (var z in this._levels) { - this._levels[z].el.style.zIndex = -Math.abs(zoom - z); + if (this._levels[z].el.children.length || z === zoom) { + this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); + } else { + L.DomUtil.remove(this._levels[z].el); + delete this._levels[z]; + } } var level = this._levels[zoom], @@ -204,7 +210,7 @@ L.GridLayer = L.Layer.extend({ level = this._levels[zoom] = {}; level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); - level.el.style.zIndex = 0; + level.el.style.zIndex = maxZoom; level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); level.zoom = zoom; From e47c568a0d49b4e9644ed9294350e6d92cf63d8f Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 6 Feb 2015 15:50:24 -0800 Subject: [PATCH 43/89] Stricter selectors for will-change properties --- dist/leaflet.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/leaflet.css b/dist/leaflet.css index 3c6cb616..e50e886c 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -139,7 +139,7 @@ /* zoom and fade animations */ -.leaflet-tile { +.leaflet-fade-anim .leaflet-tile { will-change: opacity; } .leaflet-fade-anim .leaflet-popup { @@ -152,7 +152,7 @@ .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } -.leaflet-zoom-animated { +.leaflet-zoom-anim .leaflet-zoom-animated { will-change: transform; } .leaflet-zoom-anim .leaflet-zoom-animated { From 875ec02e5f511406dec8a51bc01034416f76d691 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 6 Feb 2015 15:51:20 -0800 Subject: [PATCH 44/89] Remove obsolete unloadInvisibleTiles option --- src/layer/tile/GridLayer.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index f12336f9..c2341a4a 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -10,7 +10,6 @@ L.GridLayer = L.Layer.extend({ tileSize: 256, opacity: 1, - unloadInvisibleTiles: L.Browser.mobile, updateWhenIdle: L.Browser.mobile, updateInterval: 200, @@ -304,9 +303,6 @@ L.GridLayer = L.Layer.extend({ _viewReset: function (e) { var map = this._map; this._reset(map.getCenter(), map.getZoom(), e && e.hard); - if (this.options.unloadInvisibleTiles) { - this._removeOtherTiles(map.getBounds()); - } }, _animateZoom: function (e) { @@ -487,16 +483,6 @@ L.GridLayer = L.Layer.extend({ return coords; }, - // remove any present tiles that are off the specified bounds - _removeOtherTiles: function (bounds) { - for (var key in this._tiles) { - var tileBounds = this._keyToBounds(key); - if (!bounds.intersects(tileBounds)) { - this._removeTile(key); - } - } - }, - _removeTile: function (key) { var tile = this._tiles[key]; if (!tile) { return; } From 71c11987b59d996f9e28485a0629da27955402e3 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 6 Feb 2015 15:54:13 -0800 Subject: [PATCH 45/89] Ensure tiles have 0 opacity when first painted Fixes #3206 --- src/layer/tile/GridLayer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index c2341a4a..7fa06dcb 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -567,6 +567,7 @@ L.GridLayer = L.Layer.extend({ tile.loaded = +new Date(); if (this._map._fadeAnimated) { + L.DomUtil.setOpacity(tile.el, 0); L.Util.cancelAnimFrame(this._fadeFrame); this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this); } else { From e52c5516522b33a424e9c6e75f2c01c637f4fe6b Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 6 Feb 2015 16:36:27 -0800 Subject: [PATCH 46/89] Remove trailing whitespace --- spec/suites/geometry/LineUtilSpec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/suites/geometry/LineUtilSpec.js b/spec/suites/geometry/LineUtilSpec.js index c8873e6a..c09ac761 100644 --- a/spec/suites/geometry/LineUtilSpec.js +++ b/spec/suites/geometry/LineUtilSpec.js @@ -33,18 +33,18 @@ describe('LineUtil', function () { expect(segment).to.be(false); }); - + it('can round numbers in clipped bounds', function () { var a = new L.Point(4, 5); var b = new L.Point(8, 6); - + var segment1 = L.LineUtil.clipSegment(a, b, bounds); - + expect(segment1[0]).to.eql(new L.Point(5, 5.25)); expect(segment1[1]).to.eql(b); - + var segment2 = L.LineUtil.clipSegment(a, b, bounds, false, true); - + expect(segment2[0]).to.eql(new L.Point(5, 5)); expect(segment2[1]).to.eql(b); }); From 21701a6faef880403b7ecd80d3fa35ca814b3123 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 6 Feb 2015 16:36:43 -0800 Subject: [PATCH 47/89] Remove test specific to old zoom code --- spec/suites/layer/tile/GridLayerSpec.js | 26 ------------------------- 1 file changed, 26 deletions(-) diff --git a/spec/suites/layer/tile/GridLayerSpec.js b/spec/suites/layer/tile/GridLayerSpec.js index 06c5f066..1ca42279 100644 --- a/spec/suites/layer/tile/GridLayerSpec.js +++ b/spec/suites/layer/tile/GridLayerSpec.js @@ -104,32 +104,6 @@ describe('GridLayer', function () { map.setZoom(0, {animate: false}); clock.tick(250); }); - - it('prunes and retains the correct tiles for back-to-back zooms', function () { - map.setView([0, 0], 1); - - var grid = L.gridLayer(); - var tiles = {}; - - grid.createTile = function (coords) { - tiles[grid._tileCoordsToKey(coords)] = true; - return document.createElement('div'); - }; - - map.addLayer(grid); - clock.tick(500); - - map.setZoom(0, {animate: false}); - clock.tick(250); - - map.setZoom(1, {animate: false}); - clock.tick(500); - - var tileContainers = div.querySelectorAll('.leaflet-tile-container'); - expect(tileContainers.length).to.eql(2); - expect(tileContainers[0].childNodes.length).to.equal(8); - expect(tileContainers[1].childNodes.length).to.equal(0); - }); }); describe("#onAdd", function () { From a7fc9c9a5f40472ebc3da9364f7ef78b3f02c69f Mon Sep 17 00:00:00 2001 From: Asko Kauppi Date: Sun, 8 Feb 2015 17:06:16 +0200 Subject: [PATCH 48/89] two small typos I spotted during code read-through --- src/core/Util.js | 2 +- src/geo/crs/CRS.Earth.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Util.js b/src/core/Util.js index f052bd9a..5927ef01 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -117,7 +117,7 @@ L.Util = { return obj.options; }, - // make an URL with GET parameters out of a set of properties/values + // make a URL with GET parameters out of a set of properties/values getParamString: function (obj, existingUrl, uppercase) { var params = []; for (var i in obj) { diff --git a/src/geo/crs/CRS.Earth.js b/src/geo/crs/CRS.Earth.js index b1fb4458..d6947501 100644 --- a/src/geo/crs/CRS.Earth.js +++ b/src/geo/crs/CRS.Earth.js @@ -7,7 +7,7 @@ L.CRS.Earth = L.extend({}, L.CRS, { R: 6378137, - // distane between two geographical points using spherical law of cosines approximation + // distance between two geographical points using spherical law of cosines approximation distance: function (latlng1, latlng2) { var rad = Math.PI / 180, lat1 = latlng1.lat * rad, From 5b3ba078bfeac93fc1a2ed8e3ec5d590baba5f6d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 13 Feb 2015 17:53:23 +0100 Subject: [PATCH 49/89] Do not try to remove drag class if marker as no icon Since 4c46abe7810272704cc1a7ce0ccfb84c45456791 it's possible to have a marker off the map with dragging still enabled. We want to be able to disable it in this situation too. --- spec/suites/layer/marker/MarkerSpec.js | 8 +++++++- src/layer/marker/Marker.Drag.js | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/spec/suites/layer/marker/MarkerSpec.js b/spec/suites/layer/marker/MarkerSpec.js index e709c6fc..ec4e98e5 100644 --- a/spec/suites/layer/marker/MarkerSpec.js +++ b/spec/suites/layer/marker/MarkerSpec.js @@ -41,10 +41,16 @@ describe("Marker", function () { expect(marker.dragging.enabled()).to.be(true); - map.removeLayer(marker); + map.removeLayer(marker); map.addLayer(marker); expect(marker.dragging.enabled()).to.be(true); + + map.removeLayer(marker); + // Dragging is still enabled, we should be able to disable it, + // even if marker is off the map. + marker.dragging.disable(); + map.addLayer(marker); }); it("changes the icon to another DivIcon", function () { diff --git a/src/layer/marker/Marker.Drag.js b/src/layer/marker/Marker.Drag.js index 911fa852..c0d6629b 100644 --- a/src/layer/marker/Marker.Drag.js +++ b/src/layer/marker/Marker.Drag.js @@ -30,7 +30,9 @@ L.Handler.MarkerDrag = L.Handler.extend({ dragend: this._onDragEnd }, this).disable(); - L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); + if (this._marker._icon) { + L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); + } }, moved: function () { From c5de3fcbb8e817721a68bda315eaeaa997e78c8a Mon Sep 17 00:00:00 2001 From: vsn4ik Date: Sat, 14 Feb 2015 15:12:44 +0300 Subject: [PATCH 50/89] Changed deprecated gem options on travis.yml and updated links on CONTRIBUTING.md --- .travis.yml | 4 ++-- CONTRIBUTING.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index dbbdb7ac..a8a9d28f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,8 @@ before_script: > test ${TRAVIS_BRANCH} = master || test ${TRAVIS_BRANCH} = stable && test ${TRAVIS_PULL_REQUEST} = false && - gem install --no-rdoc --no-ri --version 0.8.9 faraday && - gem install --no-rdoc --no-ri travis-artifacts || true + gem install --no-document --version 0.8.9 faraday && + gem install --no-document travis-artifacts || true after_success: > test ${TRAVIS_BRANCH} = master || test ${TRAVIS_BRANCH} = stable && diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49c80cf6..b17605e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ here are some tips for creating a helpful report that will make fixing it much e * Write a **descriptive, specific title**. Bad: *Problem with polylines*. Good: *Doing X in IE9 causes Z*. * Include **browser, OS and Leaflet version** info in the description. - * Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/)). + * Create a **simple test case** that demonstrates the bug (e.g. using [JSFiddle](http://jsfiddle.net/) or [JS Bin](http://jsbin.com/)). * Check whether the bug can be reproduced in **other browsers**. * Check if the bug occurs in the stable version, master, or both. * *Bonus tip:* if the bug only appears in the master version but the stable version is fine, @@ -125,7 +125,7 @@ From there you can click through folders/files to get details on their individua ## Improving Documentation The code of the live Leaflet website that contains all documentation and examples is located in the `gh-pages` branch -and is automatically generated from a set of HTML and Markdown files by [Jekyll](https://github.com/mojombo/jekyll). +and is automatically generated from a set of HTML and Markdown files by [Jekyll](http://jekyllrb.com/). The easiest way to make little improvements such as fixing typos without even leaving the browser is by editing one of the files with the online GitHub editor: From f02ccaa346085372ae165ae127e8a1b12bdbe4c9 Mon Sep 17 00:00:00 2001 From: Asko Kauppi Date: Sat, 21 Feb 2015 22:59:37 +0200 Subject: [PATCH 51/89] ;; --- debug/map/image-overlay.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug/map/image-overlay.html b/debug/map/image-overlay.html index f1068092..cb136015 100644 --- a/debug/map/image-overlay.html +++ b/debug/map/image-overlay.html @@ -40,8 +40,8 @@ map.addLayer(overlay); overlay.on('dblclick',function (e) { - console.log('Double click on image.') - }) + console.log('Double click on image.'); + }); From b995cf902342ae39a47c9c4e4fcf1071aa22ca04 Mon Sep 17 00:00:00 2001 From: Asko Kauppi Date: Sat, 21 Feb 2015 23:01:58 +0200 Subject: [PATCH 52/89] to --- PLUGIN-GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PLUGIN-GUIDE.md b/PLUGIN-GUIDE.md index dfd0cede..f7c35116 100644 --- a/PLUGIN-GUIDE.md +++ b/PLUGIN-GUIDE.md @@ -113,7 +113,7 @@ Function, method and property names should be in `camelCase`.
Class names should be in `CapitalizedCamelCase`. If you have a lot of arguments in your function, consider accepting an options object instead -(putting default values where possible so that users don't need specify all of them): +(putting default values where possible so that users don't need to specify all of them): ```js // bad From a9658d71785e71b59ef717baa995ba9a375423fe Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 26 Feb 2015 11:29:42 +0100 Subject: [PATCH 53/89] Use standard .eslintrc naming --- build/eslintrc.json => .eslintrc | 0 Jakefile.js | 4 ++-- spec/{eslintrc.json => .eslintrc} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename build/eslintrc.json => .eslintrc (100%) rename spec/{eslintrc.json => .eslintrc} (100%) diff --git a/build/eslintrc.json b/.eslintrc similarity index 100% rename from build/eslintrc.json rename to .eslintrc diff --git a/Jakefile.js b/Jakefile.js index dcb73dfc..62e9ed2d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -27,10 +27,10 @@ function hint(msg, args) { } desc('Check Leaflet source for errors with ESLint'); -task('lint', {async: true}, hint('Checking for JS errors...', 'src --config build/eslintrc.json')); +task('lint', {async: true}, hint('Checking for JS errors...', 'src --config .eslintrc')); desc('Check Leaflet specs source for errors with ESLint'); -task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/eslintrc.json')); +task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/suites --config spec/.eslintrc')); desc('Combine and compress Leaflet source files'); task('build', {async: true}, function (compsBase32, buildName) { diff --git a/spec/eslintrc.json b/spec/.eslintrc similarity index 100% rename from spec/eslintrc.json rename to spec/.eslintrc From 4e21a3b54b72894d409891b622c9d495d79f0ec2 Mon Sep 17 00:00:00 2001 From: Jan Pieter Waagmeester Date: Thu, 26 Feb 2015 15:58:22 +0100 Subject: [PATCH 54/89] Update PLUGIN-GUIDE.md whitespace in plugin example --- PLUGIN-GUIDE.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PLUGIN-GUIDE.md b/PLUGIN-GUIDE.md index f7c35116..742583d0 100644 --- a/PLUGIN-GUIDE.md +++ b/PLUGIN-GUIDE.md @@ -168,12 +168,11 @@ You can add support for AMD/CommonJS loaders to your Leaflet plugin by following // define a Common JS module that relies on 'leaflet' } else if (typeof exports === 'object') { module.exports = factory(require('leaflet')); - } // attach your plugin to the global 'L' variable - if(typeof window !== 'undefined' && window.L){ - window.L.YourPlugin = factory(L); + if (typeof window !== 'undefined' && window.L) { + window.L.YourPlugin = factory(L); } }(function (L) { var MyLeafletPlugin = {}; From a7d7bedda77992b28f04a44ddc6e644711029fce Mon Sep 17 00:00:00 2001 From: Jan Pieter Waagmeester Date: Fri, 27 Feb 2015 15:15:16 +0100 Subject: [PATCH 55/89] Add a space to make comments look consistant And now back to real work. --- src/core/Class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Class.js b/src/core/Class.js index c7a18ab4..e21a2420 100644 --- a/src/core/Class.js +++ b/src/core/Class.js @@ -26,7 +26,7 @@ L.Class.extend = function (props) { NewClass.prototype = proto; - //inherit parent's statics + // inherit parent's statics for (var i in this) { if (this.hasOwnProperty(i) && i !== 'prototype') { NewClass[i] = this[i]; From 66654b94ebe0c235309dd6960e0e738ccfebe4c3 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 27 Feb 2015 17:23:30 +0100 Subject: [PATCH 56/89] Forward original event on drag --- src/dom/Draggable.js | 11 +++++++---- src/layer/marker/Marker.Drag.js | 7 ++++--- src/map/handler/Map.Drag.js | 6 +++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index 2a6e9ff7..20686673 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -99,13 +99,16 @@ L.Draggable = L.Evented.extend({ this._moving = true; L.Util.cancelAnimFrame(this._animRequest); - this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); + var animationCallback = function () { + this._updatePosition(e); + }; + this._animRequest = L.Util.requestAnimFrame(animationCallback, this, true, this._dragStartTarget); }, - _updatePosition: function () { - this.fire('predrag'); + _updatePosition: function (e) { + this.fire('predrag', e); L.DomUtil.setPosition(this._element, this._newPos); - this.fire('drag'); + this.fire('drag', e); }, _onUp: function () { diff --git a/src/layer/marker/Marker.Drag.js b/src/layer/marker/Marker.Drag.js index c0d6629b..a28c99ef 100644 --- a/src/layer/marker/Marker.Drag.js +++ b/src/layer/marker/Marker.Drag.js @@ -46,7 +46,7 @@ L.Handler.MarkerDrag = L.Handler.extend({ .fire('dragstart'); }, - _onDrag: function () { + _onDrag: function (e) { var marker = this._marker, shadow = marker._shadow, iconPos = L.DomUtil.getPosition(marker._icon), @@ -58,10 +58,11 @@ L.Handler.MarkerDrag = L.Handler.extend({ } marker._latlng = latlng; + e.latlng = latlng; marker - .fire('move', {latlng: latlng}) - .fire('drag'); + .fire('move', e) + .fire('drag', e); }, _onDragEnd: function (e) { diff --git a/src/map/handler/Map.Drag.js b/src/map/handler/Map.Drag.js index 9980d4d3..62ec3323 100644 --- a/src/map/handler/Map.Drag.js +++ b/src/map/handler/Map.Drag.js @@ -63,7 +63,7 @@ L.Map.Drag = L.Handler.extend({ } }, - _onDrag: function () { + _onDrag: function (e) { if (this._map.options.inertia) { var time = this._lastTime = +new Date(), pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; @@ -78,8 +78,8 @@ L.Map.Drag = L.Handler.extend({ } this._map - .fire('move') - .fire('drag'); + .fire('move', e) + .fire('drag', e); }, _onViewReset: function () { From 1e4b82b990b38d8b14b1cac5d820ba96b9a27c7d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 27 Feb 2015 17:57:46 +0100 Subject: [PATCH 57/89] Cleaner syntax for event forwarding --- src/dom/Draggable.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index 20686673..87c66280 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -99,13 +99,12 @@ L.Draggable = L.Evented.extend({ this._moving = true; L.Util.cancelAnimFrame(this._animRequest); - var animationCallback = function () { - this._updatePosition(e); - }; - this._animRequest = L.Util.requestAnimFrame(animationCallback, this, true, this._dragStartTarget); + this._lastEvent = e; + this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); }, - _updatePosition: function (e) { + _updatePosition: function () { + var e = {originalEvent: this._lastEvent}; this.fire('predrag', e); L.DomUtil.setPosition(this._element, this._newPos); this.fire('drag', e); From 721cf6a6894d909739651665a5bd7227cce79691 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Sat, 28 Feb 2015 20:48:14 -0700 Subject: [PATCH 58/89] Added ability to specify pane for vector layer creation, which will automatically create a renderer for that pane if one does not already exists. --- src/layer/vector/Renderer.js | 15 +++++++++++++-- src/map/Map.js | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/layer/vector/Renderer.js b/src/layer/vector/Renderer.js index c45e885f..5f11e93f 100644 --- a/src/layer/vector/Renderer.js +++ b/src/layer/vector/Renderer.js @@ -64,7 +64,7 @@ L.Renderer = L.Layer.extend({ L.Map.include({ // used by each vector layer to decide which renderer to use getRenderer: function (layer) { - var renderer = layer.options.renderer || this.options.renderer || this._renderer; + var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; if (!renderer) { renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas()); @@ -74,5 +74,16 @@ L.Map.include({ this.addLayer(renderer); } return renderer; - } + }, + + _getPaneRenderer: function (name) { + if (name == 'overlayPane' || typeof name === 'undefined') return false; + + var renderer = this._paneRenderers[name]; + if (typeof renderer === 'undefined') { + renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name})); + this._paneRenderers[name] = renderer; + } + return renderer; + }, }); diff --git a/src/map/Map.js b/src/map/Map.js index 0d5166f2..7dc39816 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -470,6 +470,7 @@ L.Map = L.Evented.extend({ _initPanes: function () { var panes = this._panes = {}; + this._paneRenderers = {}; this._mapPane = this.createPane('mapPane', this._container); From 8c30707e56ddbe501cedee6f195a047e44ee501a Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Sat, 28 Feb 2015 21:21:45 -0700 Subject: [PATCH 59/89] Fixed some code style issues. --- src/layer/vector/Renderer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/layer/vector/Renderer.js b/src/layer/vector/Renderer.js index 5f11e93f..af0958a5 100644 --- a/src/layer/vector/Renderer.js +++ b/src/layer/vector/Renderer.js @@ -77,7 +77,9 @@ L.Map.include({ }, _getPaneRenderer: function (name) { - if (name == 'overlayPane' || typeof name === 'undefined') return false; + if (name == 'overlayPane' || typeof name == 'undefined') { + return false; + } var renderer = this._paneRenderers[name]; if (typeof renderer === 'undefined') { @@ -85,5 +87,5 @@ L.Map.include({ this._paneRenderers[name] = renderer; } return renderer; - }, + } }); From d0b5aada79ad5184cee8d200ed74db1621732643 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Sat, 28 Feb 2015 21:26:20 -0700 Subject: [PATCH 60/89] Code consistency changes --- src/layer/vector/Renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layer/vector/Renderer.js b/src/layer/vector/Renderer.js index af0958a5..a9183f11 100644 --- a/src/layer/vector/Renderer.js +++ b/src/layer/vector/Renderer.js @@ -77,12 +77,12 @@ L.Map.include({ }, _getPaneRenderer: function (name) { - if (name == 'overlayPane' || typeof name == 'undefined') { + if (name === 'overlayPane' || name === undefined) { return false; } var renderer = this._paneRenderers[name]; - if (typeof renderer === 'undefined') { + if (renderer === undefined) { renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name})); this._paneRenderers[name] = renderer; } From 954ef1cc10a80008b04241258f3be237f0197113 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Sun, 1 Mar 2015 12:01:28 +0200 Subject: [PATCH 61/89] add indent eslint rule and fix some whitespace --- .eslintrc | 1 + package.json | 2 +- src/core/Events.js | 4 ++-- src/layer/vector/Canvas.js | 2 +- src/map/handler/Map.Keyboard.js | 16 ++++++++-------- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.eslintrc b/.eslintrc index 19e75a8f..c6678be8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,6 +10,7 @@ "space-after-keywords": 2, "no-lonely-if": 2, "comma-style": 2, + "indent": [2, "tab"], "no-underscore-dangle": 0, "no-constant-condition": 0, "no-multi-spaces": 0, diff --git a/package.json b/package.json index af41f9d8..b6e4b128 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "JavaScript library for mobile-friendly interactive maps", "devDependencies": { "copyfiles": "0.1.0", - "eslint": "^0.13.0", + "eslint": "^0.15.1", "happen": "~0.1.3", "jake": "~8.0.10", "karma": "~0.12.31", diff --git a/src/core/Events.js b/src/core/Events.js index b0125488..6702d2ae 100644 --- a/src/core/Events.js +++ b/src/core/Events.js @@ -134,8 +134,8 @@ L.Evented = L.Class.extend({ events = this._events; if (events) { - var typeIndex = events[type + '_idx'], - i, len, listeners, id; + var typeIndex = events[type + '_idx'], + i, len, listeners, id; if (events[type]) { // make sure adding/removing listeners inside other listeners won't cause infinite loop diff --git a/src/layer/vector/Canvas.js b/src/layer/vector/Canvas.js index ace5e00e..6cf2f356 100644 --- a/src/layer/vector/Canvas.js +++ b/src/layer/vector/Canvas.js @@ -114,7 +114,7 @@ L.Canvas = L.Renderer.extend({ len = parts.length, ctx = this._ctx; - if (!len) { return; } + if (!len) { return; } ctx.beginPath(); diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 789a50fb..51fbfdbc 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -35,14 +35,14 @@ L.Map.Keyboard = L.Handler.extend({ } L.DomEvent.on(container, { - focus: this._onFocus, - blur: this._onBlur, - mousedown: this._onMouseDown + focus: this._onFocus, + blur: this._onBlur, + mousedown: this._onMouseDown }, this); this._map.on({ focus: this._addHooks, - blur: this._removeHooks + blur: this._removeHooks }, this); }, @@ -50,14 +50,14 @@ L.Map.Keyboard = L.Handler.extend({ this._removeHooks(); L.DomEvent.off(this._map._container, { - focus: this._onFocus, - blur: this._onBlur, - mousedown: this._onMouseDown + focus: this._onFocus, + blur: this._onBlur, + mousedown: this._onMouseDown }, this); this._map.off({ focus: this._addHooks, - blur: this._removeHooks + blur: this._removeHooks }, this); }, From f61a681c5607c9a7745877606e6560c135000cbd Mon Sep 17 00:00:00 2001 From: Asko Kauppi Date: Sun, 1 Mar 2015 22:51:04 +0200 Subject: [PATCH 62/89] fix 'jake' --- spec/suites/core/EventsSpec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/suites/core/EventsSpec.js b/spec/suites/core/EventsSpec.js index 72df55cc..2860ae53 100644 --- a/spec/suites/core/EventsSpec.js +++ b/spec/suites/core/EventsSpec.js @@ -358,15 +358,15 @@ describe('Events', function () { }); it("doesn't call listeners to events that have been removed", function () { - var obj = new L.Evented(), - spy = sinon.spy(); + var obj = new L.Evented(), + spy = sinon.spy(); - obj.once('test', spy, obj); - obj.off('test', spy, obj); + obj.once('test', spy, obj); + obj.off('test', spy, obj); - obj.fire('test'); + obj.fire('test'); - expect(spy.called).to.be(false); + expect(spy.called).to.be(false); }); it('works if called from a context that doesnt implement #Events', function () { From bb230d0ac266293ec3d87ab7e818ca9e33e9723e Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 2 Mar 2015 13:35:02 +0200 Subject: [PATCH 63/89] fix race condition in tests --- src/layer/tile/GridLayer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index 7fa06dcb..d98994f3 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -552,6 +552,8 @@ L.GridLayer = L.Layer.extend({ }, _tileReady: function (coords, err, tile) { + if (!this._map) { return; } + if (err) { this.fire('tileerror', { error: err, From 4c8ffadf30067c619212ba7f10ac3e8699d041e4 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 2 Mar 2015 14:48:09 +0200 Subject: [PATCH 64/89] don't animate negligible touch movements, close #2519 --- src/map/handler/Map.TouchZoom.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/map/handler/Map.TouchZoom.js b/src/map/handler/Map.TouchZoom.js index 49120ddc..bb4ae01c 100644 --- a/src/map/handler/Map.TouchZoom.js +++ b/src/map/handler/Map.TouchZoom.js @@ -80,9 +80,12 @@ L.Map.TouchZoom = L.Handler.extend({ } else { this._center = map.layerPointToLatLng(this._getTargetCenter()); } + this._zoom = map.getScaleZoom(this._scale); - map._animateZoom(this._center, this._zoom); + if (this._scale !== 1 || this._delta.x !== 0 || this._delta.y !== 0) { + map._animateZoom(this._center, this._zoom); + } }, _onTouchEnd: function () { From bbc3f2fab1297a2dd1487343528e7f5960c97bef Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 2 Mar 2015 15:26:05 +0200 Subject: [PATCH 65/89] fix zoom anim freeze race condition, close #2693, close #2478 --- src/map/anim/Map.ZoomAnimation.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/map/anim/Map.ZoomAnimation.js b/src/map/anim/Map.ZoomAnimation.js index 085b2663..bd881c55 100644 --- a/src/map/anim/Map.ZoomAnimation.js +++ b/src/map/anim/Map.ZoomAnimation.js @@ -34,7 +34,15 @@ L.Map.include(!zoomAnimated ? {} : { this._panes.mapPane.appendChild(proxy); this.on('zoomanim', function (e) { + var prop = L.DomUtil.TRANSFORM, + transform = proxy.style[prop]; + L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); + + // workaround for case when transform is the same and so transitionend event is not fired + if (transform === proxy.style[prop] & this._animatingZoom) { + this._onZoomTransitionEnd(); + } }, this); this.on('load moveend', function () { From b149f2d7548d0daaeb01ed5a3f1ad04cb53c38e9 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 17 Nov 2014 14:39:47 +0200 Subject: [PATCH 66/89] Layer add/removeInteractiveTarget methods registers DOM elements for event delegation --- src/layer/Layer.js | 10 ++++++++++ src/map/Map.js | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/layer/Layer.js b/src/layer/Layer.js index 32b2c44b..579d30b5 100644 --- a/src/layer/Layer.js +++ b/src/layer/Layer.js @@ -25,6 +25,16 @@ L.Layer = L.Evented.extend({ return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); }, + addInteractiveTarget: function (targetEl) { + this._map._targets[L.stamp(targetEl)] = this; + return this; + }, + + removeInteractiveTarget: function (targetEl) { + delete this._map._targets[L.stamp(targetEl)]; + return this; + }, + _layerAdd: function (e) { var map = e.target; diff --git a/src/map/Map.js b/src/map/Map.js index 7dc39816..1c111bf7 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -557,6 +557,8 @@ L.Map = L.Evented.extend({ 'click dblclick mousedown mouseup mouseenter mouseleave mousemove contextmenu', this._handleMouseEvent, this); + this._targets = {}; + if (this.options.trackResize) { L.DomEvent[onOff](window, 'resize', this._onResize, this); } @@ -571,9 +573,11 @@ L.Map = L.Evented.extend({ _handleMouseEvent: function (e) { if (!this._loaded) { return; } - this._fireMouseEvent(this, e, + var target = this._targets[L.stamp(e.target || e.srcElement)]; + + this._fireMouseEvent(target || this, e, e.type === 'mouseenter' ? 'mouseover' : - e.type === 'mouseleave' ? 'mouseout' : e.type); + e.type === 'mouseleave' ? 'mouseout' : e.type, true); }, _fireMouseEvent: function (obj, e, type, propagate, latlng) { From 8e7bbff6d8552b2cffc3f3f1b4047b2e96a612a8 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 17 Nov 2014 14:40:20 +0200 Subject: [PATCH 67/89] switch SVG paths to new delegation API --- src/layer/vector/Path.js | 4 ---- src/layer/vector/SVG.js | 26 ++++---------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/layer/vector/Path.js b/src/layer/vector/Path.js index 6247c997..c305e516 100644 --- a/src/layer/vector/Path.js +++ b/src/layer/vector/Path.js @@ -74,10 +74,6 @@ L.Path = L.Layer.extend({ return this; }, - _fireMouseEvent: function (e, type) { - this._map._fireMouseEvent(this, e, type, true); - }, - _clickTolerance: function () { // used when doing hit detection for Canvas layers return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0); diff --git a/src/layer/vector/SVG.js b/src/layer/vector/SVG.js index d230d697..04599781 100644 --- a/src/layer/vector/SVG.js +++ b/src/layer/vector/SVG.js @@ -7,9 +7,6 @@ L.SVG = L.Renderer.extend({ _initContainer: function () { this._container = L.SVG.create('svg'); - this._paths = {}; - this._initEvents(); - // makes it possible to click through svg root; we'll reset it back in individual paths this._container.setAttribute('pointer-events', 'none'); }, @@ -54,15 +51,13 @@ L.SVG = L.Renderer.extend({ }, _addPath: function (layer) { - var path = layer._path; - this._container.appendChild(path); - this._paths[L.stamp(path)] = layer; + this._container.appendChild(layer._path); + layer.addInteractiveTarget(layer._path); }, _removePath: function (layer) { - var path = layer._path; - L.DomUtil.remove(path); - delete this._paths[L.stamp(path)]; + L.DomUtil.remove(layer._path); + layer.removeInteractiveTarget(layer._path); }, _updatePath: function (layer) { @@ -139,19 +134,6 @@ L.SVG = L.Renderer.extend({ _bringToBack: function (layer) { L.DomUtil.toBack(layer._path); - }, - - // TODO remove duplication with L.Map - _initEvents: function () { - L.DomEvent.on(this._container, 'click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu', - this._fireMouseEvent, this); - }, - - _fireMouseEvent: function (e) { - var path = this._paths[L.stamp(e.target || e.srcElement)]; - if (path) { - path._fireMouseEvent(e); - } } }); From 22c97d1b3356a34cd9df1381ca778388de46f2e7 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 17 Nov 2014 15:06:48 +0200 Subject: [PATCH 68/89] use new event delegation API in ImageOverlay --- src/layer/ImageOverlay.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/layer/ImageOverlay.js b/src/layer/ImageOverlay.js index 5862e965..fdd80496 100644 --- a/src/layer/ImageOverlay.js +++ b/src/layer/ImageOverlay.js @@ -26,13 +26,20 @@ L.ImageOverlay = L.Layer.extend({ } } + if (this.options.interactive) { + L.DomUtil.addClass(this._image, 'leaflet-interactive'); + this.addInteractiveTarget(this._image); + } + this.getPane().appendChild(this._image); - this._initInteraction(); this._reset(); }, onRemove: function () { L.DomUtil.remove(this._image); + if (this.options.interactive) { + this.removeInteractiveTarget(this._image); + } }, setOpacity: function (opacity) { @@ -65,19 +72,6 @@ L.ImageOverlay = L.Layer.extend({ return this; }, - _initInteraction: function () { - if (!this.options.interactive) { return; } - L.DomUtil.addClass(this._image, 'leaflet-interactive'); - L.DomEvent.on(this._image, 'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu', - this._fireMouseEvent, this); - }, - - _fireMouseEvent: function (e, type) { - if (this._map) { - this._map._fireMouseEvent(this, e, type, true); - } - }, - setUrl: function (url) { this._url = url; From 34bc7fa4eaacdce5410eb9b17ec7e74706fe89fe Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 17 Nov 2014 18:35:31 +0200 Subject: [PATCH 69/89] switch markers to event delegation API --- src/layer/marker/Marker.js | 34 ++++++++-------------------------- src/map/Map.js | 34 ++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/layer/marker/Marker.js b/src/layer/marker/Marker.js index 309c89aa..575a318c 100644 --- a/src/layer/marker/Marker.js +++ b/src/layer/marker/Marker.js @@ -123,11 +123,11 @@ L.Marker = L.Layer.extend({ this._icon = icon; this._initInteraction(); - if (L.DomEvent && options.riseOnHover) { - L.DomEvent.on(icon, { + if (options.riseOnHover) { + this.on({ mouseover: this._bringToFront, mouseout: this._resetZIndex - }, this); + }); } var newShadow = options.icon.createShadow(this._shadow), @@ -158,14 +158,15 @@ L.Marker = L.Layer.extend({ }, _removeIcon: function () { - if (L.DomEvent && this.options.riseOnHover) { - L.DomEvent.off(this._icon, { + if (this.options.riseOnHover) { + this.off({ mouseover: this._bringToFront, mouseout: this._resetZIndex - }, this); + }); } L.DomUtil.remove(this._icon); + this.removeInteractiveTarget(this._icon); this._icon = null; }, @@ -205,11 +206,7 @@ L.Marker = L.Layer.extend({ L.DomUtil.addClass(this._icon, 'leaflet-interactive'); - if (L.DomEvent) { - L.DomEvent.on(this._icon, - 'click dblclick mousedown mouseup mouseover mousemove mouseout contextmenu keypress', - this._fireMouseEvent, this); - } + this.addInteractiveTarget(this._icon); if (L.Handler.MarkerDrag) { var draggable = this.options.draggable; @@ -226,21 +223,6 @@ L.Marker = L.Layer.extend({ } }, - _fireMouseEvent: function (e, type) { - // to prevent outline when clicking on keyboard-focusable marker - if (e.type === 'mousedown') { - L.DomEvent.preventDefault(e); - } - - if (e.type === 'keypress' && e.keyCode === 13) { - type = 'click'; - } - - if (this._map) { - this._map._fireMouseEvent(this, e, type, true, this._latlng); - } - }, - setOpacity: function (opacity) { this.options.opacity = opacity; if (this._map) { diff --git a/src/map/Map.js b/src/map/Map.js index 1c111bf7..27ce7531 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -554,7 +554,7 @@ L.Map = L.Evented.extend({ onOff = onOff || 'on'; L.DomEvent[onOff](this._container, - 'click dblclick mousedown mouseup mouseenter mouseleave mousemove contextmenu', + 'click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress', this._handleMouseEvent, this); this._targets = {}; @@ -575,9 +575,24 @@ L.Map = L.Evented.extend({ var target = this._targets[L.stamp(e.target || e.srcElement)]; - this._fireMouseEvent(target || this, e, - e.type === 'mouseenter' ? 'mouseover' : - e.type === 'mouseleave' ? 'mouseout' : e.type, true); + var type = + e.type === 'mouseenter' ? 'mouseover' : + e.type === 'mouseleave' ? 'mouseout' : e.type; + + if (e.type === 'keypress' && e.keyCode === 13) { + type = 'click'; + } + + // to prevent outline when clicking on keyboard-focusable element + if (type === 'mousedown') { + L.DomEvent.preventDefault(e); + } + + // special case for map mouseover/mouseout events so that they're actually mouseenter/mouseleave + if (!target && (e.type === 'mouseover' || e.type === 'mouseout') && + !L.DomEvent._checkMouse(this._container, e)) { return; } + + this._fireMouseEvent(target || this, e, type, true); }, _fireMouseEvent: function (obj, e, type, propagate, latlng) { @@ -604,12 +619,15 @@ L.Map = L.Evented.extend({ } var data = { - originalEvent: e, - containerPoint: this.mouseEventToContainerPoint(e) + originalEvent: e }; - data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); - data.latlng = latlng || this.layerPointToLatLng(data.layerPoint); + if (e.type !== 'keypress') { + // TODO latlng isn't used, wrong latlng for markers + data.containerPoint = this.mouseEventToContainerPoint(e); + data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); + data.latlng = latlng || this.layerPointToLatLng(data.layerPoint); + } obj.fire(type, data, propagate); }, From 43c3f01a1a1f875d60d3b10776d9b6bc2c6faafb Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 18 Nov 2014 20:16:33 +0200 Subject: [PATCH 70/89] cleaner DOM event handling in Map --- src/map/Map.js | 78 ++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/src/map/Map.js b/src/map/Map.js index 27ce7531..d0914503 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -220,7 +220,7 @@ L.Map = L.Evented.extend({ remove: function () { - this._initEvents('off'); + this._initEvents(true); try { // throws error in IE6-8 @@ -546,19 +546,18 @@ L.Map = L.Evented.extend({ } }, - // map events + // DOM event handling - _initEvents: function (onOff) { + _initEvents: function (remove) { if (!L.DomEvent) { return; } - onOff = onOff || 'on'; - - L.DomEvent[onOff](this._container, - 'click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress', - this._handleMouseEvent, this); - this._targets = {}; + var onOff = remove ? 'off' : 'on'; + + L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' + + 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); + if (this.options.trackResize) { L.DomEvent[onOff](window, 'resize', this._onResize, this); } @@ -570,66 +569,51 @@ L.Map = L.Evented.extend({ function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container); }, - _handleMouseEvent: function (e) { - if (!this._loaded) { return; } + _handleDOMEvent: function (e) { + if (!this._loaded || L.DomEvent._skipped(e)) { return; } - var target = this._targets[L.stamp(e.target || e.srcElement)]; + // find the layer the event is propagating from + var target = this._targets[L.stamp(e.target || e.srcElement)], + type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type; - var type = - e.type === 'mouseenter' ? 'mouseover' : - e.type === 'mouseleave' ? 'mouseout' : e.type; + // special case for map mouseover/mouseout events so that they're actually mouseenter/mouseleave + if (!target && (type === 'mouseover' || type === 'mouseout') && + !L.DomEvent._checkMouse(this._container, e)) { return; } - if (e.type === 'keypress' && e.keyCode === 13) { - type = 'click'; - } - - // to prevent outline when clicking on keyboard-focusable element + // prevents outline when clicking on keyboard-focusable element if (type === 'mousedown') { L.DomEvent.preventDefault(e); } - // special case for map mouseover/mouseout events so that they're actually mouseenter/mouseleave - if (!target && (e.type === 'mouseover' || e.type === 'mouseout') && - !L.DomEvent._checkMouse(this._container, e)) { return; } + target = target || this; - this._fireMouseEvent(target || this, e, type, true); - }, - - _fireMouseEvent: function (obj, e, type, propagate, latlng) { - type = type || e.type; - - if (L.DomEvent._skipped(e)) { return; } - if (type === 'click') { - var draggableObj = obj.options.draggable === true ? obj : this; - if (!e._simulated && ((draggableObj.dragging && draggableObj.dragging.moved()) || - (this.boxZoom && this.boxZoom.moved()))) { - L.DomEvent.stopPropagation(e); - return; - } - obj.fire('preclick'); - } - - if (!obj.listens(type, propagate)) { return; } + if (!target.listens(type, true) && (type !== 'click' || !target.listens('preclick', true))) { return; } if (type === 'contextmenu') { L.DomEvent.preventDefault(e); } - if (type === 'click' || type === 'dblclick' || type === 'contextmenu') { - L.DomEvent.stopPropagation(e); - } + + // prevents firing click after you just dragged an object + if (e.type === 'click' && !e._simulated && this._draggableMoved(target)) { return; } var data = { originalEvent: e }; - if (e.type !== 'keypress') { // TODO latlng isn't used, wrong latlng for markers data.containerPoint = this.mouseEventToContainerPoint(e); data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); - data.latlng = latlng || this.layerPointToLatLng(data.layerPoint); + data.latlng = this.layerPointToLatLng(data.layerPoint); } + if (type === 'click') { + target.fire('preclick', data, true); + } + target.fire(type, data, true); + }, - obj.fire(type, data, propagate); + _draggableMoved: function (obj) { + obj = obj.options.draggable ? obj : this; + return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); }, _clearHandlers: function () { From 0a68b25ce560fa701e206da8e8dcb0c80cb325ca Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 18 Nov 2014 21:01:32 +0200 Subject: [PATCH 71/89] fix Canvas vector events --- src/layer/vector/Canvas.js | 13 +++++++++---- src/map/Map.js | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/layer/vector/Canvas.js b/src/layer/vector/Canvas.js index 6cf2f356..9445ff6a 100644 --- a/src/layer/vector/Canvas.js +++ b/src/layer/vector/Canvas.js @@ -190,7 +190,8 @@ L.Canvas = L.Renderer.extend({ for (var id in this._layers) { if (this._layers[id]._containsPoint(point)) { - this._layers[id]._fireMouseEvent(e); + L.DomEvent._fakeStop(e); + this._fireEvent(this._layers[id], e); } } }, @@ -213,20 +214,24 @@ L.Canvas = L.Renderer.extend({ // if we just got inside the layer, fire mouseover if (!layer._mouseInside) { L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor - layer._fireMouseEvent(e, 'mouseover'); + this._fireEvent(layer, e, 'mouseover'); layer._mouseInside = true; } // fire mousemove - layer._fireMouseEvent(e); + this._fireEvent(layer, e); } else if (layer._mouseInside) { // if we're leaving the layer, fire mouseout L.DomUtil.removeClass(this._container, 'leaflet-interactive'); - layer._fireMouseEvent(e, 'mouseout'); + this._fireEvent(layer, e, 'mouseout'); layer._mouseInside = false; } }, + _fireEvent: function (layer, e, type) { + this._map._fireDOMEvent(layer, e, type || e.type); + }, + // TODO _bringToFront & _bringToBack, pretty tricky _bringToFront: L.Util.falseFn, diff --git a/src/map/Map.js b/src/map/Map.js index d0914503..141525bf 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -585,8 +585,10 @@ L.Map = L.Evented.extend({ L.DomEvent.preventDefault(e); } - target = target || this; + this._fireDOMEvent(target || this, e, type); + }, + _fireDOMEvent: function (target, e, type) { if (!target.listens(type, true) && (type !== 'click' || !target.listens('preclick', true))) { return; } if (type === 'contextmenu') { From 63753d8564819d45be709299e542fa4860bc0f05 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 18 Nov 2014 20:27:52 +0200 Subject: [PATCH 72/89] prevent focus outline when dragging objects --- src/dom/Draggable.js | 7 ++++++- src/layer/marker/Marker.Drag.js | 2 +- src/map/Map.js | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index 2a6e9ff7..7e6748f0 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -20,9 +20,10 @@ L.Draggable = L.Evented.extend({ } }, - initialize: function (element, dragStartTarget) { + initialize: function (element, dragStartTarget, preventOutline) { this._element = element; this._dragStartTarget = dragStartTarget || element; + this._preventOutline = preventOutline; }, enable: function () { @@ -49,6 +50,10 @@ L.Draggable = L.Evented.extend({ L.DomEvent.stopPropagation(e); + if (this._preventOutline) { + L.DomEvent.preventDefault(e); + } + if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; } L.DomUtil.disableImageDrag(); diff --git a/src/layer/marker/Marker.Drag.js b/src/layer/marker/Marker.Drag.js index c0d6629b..f1d5779b 100644 --- a/src/layer/marker/Marker.Drag.js +++ b/src/layer/marker/Marker.Drag.js @@ -11,7 +11,7 @@ L.Handler.MarkerDrag = L.Handler.extend({ var icon = this._marker._icon; if (!this._draggable) { - this._draggable = new L.Draggable(icon, icon); + this._draggable = new L.Draggable(icon, icon, true); } this._draggable.on({ diff --git a/src/map/Map.js b/src/map/Map.js index 141525bf..c78d71a2 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -581,7 +581,7 @@ L.Map = L.Evented.extend({ !L.DomEvent._checkMouse(this._container, e)) { return; } // prevents outline when clicking on keyboard-focusable element - if (type === 'mousedown') { + if (target && type === 'mousedown') { L.DomEvent.preventDefault(e); } From a865d2b64972699a7c87d83ec6da89fa6868d3cb Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 18 Nov 2014 14:21:28 -0800 Subject: [PATCH 73/89] CSS-based outline prevention --- src/dom/DomUtil.js | 15 +++++++++++++++ src/dom/Draggable.js | 2 +- src/map/Map.js | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index 489e9bee..67de1558 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -219,4 +219,19 @@ L.DomUtil = { L.DomUtil.enableImageDrag = function () { L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault); }; + + L.DomUtil.preventOutline = function (element) { + L.DomUtil.restoreOutline(); + this._outlineElement = element; + this._outlineStyle = element.style.outline; + element.style.outline = 'none'; + L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this); + }; + L.DomUtil.restoreOutline = function () { + if (!this._outlineElement) { return; } + this._outlineElement.style.outline = this._outlineStyle; + delete this._outlineElement; + delete this._outlineStyle; + L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this); + }; })(); diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index 7e6748f0..428a2b58 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -51,7 +51,7 @@ L.Draggable = L.Evented.extend({ L.DomEvent.stopPropagation(e); if (this._preventOutline) { - L.DomEvent.preventDefault(e); + L.DomUtil.preventOutline(this._element); } if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; } diff --git a/src/map/Map.js b/src/map/Map.js index c78d71a2..b07b50c1 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -581,8 +581,8 @@ L.Map = L.Evented.extend({ !L.DomEvent._checkMouse(this._container, e)) { return; } // prevents outline when clicking on keyboard-focusable element - if (target && type === 'mousedown') { - L.DomEvent.preventDefault(e); + if (type === 'mousedown') { + L.DomUtil.preventOutline(e.target || e.srcElement); } this._fireDOMEvent(target || this, e, type); From 35860a0213e77362ece46f43d30a9d7aa6989734 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 2 Mar 2015 19:15:52 +0200 Subject: [PATCH 74/89] fix marker event data --- src/map/Map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/Map.js b/src/map/Map.js index b07b50c1..74f44822 100644 --- a/src/map/Map.js +++ b/src/map/Map.js @@ -602,8 +602,8 @@ L.Map = L.Evented.extend({ originalEvent: e }; if (e.type !== 'keypress') { - // TODO latlng isn't used, wrong latlng for markers - data.containerPoint = this.mouseEventToContainerPoint(e); + data.containerPoint = target instanceof L.Marker ? + this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); data.latlng = this.layerPointToLatLng(data.layerPoint); } From 4e44e12ecff37c8694ca801cc8cb96c01224804c Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Mon, 2 Mar 2015 19:33:39 +0200 Subject: [PATCH 75/89] add basic marker click test --- spec/suites/layer/marker/MarkerSpec.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/spec/suites/layer/marker/MarkerSpec.js b/spec/suites/layer/marker/MarkerSpec.js index ec4e98e5..2d28fac3 100644 --- a/spec/suites/layer/marker/MarkerSpec.js +++ b/spec/suites/layer/marker/MarkerSpec.js @@ -1,11 +1,16 @@ describe("Marker", function () { var map, spy, + div, icon1, icon2; beforeEach(function () { - map = L.map(document.createElement('div')).setView([0, 0], 0); + div = document.createElement('div'); + div.style.height = '100px'; + document.body.appendChild(div); + + map = L.map(div).setView([0, 0], 0); icon1 = new L.Icon.Default(); icon2 = new L.Icon.Default({ iconUrl: icon1._getIconUrl('icon') + '?2', @@ -13,6 +18,10 @@ describe("Marker", function () { }); }); + afterEach(function () { + document.body.removeChild(div); + }); + describe("#setIcon", function () { it("changes the icon to another image", function () { var marker = new L.Marker([0, 0], {icon: icon1}); @@ -145,4 +154,17 @@ describe("Marker", function () { expect(marker.getLatLng()).to.be(afterLatLng); }); }); + + describe('events', function () { + it('fires click event when clicked', function () { + var spy = sinon.spy(); + + var marker = L.marker([0, 0]).addTo(map); + + marker.on('click', spy); + happen.click(marker._icon); + + expect(spy.called).to.be.ok(); + }); + }); }); From 142e0661b190581c6e45c74c3419e46fdf98a5c5 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Tue, 3 Mar 2015 12:28:55 +0200 Subject: [PATCH 76/89] fix touch zoom regression --- src/layer/tile/GridLayer.js | 21 ++++++++++++++------- src/map/anim/Map.ZoomAnimation.js | 5 +++-- src/map/handler/Map.TouchZoom.js | 4 ++-- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/layer/tile/GridLayer.js b/src/layer/tile/GridLayer.js index d98994f3..7b4a04b7 100644 --- a/src/layer/tile/GridLayer.js +++ b/src/layer/tile/GridLayer.js @@ -215,7 +215,9 @@ L.GridLayer = L.Layer.extend({ level.zoom = zoom; this._setZoomTransform(level, map.getCenter(), map.getZoom()); - L.Util.falseFn(level.el.offsetWidth); // Force recalculation to trigger transitions. + + // force the browser to consider the newly added element for transition + L.Util.falseFn(level.el.offsetWidth); } this._level = level; @@ -301,26 +303,31 @@ L.GridLayer = L.Layer.extend({ }, _viewReset: function (e) { - var map = this._map; - this._reset(map.getCenter(), map.getZoom(), e && e.hard); + this._reset(this._map.getCenter(), this._map.getZoom(), e && e.hard); }, _animateZoom: function (e) { - this._reset(e.center, e.zoom, false, true); + this._reset(e.center, e.zoom, false, true, e.noUpdate); }, - _reset: function (center, zoom, hard, noPrune) { + _reset: function (center, zoom, hard, noPrune, noUpdate) { var tileZoom = Math.round(zoom), tileZoomChanged = this._tileZoom !== tileZoom; - if (tileZoomChanged || hard) { + if (!noUpdate && (hard || tileZoomChanged)) { + if (this._abortLoading) { this._abortLoading(); } + this._tileZoom = tileZoom; this._updateLevels(); this._resetGrid(); - this._update(center, tileZoom); + + if (!L.Browser.mobileWebkit) { + this._update(center, tileZoom); + } + if (!noPrune) { this._pruneTiles(); } diff --git a/src/map/anim/Map.ZoomAnimation.js b/src/map/anim/Map.ZoomAnimation.js index 085b2663..501b94d9 100644 --- a/src/map/anim/Map.ZoomAnimation.js +++ b/src/map/anim/Map.ZoomAnimation.js @@ -81,7 +81,7 @@ L.Map.include(!zoomAnimated ? {} : { return true; }, - _animateZoom: function (center, zoom, startAnim) { + _animateZoom: function (center, zoom, startAnim, noUpdate) { if (startAnim) { this._animatingZoom = true; @@ -97,7 +97,8 @@ L.Map.include(!zoomAnimated ? {} : { zoom: zoom, scale: this.getZoomScale(zoom), origin: this.latLngToLayerPoint(center), - offset: this._getCenterOffset(center).multiplyBy(-1) + offset: this._getCenterOffset(center).multiplyBy(-1), + noUpdate: noUpdate }); }, diff --git a/src/map/handler/Map.TouchZoom.js b/src/map/handler/Map.TouchZoom.js index bb4ae01c..e8b3754c 100644 --- a/src/map/handler/Map.TouchZoom.js +++ b/src/map/handler/Map.TouchZoom.js @@ -84,7 +84,7 @@ L.Map.TouchZoom = L.Handler.extend({ this._zoom = map.getScaleZoom(this._scale); if (this._scale !== 1 || this._delta.x !== 0 || this._delta.y !== 0) { - map._animateZoom(this._center, this._zoom); + map._animateZoom(this._center, this._zoom, false, true); } }, @@ -106,7 +106,7 @@ L.Map.TouchZoom = L.Handler.extend({ zoomDelta = this._zoom - oldZoom, finalZoom = map._limitZoom(zoomDelta > 0 ? Math.ceil(this._zoom) : Math.floor(this._zoom)); - map._animateZoom(this._center, finalZoom, true); + map._animateZoom(this._center, finalZoom, true, true); }, _getTargetCenter: function () { From 8842385c376a6ba14573f85b38e7b6fe8e05e7ba Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Tue, 3 Mar 2015 22:17:24 -0700 Subject: [PATCH 77/89] Prevent map refocus if control click event is from the keyboard, which is determined/assumed by the click X,Y being at 0,0. --- src/control/Control.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/control/Control.js b/src/control/Control.js index 786ba559..abcd5bff 100644 --- a/src/control/Control.js +++ b/src/control/Control.js @@ -71,8 +71,9 @@ L.Control = L.Class.extend({ return this; }, - _refocusOnMap: function () { - if (this._map) { + _refocusOnMap: function (e) { + // if map exists and event is keyboard event + if (this._map && (e.x > 0 && e.y > 0 && e.screenX > 0 && e.screenY > 0)) { this._map.getContainer().focus(); } } From 455fbb087b39f6d0a3b5c9aa755b728c5db6289e Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Wed, 4 Mar 2015 11:46:03 +0200 Subject: [PATCH 78/89] fix maxNativeZoom regression, close #3096 --- src/layer/tile/TileLayer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layer/tile/TileLayer.js b/src/layer/tile/TileLayer.js index c4a2f2b9..c23e8688 100644 --- a/src/layer/tile/TileLayer.js +++ b/src/layer/tile/TileLayer.js @@ -99,12 +99,12 @@ L.TileLayer = L.GridLayer.extend({ _getTileSize: function () { var map = this._map, options = this.options, - zoom = map.getZoom() + options.zoomOffset, + zoom = this._tileZoom + options.zoomOffset, zoomN = options.maxNativeZoom; // increase tile size when overscaling return zoomN !== null && zoom > zoomN ? - Math.round(map.getZoomScale(zoomN, zoom) * options.tileSize) : + Math.round(options.tileSize / map.getZoomScale(zoomN, zoom)) : options.tileSize; }, From 39425924ac3cd21b9cf329c13c26cd30a0c52a82 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Wed, 4 Mar 2015 18:58:51 -0700 Subject: [PATCH 79/89] Added truthy check to the event. --- src/control/Control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/control/Control.js b/src/control/Control.js index abcd5bff..34301c09 100644 --- a/src/control/Control.js +++ b/src/control/Control.js @@ -73,7 +73,7 @@ L.Control = L.Class.extend({ _refocusOnMap: function (e) { // if map exists and event is keyboard event - if (this._map && (e.x > 0 && e.y > 0 && e.screenX > 0 && e.screenY > 0)) { + if (this._map && e && e.screenX > 0 && e.screenY > 0) { this._map.getContainer().focus(); } } From aae1df5d49402aec0427313b9644a8287be21c44 Mon Sep 17 00:00:00 2001 From: Jake Wilson Date: Wed, 4 Mar 2015 19:00:52 -0700 Subject: [PATCH 80/89] Fixed typo in comment --- src/control/Control.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/control/Control.js b/src/control/Control.js index 34301c09..a21b1023 100644 --- a/src/control/Control.js +++ b/src/control/Control.js @@ -72,7 +72,7 @@ L.Control = L.Class.extend({ }, _refocusOnMap: function (e) { - // if map exists and event is keyboard event + // if map exists and event is not a keyboard event if (this._map && e && e.screenX > 0 && e.screenY > 0) { this._map.getContainer().focus(); } From 0d1716d7964491ae894b58ef006cda9dd07d078f Mon Sep 17 00:00:00 2001 From: Szunti Date: Fri, 6 Mar 2015 20:25:04 +0100 Subject: [PATCH 81/89] e.button === 0 is the left mouse button. --- src/dom/Draggable.js | 2 +- src/map/handler/Map.BoxZoom.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dom/Draggable.js b/src/dom/Draggable.js index 670c8a54..412004ab 100644 --- a/src/dom/Draggable.js +++ b/src/dom/Draggable.js @@ -46,7 +46,7 @@ L.Draggable = L.Evented.extend({ _onDown: function (e) { this._moved = false; - if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + if (e.shiftKey || ((e.which !== 1) && (e.button !== 0) && !e.touches)) { return; } L.DomEvent.stopPropagation(e); diff --git a/src/map/handler/Map.BoxZoom.js b/src/map/handler/Map.BoxZoom.js index dff50690..d94478b5 100644 --- a/src/map/handler/Map.BoxZoom.js +++ b/src/map/handler/Map.BoxZoom.js @@ -27,7 +27,7 @@ L.Map.BoxZoom = L.Handler.extend({ }, _onMouseDown: function (e) { - if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } + if (!e.shiftKey || ((e.which !== 1) && (e.button !== 0))) { return false; } this._moved = false; @@ -83,7 +83,7 @@ L.Map.BoxZoom = L.Handler.extend({ }, _onMouseUp: function (e) { - if ((e.which !== 1) && (e.button !== 1)) { return; } + if ((e.which !== 1) && (e.button !== 0)) { return; } this._finish(); From 196040b7950a5502bbc480f3dd1fa1ca0e96a956 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 7 Mar 2015 12:45:41 +0100 Subject: [PATCH 82/89] Attempt to fix random PhantomJS DISCONNECTED errors on Travis --- spec/karma.conf.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/karma.conf.js b/spec/karma.conf.js index d287ddac..af4023a7 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -63,6 +63,11 @@ module.exports = function (config) { // If browser does not capture in given timeout [ms], kill it captureTimeout: 5000, + // Workaround for PhantomJS random DISCONNECTED error + browserDisconnectTimeout: 10000, // default 2000 + browserDisconnectTolerance: 1, // default 0 + browserNoActivityTimeout: 60000, //default 10000 + // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun: true From d53a0a9779e768d41278f85c44e73290d42f3c96 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 7 Mar 2015 12:48:46 +0100 Subject: [PATCH 83/89] Allow to use console in tests This prevents us from adding /* eslint no-console: 0 */ each time we need to use console.log/trace for debugging. This also means that we will not be blocked if we forget a console in the tests before commiting, but this should also happen while using the inline "no-console" config, and given that this only affects tests files, I think the confort of being able to use console easily worths the risk of pushing a console in the tests files. --- spec/.eslintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/.eslintrc b/spec/.eslintrc index 32b81cc5..e3a29ac7 100644 --- a/spec/.eslintrc +++ b/spec/.eslintrc @@ -17,7 +17,8 @@ "strict": 0, "key-spacing": 0, "no-shadow": 0, - "no-irregular-whitespace": 0 + "no-irregular-whitespace": 0, + "no-console": 0 }, "globals": { "L": true, From 250f03339bbd00597a1f7801e6cc9e91d819c75d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 7 Mar 2015 19:00:27 +0100 Subject: [PATCH 84/89] Fix popupAnchor not taken into account when passing L.Popup to bindPopup --- spec/suites/layer/PopupSpec.js | 35 ++++++++++++++++++++++++++++++++++ src/layer/Layer.Popup.js | 1 + 2 files changed, 36 insertions(+) diff --git a/spec/suites/layer/PopupSpec.js b/spec/suites/layer/PopupSpec.js index 09e8b725..941e2bc9 100644 --- a/spec/suites/layer/PopupSpec.js +++ b/spec/suites/layer/PopupSpec.js @@ -116,6 +116,41 @@ describe('Popup', function () { marker1.closePopup(); expect(spy.callCount).to.be(2); }); + + it("should take into account icon popupAnchor option", function () { + var autoPanBefore = L.Popup.prototype.options.autoPan; + L.Popup.prototype.options.autoPan = false; + var popupAnchorBefore = L.Icon.Default.prototype.options.popupAnchor; + L.Icon.Default.prototype.options.popupAnchor = [0, 0]; + + var latlng = new L.LatLng(55.8, 37.6), + offset = new L.Point(20, 30), + icon = new L.DivIcon({popupAnchor: offset}), + marker1 = new L.Marker(latlng), + marker2 = new L.Marker(latlng, {icon: icon}); + marker1.bindPopup('Popup').addTo(map); + marker1.openPopup(); + var defaultLeft = parseInt(marker1._popup._container.style.left, 10); + var defaultBottom = parseInt(marker1._popup._container.style.bottom, 10); + marker2.bindPopup('Popup').addTo(map); + marker2.openPopup(); + var offsetLeft = parseInt(marker2._popup._container.style.left, 10); + var offsetBottom = parseInt(marker2._popup._container.style.bottom, 10); + expect(offsetLeft - offset.x).to.eql(defaultLeft); + expect(offsetBottom + offset.y).to.eql(defaultBottom); + + // Now retry passing a popup instance to bindPopup + marker2.bindPopup(new L.Popup()); + marker2.openPopup(); + offsetLeft = parseInt(marker2._popup._container.style.left, 10); + offsetBottom = parseInt(marker2._popup._container.style.bottom, 10); + expect(offsetLeft - offset.x).to.eql(defaultLeft); + expect(offsetBottom + offset.y).to.eql(defaultBottom); + + L.Popup.prototype.options.autoPan = autoPanBefore; + L.Icon.Default.prototype.options.popupAnchor = popupAnchorBefore; + }); + }); describe("L.Map#openPopup", function () { diff --git a/src/layer/Layer.Popup.js b/src/layer/Layer.Popup.js index 5b276772..5561f97c 100644 --- a/src/layer/Layer.Popup.js +++ b/src/layer/Layer.Popup.js @@ -7,6 +7,7 @@ L.Layer.include({ bindPopup: function (content, options) { if (content instanceof L.Popup) { + L.setOptions(content, options); this._popup = content; content._source = this; } else { From 00b97f3b010bd2d2cd9612428aa0c640a2d1d3de Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 7 Mar 2015 23:12:15 +0100 Subject: [PATCH 85/89] Remove browserNoActivityTimeout from karma config --- spec/karma.conf.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/karma.conf.js b/spec/karma.conf.js index af4023a7..b48792e4 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -66,7 +66,6 @@ module.exports = function (config) { // Workaround for PhantomJS random DISCONNECTED error browserDisconnectTimeout: 10000, // default 2000 browserDisconnectTolerance: 1, // default 0 - browserNoActivityTimeout: 60000, //default 10000 // Continuous Integration mode // if true, it capture browsers, run tests and exit From 60df92045202ea8517b110b7852707ab18c313fe Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sun, 8 Mar 2015 12:21:43 +0100 Subject: [PATCH 86/89] Add optional callback to map.flyTo --- spec/suites/map/MapSpec.js | 20 ++++++++++++++++++++ src/map/anim/Map.FlyTo.js | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/spec/suites/map/MapSpec.js b/spec/suites/map/MapSpec.js index e7261ce0..b4f4c8de 100644 --- a/spec/suites/map/MapSpec.js +++ b/spec/suites/map/MapSpec.js @@ -570,4 +570,24 @@ describe("Map", function () { expect(spy.called).to.be.ok(); }); }); + + describe('#flyTo', function () { + + it('move to requested center and zoom, and call callback once', function (done) { + var spy = sinon.spy(), + newCenter = new L.LatLng(10, 11), + newZoom = 12, + callback = function () { + expect(map.getCenter()).to.eql(newCenter); + expect(map.getZoom()).to.eql(newZoom); + spy(); + expect(spy.calledOnce).to.be.ok(); + done(); + }; + map.setView([0, 0], 0); + map.flyTo(newCenter, newZoom, callback); + }); + + }); + }); diff --git a/src/map/anim/Map.FlyTo.js b/src/map/anim/Map.FlyTo.js index 81128f1a..44e12b25 100644 --- a/src/map/anim/Map.FlyTo.js +++ b/src/map/anim/Map.FlyTo.js @@ -1,6 +1,6 @@ L.Map.include({ - flyTo: function (targetCenter, targetZoom) { + flyTo: function (targetCenter, targetZoom, callback) { this.stop(); @@ -51,6 +51,9 @@ L.Map.include({ } else { this._resetView(targetCenter, targetZoom, true, true); + if (callback) { + callback(); + } } } From c10f81f95d8658189f7d39f19029fd352181f239 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Mon, 9 Mar 2015 10:11:20 +0100 Subject: [PATCH 87/89] Remove flyTo callback (but keep test refactored) --- spec/suites/map/MapSpec.js | 2 +- src/map/anim/Map.FlyTo.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/suites/map/MapSpec.js b/spec/suites/map/MapSpec.js index b4f4c8de..6306f7b6 100644 --- a/spec/suites/map/MapSpec.js +++ b/spec/suites/map/MapSpec.js @@ -585,7 +585,7 @@ describe("Map", function () { done(); }; map.setView([0, 0], 0); - map.flyTo(newCenter, newZoom, callback); + map.once('zoomend', callback).flyTo(newCenter, newZoom); }); }); diff --git a/src/map/anim/Map.FlyTo.js b/src/map/anim/Map.FlyTo.js index 44e12b25..81128f1a 100644 --- a/src/map/anim/Map.FlyTo.js +++ b/src/map/anim/Map.FlyTo.js @@ -1,6 +1,6 @@ L.Map.include({ - flyTo: function (targetCenter, targetZoom, callback) { + flyTo: function (targetCenter, targetZoom) { this.stop(); @@ -51,9 +51,6 @@ L.Map.include({ } else { this._resetView(targetCenter, targetZoom, true, true); - if (callback) { - callback(); - } } } From 4e2505670305f69d8ccacd7afd50e8ca0d06dabb Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Mon, 9 Mar 2015 10:13:42 +0100 Subject: [PATCH 88/89] Typo in test description --- spec/suites/map/MapSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/suites/map/MapSpec.js b/spec/suites/map/MapSpec.js index 6306f7b6..f466f701 100644 --- a/spec/suites/map/MapSpec.js +++ b/spec/suites/map/MapSpec.js @@ -573,7 +573,7 @@ describe("Map", function () { describe('#flyTo', function () { - it('move to requested center and zoom, and call callback once', function (done) { + it('move to requested center and zoom, and call zoomend once', function (done) { var spy = sinon.spy(), newCenter = new L.LatLng(10, 11), newZoom = 12, From 7db3473727795225545e4d241127ee6f9255f7bd Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Sun, 8 Mar 2015 20:34:35 -0700 Subject: [PATCH 89/89] fix typo, thanks @uniphil --- src/map/anim/Map.ZoomAnimation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/anim/Map.ZoomAnimation.js b/src/map/anim/Map.ZoomAnimation.js index 4d79fc5b..27e14a26 100644 --- a/src/map/anim/Map.ZoomAnimation.js +++ b/src/map/anim/Map.ZoomAnimation.js @@ -40,7 +40,7 @@ L.Map.include(!zoomAnimated ? {} : { L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); // workaround for case when transform is the same and so transitionend event is not fired - if (transform === proxy.style[prop] & this._animatingZoom) { + if (transform === proxy.style[prop] && this._animatingZoom) { this._onZoomTransitionEnd(); } }, this);