leaflet-rails 0.4.0.alpha5 → 0.4.0.alpha6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }());