leaflet-rails 0.4.0.alpha5 → 0.4.0.alpha6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  module Leaflet
2
2
  module Rails
3
- VERSION = "0.4.0.alpha5"
3
+ VERSION = "0.4.0.alpha6"
4
4
  end
5
5
  end
@@ -10,13 +10,12 @@ var L, originalL;
10
10
  if (typeof exports !== 'undefined') {
11
11
  L = exports;
12
12
  } else {
13
- L = {};
14
-
15
13
  originalL = window.L;
14
+ L = {};
16
15
 
17
16
  L.noConflict = function () {
18
17
  window.L = originalL;
19
- return L;
18
+ return this;
20
19
  };
21
20
 
22
21
  window.L = L;
@@ -99,7 +98,7 @@ L.Util = {
99
98
 
100
99
  limitExecByInterval: function (fn, time, context) {
101
100
  var lock, execOnUnlock;
102
-
101
+
103
102
  return function wrapperFn() {
104
103
  var args = arguments;
105
104
 
@@ -109,10 +108,10 @@ L.Util = {
109
108
  }
110
109
 
111
110
  lock = true;
112
-
111
+
113
112
  setTimeout(function () {
114
113
  lock = false;
115
-
114
+
116
115
  if (execOnUnlock) {
117
116
  wrapperFn.apply(context, args);
118
117
  execOnUnlock = false;
@@ -292,54 +291,65 @@ L.Mixin.Events.fire = L.Mixin.Events.fireEvent;
292
291
  (function () {
293
292
  var ua = navigator.userAgent.toLowerCase(),
294
293
  ie = !!window.ActiveXObject,
294
+ ie6 = ie && !window.XMLHttpRequest,
295
295
  webkit = ua.indexOf("webkit") !== -1,
296
- mobile = typeof orientation !== 'undefined' ? true : false,
296
+ gecko = ua.indexOf("gecko") !== -1,
297
+ opera = window.opera,
297
298
  android = ua.indexOf("android") !== -1,
298
- opera = window.opera;
299
-
300
- L.Browser = {
301
- ie: ie,
302
- ie6: ie && !window.XMLHttpRequest,
299
+ mobile = typeof orientation !== 'undefined' ? true : false,
300
+ doc = document.documentElement,
301
+ ie3d = ie && ('transition' in doc.style),
302
+ webkit3d = webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
303
+ gecko3d = gecko && ('MozPerspective' in doc.style),
304
+ opera3d = opera && ('OTransition' in doc.style);
303
305
 
304
- webkit: webkit,
305
- webkit3d: webkit && ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()),
306
+ var touch = (function () {
307
+ var startName = 'ontouchstart';
306
308
 
307
- gecko: ua.indexOf("gecko") !== -1,
309
+ // WebKit, etc
310
+ if (startName in doc) {
311
+ return true;
312
+ }
308
313
 
309
- opera: opera,
314
+ // Firefox/Gecko
315
+ var div = document.createElement('div'),
316
+ supported = false;
310
317
 
311
- android: android,
312
- mobileWebkit: mobile && webkit,
313
- mobileOpera: mobile && opera,
318
+ if (!div.setAttribute) {
319
+ return false;
320
+ }
321
+ div.setAttribute(startName, 'return;');
314
322
 
315
- mobile: mobile,
316
- touch: (function () {
317
- var touchSupported = false,
318
- startName = 'ontouchstart';
323
+ if (typeof div[startName] === 'function') {
324
+ supported = true;
325
+ }
319
326
 
320
- // WebKit, etc
321
- if (startName in document.documentElement) {
322
- return true;
323
- }
327
+ div.removeAttribute(startName);
328
+ div = null;
324
329
 
325
- // Firefox/Gecko
326
- var e = document.createElement('div');
330
+ return supported;
331
+ }());
327
332
 
328
- // If no support for basic event stuff, unlikely to have touch support
329
- if (!e.setAttribute || !e.removeAttribute) {
330
- return false;
331
- }
333
+ L.Browser = {
334
+ ie: ie,
335
+ ie6: ie6,
336
+ webkit: webkit,
337
+ gecko: gecko,
338
+ opera: opera,
339
+ android: android,
332
340
 
333
- e.setAttribute(startName, 'return;');
334
- if (typeof e[startName] === 'function') {
335
- touchSupported = true;
336
- }
341
+ ie3d: ie3d,
342
+ webkit3d: webkit3d,
343
+ gecko3d: gecko3d,
344
+ opera3d: opera3d,
345
+ any3d: ie3d || webkit3d || gecko3d || opera3d,
337
346
 
338
- e.removeAttribute(startName);
339
- e = null;
347
+ mobile: mobile,
348
+ mobileWebkit: mobile && webkit,
349
+ mobileWebkit3d: mobile && webkit3d,
350
+ mobileOpera: mobile && opera,
340
351
 
341
- return touchSupported;
342
- }())
352
+ touch: touch
343
353
  };
344
354
  }());
345
355
 
@@ -622,8 +632,6 @@ L.DomUtil = {
622
632
  }
623
633
  },
624
634
 
625
- //TODO refactor away this ugly translate/position mess
626
-
627
635
  testProp: function (props) {
628
636
  var style = document.documentElement.style;
629
637
 
@@ -636,9 +644,15 @@ L.DomUtil = {
636
644
  },
637
645
 
638
646
  getTranslateString: function (point) {
639
- return L.DomUtil.TRANSLATE_OPEN +
640
- point.x + 'px,' + point.y + 'px' +
641
- L.DomUtil.TRANSLATE_CLOSE;
647
+ // On webkit browsers (Chrome/Safari/MobileSafari/Android) using translate3d instead of translate
648
+ // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care
649
+ // (same speed either way), Opera 12 doesn't support translate3d
650
+
651
+ var is3d = L.Browser.webkit3d,
652
+ open = 'translate' + (is3d ? '3d' : '') + '(',
653
+ close = (is3d ? ',0' : '') + ')';
654
+
655
+ return open + point.x + 'px,' + point.y + 'px' + close;
642
656
  },
643
657
 
644
658
  getScaleString: function (scale, origin) {
@@ -649,11 +663,15 @@ L.DomUtil = {
649
663
  return preTranslateStr + scaleStr + postTranslateStr;
650
664
  },
651
665
 
652
- setPosition: function (el, point) {
666
+ setPosition: function (el, point, disable3D) {
653
667
  el._leaflet_pos = point;
654
- if (L.Browser.webkit3d) {
668
+ if (!disable3D && L.Browser.any3d) {
655
669
  el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point);
656
- el.style['-webkit-backface-visibility'] = 'hidden';
670
+
671
+ // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69)
672
+ if (L.Browser.mobileWebkit3d) {
673
+ el.style.WebkitBackfaceVisibility = 'hidden';
674
+ }
657
675
  } else {
658
676
  el.style.left = point.x + 'px';
659
677
  el.style.top = point.y + 'px';
@@ -667,10 +685,7 @@ L.DomUtil = {
667
685
 
668
686
  L.Util.extend(L.DomUtil, {
669
687
  TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']),
670
- TRANSFORM: L.DomUtil.testProp(['transformProperty', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']),
671
-
672
- TRANSLATE_OPEN: 'translate' + (L.Browser.webkit3d ? '3d(' : '('),
673
- TRANSLATE_CLOSE: L.Browser.webkit3d ? ',0)' : ')'
688
+ TRANSFORM: L.DomUtil.testProp(['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform'])
674
689
  });
675
690
 
676
691
 
@@ -869,12 +884,13 @@ L.Projection.SphericalMercator = {
869
884
  return new L.Point(x, y);
870
885
  },
871
886
 
872
- unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
887
+ unproject: function (point) { // (Point, Boolean) -> LatLng
873
888
  var d = L.LatLng.RAD_TO_DEG,
874
889
  lng = point.x * d,
875
890
  lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
876
891
 
877
- return new L.LatLng(lat, lng, unbounded);
892
+ // TODO refactor LatLng wrapping
893
+ return new L.LatLng(lat, lng, true);
878
894
  }
879
895
  };
880
896
 
@@ -885,8 +901,8 @@ L.Projection.LonLat = {
885
901
  return new L.Point(latlng.lng, latlng.lat);
886
902
  },
887
903
 
888
- unproject: function (point, unbounded) {
889
- return new L.LatLng(point.y, point.x, unbounded);
904
+ unproject: function (point) {
905
+ return new L.LatLng(point.y, point.x, true);
890
906
  }
891
907
  };
892
908
 
@@ -900,12 +916,11 @@ L.CRS = {
900
916
  return this.transformation._transform(projectedPoint, scale);
901
917
  },
902
918
 
903
- pointToLatLng: function (point, zoom, unbounded) { // (Point, Number[, Boolean]) -> LatLng
919
+ pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
904
920
  var scale = this.scale(zoom),
905
921
  untransformedPoint = this.transformation.untransform(point, scale);
906
922
 
907
- return this.projection.unproject(untransformedPoint, unbounded);
908
- //TODO get rid of 'unbounded' everywhere
923
+ return this.projection.unproject(untransformedPoint);
909
924
  },
910
925
 
911
926
  project: function (latlng) {
@@ -963,7 +978,8 @@ L.Map = L.Class.extend({
963
978
  */
964
979
 
965
980
  fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android,
966
- trackResize: true
981
+ trackResize: true,
982
+ markerZoomAnimation: true
967
983
  },
968
984
 
969
985
  initialize: function (id, options) { // (HTMLElement or String, Object)
@@ -1180,11 +1196,11 @@ L.Map = L.Class.extend({
1180
1196
 
1181
1197
  // public methods for getting map state
1182
1198
 
1183
- getCenter: function (unbounded) { // (Boolean) -> LatLng
1199
+ getCenter: function () { // (Boolean) -> LatLng
1184
1200
  var viewHalf = this.getSize().divideBy(2),
1185
1201
  centerPoint = this._getTopLeftPoint().add(viewHalf);
1186
1202
 
1187
- return this.unproject(centerPoint, this._zoom, unbounded);
1203
+ return this.unproject(centerPoint, this._zoom);
1188
1204
  },
1189
1205
 
1190
1206
  getZoom: function () {
@@ -1272,7 +1288,7 @@ L.Map = L.Class.extend({
1272
1288
  getPanes: function () {
1273
1289
  return this._panes;
1274
1290
  },
1275
-
1291
+
1276
1292
  getContainer: function () {
1277
1293
  return this._container;
1278
1294
  },
@@ -1321,10 +1337,9 @@ L.Map = L.Class.extend({
1321
1337
  return this.options.crs.latLngToPoint(latlng, zoom);
1322
1338
  },
1323
1339
 
1324
- unproject: function (point, zoom, unbounded) { // (Point[, Number, Boolean]) -> LatLng
1325
- // TODO remove unbounded, making it true all the time?
1340
+ unproject: function (point, zoom) { // (Point[, Number, Boolean]) -> LatLng
1326
1341
  zoom = typeof zoom === 'undefined' ? this._zoom : zoom;
1327
- return this.options.crs.pointToLatLng(point, zoom, unbounded);
1342
+ return this.options.crs.pointToLatLng(point, zoom);
1328
1343
  },
1329
1344
 
1330
1345
 
@@ -1356,7 +1371,7 @@ L.Map = L.Class.extend({
1356
1371
 
1357
1372
  var position = L.DomUtil.getStyle(container, 'position');
1358
1373
 
1359
- if (position !== 'absolute' && position !== 'relative') {
1374
+ if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
1360
1375
  container.style.position = 'relative';
1361
1376
  }
1362
1377
 
@@ -1379,6 +1394,12 @@ L.Map = L.Class.extend({
1379
1394
  panes.overlayPane = this._createPane('leaflet-overlay-pane');
1380
1395
  panes.markerPane = this._createPane('leaflet-marker-pane');
1381
1396
  panes.popupPane = this._createPane('leaflet-popup-pane');
1397
+
1398
+ if (!this.options.markerZoomAnimation) {
1399
+ panes.markerPane.className += ' leaflet-zoom-hide';
1400
+ panes.shadowPane.className += ' leaflet-zoom-hide';
1401
+ panes.popupPane.className += ' leaflet-zoom-hide';
1402
+ }
1382
1403
  },
1383
1404
 
1384
1405
  _createPane: function (className, container) {
@@ -1531,10 +1552,17 @@ L.Map = L.Class.extend({
1531
1552
  return this._initialTopLeftPoint.subtract(mapPanePos);
1532
1553
  },
1533
1554
 
1534
- _getNewTopLeftPoint: function (center) {
1555
+ _getNewTopLeftPoint: function (center, zoom) {
1535
1556
  var viewHalf = this.getSize().divideBy(2);
1536
1557
  // TODO round on display, not calculation to increase precision?
1537
- return this.project(center)._subtract(viewHalf)._round();
1558
+ return this.project(center, zoom)._subtract(viewHalf)._round();
1559
+ },
1560
+
1561
+ _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) {
1562
+ var mapPaneOffset = L.DomUtil.getPosition(this._mapPane),
1563
+ topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(mapPaneOffset);
1564
+
1565
+ return this.project(latlng, newZoom)._round()._subtract(topLeft);
1538
1566
  },
1539
1567
 
1540
1568
  _limitZoom: function (zoom) {
@@ -1582,7 +1610,7 @@ L.Projection.Mercator = {
1582
1610
  return new L.Point(x, y);
1583
1611
  },
1584
1612
 
1585
- unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
1613
+ unproject: function (point) { // (Point, Boolean) -> LatLng
1586
1614
  var d = L.LatLng.RAD_TO_DEG,
1587
1615
  r = this.R_MAJOR,
1588
1616
  r2 = this.R_MINOR,
@@ -1603,7 +1631,7 @@ L.Projection.Mercator = {
1603
1631
  phi += dphi;
1604
1632
  }
1605
1633
 
1606
- return new L.LatLng(phi * d, lng, unbounded);
1634
+ return new L.LatLng(phi * d, lng, true);
1607
1635
  }
1608
1636
  };
1609
1637
 
@@ -1712,6 +1740,19 @@ L.TileLayer = L.Class.extend({
1712
1740
  this._map = null;
1713
1741
  },
1714
1742
 
1743
+ bringToFront: function () {
1744
+ if (this._container) {
1745
+ this._map._panes.tilePane.appendChild(this._container);
1746
+ }
1747
+ },
1748
+
1749
+ bringToBack: function () {
1750
+ var pane = this._map._panes.tilePane;
1751
+ if (this._container) {
1752
+ pane.insertBefore(this._container, pane.firstChild);
1753
+ }
1754
+ },
1755
+
1715
1756
  getAttribution: function () {
1716
1757
  return this.options.attribution;
1717
1758
  },
@@ -1722,6 +1763,10 @@ L.TileLayer = L.Class.extend({
1722
1763
  if (this._map) {
1723
1764
  this._updateOpacity();
1724
1765
  }
1766
+ },
1767
+
1768
+ _updateOpacity: function () {
1769
+ L.DomUtil.setOpacity(this._container, this.options.opacity);
1725
1770
 
1726
1771
  // stupid webkit hack to force redrawing of tiles
1727
1772
  var i,
@@ -1736,10 +1781,6 @@ L.TileLayer = L.Class.extend({
1736
1781
  }
1737
1782
  },
1738
1783
 
1739
- _updateOpacity: function () {
1740
- L.DomUtil.setOpacity(this._container, this.options.opacity);
1741
- },
1742
-
1743
1784
  _initContainer: function () {
1744
1785
  var tilePane = this._map._panes.tilePane,
1745
1786
  first = tilePane.firstChild;
@@ -1825,6 +1866,8 @@ L.TileLayer = L.Class.extend({
1825
1866
  }
1826
1867
  }
1827
1868
 
1869
+ if (queue.length === 0) { return; }
1870
+
1828
1871
  // load tiles in order of their distance to center
1829
1872
  queue.sort(function (a, b) {
1830
1873
  return a.distanceTo(center) - b.distanceTo(center);
@@ -1843,7 +1886,7 @@ L.TileLayer = L.Class.extend({
1843
1886
  },
1844
1887
 
1845
1888
  _removeOtherTiles: function (bounds) {
1846
- var kArr, x, y, key, tile;
1889
+ var kArr, x, y, key;
1847
1890
 
1848
1891
  for (key in this._tiles) {
1849
1892
  if (this._tiles.hasOwnProperty(key)) {
@@ -1864,11 +1907,11 @@ L.TileLayer = L.Class.extend({
1864
1907
 
1865
1908
  this.fire("tileunload", {tile: tile, url: tile.src});
1866
1909
 
1867
- if (tile.parentNode === this._container) {
1868
- this._container.removeChild(tile);
1869
- }
1870
1910
  if (this.options.reuseTiles) {
1911
+ tile.className = tile.className.replace(' leaflet-tile-loaded', '');
1871
1912
  this._unusedTiles.push(tile);
1913
+ } else if (tile.parentNode === this._container) {
1914
+ this._container.removeChild(tile);
1872
1915
  }
1873
1916
 
1874
1917
  tile.src = L.Util.emptyImageUrl;
@@ -1899,7 +1942,7 @@ L.TileLayer = L.Class.extend({
1899
1942
 
1900
1943
  // get unused tile - or create a new tile
1901
1944
  var tile = this._getTile();
1902
- L.DomUtil.setPosition(tile, tilePos);
1945
+ L.DomUtil.setPosition(tile, tilePos, true);
1903
1946
 
1904
1947
  this._tiles[key] = tile;
1905
1948
 
@@ -1909,7 +1952,9 @@ L.TileLayer = L.Class.extend({
1909
1952
 
1910
1953
  this._loadTile(tile, tilePoint, zoom);
1911
1954
 
1912
- container.appendChild(tile);
1955
+ if (tile.parentNode !== this._container) {
1956
+ container.appendChild(tile);
1957
+ }
1913
1958
  },
1914
1959
 
1915
1960
  _getOffsetZoom: function (zoom) {
@@ -1986,14 +2031,17 @@ L.TileLayer = L.Class.extend({
1986
2031
  _tileOnLoad: function (e) {
1987
2032
  var layer = this._layer;
1988
2033
 
1989
- this.className += ' leaflet-tile-loaded';
2034
+ //Only if we are loading an actual image
2035
+ if (this.src !== L.Util.emptyImageUrl) {
2036
+ this.className += ' leaflet-tile-loaded';
1990
2037
 
1991
- layer.fire('tileload', {
1992
- tile: this,
1993
- url: this.src
1994
- });
2038
+ layer.fire('tileload', {
2039
+ tile: this,
2040
+ url: this.src
2041
+ });
2042
+ }
1995
2043
 
1996
- layer._tileLoaded();
2044
+ layer._tileLoaded();
1997
2045
  },
1998
2046
 
1999
2047
  _tileOnError: function (e) {
@@ -2135,9 +2183,15 @@ L.TileLayer.Canvas = L.TileLayer.extend({
2135
2183
  L.ImageOverlay = L.Class.extend({
2136
2184
  includes: L.Mixin.Events,
2137
2185
 
2138
- initialize: function (/*String*/ url, /*LatLngBounds*/ bounds) {
2186
+ options: {
2187
+ opacity: 1
2188
+ },
2189
+
2190
+ initialize: function (url, bounds, options) { // (String, LatLngBounds)
2139
2191
  this._url = url;
2140
2192
  this._bounds = bounds;
2193
+
2194
+ L.Util.setOptions(this, options);
2141
2195
  },
2142
2196
 
2143
2197
  onAdd: function (map) {
@@ -2149,6 +2203,7 @@ L.ImageOverlay = L.Class.extend({
2149
2203
 
2150
2204
  map._panes.overlayPane.appendChild(this._image);
2151
2205
 
2206
+ map.on('zoomanim', this._zoomAnimation, this);
2152
2207
  map.on('viewreset', this._reset, this);
2153
2208
  this._reset();
2154
2209
  },
@@ -2158,11 +2213,17 @@ L.ImageOverlay = L.Class.extend({
2158
2213
  map.off('viewreset', this._reset, this);
2159
2214
  },
2160
2215
 
2216
+ setOpacity: function (opacity) {
2217
+ this.options.opacity = opacity;
2218
+ this._updateOpacity();
2219
+ },
2220
+
2161
2221
  _initImage: function () {
2162
- this._image = L.DomUtil.create('img', 'leaflet-image-layer');
2222
+ this._image = L.DomUtil.create('img', 'leaflet-image-layer leaflet-zoom-animated');
2163
2223
 
2164
2224
  this._image.style.visibility = 'hidden';
2165
- //TODO opacity option
2225
+
2226
+ this._updateOpacity();
2166
2227
 
2167
2228
  //TODO createImage util method to remove duplication
2168
2229
  L.Util.extend(this._image, {
@@ -2174,6 +2235,16 @@ L.ImageOverlay = L.Class.extend({
2174
2235
  });
2175
2236
  },
2176
2237
 
2238
+ _zoomAnimation: function (opt) {
2239
+ var image = this._image,
2240
+ scale = Math.pow(2, opt.zoom - this._map._zoom),
2241
+ topLeft = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), opt.zoom, opt.center),
2242
+ size = this._map._latLngToNewLayerPoint(this._bounds.getSouthEast(), opt.zoom, opt.center).subtract(topLeft),
2243
+ currentSize = this._map.latLngToLayerPoint(this._bounds.getSouthEast()).subtract(this._map.latLngToLayerPoint(this._bounds.getNorthWest()));
2244
+
2245
+ image.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(topLeft.add(size.subtract(currentSize).divideBy(2))) + ' scale(' + scale + ') ';
2246
+ },
2247
+
2177
2248
  _reset: function () {
2178
2249
  var image = this._image,
2179
2250
  topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
@@ -2188,6 +2259,10 @@ L.ImageOverlay = L.Class.extend({
2188
2259
  _onImageLoad: function () {
2189
2260
  this._image.style.visibility = '';
2190
2261
  this.fire('load');
2262
+ },
2263
+
2264
+ _updateOpacity: function () {
2265
+ L.DomUtil.setOpacity(this._image, this.options.opacity);
2191
2266
  }
2192
2267
  });
2193
2268
 
@@ -2221,7 +2296,7 @@ L.Icon = L.Class.extend({
2221
2296
  var src = this._getIconUrl(name);
2222
2297
 
2223
2298
  if (!src) { return null; }
2224
-
2299
+
2225
2300
  var img = this._createImg(src);
2226
2301
  this._setIconStyles(img, name);
2227
2302
 
@@ -2241,7 +2316,7 @@ L.Icon = L.Class.extend({
2241
2316
  anchor._add(options.shadowOffset);
2242
2317
  }
2243
2318
 
2244
- img.className = 'leaflet-marker-' + name + ' ' + options.className;
2319
+ img.className = 'leaflet-marker-' + name + ' ' + options.className + ' leaflet-zoom-animated';
2245
2320
 
2246
2321
  if (anchor) {
2247
2322
  img.style.marginLeft = (-anchor.x) + 'px';
@@ -2335,10 +2410,14 @@ L.Marker = L.Class.extend({
2335
2410
  onAdd: function (map) {
2336
2411
  this._map = map;
2337
2412
 
2338
- map.on('viewreset', this._reset, this);
2413
+ map.on('viewreset', this.update, this);
2414
+
2415
+ if (map.options.zoomAnimation && map.options.markerZoomAnimation) {
2416
+ map.on('zoomanim', this._zoomAnimation, this);
2417
+ }
2339
2418
 
2340
2419
  this._initIcon();
2341
- this._reset();
2420
+ this.update();
2342
2421
  },
2343
2422
 
2344
2423
  onRemove: function (map) {
@@ -2349,7 +2428,8 @@ L.Marker = L.Class.extend({
2349
2428
  this.closePopup();
2350
2429
  }
2351
2430
 
2352
- map.off('viewreset', this._reset, this);
2431
+ map.off('viewreset', this.update, this)
2432
+ .off('zoomanim', this._zoomAnimation, this);
2353
2433
 
2354
2434
  this._map = null;
2355
2435
  },
@@ -2361,7 +2441,7 @@ L.Marker = L.Class.extend({
2361
2441
  setLatLng: function (latlng) {
2362
2442
  this._latlng = latlng;
2363
2443
 
2364
- this._reset();
2444
+ this.update();
2365
2445
 
2366
2446
  if (this._popup) {
2367
2447
  this._popup.setLatLng(latlng);
@@ -2370,7 +2450,7 @@ L.Marker = L.Class.extend({
2370
2450
 
2371
2451
  setZIndexOffset: function (offset) {
2372
2452
  this.options.zIndexOffset = offset;
2373
- this._reset();
2453
+ this.update();
2374
2454
  },
2375
2455
 
2376
2456
  setIcon: function (icon) {
@@ -2382,10 +2462,17 @@ L.Marker = L.Class.extend({
2382
2462
 
2383
2463
  if (this._map) {
2384
2464
  this._initIcon();
2385
- this._reset();
2465
+ this.update();
2386
2466
  }
2387
2467
  },
2388
2468
 
2469
+ update: function () {
2470
+ if (!this._icon) { return; }
2471
+
2472
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
2473
+ this._setPos(pos);
2474
+ },
2475
+
2389
2476
  _initIcon: function () {
2390
2477
  var options = this.options;
2391
2478
 
@@ -2424,22 +2511,20 @@ L.Marker = L.Class.extend({
2424
2511
  this._icon = this._shadow = null;
2425
2512
  },
2426
2513
 
2427
- _reset: function () {
2428
- var icon = this._icon;
2429
-
2430
- if (!icon) {
2431
- return;
2432
- }
2433
-
2434
- var pos = this._map.latLngToLayerPoint(this._latlng).round();
2435
-
2436
- L.DomUtil.setPosition(icon, pos);
2514
+ _setPos: function (pos) {
2515
+ L.DomUtil.setPosition(this._icon, pos);
2437
2516
 
2438
2517
  if (this._shadow) {
2439
2518
  L.DomUtil.setPosition(this._shadow, pos);
2440
2519
  }
2441
2520
 
2442
- icon.style.zIndex = pos.y + this.options.zIndexOffset;
2521
+ this._icon.style.zIndex = pos.y + this.options.zIndexOffset;
2522
+ },
2523
+
2524
+ _zoomAnimation: function (opt) {
2525
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);
2526
+
2527
+ this._setPos(pos);
2443
2528
  },
2444
2529
 
2445
2530
  _initInteraction: function () {
@@ -2557,6 +2642,10 @@ L.Popup = L.Class.extend({
2557
2642
 
2558
2643
  map.on('viewreset', this._updatePosition, this);
2559
2644
 
2645
+ if (L.Browser.any3d) {
2646
+ map.on('zoomanim', this._zoomAnimation, this);
2647
+ }
2648
+
2560
2649
  if (map.options.closePopupOnClick) {
2561
2650
  map.on('preclick', this._close, this);
2562
2651
  }
@@ -2572,7 +2661,8 @@ L.Popup = L.Class.extend({
2572
2661
  L.Util.falseFn(this._container.offsetWidth);
2573
2662
 
2574
2663
  map.off('viewreset', this._updatePosition, this)
2575
- .off('preclick', this._close, this);
2664
+ .off('preclick', this._close, this)
2665
+ .off('zoomanim', this._zoomAnimation, this);
2576
2666
 
2577
2667
  this._container.style.opacity = '0';
2578
2668
 
@@ -2605,7 +2695,7 @@ L.Popup = L.Class.extend({
2605
2695
 
2606
2696
  _initLayout: function () {
2607
2697
  var prefix = 'leaflet-popup',
2608
- container = this._container = L.DomUtil.create('div', prefix + ' ' + this.options.className),
2698
+ container = this._container = L.DomUtil.create('div', prefix + ' ' + this.options.className + ' leaflet-zoom-animated'),
2609
2699
  closeButton;
2610
2700
 
2611
2701
  if (this.options.closeButton) {
@@ -2645,7 +2735,9 @@ L.Popup = L.Class.extend({
2645
2735
  if (typeof this._content === 'string') {
2646
2736
  this._contentNode.innerHTML = this._content;
2647
2737
  } else {
2648
- this._contentNode.innerHTML = '';
2738
+ while (this._contentNode.hasChildNodes()) {
2739
+ this._contentNode.removeChild(this._contentNode.firstChild);
2740
+ }
2649
2741
  this._contentNode.appendChild(this._content);
2650
2742
  }
2651
2743
  this.fire('contentupdate');
@@ -2681,15 +2773,28 @@ L.Popup = L.Class.extend({
2681
2773
  },
2682
2774
 
2683
2775
  _updatePosition: function () {
2684
- var pos = this._map.latLngToLayerPoint(this._latlng);
2776
+ var pos = this._map.latLngToLayerPoint(this._latlng),
2777
+ is3d = L.Browser.any3d,
2778
+ offset = this.options.offset;
2685
2779
 
2686
- this._containerBottom = -pos.y - this.options.offset.y;
2687
- this._containerLeft = pos.x - Math.round(this._containerWidth / 2) + this.options.offset.x;
2780
+ if (is3d) {
2781
+ L.DomUtil.setPosition(this._container, pos);
2782
+ }
2783
+
2784
+ this._containerBottom = -offset.y - (is3d ? 0 : pos.y);
2785
+ this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (is3d ? 0 : pos.x);
2688
2786
 
2787
+ //Bottom position the popup in case the height of the popup changes (images loading etc)
2689
2788
  this._container.style.bottom = this._containerBottom + 'px';
2690
2789
  this._container.style.left = this._containerLeft + 'px';
2691
2790
  },
2692
2791
 
2792
+ _zoomAnimation: function (opt) {
2793
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center)._round();
2794
+
2795
+ L.DomUtil.setPosition(this._container, pos);
2796
+ },
2797
+
2693
2798
  _adjustPan: function () {
2694
2799
  if (!this.options.autoPan) { return; }
2695
2800
 
@@ -2697,30 +2802,33 @@ L.Popup = L.Class.extend({
2697
2802
  containerHeight = this._container.offsetHeight,
2698
2803
  containerWidth = this._containerWidth,
2699
2804
 
2700
- layerPos = new L.Point(
2701
- this._containerLeft,
2702
- -containerHeight - this._containerBottom),
2805
+ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
2806
+
2807
+ if (L.Browser.any3d) {
2808
+ layerPos._add(L.DomUtil.getPosition(this._container));
2809
+ }
2703
2810
 
2704
- containerPos = map.layerPointToContainerPoint(layerPos),
2705
- adjustOffset = new L.Point(0, 0),
2706
- padding = this.options.autoPanPadding,
2707
- size = map.getSize();
2811
+ var containerPos = map.layerPointToContainerPoint(layerPos),
2812
+ padding = this.options.autoPanPadding,
2813
+ size = map.getSize(),
2814
+ dx = 0,
2815
+ dy = 0;
2708
2816
 
2709
2817
  if (containerPos.x < 0) {
2710
- adjustOffset.x = containerPos.x - padding.x;
2818
+ dx = containerPos.x - padding.x;
2711
2819
  }
2712
2820
  if (containerPos.x + containerWidth > size.x) {
2713
- adjustOffset.x = containerPos.x + containerWidth - size.x + padding.x;
2821
+ dx = containerPos.x + containerWidth - size.x + padding.x;
2714
2822
  }
2715
2823
  if (containerPos.y < 0) {
2716
- adjustOffset.y = containerPos.y - padding.y;
2824
+ dy = containerPos.y - padding.y;
2717
2825
  }
2718
2826
  if (containerPos.y + containerHeight > size.y) {
2719
- adjustOffset.y = containerPos.y + containerHeight - size.y + padding.y;
2827
+ dy = containerPos.y + containerHeight - size.y + padding.y;
2720
2828
  }
2721
2829
 
2722
- if (adjustOffset.x || adjustOffset.y) {
2723
- map.panBy(adjustOffset);
2830
+ if (dx || dy) {
2831
+ map.panBy(new L.Point(dx, dy));
2724
2832
  }
2725
2833
  },
2726
2834
 
@@ -3034,6 +3142,19 @@ L.Path = L.Path.extend({
3034
3142
  SVG: L.Browser.svg
3035
3143
  },
3036
3144
 
3145
+ bringToFront: function () {
3146
+ if (this._container) {
3147
+ this._map._pathRoot.appendChild(this._container);
3148
+ }
3149
+ },
3150
+
3151
+ bringToBack: function () {
3152
+ if (this._container) {
3153
+ var root = this._map._pathRoot;
3154
+ root.insertBefore(this._container, root.firstChild);
3155
+ }
3156
+ },
3157
+
3037
3158
  getPathString: function () {
3038
3159
  // form path string here
3039
3160
  },
@@ -3147,12 +3268,43 @@ L.Map.include({
3147
3268
  this._pathRoot = L.Path.prototype._createElement('svg');
3148
3269
  this._panes.overlayPane.appendChild(this._pathRoot);
3149
3270
 
3271
+ if (this.options.zoomAnimation) {
3272
+ this._pathRoot.setAttribute('class', ' leaflet-zoom-animated');
3273
+ this.on('zoomanim', this._animatePathZoom);
3274
+ this.on('zoomend', this._endPathZoom);
3275
+ }
3276
+
3150
3277
  this.on('moveend', this._updateSvgViewport);
3151
3278
  this._updateSvgViewport();
3152
3279
  }
3153
3280
  },
3154
3281
 
3282
+ _animatePathZoom: function (opt) {
3283
+ // TODO refactor into something more manageable
3284
+ var centerOffset = this._getNewTopLeftPoint(opt.center).subtract(this._getTopLeftPoint()),
3285
+ scale = Math.pow(2, opt.zoom - this._zoom),
3286
+ offset = centerOffset.divideBy(1 - 1 / scale),
3287
+ centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(-2)),
3288
+ origin = centerPoint.add(offset).round(),
3289
+ pathRootStyle = this._pathRoot.style;
3290
+
3291
+ pathRootStyle[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString((origin.multiplyBy(-1).add(L.DomUtil.getPosition(this._pathRoot)).multiplyBy(scale).add(origin))) + ' scale(' + scale + ') ';
3292
+
3293
+ this._pathZooming = true;
3294
+ },
3295
+
3296
+ _endPathZoom: function () {
3297
+ this._pathZooming = false;
3298
+ },
3299
+
3155
3300
  _updateSvgViewport: function () {
3301
+ if (this._pathZooming) {
3302
+ //Do not update SVGs while a zoom animation is going on otherwise the animation will break.
3303
+ //When the zoom animation ends we will be updated again anyway
3304
+ //This fixes the case where you do a momentum move and zoom while the move is still ongoing.
3305
+ return;
3306
+ }
3307
+
3156
3308
  this._updatePathViewport();
3157
3309
 
3158
3310
  var vp = this._pathViewport,
@@ -3165,7 +3317,7 @@ L.Map.include({
3165
3317
 
3166
3318
  // Hack to make flicker on drag end on mobile webkit less irritating
3167
3319
  // Unfortunately I haven't found a good workaround for this yet
3168
- if (L.Browser.webkit) {
3320
+ if (L.Browser.mobileWebkit) {
3169
3321
  pane.removeChild(root);
3170
3322
  }
3171
3323
 
@@ -3174,7 +3326,7 @@ L.Map.include({
3174
3326
  root.setAttribute('height', height);
3175
3327
  root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
3176
3328
 
3177
- if (L.Browser.webkit) {
3329
+ if (L.Browser.mobileWebkit) {
3178
3330
  pane.appendChild(root);
3179
3331
  }
3180
3332
  }
@@ -3254,21 +3406,6 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
3254
3406
  },
3255
3407
 
3256
3408
  _initStyle: function () {
3257
- var container = this._container,
3258
- stroke,
3259
- fill;
3260
-
3261
- if (this.options.stroke) {
3262
- stroke = this._stroke = this._createElement('stroke');
3263
- stroke.endcap = 'round';
3264
- container.appendChild(stroke);
3265
- }
3266
-
3267
- if (this.options.fill) {
3268
- fill = this._fill = this._createElement('fill');
3269
- container.appendChild(fill);
3270
- }
3271
-
3272
3409
  this._updateStyle();
3273
3410
  },
3274
3411
 
@@ -3282,14 +3419,29 @@ L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({
3282
3419
  container.filled = options.fill;
3283
3420
 
3284
3421
  if (options.stroke) {
3285
- stroke.weight = options.weight + 'px';
3286
- stroke.color = options.color;
3422
+ if (!stroke) {
3423
+ stroke = this._stroke = this._createElement('stroke');
3424
+ stroke.endcap = 'round';
3425
+ container.appendChild(stroke);
3426
+ }
3427
+ stroke.weight = options.weight + 'px';
3428
+ stroke.color = options.color;
3287
3429
  stroke.opacity = options.opacity;
3430
+ } else if (stroke) {
3431
+ container.removeChild(stroke);
3432
+ this._stroke = null;
3288
3433
  }
3289
3434
 
3290
3435
  if (options.fill) {
3291
- fill.color = options.fillColor || options.color;
3436
+ if (!fill) {
3437
+ fill = this._fill = this._createElement('fill');
3438
+ container.appendChild(fill);
3439
+ }
3440
+ fill.color = options.fillColor || options.color;
3292
3441
  fill.opacity = options.fillOpacity;
3442
+ } else if (fill) {
3443
+ container.removeChild(fill);
3444
+ this._fill = null;
3293
3445
  }
3294
3446
  },
3295
3447
 
@@ -3437,12 +3589,21 @@ L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {}
3437
3589
 
3438
3590
  this._panes.overlayPane.appendChild(root);
3439
3591
 
3592
+ if (this.options.zoomAnimation) {
3593
+ this._pathRoot.className = 'leaflet-zoom-animated';
3594
+ this.on('zoomanim', this._animatePathZoom);
3595
+ this.on('zoomend', this._endPathZoom);
3596
+ }
3440
3597
  this.on('moveend', this._updateCanvasViewport);
3441
3598
  this._updateCanvasViewport();
3442
3599
  }
3443
3600
  },
3444
3601
 
3445
3602
  _updateCanvasViewport: function () {
3603
+ if (this._pathZooming) {
3604
+ //Don't redraw while zooming. See _updateSvgViewport for more details
3605
+ return;
3606
+ }
3446
3607
  this._updatePathViewport();
3447
3608
 
3448
3609
  var vp = this._pathViewport,
@@ -3731,10 +3892,10 @@ L.Polyline = L.Path.extend({
3731
3892
  for (var i = 1, len = points.length; i < len; i++) {
3732
3893
  p1 = points[i - 1];
3733
3894
  p2 = points[i];
3734
- var point = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
3735
- if (point._sqDist < minDistance) {
3736
- minDistance = point._sqDist;
3737
- minPoint = point;
3895
+ var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true);
3896
+ if (sqDist < minDistance) {
3897
+ minDistance = sqDist;
3898
+ minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2);
3738
3899
  }
3739
3900
  }
3740
3901
  }
@@ -4016,7 +4177,7 @@ L.Rectangle = L.Polygon.extend({
4016
4177
  setBounds: function (latLngBounds) {
4017
4178
  this.setLatLngs(this._boundsToLatLngs(latLngBounds));
4018
4179
  },
4019
-
4180
+
4020
4181
  _boundsToLatLngs: function (latLngBounds) {
4021
4182
  return [
4022
4183
  latLngBounds.getSouthWest(),
@@ -4076,7 +4237,7 @@ L.Circle = L.Path.extend({
4076
4237
 
4077
4238
  return new L.LatLngBounds(sw, ne);
4078
4239
  },
4079
-
4240
+
4080
4241
  getLatLng: function () {
4081
4242
  return this._latlng;
4082
4243
  },
@@ -4099,7 +4260,7 @@ L.Circle = L.Path.extend({
4099
4260
  return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
4100
4261
  }
4101
4262
  },
4102
-
4263
+
4103
4264
  getRadius: function () {
4104
4265
  return this._mRadius;
4105
4266
  },
@@ -4236,7 +4397,6 @@ L.Circle.include(!L.Path.CANVAS ? {} : {
4236
4397
  });
4237
4398
 
4238
4399
 
4239
-
4240
4400
  L.GeoJSON = L.FeatureGroup.extend({
4241
4401
  initialize: function (geojson, options) {
4242
4402
  L.Util.setOptions(this, options);
@@ -4269,7 +4429,8 @@ L.GeoJSON = L.FeatureGroup.extend({
4269
4429
  properties: geojson.properties,
4270
4430
  geometryType: geometry.type,
4271
4431
  bbox: geojson.bbox,
4272
- id: geojson.id
4432
+ id: geojson.id,
4433
+ geometry: geojson.geometry
4273
4434
  });
4274
4435
 
4275
4436
  this.addLayer(layer);
@@ -4587,22 +4748,23 @@ L.Draggable = L.Class.extend({
4587
4748
  },
4588
4749
 
4589
4750
  _onMove: function (e) {
4590
- if (e.touches && e.touches.length > 1) {
4591
- return;
4592
- }
4751
+ if (e.touches && e.touches.length > 1) { return; }
4593
4752
 
4594
- L.DomEvent.preventDefault(e);
4753
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
4754
+ newPoint = new L.Point(first.clientX, first.clientY),
4755
+ diffVec = newPoint.subtract(this._startPoint);
4595
4756
 
4596
- var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e);
4757
+ if (!diffVec.x && !diffVec.y) { return; }
4758
+
4759
+ L.DomEvent.preventDefault(e);
4597
4760
 
4598
4761
  if (!this._moved) {
4599
4762
  this.fire('dragstart');
4600
4763
  this._moved = true;
4601
4764
  }
4602
- this._moving = true;
4603
4765
 
4604
- var newPoint = new L.Point(first.clientX, first.clientY);
4605
- this._newPos = this._startPos.add(newPoint).subtract(this._startPoint);
4766
+ this._newPos = this._startPos.add(diffVec);
4767
+ this._moving = true;
4606
4768
 
4607
4769
  L.Util.cancelAnimFrame(this._animRequest);
4608
4770
  this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget);
@@ -4638,6 +4800,9 @@ L.Draggable = L.Class.extend({
4638
4800
  L.DomEvent.removeListener(document, L.Draggable.END, this._onUp);
4639
4801
 
4640
4802
  if (this._moved) {
4803
+ // ensure drag is not fired after dragend
4804
+ L.Util.cancelAnimFrame(this._animRequest);
4805
+
4641
4806
  this.fire('dragend');
4642
4807
  }
4643
4808
  this._moving = false;
@@ -5032,21 +5197,32 @@ L.Map.TouchZoom = L.Handler.extend({
5032
5197
 
5033
5198
  if (this._scale === 1) { return; }
5034
5199
 
5200
+ var zoom = this._map._zoom + Math.log(this._scale) / Math.LN2;
5201
+
5202
+ var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale),
5203
+ centerPoint = this._map.getPixelOrigin().add(this._startCenter).add(centerOffset),
5204
+ center = this._map.unproject(centerPoint);
5205
+
5035
5206
  if (!this._moved) {
5036
- map._mapPane.className += ' leaflet-zoom-anim';
5207
+ map._mapPane.className += ' leaflet-zoom-anim leaflet-touching';
5037
5208
 
5038
5209
  map
5039
- .fire('zoomstart')
5040
5210
  .fire('movestart')
5211
+ .fire('zoomstart')
5041
5212
  ._prepareTileBg();
5042
5213
 
5043
5214
  this._moved = true;
5044
5215
  }
5045
5216
 
5217
+ map.fire('zoomanim', {
5218
+ center: center,
5219
+ zoom: zoom
5220
+ });
5221
+
5046
5222
  // Used 2 translates instead of transform-origin because of a very strange bug -
5047
5223
  // it didn't count the origin on the first touch-zoom but worked correctly afterwards
5048
5224
 
5049
- map._tileBg.style.webkitTransform =
5225
+ map._tileBg.style[L.DomUtil.TRANSFORM] =
5050
5226
  L.DomUtil.getTranslateString(this._delta) + ' ' +
5051
5227
  L.DomUtil.getScaleString(this._scale, this._startCenter);
5052
5228
 
@@ -5057,6 +5233,7 @@ L.Map.TouchZoom = L.Handler.extend({
5057
5233
  if (!this._moved || !this._zooming) { return; }
5058
5234
 
5059
5235
  this._zooming = false;
5236
+ this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-touching', ''); //TODO toggleClass util
5060
5237
 
5061
5238
  L.DomEvent
5062
5239
  .removeListener(document, 'touchmove', this._onTouchMove)
@@ -5072,7 +5249,12 @@ L.Map.TouchZoom = L.Handler.extend({
5072
5249
  zoom = this._map._limitZoom(oldZoom + roundZoomDelta),
5073
5250
  finalScale = Math.pow(2, zoom - oldZoom);
5074
5251
 
5075
- this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset));
5252
+ this._map.fire('zoomanim', {
5253
+ center: center,
5254
+ zoom: zoom
5255
+ });
5256
+
5257
+ this._map._runAnimation(center, zoom, finalScale / this._scale, this._startCenter.add(centerOffset), true);
5076
5258
  }
5077
5259
  });
5078
5260
 
@@ -5118,7 +5300,7 @@ L.Map.BoxZoom = L.Handler.extend({
5118
5300
  .addListener(document, 'mousemove', this._onMouseMove, this)
5119
5301
  .addListener(document, 'mouseup', this._onMouseUp, this)
5120
5302
  .preventDefault(e);
5121
-
5303
+
5122
5304
  this._map.fire("boxzoomstart");
5123
5305
  },
5124
5306
 
@@ -5158,7 +5340,7 @@ L.Map.BoxZoom = L.Handler.extend({
5158
5340
  map.layerPointToLatLng(layerPoint));
5159
5341
 
5160
5342
  map.fitBounds(bounds);
5161
-
5343
+
5162
5344
  map.fire("boxzoomend", {
5163
5345
  boxZoomBounds: bounds
5164
5346
  });
@@ -5261,7 +5443,9 @@ L.Handler.PolyEdit = L.Handler.extend({
5261
5443
  },
5262
5444
 
5263
5445
  _initMarkers: function () {
5264
- this._markerGroup = new L.LayerGroup();
5446
+ if (!this._markerGroup) {
5447
+ this._markerGroup = new L.LayerGroup();
5448
+ }
5265
5449
  this._markers = [];
5266
5450
 
5267
5451
  var latlngs = this._poly._latlngs,
@@ -5332,10 +5516,10 @@ L.Handler.PolyEdit = L.Handler.extend({
5332
5516
  if (this._poly._latlngs.length < 3) {
5333
5517
  return;
5334
5518
  }
5335
-
5519
+
5336
5520
  var marker = e.target,
5337
5521
  i = marker._index;
5338
-
5522
+
5339
5523
  // Check existence of previous and next markers since they wouldn't exist for edge points on the polyline
5340
5524
  if (marker._prev && marker._next) {
5341
5525
  this._createMiddleMarker(marker._prev, marker._next);
@@ -5351,6 +5535,7 @@ L.Handler.PolyEdit = L.Handler.extend({
5351
5535
  if (marker._middleRight) {
5352
5536
  this._markerGroup.removeLayer(marker._middleRight);
5353
5537
  }
5538
+ this._markers.splice(i, 1);
5354
5539
  this._poly.spliceLatLngs(i, 1);
5355
5540
  this._updateIndexes(i, -1);
5356
5541
  this._poly.fire('edit');
@@ -5384,6 +5569,8 @@ L.Handler.PolyEdit = L.Handler.extend({
5384
5569
  .off('click', onClick)
5385
5570
  .on('click', this._onMarkerClick, this);
5386
5571
 
5572
+ latlng.lat = marker.getLatLng().lat;
5573
+ latlng.lng = marker.getLatLng().lng;
5387
5574
  this._poly.spliceLatLngs(i, 0, latlng);
5388
5575
  this._markers.splice(i, 0, marker);
5389
5576
 
@@ -5710,14 +5897,17 @@ L.Control.Scale = L.Control.extend({
5710
5897
 
5711
5898
  size = this._map.getSize(),
5712
5899
  options = this.options,
5900
+ maxMeters = 0;
5713
5901
 
5714
- maxMeters = left.distanceTo(right) * (options.maxWidth / size.x);
5902
+ if (size.x > 0) {
5903
+ maxMeters = left.distanceTo(right) * (options.maxWidth / size.x);
5904
+ }
5715
5905
 
5716
- if (options.metric) {
5906
+ if (options.metric && maxMeters) {
5717
5907
  this._updateMetric(maxMeters);
5718
5908
  }
5719
5909
 
5720
- if (options.imperial) {
5910
+ if (options.imperial && maxMeters) {
5721
5911
  this._updateImperial(maxMeters);
5722
5912
  }
5723
5913
  },
@@ -5764,6 +5954,7 @@ L.Control.Scale = L.Control.extend({
5764
5954
  });
5765
5955
 
5766
5956
 
5957
+
5767
5958
  L.Control.Layers = L.Control.extend({
5768
5959
  options: {
5769
5960
  collapsed: true,
@@ -5835,7 +6026,15 @@ L.Control.Layers = L.Control.extend({
5835
6026
  link.href = '#';
5836
6027
  link.title = 'Layers';
5837
6028
 
5838
- L.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);
6029
+ if (L.Browser.touch) {
6030
+ L.DomEvent
6031
+ .addListener(link, 'click', L.DomEvent.stopPropagation)
6032
+ .addListener(link, 'click', L.DomEvent.preventDefault)
6033
+ .addListener(link, 'click', this._expand, this);
6034
+ }
6035
+ else {
6036
+ L.DomEvent.addListener(link, 'focus', this._expand, this);
6037
+ }
5839
6038
 
5840
6039
  this._map.on('movestart', this._collapse, this);
5841
6040
  // TODO keyboard accessibility
@@ -5983,7 +6182,7 @@ L.Transition = L.Transition.extend({
5983
6182
 
5984
6183
  // transition-property value to use with each particular custom property
5985
6184
  CUSTOM_PROPS_PROPERTIES: {
5986
- position: L.Browser.webkit ? L.DomUtil.TRANSFORM : 'top, left'
6185
+ position: L.Browser.any3d ? L.DomUtil.TRANSFORM : 'top, left'
5987
6186
  }
5988
6187
  };
5989
6188
  }()),
@@ -6056,7 +6255,7 @@ L.Transition = L.Transition.extend({
6056
6255
  this._inProgress = false;
6057
6256
  clearInterval(this._timer);
6058
6257
 
6059
- this._el.style[L.Transition.PROPERTY] = 'none';
6258
+ this._el.style[L.Transition.TRANSITION] = '';
6060
6259
 
6061
6260
  this.fire('step');
6062
6261
 
@@ -6301,22 +6500,37 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
6301
6500
 
6302
6501
  this._mapPane.className += ' leaflet-zoom-anim';
6303
6502
 
6304
- this
6503
+ this
6305
6504
  .fire('movestart')
6306
6505
  .fire('zoomstart');
6307
6506
 
6308
- this._prepareTileBg();
6507
+ //Hack: Disable this for android due to it not supporting double translate (mentioned in _runAnimation below)
6508
+ //if Foreground layer doesn't have many tiles but bg layer does, keep the existing bg layer
6509
+ if (!L.Browser.android && this._tileBg && this._getLoadedTilesPercentage(this._tileBg) > 0.5 && this._getLoadedTilesPercentage(this._tilePane) < 0.5) {
6510
+ //Leave current bg and just zoom it some more
6511
+
6512
+ this._tilePane.style.visibility = 'hidden';
6513
+ this._tilePane.empty = true;
6514
+ this._stopLoadingImages(this._tilePane);
6515
+ } else {
6516
+ this._prepareTileBg();
6517
+ }
6309
6518
 
6310
6519
  var centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(2)),
6311
6520
  origin = centerPoint.add(offset);
6312
6521
 
6522
+ this.fire('zoomanim', {
6523
+ center: center,
6524
+ zoom: zoom
6525
+ });
6526
+
6313
6527
  this._runAnimation(center, zoom, scale, origin);
6314
6528
 
6315
6529
  return true;
6316
6530
  },
6317
6531
 
6318
6532
 
6319
- _runAnimation: function (center, zoom, scale, origin) {
6533
+ _runAnimation: function (center, zoom, scale, origin, backwardsTransform) {
6320
6534
  this._animatingZoom = true;
6321
6535
 
6322
6536
  this._animateToCenter = center;
@@ -6348,7 +6562,11 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
6348
6562
  L.Util.falseFn(tileBg.offsetWidth); //hack to make sure transform is updated before running animation
6349
6563
 
6350
6564
  var options = {};
6351
- options[transform] = tileBg.style[transform] + ' ' + scaleStr;
6565
+ if (backwardsTransform) {
6566
+ options[transform] = tileBg.style[transform] + ' ' + scaleStr;
6567
+ } else {
6568
+ options[transform] = scaleStr + ' ' + tileBg.style[transform];
6569
+ }
6352
6570
 
6353
6571
  tileBg.transition.run(options);
6354
6572
  },
@@ -6367,9 +6585,10 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
6367
6585
  tileBg.style.visibility = 'hidden';
6368
6586
 
6369
6587
  // tells tile layers to reinitialize their containers
6370
- tileBg.empty = true;
6371
- tilePane.empty = false;
6588
+ tileBg.empty = true; //new FG
6589
+ tilePane.empty = false; //new BG
6372
6590
 
6591
+ //Switch out the current layer to be the new bg layer (And vice-versa)
6373
6592
  this._tilePane = this._panes.tilePane = tileBg;
6374
6593
  var newTileBg = this._tileBg = tilePane;
6375
6594
 
@@ -6385,6 +6604,18 @@ L.Map.include(!L.DomUtil.TRANSITION ? {} : {
6385
6604
  this._stopLoadingImages(newTileBg);
6386
6605
  },
6387
6606
 
6607
+ _getLoadedTilesPercentage: function (container) {
6608
+ var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
6609
+ i, len, count = 0;
6610
+
6611
+ for (i = 0, len = tiles.length; i < len; i++) {
6612
+ if (tiles[i].complete) {
6613
+ count++;
6614
+ }
6615
+ }
6616
+ return count / len;
6617
+ },
6618
+
6388
6619
  // stops loading all tiles in the background layer
6389
6620
  _stopLoadingImages: function (container) {
6390
6621
  var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')),
@@ -6517,4 +6748,4 @@ L.Map.include({
6517
6748
 
6518
6749
 
6519
6750
 
6520
- }());
6751
+ }());