leaflet-rails 0.4.0.alpha2 → 0.4.0alpha3

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.
@@ -3,40 +3,26 @@
3
3
  Leaflet is a modern open-source JavaScript library for interactive maps.
4
4
  http://leaflet.cloudmade.com
5
5
  */
6
+ (function () {
6
7
 
7
- (function (root) {
8
- root.L = {
9
- VERSION: '0.4',
10
-
11
- ROOT_URL: root.L_ROOT_URL || (function () {
12
- var scripts = document.getElementsByTagName('script'),
13
- leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
14
-
15
- var i, len, src, matches;
16
-
17
- for (i = 0, len = scripts.length; i < len; i++) {
18
- src = scripts[i].src;
19
- matches = src.match(leafletRe);
8
+ var L, originalL;
20
9
 
21
- if (matches) {
22
- if (matches[1] === 'include') {
23
- return '../../dist/';
24
- }
25
- return src.split(leafletRe)[0] + '/';
26
- }
27
- }
10
+ if (typeof exports !== 'undefined') {
11
+ L = exports;
12
+ } else {
13
+ L = {};
14
+
15
+ originalL = window.L;
28
16
 
29
- return '';
30
- }()),
17
+ L.noConflict = function () {
18
+ window.L = originalL;
19
+ return L;
20
+ };
31
21
 
32
- noConflict: function () {
33
- root.L = this._originalL;
34
- return this;
35
- },
22
+ window.L = L;
23
+ }
36
24
 
37
- _originalL: root.L
38
- };
39
- }(this));
25
+ L.version = '0.4';
40
26
 
41
27
 
42
28
  /*
@@ -57,9 +43,10 @@ L.Util = {
57
43
  return dest;
58
44
  },
59
45
 
60
- bind: function (/*Function*/ fn, /*Object*/ obj) /*-> Object*/ {
46
+ bind: function (fn, obj) { // (Function, Object) -> Function
47
+ var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null;
61
48
  return function () {
62
- return fn.apply(obj, arguments);
49
+ return fn.apply(obj, args || arguments);
63
50
  };
64
51
  },
65
52
 
@@ -111,23 +98,28 @@ L.Util = {
111
98
  }()),
112
99
 
113
100
  limitExecByInterval: function (fn, time, context) {
114
- var lock, execOnUnlock, args;
115
- function exec() {
116
- lock = false;
117
- if (execOnUnlock) {
118
- args.callee.apply(context, args);
119
- execOnUnlock = false;
120
- }
121
- }
122
- return function () {
123
- args = arguments;
124
- if (!lock) {
125
- lock = true;
126
- setTimeout(exec, time);
127
- fn.apply(context, args);
128
- } else {
101
+ var lock, execOnUnlock;
102
+
103
+ return function wrapperFn() {
104
+ var args = arguments;
105
+
106
+ if (lock) {
129
107
  execOnUnlock = true;
108
+ return;
130
109
  }
110
+
111
+ lock = true;
112
+
113
+ setTimeout(function () {
114
+ lock = false;
115
+
116
+ if (execOnUnlock) {
117
+ wrapperFn.apply(context, args);
118
+ execOnUnlock = false;
119
+ }
120
+ }, time);
121
+
122
+ fn.apply(context, args);
131
123
  };
132
124
  },
133
125
 
@@ -142,6 +134,7 @@ L.Util = {
142
134
 
143
135
  setOptions: function (obj, options) {
144
136
  obj.options = L.Util.extend({}, obj.options, options);
137
+ return obj.options;
145
138
  },
146
139
 
147
140
  getParamString: function (obj) {
@@ -186,20 +179,15 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ {
186
179
  // instantiate class without calling constructor
187
180
  var F = function () {};
188
181
  F.prototype = this.prototype;
189
- var proto = new F();
190
182
 
183
+ var proto = new F();
191
184
  proto.constructor = NewClass;
192
- NewClass.prototype = proto;
193
185
 
194
- // add superclass access
195
- NewClass.superclass = this.prototype;
196
-
197
- // add class name
198
- //proto.className = props;
186
+ NewClass.prototype = proto;
199
187
 
200
188
  //inherit parent's statics
201
189
  for (var i in this) {
202
- if (this.hasOwnProperty(i) && i !== 'prototype' && i !== 'superclass') {
190
+ if (this.hasOwnProperty(i) && i !== 'prototype') {
203
191
  NewClass[i] = this[i];
204
192
  }
205
193
  }
@@ -224,17 +212,18 @@ L.Class.extend = function (/*Object*/ props) /*-> Class*/ {
224
212
  // mix given properties into the prototype
225
213
  L.Util.extend(proto, props);
226
214
 
227
- // allow inheriting further
228
- NewClass.extend = L.Class.extend;
215
+ return NewClass;
216
+ };
229
217
 
230
- // method for adding properties to prototype
231
- NewClass.include = function (props) {
232
- L.Util.extend(this.prototype, props);
233
- };
234
218
 
235
- return NewClass;
219
+ // method for adding properties to prototype
220
+ L.Class.include = function (props) {
221
+ L.Util.extend(this.prototype, props);
236
222
  };
237
223
 
224
+ L.Class.mergeOptions = function (options) {
225
+ L.Util.extend(this.prototype.options, options);
226
+ };
238
227
 
239
228
  /*
240
229
  * L.Mixin.Events adds custom events functionality to Leaflet classes
@@ -556,6 +545,12 @@ L.DomUtil = {
556
545
  L.DomUtil.getStyle(el, 'position') === 'absolute') {
557
546
  break;
558
547
  }
548
+ if (L.DomUtil.getStyle(el, 'position') === 'fixed') {
549
+ top += docBody.scrollTop || 0;
550
+ left += docBody.scrollLeft || 0;
551
+ break;
552
+ }
553
+
559
554
  el = el.offsetParent;
560
555
  } while (el);
561
556
 
@@ -621,7 +616,7 @@ L.DomUtil = {
621
616
 
622
617
  setOpacity: function (el, value) {
623
618
  if (L.Browser.ie) {
624
- el.style.filter = 'alpha(opacity=' + Math.round(value * 100) + ')';
619
+ el.style.filter += value !== 1 ? 'alpha(opacity=' + Math.round(value * 100) + ')' : '';
625
620
  } else {
626
621
  el.style.opacity = value;
627
622
  }
@@ -756,16 +751,21 @@ L.LatLngBounds = L.Class.extend({
756
751
  }
757
752
  },
758
753
 
759
- // extend the bounds to contain the given point
760
- extend: function (/*LatLng*/ latlng) {
761
- if (!this._southWest && !this._northEast) {
762
- this._southWest = new L.LatLng(latlng.lat, latlng.lng, true);
763
- this._northEast = new L.LatLng(latlng.lat, latlng.lng, true);
764
- } else {
765
- this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
766
- this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
767
- this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
768
- this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
754
+ // extend the bounds to contain the given point or bounds
755
+ extend: function (/*LatLng or LatLngBounds*/ obj) {
756
+ if (obj instanceof L.LatLng) {
757
+ if (!this._southWest && !this._northEast) {
758
+ this._southWest = new L.LatLng(obj.lat, obj.lng, true);
759
+ this._northEast = new L.LatLng(obj.lat, obj.lng, true);
760
+ } else {
761
+ this._southWest.lat = Math.min(obj.lat, this._southWest.lat);
762
+ this._southWest.lng = Math.min(obj.lng, this._southWest.lng);
763
+ this._northEast.lat = Math.max(obj.lat, this._northEast.lat);
764
+ this._northEast.lng = Math.max(obj.lng, this._northEast.lng);
765
+ }
766
+ } else if (obj instanceof L.LatLngBounds) {
767
+ this.extend(obj._southWest);
768
+ this.extend(obj._northEast);
769
769
  }
770
770
  return this;
771
771
  },
@@ -858,7 +858,7 @@ L.Projection = {};
858
858
  L.Projection.SphericalMercator = {
859
859
  MAX_LATITUDE: 85.0511287798,
860
860
 
861
- project: function (/*LatLng*/ latlng) /*-> Point*/ {
861
+ project: function (latlng) { // (LatLng) -> Point
862
862
  var d = L.LatLng.DEG_TO_RAD,
863
863
  max = this.MAX_LATITUDE,
864
864
  lat = Math.max(Math.min(max, latlng.lat), -max),
@@ -869,7 +869,7 @@ L.Projection.SphericalMercator = {
869
869
  return new L.Point(x, y);
870
870
  },
871
871
 
872
- unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
872
+ unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
873
873
  var d = L.LatLng.RAD_TO_DEG,
874
874
  lng = point.x * d,
875
875
  lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d;
@@ -893,19 +893,27 @@ L.Projection.LonLat = {
893
893
 
894
894
 
895
895
  L.CRS = {
896
- latLngToPoint: function (/*LatLng*/ latlng, /*Number*/ scale)/*-> Point*/ {
897
- var projectedPoint = this.projection.project(latlng);
896
+ latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
897
+ var projectedPoint = this.projection.project(latlng),
898
+ scale = this.scale(zoom);
899
+
898
900
  return this.transformation._transform(projectedPoint, scale);
899
901
  },
900
902
 
901
- pointToLatLng: function (/*Point*/ point, /*Number*/ scale, /*(optional) Boolean*/ unbounded)/*-> LatLng*/ {
902
- var untransformedPoint = this.transformation.untransform(point, scale);
903
+ pointToLatLng: function (point, zoom, unbounded) { // (Point, Number[, Boolean]) -> LatLng
904
+ var scale = this.scale(zoom),
905
+ untransformedPoint = this.transformation.untransform(point, scale);
906
+
903
907
  return this.projection.unproject(untransformedPoint, unbounded);
904
908
  //TODO get rid of 'unbounded' everywhere
905
909
  },
906
910
 
907
911
  project: function (latlng) {
908
912
  return this.projection.project(latlng);
913
+ },
914
+
915
+ scale: function (zoom) {
916
+ return 256 * Math.pow(2, zoom);
909
917
  }
910
918
  };
911
919
 
@@ -917,7 +925,7 @@ L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, {
917
925
  projection: L.Projection.SphericalMercator,
918
926
  transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5),
919
927
 
920
- project: function (/*LatLng*/ latlng)/*-> Point*/ {
928
+ project: function (latlng) { // (LatLng) -> Point
921
929
  var projectedPoint = this.projection.project(latlng),
922
930
  earthRadius = 6378137;
923
931
  return projectedPoint.multiplyBy(earthRadius);
@@ -946,79 +954,32 @@ L.Map = L.Class.extend({
946
954
  includes: L.Mixin.Events,
947
955
 
948
956
  options: {
949
- // projection
950
- crs: L.CRS.EPSG3857 || L.CRS.EPSG4326,
951
- scale: function (zoom) {
952
- return 256 * Math.pow(2, zoom);
953
- },
954
-
955
- // state
956
- center: null,
957
- zoom: null,
958
- layers: [],
959
-
960
- // interaction
961
- dragging: true,
962
- touchZoom: L.Browser.touch && !L.Browser.android,
963
- scrollWheelZoom: !L.Browser.touch,
964
- doubleClickZoom: true,
965
- boxZoom: true,
966
-
967
- inertia: !L.Browser.android,
968
- inertiaDeceleration: L.Browser.touch ? 3000 : 2000, // px/s^2
969
- inertiaMaxSpeed: L.Browser.touch ? 1500 : 1000, // px/s
970
- inertiaThreshold: L.Browser.touch ? 32 : 16, // ms
957
+ crs: L.CRS.EPSG3857,
971
958
 
972
- // controls
973
- zoomControl: true,
974
- attributionControl: true,
959
+ /*
960
+ center: LatLng,
961
+ zoom: Number,
962
+ layers: Array,
963
+ */
975
964
 
976
- // animation
977
965
  fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android,
978
- zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera,
979
-
980
- // misc
981
- trackResize: true,
982
- closePopupOnClick: true,
983
- worldCopyJump: true
966
+ trackResize: true
984
967
  },
985
968
 
986
-
987
969
  initialize: function (id, options) { // (HTMLElement or String, Object)
988
- L.Util.setOptions(this, options);
989
-
990
- // TODO method is too big, refactor
991
-
992
- var container = this._container = L.DomUtil.get(id);
993
-
994
- if (container._leaflet) {
995
- throw new Error("Map container is already initialized.");
996
- }
997
- container._leaflet = true;
970
+ options = L.Util.setOptions(this, options);
998
971
 
972
+ this._initContainer(id);
999
973
  this._initLayout();
1000
-
1001
- if (L.DomEvent) {
1002
- this._initEvents();
1003
- if (L.Handler) {
1004
- this._initInteraction();
1005
- }
1006
- if (L.Control) {
1007
- this._initControls();
1008
- }
1009
- }
1010
-
1011
- options = this.options;
974
+ this._initHooks();
975
+ this._initEvents();
1012
976
 
1013
977
  if (options.maxBounds) {
1014
978
  this.setMaxBounds(options.maxBounds);
1015
979
  }
1016
980
 
1017
- var center = options.center,
1018
- zoom = options.zoom;
1019
-
1020
- if (center && typeof zoom !== 'undefined') {
1021
- this.setView(center, zoom, true);
981
+ if (options.center && typeof options.zoom !== 'undefined') {
982
+ this.setView(options.center, options.zoom, true);
1022
983
  }
1023
984
 
1024
985
  this._initLayers(options.layers);
@@ -1029,7 +990,6 @@ L.Map = L.Class.extend({
1029
990
 
1030
991
  // replaced by animation-powered implementation in Map.PanAnimation.js
1031
992
  setView: function (center, zoom) {
1032
- // reset the map view
1033
993
  this._resetView(center, this._limitZoom(zoom));
1034
994
  return this;
1035
995
  },
@@ -1125,9 +1085,7 @@ L.Map = L.Class.extend({
1125
1085
 
1126
1086
  var id = L.Util.stamp(layer);
1127
1087
 
1128
- if (this._layers[id]) {
1129
- return this;
1130
- }
1088
+ if (this._layers[id]) { return this; }
1131
1089
 
1132
1090
  this._layers[id] = layer;
1133
1091
 
@@ -1145,10 +1103,6 @@ L.Map = L.Class.extend({
1145
1103
  layer.on('load', this._onTileLayerLoad, this);
1146
1104
  }
1147
1105
 
1148
- if (this.attributionControl && layer.getAttribution) {
1149
- this.attributionControl.addAttribution(layer.getAttribution());
1150
- }
1151
-
1152
1106
  var onMapLoad = function () {
1153
1107
  layer.onAdd(this, insertAtTheBottom);
1154
1108
  this.fire('layeradd', {layer: layer});
@@ -1178,10 +1132,6 @@ L.Map = L.Class.extend({
1178
1132
  layer.off('load', this._onTileLayerLoad, this);
1179
1133
  }
1180
1134
 
1181
- if (this.attributionControl && layer.getAttribution) {
1182
- this.attributionControl.removeAttribution(layer.getAttribution());
1183
- }
1184
-
1185
1135
  return this.fire('layerremove', {layer: layer});
1186
1136
  },
1187
1137
 
@@ -1206,16 +1156,13 @@ L.Map = L.Class.extend({
1206
1156
 
1207
1157
  this.fire('move');
1208
1158
 
1209
- function fireMoveEnd() {
1210
- this.fire('moveend');
1211
- }
1212
-
1213
1159
  clearTimeout(this._sizeTimer);
1214
- this._sizeTimer = setTimeout(L.Util.bind(fireMoveEnd, this), 200);
1160
+ this._sizeTimer = setTimeout(L.Util.bind(this.fire, this, 'moveend'), 200);
1215
1161
 
1216
1162
  return this;
1217
1163
  },
1218
1164
 
1165
+ // TODO handler.addTo
1219
1166
  addHandler: function (name, HandlerClass) {
1220
1167
  if (!HandlerClass) { return; }
1221
1168
 
@@ -1231,7 +1178,7 @@ L.Map = L.Class.extend({
1231
1178
 
1232
1179
  // public methods for getting map state
1233
1180
 
1234
- getCenter: function (unbounded) { // (Boolean)
1181
+ getCenter: function (unbounded) { // (Boolean) -> LatLng
1235
1182
  var viewHalf = this.getSize().divideBy(2),
1236
1183
  centerPoint = this._getTopLeftPoint().add(viewHalf);
1237
1184
 
@@ -1323,6 +1270,10 @@ L.Map = L.Class.extend({
1323
1270
  getPanes: function () {
1324
1271
  return this._panes;
1325
1272
  },
1273
+
1274
+ getContainer: function () {
1275
+ return this._container;
1276
+ },
1326
1277
 
1327
1278
 
1328
1279
  // conversion methods
@@ -1365,18 +1316,28 @@ L.Map = L.Class.extend({
1365
1316
 
1366
1317
  project: function (latlng, zoom) { // (LatLng[, Number]) -> Point
1367
1318
  zoom = typeof zoom === 'undefined' ? this._zoom : zoom;
1368
- return this.options.crs.latLngToPoint(latlng, this.options.scale(zoom));
1319
+ return this.options.crs.latLngToPoint(latlng, zoom);
1369
1320
  },
1370
1321
 
1371
1322
  unproject: function (point, zoom, unbounded) { // (Point[, Number, Boolean]) -> LatLng
1372
1323
  // TODO remove unbounded, making it true all the time?
1373
1324
  zoom = typeof zoom === 'undefined' ? this._zoom : zoom;
1374
- return this.options.crs.pointToLatLng(point, this.options.scale(zoom), unbounded);
1325
+ return this.options.crs.pointToLatLng(point, zoom, unbounded);
1375
1326
  },
1376
1327
 
1377
1328
 
1378
1329
  // private methods that modify map state
1379
1330
 
1331
+ _initContainer: function (id) {
1332
+ var container = this._container = L.DomUtil.get(id);
1333
+
1334
+ if (container._leaflet) {
1335
+ throw new Error("Map container is already initialized.");
1336
+ }
1337
+
1338
+ container._leaflet = true;
1339
+ },
1340
+
1380
1341
  _initLayout: function () {
1381
1342
  var container = this._container;
1382
1343
 
@@ -1422,6 +1383,15 @@ L.Map = L.Class.extend({
1422
1383
  return L.DomUtil.create('div', className, container || this._objectsPane);
1423
1384
  },
1424
1385
 
1386
+ _initializers: [],
1387
+
1388
+ _initHooks: function () {
1389
+ var i, len;
1390
+ for (i = 0, len = this._initializers.length; i < len; i++) {
1391
+ this._initializers[i].call(this);
1392
+ }
1393
+ },
1394
+
1425
1395
  _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) {
1426
1396
 
1427
1397
  var zoomChanged = (this._zoom !== zoom);
@@ -1463,7 +1433,7 @@ L.Map = L.Class.extend({
1463
1433
  },
1464
1434
 
1465
1435
  _initLayers: function (layers) {
1466
- layers = layers instanceof Array ? layers : [layers];
1436
+ layers = layers ? (layers instanceof Array ? layers : [layers]) : [];
1467
1437
 
1468
1438
  this._layers = {};
1469
1439
  this._tileLayersNum = 0;
@@ -1475,18 +1445,6 @@ L.Map = L.Class.extend({
1475
1445
  }
1476
1446
  },
1477
1447
 
1478
- _initControls: function () {
1479
- // TODO refactor, this should happen automatically
1480
- if (this.options.zoomControl) {
1481
- this.zoomControl = new L.Control.Zoom();
1482
- this.addControl(this.zoomControl);
1483
- }
1484
- if (this.options.attributionControl) {
1485
- this.attributionControl = new L.Control.Attribution();
1486
- this.addControl(this.attributionControl);
1487
- }
1488
- },
1489
-
1490
1448
  _rawPanBy: function (offset) {
1491
1449
  var newPos = L.DomUtil.getPosition(this._mapPane).subtract(offset);
1492
1450
  L.DomUtil.setPosition(this._mapPane, newPos);
@@ -1496,6 +1454,8 @@ L.Map = L.Class.extend({
1496
1454
  // map events
1497
1455
 
1498
1456
  _initEvents: function () {
1457
+ if (!L.DomEvent) { return; }
1458
+
1499
1459
  L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
1500
1460
 
1501
1461
  var events = ['dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'contextmenu'];
@@ -1547,15 +1507,6 @@ L.Map = L.Class.extend({
1547
1507
  });
1548
1508
  },
1549
1509
 
1550
- _initInteraction: function () {
1551
- this
1552
- .addHandler('dragging', L.Map.Drag)
1553
- .addHandler('touchZoom', L.Map.TouchZoom)
1554
- .addHandler('doubleClickZoom', L.Map.DoubleClickZoom)
1555
- .addHandler('scrollWheelZoom', L.Map.ScrollWheelZoom)
1556
- .addHandler('boxZoom', L.Map.BoxZoom);
1557
- },
1558
-
1559
1510
  _onTileLayerLoad: function () {
1560
1511
  // TODO super-ugly, refactor!!!
1561
1512
  // clear scaled tiles after all new tiles are loaded (for performance)
@@ -1592,6 +1543,15 @@ L.Map = L.Class.extend({
1592
1543
  }
1593
1544
  });
1594
1545
 
1546
+ L.Map.addInitHook = function (fn) {
1547
+ var args = Array.prototype.slice.call(arguments, 1);
1548
+
1549
+ var init = typeof fn === 'function' ? fn : function () {
1550
+ this[fn].apply(this, args);
1551
+ };
1552
+
1553
+ this.prototype._initializers.push(init);
1554
+ };
1595
1555
 
1596
1556
 
1597
1557
  L.Projection.Mercator = {
@@ -1600,7 +1560,7 @@ L.Projection.Mercator = {
1600
1560
  R_MINOR: 6356752.3142,
1601
1561
  R_MAJOR: 6378137,
1602
1562
 
1603
- project: function (/*LatLng*/ latlng) /*-> Point*/ {
1563
+ project: function (latlng) { // (LatLng) -> Point
1604
1564
  var d = L.LatLng.DEG_TO_RAD,
1605
1565
  max = this.MAX_LATITUDE,
1606
1566
  lat = Math.max(Math.min(max, latlng.lat), -max),
@@ -1620,7 +1580,7 @@ L.Projection.Mercator = {
1620
1580
  return new L.Point(x, y);
1621
1581
  },
1622
1582
 
1623
- unproject: function (/*Point*/ point, /*Boolean*/ unbounded) /*-> LatLng*/ {
1583
+ unproject: function (point, unbounded) { // (Point, Boolean) -> LatLng
1624
1584
  var d = L.LatLng.RAD_TO_DEG,
1625
1585
  r = this.R_MAJOR,
1626
1586
  r2 = this.R_MINOR,
@@ -1651,6 +1611,7 @@ L.CRS.EPSG3395 = L.Util.extend({}, L.CRS, {
1651
1611
  code: 'EPSG:3395',
1652
1612
 
1653
1613
  projection: L.Projection.Mercator,
1614
+
1654
1615
  transformation: (function () {
1655
1616
  var m = L.Projection.Mercator,
1656
1617
  r = m.R_MAJOR,
@@ -1681,6 +1642,7 @@ L.TileLayer = L.Class.extend({
1681
1642
  noWrap: false,
1682
1643
  zoomOffset: 0,
1683
1644
  zoomReverse: false,
1645
+ detectRetina: false,
1684
1646
 
1685
1647
  unloadInvisibleTiles: L.Browser.mobile,
1686
1648
  updateWhenIdle: L.Browser.mobile,
@@ -1688,7 +1650,19 @@ L.TileLayer = L.Class.extend({
1688
1650
  },
1689
1651
 
1690
1652
  initialize: function (url, options) {
1691
- L.Util.setOptions(this, options);
1653
+ options = L.Util.setOptions(this, options);
1654
+
1655
+ // detecting retina displays, adjusting tileSize and zoom levels
1656
+ if (options.detectRetina && window.devicePixelRatio > 1 && options.maxZoom > 0) {
1657
+
1658
+ options.tileSize = Math.floor(options.tileSize / 2);
1659
+ options.zoomOffset++;
1660
+
1661
+ if (options.minZoom > 0) {
1662
+ options.minZoom--;
1663
+ }
1664
+ this.options.maxZoom--;
1665
+ }
1692
1666
 
1693
1667
  this._url = url;
1694
1668
 
@@ -2232,12 +2206,17 @@ L.Icon = L.Class.extend({
2232
2206
  },
2233
2207
 
2234
2208
  createShadow: function () {
2235
- return this.options.shadowUrl ? this._createIcon('shadow') : null;
2209
+ return this._createIcon('shadow');
2236
2210
  },
2237
2211
 
2238
2212
  _createIcon: function (name) {
2239
- var img = this._createImg(this.options[name + 'Url']);
2213
+ var src = this._getIconUrl(name);
2214
+
2215
+ if (!src) { return null; }
2216
+
2217
+ var img = this._createImg(src);
2240
2218
  this._setIconStyles(img, name);
2219
+
2241
2220
  return img;
2242
2221
  },
2243
2222
 
@@ -2269,6 +2248,7 @@ L.Icon = L.Class.extend({
2269
2248
 
2270
2249
  _createImg: function (src) {
2271
2250
  var el;
2251
+
2272
2252
  if (!L.Browser.ie6) {
2273
2253
  el = document.createElement('img');
2274
2254
  el.src = src;
@@ -2277,21 +2257,50 @@ L.Icon = L.Class.extend({
2277
2257
  el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
2278
2258
  }
2279
2259
  return el;
2260
+ },
2261
+
2262
+ _getIconUrl: function (name) {
2263
+ return this.options[name + 'Url'];
2280
2264
  }
2281
2265
  });
2282
2266
 
2267
+
2268
+ // TODO move to a separate file
2269
+
2283
2270
  L.Icon.Default = L.Icon.extend({
2284
2271
  options: {
2285
- iconUrl: L.ROOT_URL + 'images/marker.png',
2286
2272
  iconSize: new L.Point(25, 41),
2287
2273
  iconAnchor: new L.Point(13, 41),
2288
2274
  popupAnchor: new L.Point(0, -33),
2289
2275
 
2290
- shadowUrl: L.ROOT_URL + 'images/marker-shadow.png',
2291
2276
  shadowSize: new L.Point(41, 41)
2277
+ },
2278
+
2279
+ _getIconUrl: function (name) {
2280
+ var path = L.Icon.Default.imagePath;
2281
+ if (!path) {
2282
+ throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");
2283
+ }
2284
+
2285
+ return path + '/marker-' + name + '.png';
2292
2286
  }
2293
2287
  });
2294
2288
 
2289
+ L.Icon.Default.imagePath = (function () {
2290
+ var scripts = document.getElementsByTagName('script'),
2291
+ leafletRe = /\/?leaflet[\-\._]?([\w\-\._]*)\.js\??/;
2292
+
2293
+ var i, len, src, matches;
2294
+
2295
+ for (i = 0, len = scripts.length; i < len; i++) {
2296
+ src = scripts[i].src;
2297
+ matches = src.match(leafletRe);
2298
+
2299
+ if (matches) {
2300
+ return src.split(leafletRe)[0] + '/images';
2301
+ }
2302
+ }
2303
+ }());
2295
2304
 
2296
2305
  /*
2297
2306
  * L.Marker is used to display clickable/draggable icons on the map.
@@ -2503,6 +2512,10 @@ L.DivIcon = L.Icon.extend({
2503
2512
 
2504
2513
 
2505
2514
 
2515
+ L.Map.mergeOptions({
2516
+ closePopupOnClick: true
2517
+ });
2518
+
2506
2519
  L.Popup = L.Class.extend({
2507
2520
  includes: L.Mixin.Events,
2508
2521
 
@@ -2571,9 +2584,14 @@ L.Popup = L.Class.extend({
2571
2584
  },
2572
2585
 
2573
2586
  _close: function () {
2574
- if (this._map) {
2575
- this._map._popup = null;
2576
- this._map.removeLayer(this);
2587
+ var map = this._map;
2588
+
2589
+ if (map) {
2590
+ map._popup = null;
2591
+
2592
+ map
2593
+ .removeLayer(this)
2594
+ .fire('popupclose', {popup: this});
2577
2595
  }
2578
2596
  },
2579
2597
 
@@ -2622,6 +2640,7 @@ L.Popup = L.Class.extend({
2622
2640
  this._contentNode.innerHTML = '';
2623
2641
  this._contentNode.appendChild(this._content);
2624
2642
  }
2643
+ this.fire('contentupdate');
2625
2644
  },
2626
2645
 
2627
2646
  _updateLayout: function () {
@@ -2768,17 +2787,12 @@ L.Map.include({
2768
2787
 
2769
2788
  closePopup: function () {
2770
2789
  if (this._popup) {
2771
- this
2772
- .removeLayer(this._popup)
2773
- .fire('popupclose', {popup: this._popup});
2774
-
2775
- this._popup = null;
2790
+ this._popup._close();
2776
2791
  }
2777
2792
  return this;
2778
2793
  }
2779
2794
  });
2780
2795
 
2781
-
2782
2796
  /*
2783
2797
  * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer.
2784
2798
  */
@@ -2888,6 +2902,14 @@ L.FeatureGroup = L.LayerGroup.extend({
2888
2902
  return this.invoke('setStyle', style);
2889
2903
  },
2890
2904
 
2905
+ getBounds: function () {
2906
+ var bounds = new L.LatLngBounds();
2907
+ this._iterateLayers(function (layer) {
2908
+ bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds());
2909
+ }, this);
2910
+ return bounds;
2911
+ },
2912
+
2891
2913
  _initEvents: function (layer) {
2892
2914
  var events = ['click', 'dblclick', 'mouseover', 'mouseout'],
2893
2915
  i, len;
@@ -3066,7 +3088,7 @@ L.Path = L.Path.extend({
3066
3088
  // TODO remove duplication with L.Map
3067
3089
  _initEvents: function () {
3068
3090
  if (this.options.clickable) {
3069
- if (!L.Browser.vml) {
3091
+ if (L.Browser.svg || !L.Browser.vml) {
3070
3092
  this._path.setAttribute('class', 'leaflet-clickable');
3071
3093
  }
3072
3094
 
@@ -3728,7 +3750,7 @@ L.Polyline = L.Path.extend({
3728
3750
  onAdd: function (map) {
3729
3751
  L.Path.prototype.onAdd.call(this, map);
3730
3752
 
3731
- if (this.editing.enabled()) {
3753
+ if (this.editing && this.editing.enabled()) {
3732
3754
  this.editing.addHooks();
3733
3755
  }
3734
3756
  },
@@ -3742,7 +3764,7 @@ L.Polyline = L.Path.extend({
3742
3764
  },
3743
3765
 
3744
3766
  _initEvents: function () {
3745
- L.Polyline.superclass._initEvents.call(this);
3767
+ L.Path.prototype._initEvents.call(this);
3746
3768
  },
3747
3769
 
3748
3770
  _getPathPartStr: function (points) {
@@ -4032,7 +4054,7 @@ L.Circle = L.Path.extend({
4032
4054
  point2 = this._map.latLngToLayerPoint(latlng2);
4033
4055
 
4034
4056
  this._point = this._map.latLngToLayerPoint(this._latlng);
4035
- this._radius = Math.round(this._point.x - point2.x);
4057
+ this._radius = Math.max(Math.round(this._point.x - point2.x), 1);
4036
4058
  },
4037
4059
 
4038
4060
  getBounds: function () {
@@ -4047,6 +4069,10 @@ L.Circle = L.Path.extend({
4047
4069
 
4048
4070
  return new L.LatLngBounds(sw, ne);
4049
4071
  },
4072
+
4073
+ getLatLng: function () {
4074
+ return this._latlng;
4075
+ },
4050
4076
 
4051
4077
  getPathString: function () {
4052
4078
  var p = this._point,
@@ -4066,6 +4092,10 @@ L.Circle = L.Path.extend({
4066
4092
  return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
4067
4093
  }
4068
4094
  },
4095
+
4096
+ getRadius: function () {
4097
+ return this._mRadius;
4098
+ },
4069
4099
 
4070
4100
  _getLngRadius: function () {
4071
4101
  var equatorLength = 40075017,
@@ -4075,6 +4105,9 @@ L.Circle = L.Path.extend({
4075
4105
  },
4076
4106
 
4077
4107
  _checkIfEmpty: function () {
4108
+ if (!this._map) {
4109
+ return false;
4110
+ }
4078
4111
  var vp = this._map._pathViewport,
4079
4112
  r = this._radius,
4080
4113
  p = this._point;
@@ -4660,6 +4693,19 @@ L.Handler = L.Class.extend({
4660
4693
  * L.Handler.MapDrag is used internally by L.Map to make the map draggable.
4661
4694
  */
4662
4695
 
4696
+ L.Map.mergeOptions({
4697
+ dragging: true,
4698
+
4699
+ inertia: !L.Browser.android,
4700
+ inertiaDeceleration: L.Browser.touch ? 3000 : 2000, // px/s^2
4701
+ inertiaMaxSpeed: L.Browser.touch ? 1500 : 1000, // px/s
4702
+ inertiaThreshold: L.Browser.touch ? 32 : 16, // ms
4703
+
4704
+ // TODO refactor, move to CRS
4705
+ worldCopyJump: true,
4706
+ continuousWorld: false
4707
+ });
4708
+
4663
4709
  L.Map.Drag = L.Handler.extend({
4664
4710
  addHooks: function () {
4665
4711
  if (!this._draggable) {
@@ -4733,7 +4779,7 @@ L.Map.Drag = L.Handler.extend({
4733
4779
 
4734
4780
  _onPreDrag: function () {
4735
4781
  var map = this._map,
4736
- worldWidth = map.options.scale(map.getZoom()),
4782
+ worldWidth = map.options.crs.scale(map.getZoom()),
4737
4783
  halfWidth = Math.round(worldWidth / 2),
4738
4784
  dx = this._initialWorldOffset.x,
4739
4785
  x = this._draggable._newPos.x,
@@ -4748,7 +4794,7 @@ L.Map.Drag = L.Handler.extend({
4748
4794
  var map = this._map,
4749
4795
  options = map.options,
4750
4796
  delay = +new Date() - this._lastTime,
4751
-
4797
+
4752
4798
  noInertia = !options.inertia ||
4753
4799
  delay > options.inertiaThreshold ||
4754
4800
  typeof this._positions[0] === 'undefined';
@@ -4793,11 +4839,16 @@ L.Map.Drag = L.Handler.extend({
4793
4839
  }
4794
4840
  });
4795
4841
 
4842
+ L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
4796
4843
 
4797
4844
  /*
4798
4845
  * L.Handler.DoubleClickZoom is used internally by L.Map to add double-click zooming.
4799
4846
  */
4800
4847
 
4848
+ L.Map.mergeOptions({
4849
+ doubleClickZoom: true
4850
+ });
4851
+
4801
4852
  L.Map.DoubleClickZoom = L.Handler.extend({
4802
4853
  addHooks: function () {
4803
4854
  this._map.on('dblclick', this._onDoubleClick);
@@ -4812,11 +4863,16 @@ L.Map.DoubleClickZoom = L.Handler.extend({
4812
4863
  }
4813
4864
  });
4814
4865
 
4866
+ L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
4815
4867
 
4816
4868
  /*
4817
4869
  * L.Handler.ScrollWheelZoom is used internally by L.Map to enable mouse scroll wheel zooming on the map.
4818
4870
  */
4819
4871
 
4872
+ L.Map.mergeOptions({
4873
+ scrollWheelZoom: !L.Browser.touch
4874
+ });
4875
+
4820
4876
  L.Map.ScrollWheelZoom = L.Handler.extend({
4821
4877
  addHooks: function () {
4822
4878
  L.DomEvent.addListener(this._map._container, 'mousewheel', this._onWheelScroll, this);
@@ -4868,6 +4924,7 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
4868
4924
  }
4869
4925
  });
4870
4926
 
4927
+ L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
4871
4928
 
4872
4929
  L.Util.extend(L.DomEvent, {
4873
4930
  // inspired by Zepto touch code by Thomas Fuchs
@@ -4918,6 +4975,10 @@ L.Util.extend(L.DomEvent, {
4918
4975
  * L.Handler.TouchZoom is used internally by L.Map to add touch-zooming on Webkit-powered mobile browsers.
4919
4976
  */
4920
4977
 
4978
+ L.Map.mergeOptions({
4979
+ touchZoom: L.Browser.touch && !L.Browser.android
4980
+ });
4981
+
4921
4982
  L.Map.TouchZoom = L.Handler.extend({
4922
4983
  addHooks: function () {
4923
4984
  L.DomEvent.addListener(this._map._container, 'touchstart', this._onTouchStart, this);
@@ -5008,11 +5069,16 @@ L.Map.TouchZoom = L.Handler.extend({
5008
5069
  }
5009
5070
  });
5010
5071
 
5072
+ L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
5011
5073
 
5012
5074
  /*
5013
5075
  * L.Handler.ShiftDragZoom is used internally by L.Map to add shift-drag zoom (zoom to a selected bounding box).
5014
5076
  */
5015
5077
 
5078
+ L.Map.mergeOptions({
5079
+ boxZoom: true
5080
+ });
5081
+
5016
5082
  L.Map.BoxZoom = L.Handler.extend({
5017
5083
  initialize: function (map) {
5018
5084
  this._map = map;
@@ -5045,6 +5111,8 @@ L.Map.BoxZoom = L.Handler.extend({
5045
5111
  .addListener(document, 'mousemove', this._onMouseMove, this)
5046
5112
  .addListener(document, 'mouseup', this._onMouseUp, this)
5047
5113
  .preventDefault(e);
5114
+
5115
+ this._map.fire("boxzoomstart");
5048
5116
  },
5049
5117
 
5050
5118
  _onMouseMove: function (e) {
@@ -5083,9 +5151,15 @@ L.Map.BoxZoom = L.Handler.extend({
5083
5151
  map.layerPointToLatLng(layerPoint));
5084
5152
 
5085
5153
  map.fitBounds(bounds);
5154
+
5155
+ map.fire("boxzoomend", {
5156
+ boxZoomBounds: bounds
5157
+ });
5086
5158
  }
5087
5159
  });
5088
5160
 
5161
+ L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
5162
+
5089
5163
 
5090
5164
  /*
5091
5165
  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
@@ -5169,6 +5243,8 @@ L.Handler.PolyEdit = L.Handler.extend({
5169
5243
  removeHooks: function () {
5170
5244
  if (this._poly._map) {
5171
5245
  this._poly._map.removeLayer(this._markerGroup);
5246
+ delete this._markerGroup;
5247
+ delete this._markers;
5172
5248
  }
5173
5249
  },
5174
5250
 
@@ -5245,17 +5321,29 @@ L.Handler.PolyEdit = L.Handler.extend({
5245
5321
  },
5246
5322
 
5247
5323
  _onMarkerClick: function (e) {
5324
+ // Default action on marker click is to remove that marker, but if we remove the marker when latlng count < 3, we don't have a valid polyline anymore
5325
+ if (this._poly._latlngs.length < 3) {
5326
+ return;
5327
+ }
5328
+
5248
5329
  var marker = e.target,
5249
5330
  i = marker._index;
5331
+
5332
+ // Check existence of previous and next markers since they wouldn't exist for edge points on the polyline
5333
+ if (marker._prev && marker._next) {
5334
+ this._createMiddleMarker(marker._prev, marker._next);
5335
+ this._updatePrevNext(marker._prev, marker._next);
5336
+ }
5250
5337
 
5251
- this._createMiddleMarker(marker._prev, marker._next);
5252
- this._updatePrevNext(marker._prev, marker._next);
5253
-
5254
- this._markerGroup
5255
- .removeLayer(marker._middleLeft)
5256
- .removeLayer(marker._middleRight)
5257
- .removeLayer(marker);
5258
-
5338
+ // The marker itself is guaranteed to exist and present in the layer, since we managed to click on it
5339
+ this._markerGroup.removeLayer(marker);
5340
+ // Check for the existence of middle left or middle right
5341
+ if (marker._middleLeft) {
5342
+ this._markerGroup.removeLayer(marker._middleLeft);
5343
+ }
5344
+ if (marker._middleRight) {
5345
+ this._markerGroup.removeLayer(marker._middleRight);
5346
+ }
5259
5347
  this._poly.spliceLatLngs(i, 1);
5260
5348
  this._updateIndexes(i, -1);
5261
5349
  this._poly.fire('edit');
@@ -5271,13 +5359,16 @@ L.Handler.PolyEdit = L.Handler.extend({
5271
5359
 
5272
5360
  _createMiddleMarker: function (marker1, marker2) {
5273
5361
  var latlng = this._getMiddleLatLng(marker1, marker2),
5274
- marker = this._createMarker(latlng);
5362
+ marker = this._createMarker(latlng),
5363
+ onClick,
5364
+ onDragStart,
5365
+ onDragEnd;
5275
5366
 
5276
5367
  marker.setOpacity(0.6);
5277
5368
 
5278
5369
  marker1._middleRight = marker2._middleLeft = marker;
5279
5370
 
5280
- function onDragStart() {
5371
+ onDragStart = function () {
5281
5372
  var i = marker2._index;
5282
5373
 
5283
5374
  marker._index = i;
@@ -5295,21 +5386,21 @@ L.Handler.PolyEdit = L.Handler.extend({
5295
5386
  marker2._index++;
5296
5387
  this._updatePrevNext(marker1, marker);
5297
5388
  this._updatePrevNext(marker, marker2);
5298
- }
5389
+ };
5299
5390
 
5300
- function onDragEnd() {
5391
+ onDragEnd = function () {
5301
5392
  marker.off('dragstart', onDragStart, this);
5302
5393
  marker.off('dragend', onDragEnd, this);
5303
5394
 
5304
5395
  this._createMiddleMarker(marker1, marker);
5305
5396
  this._createMiddleMarker(marker, marker2);
5306
- }
5397
+ };
5307
5398
 
5308
- function onClick() {
5399
+ onClick = function () {
5309
5400
  onDragStart.call(this);
5310
5401
  onDragEnd.call(this);
5311
5402
  this._poly.fire('edit');
5312
- }
5403
+ };
5313
5404
 
5314
5405
  marker
5315
5406
  .on('click', onClick, this)
@@ -5349,26 +5440,25 @@ L.Control = L.Class.extend({
5349
5440
  },
5350
5441
 
5351
5442
  setPosition: function (position) {
5352
- this.options.position = position;
5443
+ var map = this._map;
5353
5444
 
5354
- if (this._map) {
5355
- this._map.removeControl(this);
5356
- this._map.addControl(this);
5445
+ if (map) {
5446
+ map.removeControl(this);
5357
5447
  }
5358
- }
5359
- });
5360
-
5361
5448
 
5449
+ this.options.position = position;
5362
5450
 
5363
- L.Map.include({
5364
- addControl: function (control) {
5365
- var container = control.onAdd(this);
5451
+ if (map) {
5452
+ map.addControl(this);
5453
+ }
5454
+ },
5366
5455
 
5367
- control._container = container;
5368
- control._map = this;
5456
+ addTo: function (map) {
5457
+ this._map = map;
5369
5458
 
5370
- var pos = control.getPosition(),
5371
- corner = this._controlCorners[pos];
5459
+ var container = this._container = this.onAdd(map),
5460
+ pos = this.getPosition(),
5461
+ corner = map._controlCorners[pos];
5372
5462
 
5373
5463
  L.DomUtil.addClass(container, 'leaflet-control');
5374
5464
 
@@ -5377,35 +5467,55 @@ L.Map.include({
5377
5467
  } else {
5378
5468
  corner.appendChild(container);
5379
5469
  }
5470
+
5380
5471
  return this;
5381
5472
  },
5382
5473
 
5383
- removeControl: function (control) {
5384
- var pos = control.getPosition(),
5385
- corner = this._controlCorners[pos];
5474
+ removeFrom: function (map) {
5475
+ var pos = this.getPosition(),
5476
+ corner = map._controlCorners[pos];
5386
5477
 
5387
- corner.removeChild(control._container);
5388
- control._map = null;
5478
+ corner.removeChild(this._container);
5479
+ this._map = null;
5389
5480
 
5390
- if (control.onRemove) {
5391
- control.onRemove(this);
5481
+ if (this.onRemove) {
5482
+ this.onRemove(map);
5392
5483
  }
5484
+
5485
+ return this;
5486
+ }
5487
+ });
5488
+
5489
+
5490
+
5491
+ L.Map.include({
5492
+ addControl: function (control) {
5493
+ control.addTo(this);
5494
+ return this;
5495
+ },
5496
+
5497
+ removeControl: function (control) {
5498
+ control.removeFrom(this);
5393
5499
  return this;
5394
5500
  },
5395
5501
 
5396
5502
  _initControlPos: function () {
5397
- var top = 'leaflet-top',
5398
- bottom = 'leaflet-bottom',
5399
- left = 'leaflet-left',
5400
- right = 'leaflet-right',
5401
- corner = 'leaflet-corner',
5402
- container = this._container,
5403
- corners = this._controlCorners = {};
5404
-
5405
- corners.topleft = L.DomUtil.create('div', [corner, top, left].join(' '), container);
5406
- corners.topright = L.DomUtil.create('div', [corner, top, right].join(' '), container);
5407
- corners.bottomleft = L.DomUtil.create('div', [corner, bottom, left].join(' '), container);
5408
- corners.bottomright = L.DomUtil.create('div', [corner, bottom, right].join(' '), container);
5503
+ var corners = this._controlCorners = {},
5504
+ l = 'leaflet-',
5505
+ container = this._controlContainer =
5506
+ L.DomUtil.create('div', l + 'control-container', this._container);
5507
+
5508
+ function createCorner(vSide, hSide) {
5509
+ var className = l + vSide + ' ' + l + hSide;
5510
+
5511
+ corners[vSide + hSide] =
5512
+ L.DomUtil.create('div', className, container);
5513
+ }
5514
+
5515
+ createCorner('top', 'left');
5516
+ createCorner('top', 'right');
5517
+ createCorner('bottom', 'left');
5518
+ createCorner('bottom', 'right');
5409
5519
  }
5410
5520
  });
5411
5521
 
@@ -5418,7 +5528,7 @@ L.Control.Zoom = L.Control.extend({
5418
5528
  onAdd: function (map) {
5419
5529
  var className = 'leaflet-control-zoom',
5420
5530
  container = L.DomUtil.create('div', className);
5421
-
5531
+
5422
5532
  this._createButton('Zoom in', className + '-in', container, map.zoomIn, map);
5423
5533
  this._createButton('Zoom out', className + '-out', container, map.zoomOut, map);
5424
5534
 
@@ -5439,6 +5549,16 @@ L.Control.Zoom = L.Control.extend({
5439
5549
  }
5440
5550
  });
5441
5551
 
5552
+ L.Map.mergeOptions({
5553
+ zoomControl: true
5554
+ });
5555
+
5556
+ L.Map.addInitHook(function () {
5557
+ if (this.options.zoomControl) {
5558
+ this.zoomControl = new L.Control.Zoom();
5559
+ this.addControl(this.zoomControl);
5560
+ }
5561
+ });
5442
5562
 
5443
5563
  L.Control.Attribution = L.Control.extend({
5444
5564
  options: {
@@ -5453,16 +5573,25 @@ L.Control.Attribution = L.Control.extend({
5453
5573
  },
5454
5574
 
5455
5575
  onAdd: function (map) {
5456
- this._map = map;
5457
-
5458
5576
  this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
5459
5577
  L.DomEvent.disableClickPropagation(this._container);
5460
5578
 
5579
+ map
5580
+ .on('layeradd', this._onLayerAdd, this)
5581
+ .on('layerremove', this._onLayerRemove, this);
5582
+
5461
5583
  this._update();
5462
5584
 
5463
5585
  return this._container;
5464
5586
  },
5465
5587
 
5588
+ onRemove: function (map) {
5589
+ map
5590
+ .off('layeradd', this._onLayerAdd)
5591
+ .off('layerremove', this._onLayerRemove);
5592
+
5593
+ },
5594
+
5466
5595
  setPrefix: function (prefix) {
5467
5596
  this.options.prefix = prefix;
5468
5597
  this._update();
@@ -5498,7 +5627,7 @@ L.Control.Attribution = L.Control.extend({
5498
5627
  }
5499
5628
 
5500
5629
  var prefixAndAttribs = [];
5501
-
5630
+
5502
5631
  if (this.options.prefix) {
5503
5632
  prefixAndAttribs.push(this.options.prefix);
5504
5633
  }
@@ -5507,9 +5636,30 @@ L.Control.Attribution = L.Control.extend({
5507
5636
  }
5508
5637
 
5509
5638
  this._container.innerHTML = prefixAndAttribs.join(' &mdash; ');
5639
+ },
5640
+
5641
+ _onLayerAdd: function (e) {
5642
+ if (e.layer.getAttribution) {
5643
+ this.addAttribution(e.layer.getAttribution());
5644
+ }
5645
+ },
5646
+
5647
+ _onLayerRemove: function (e) {
5648
+ if (e.layer.getAttribution) {
5649
+ this.removeAttribution(e.layer.getAttribution());
5650
+ }
5510
5651
  }
5511
5652
  });
5512
5653
 
5654
+ L.Map.mergeOptions({
5655
+ attributionControl: true
5656
+ });
5657
+
5658
+ L.Map.addInitHook(function () {
5659
+ if (this.options.attributionControl) {
5660
+ this.attributionControl = (new L.Control.Attribution()).addTo(this);
5661
+ }
5662
+ });
5513
5663
 
5514
5664
  L.Control.Scale = L.Control.extend({
5515
5665
  options: {
@@ -5632,8 +5782,6 @@ L.Control.Layers = L.Control.extend({
5632
5782
  },
5633
5783
 
5634
5784
  onAdd: function (map) {
5635
- this._map = map;
5636
-
5637
5785
  this._initLayout();
5638
5786
  this._update();
5639
5787
 
@@ -5660,43 +5808,39 @@ L.Control.Layers = L.Control.extend({
5660
5808
  },
5661
5809
 
5662
5810
  _initLayout: function () {
5663
- this._container = L.DomUtil.create('div', 'leaflet-control-layers');
5811
+ var className = 'leaflet-control-layers',
5812
+ container = this._container = L.DomUtil.create('div', className);
5664
5813
 
5665
5814
  if (!L.Browser.touch) {
5666
- L.DomEvent.disableClickPropagation(this._container);
5815
+ L.DomEvent.disableClickPropagation(container);
5667
5816
  } else {
5668
- L.DomEvent.addListener(this._container, 'click', L.DomEvent.stopPropagation);
5817
+ L.DomEvent.addListener(container, 'click', L.DomEvent.stopPropagation);
5669
5818
  }
5670
5819
 
5671
- this._form = L.DomUtil.create('form', 'leaflet-control-layers-list');
5820
+ var form = this._form = L.DomUtil.create('form', className + '-list');
5672
5821
 
5673
5822
  if (this.options.collapsed) {
5674
- L.DomEvent.addListener(this._container, 'mouseover', this._expand, this);
5675
- L.DomEvent.addListener(this._container, 'mouseout', this._collapse, this);
5823
+ L.DomEvent
5824
+ .addListener(container, 'mouseover', this._expand, this)
5825
+ .addListener(container, 'mouseout', this._collapse, this);
5676
5826
 
5677
- var link = this._layersLink = L.DomUtil.create('a', 'leaflet-control-layers-toggle');
5827
+ var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
5678
5828
  link.href = '#';
5679
5829
  link.title = 'Layers';
5680
5830
 
5681
- if (L.Browser.touch) {
5682
- L.DomEvent.addListener(link, 'click', this._expand, this);
5683
- //L.DomEvent.disableClickPropagation(link);
5684
- } else {
5685
- L.DomEvent.addListener(link, 'focus', this._expand, this);
5686
- }
5831
+ L.DomEvent.addListener(link, L.Browser.touch ? 'click' : 'focus', this._expand, this);
5832
+
5687
5833
  this._map.on('movestart', this._collapse, this);
5688
5834
  // TODO keyboard accessibility
5689
-
5690
- this._container.appendChild(link);
5691
5835
  } else {
5692
5836
  this._expand();
5693
5837
  }
5694
5838
 
5695
- this._baseLayersList = L.DomUtil.create('div', 'leaflet-control-layers-base', this._form);
5696
- this._separator = L.DomUtil.create('div', 'leaflet-control-layers-separator', this._form);
5697
- this._overlaysList = L.DomUtil.create('div', 'leaflet-control-layers-overlays', this._form);
5839
+ this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
5840
+ this._separator = L.DomUtil.create('div', className + '-separator', form);
5841
+ this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
5698
5842
 
5699
- this._container.appendChild(this._form);
5843
+ container.appendChild(form);
5700
5844
  },
5701
5845
 
5702
5846
  _addLayer: function (layer, name, overlay) {
@@ -6045,6 +6189,7 @@ L.Transition = L.Transition.NATIVE ? L.Transition : L.Transition.extend({
6045
6189
  });
6046
6190
 
6047
6191
 
6192
+
6048
6193
  L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
6049
6194
  setView: function (center, zoom, forceReset) {
6050
6195
  zoom = this._limitZoom(zoom);
@@ -6125,6 +6270,10 @@ L.Map.include(!(L.Transition && L.Transition.implemented()) ? {} : {
6125
6270
  });
6126
6271
 
6127
6272
 
6273
+ L.Map.mergeOptions({
6274
+ zoomAnimation: L.DomUtil.TRANSITION && !L.Browser.android && !L.Browser.mobileOpera
6275
+ });
6276
+
6128
6277
  L.Map.include(!L.DomUtil.TRANSITION ? {} : {
6129
6278
  _zoomToIfCenterInView: function (center, zoom, centerOffset) {
6130
6279
 
@@ -6359,3 +6508,6 @@ L.Map.include({
6359
6508
  });
6360
6509
 
6361
6510
 
6511
+
6512
+
6513
+ }());