decidim 0.26.9 → 0.26.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/decidim/version.rb +1 -1
  3. data/package-lock.json +7 -7
  4. data/packages/browserslist-config/package.json +1 -1
  5. data/packages/core/node_modules/leaflet/CHANGELOG.md +2191 -0
  6. data/packages/core/node_modules/leaflet/LICENSE +26 -0
  7. data/packages/core/node_modules/leaflet/README.md +55 -0
  8. data/packages/core/node_modules/leaflet/dist/images/layers-2x.png +0 -0
  9. data/packages/core/node_modules/leaflet/dist/images/layers.png +0 -0
  10. data/packages/core/node_modules/leaflet/dist/images/marker-icon-2x.png +0 -0
  11. data/packages/core/node_modules/leaflet/dist/images/marker-icon.png +0 -0
  12. data/packages/core/node_modules/leaflet/dist/images/marker-shadow.png +0 -0
  13. data/packages/core/node_modules/leaflet/dist/leaflet-src.esm.js +14419 -0
  14. data/packages/core/node_modules/leaflet/dist/leaflet-src.esm.js.map +1 -0
  15. data/packages/core/node_modules/leaflet/dist/leaflet-src.js +14512 -0
  16. data/packages/core/node_modules/leaflet/dist/leaflet-src.js.map +1 -0
  17. data/packages/core/node_modules/leaflet/dist/leaflet.css +661 -0
  18. data/packages/core/node_modules/leaflet/dist/leaflet.js +6 -0
  19. data/packages/core/node_modules/leaflet/dist/leaflet.js.map +1 -0
  20. data/packages/core/node_modules/leaflet/package.json +149 -0
  21. data/packages/core/node_modules/leaflet/src/Leaflet.js +24 -0
  22. data/packages/core/node_modules/leaflet/src/control/Control.Attribution.js +148 -0
  23. data/packages/core/node_modules/leaflet/src/control/Control.Layers.js +443 -0
  24. data/packages/core/node_modules/leaflet/src/control/Control.Scale.js +132 -0
  25. data/packages/core/node_modules/leaflet/src/control/Control.Zoom.js +146 -0
  26. data/packages/core/node_modules/leaflet/src/control/Control.js +174 -0
  27. data/packages/core/node_modules/leaflet/src/control/index.js +17 -0
  28. data/packages/core/node_modules/leaflet/src/core/Browser.js +220 -0
  29. data/packages/core/node_modules/leaflet/src/core/Class.js +135 -0
  30. data/packages/core/node_modules/leaflet/src/core/Class.leafdoc +197 -0
  31. data/packages/core/node_modules/leaflet/src/core/Events.js +344 -0
  32. data/packages/core/node_modules/leaflet/src/core/Events.leafdoc +143 -0
  33. data/packages/core/node_modules/leaflet/src/core/Handler.js +57 -0
  34. data/packages/core/node_modules/leaflet/src/core/Util.js +241 -0
  35. data/packages/core/node_modules/leaflet/src/core/index.js +15 -0
  36. data/packages/core/node_modules/leaflet/src/dom/DomEvent.DoubleTap.js +91 -0
  37. data/packages/core/node_modules/leaflet/src/dom/DomEvent.Pointer.js +97 -0
  38. data/packages/core/node_modules/leaflet/src/dom/DomEvent.js +315 -0
  39. data/packages/core/node_modules/leaflet/src/dom/DomUtil.js +349 -0
  40. data/packages/core/node_modules/leaflet/src/dom/Draggable.js +220 -0
  41. data/packages/core/node_modules/leaflet/src/dom/PosAnimation.js +113 -0
  42. data/packages/core/node_modules/leaflet/src/dom/index.js +9 -0
  43. data/packages/core/node_modules/leaflet/src/geo/LatLng.js +137 -0
  44. data/packages/core/node_modules/leaflet/src/geo/LatLngBounds.js +251 -0
  45. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.EPSG3395.js +20 -0
  46. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.EPSG3857.js +27 -0
  47. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.EPSG4326.js +23 -0
  48. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.Earth.js +33 -0
  49. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.Simple.js +36 -0
  50. data/packages/core/node_modules/leaflet/src/geo/crs/CRS.js +139 -0
  51. data/packages/core/node_modules/leaflet/src/geo/crs/index.js +15 -0
  52. data/packages/core/node_modules/leaflet/src/geo/index.js +7 -0
  53. data/packages/core/node_modules/leaflet/src/geo/projection/Projection.LonLat.js +28 -0
  54. data/packages/core/node_modules/leaflet/src/geo/projection/Projection.Mercator.js +49 -0
  55. data/packages/core/node_modules/leaflet/src/geo/projection/Projection.SphericalMercator.js +44 -0
  56. data/packages/core/node_modules/leaflet/src/geo/projection/index.js +26 -0
  57. data/packages/core/node_modules/leaflet/src/geometry/Bounds.js +219 -0
  58. data/packages/core/node_modules/leaflet/src/geometry/LineUtil.js +306 -0
  59. data/packages/core/node_modules/leaflet/src/geometry/Point.js +222 -0
  60. data/packages/core/node_modules/leaflet/src/geometry/PolyUtil.js +129 -0
  61. data/packages/core/node_modules/leaflet/src/geometry/Transformation.js +79 -0
  62. data/packages/core/node_modules/leaflet/src/geometry/index.js +8 -0
  63. data/packages/core/node_modules/leaflet/src/images/layers.svg +1 -0
  64. data/packages/core/node_modules/leaflet/src/images/logo.svg +1 -0
  65. data/packages/core/node_modules/leaflet/src/images/marker.svg +1 -0
  66. data/packages/core/node_modules/leaflet/src/layer/DivOverlay.js +348 -0
  67. data/packages/core/node_modules/leaflet/src/layer/FeatureGroup.js +94 -0
  68. data/packages/core/node_modules/leaflet/src/layer/GeoJSON.js +452 -0
  69. data/packages/core/node_modules/leaflet/src/layer/ImageOverlay.js +270 -0
  70. data/packages/core/node_modules/leaflet/src/layer/Layer.Interactive.leafdoc +39 -0
  71. data/packages/core/node_modules/leaflet/src/layer/Layer.js +275 -0
  72. data/packages/core/node_modules/leaflet/src/layer/LayerGroup.js +159 -0
  73. data/packages/core/node_modules/leaflet/src/layer/Popup.js +506 -0
  74. data/packages/core/node_modules/leaflet/src/layer/SVGOverlay.js +50 -0
  75. data/packages/core/node_modules/leaflet/src/layer/Tooltip.js +444 -0
  76. data/packages/core/node_modules/leaflet/src/layer/VideoOverlay.js +106 -0
  77. data/packages/core/node_modules/leaflet/src/layer/index.js +24 -0
  78. data/packages/core/node_modules/leaflet/src/layer/marker/DivIcon.js +74 -0
  79. data/packages/core/node_modules/leaflet/src/layer/marker/Icon.Default.js +66 -0
  80. data/packages/core/node_modules/leaflet/src/layer/marker/Icon.js +165 -0
  81. data/packages/core/node_modules/leaflet/src/layer/marker/Marker.Drag.js +161 -0
  82. data/packages/core/node_modules/leaflet/src/layer/marker/Marker.js +419 -0
  83. data/packages/core/node_modules/leaflet/src/layer/marker/index.js +8 -0
  84. data/packages/core/node_modules/leaflet/src/layer/tile/GridLayer.js +923 -0
  85. data/packages/core/node_modules/leaflet/src/layer/tile/TileLayer.WMS.js +137 -0
  86. data/packages/core/node_modules/leaflet/src/layer/tile/TileLayer.js +289 -0
  87. data/packages/core/node_modules/leaflet/src/layer/tile/index.js +6 -0
  88. data/packages/core/node_modules/leaflet/src/layer/vector/Canvas.js +492 -0
  89. data/packages/core/node_modules/leaflet/src/layer/vector/Circle.js +113 -0
  90. data/packages/core/node_modules/leaflet/src/layer/vector/CircleMarker.js +109 -0
  91. data/packages/core/node_modules/leaflet/src/layer/vector/Path.js +148 -0
  92. data/packages/core/node_modules/leaflet/src/layer/vector/Polygon.js +159 -0
  93. data/packages/core/node_modules/leaflet/src/layer/vector/Polyline.js +307 -0
  94. data/packages/core/node_modules/leaflet/src/layer/vector/Rectangle.js +57 -0
  95. data/packages/core/node_modules/leaflet/src/layer/vector/Renderer.getRenderer.js +45 -0
  96. data/packages/core/node_modules/leaflet/src/layer/vector/Renderer.js +133 -0
  97. data/packages/core/node_modules/leaflet/src/layer/vector/SVG.Util.js +39 -0
  98. data/packages/core/node_modules/leaflet/src/layer/vector/SVG.VML.js +144 -0
  99. data/packages/core/node_modules/leaflet/src/layer/vector/SVG.js +207 -0
  100. data/packages/core/node_modules/leaflet/src/layer/vector/index.js +14 -0
  101. data/packages/core/node_modules/leaflet/src/map/Map.js +1751 -0
  102. data/packages/core/node_modules/leaflet/src/map/Map.methodOptions.leafdoc +112 -0
  103. data/packages/core/node_modules/leaflet/src/map/handler/Map.BoxZoom.js +152 -0
  104. data/packages/core/node_modules/leaflet/src/map/handler/Map.DoubleClickZoom.js +55 -0
  105. data/packages/core/node_modules/leaflet/src/map/handler/Map.Drag.js +235 -0
  106. data/packages/core/node_modules/leaflet/src/map/handler/Map.Keyboard.js +183 -0
  107. data/packages/core/node_modules/leaflet/src/map/handler/Map.ScrollWheelZoom.js +91 -0
  108. data/packages/core/node_modules/leaflet/src/map/handler/Map.TapHold.js +102 -0
  109. data/packages/core/node_modules/leaflet/src/map/handler/Map.TouchZoom.js +130 -0
  110. data/packages/core/node_modules/leaflet/src/map/index.js +17 -0
  111. data/packages/core/package.json +1 -1
  112. data/packages/dev/package.json +1 -1
  113. data/packages/elections/package.json +1 -1
  114. data/packages/eslint-config/package.json +1 -1
  115. data/packages/stylelint-config/package.json +1 -1
  116. data/packages/webpacker/package.json +1 -1
  117. metadata +148 -42
@@ -0,0 +1,1751 @@
1
+ import * as Util from '../core/Util';
2
+ import {Evented} from '../core/Events';
3
+ import {EPSG3857} from '../geo/crs/CRS.EPSG3857';
4
+ import {Point, toPoint} from '../geometry/Point';
5
+ import {Bounds, toBounds} from '../geometry/Bounds';
6
+ import {LatLng, toLatLng} from '../geo/LatLng';
7
+ import {LatLngBounds, toLatLngBounds} from '../geo/LatLngBounds';
8
+ import Browser from '../core/Browser';
9
+ import * as DomEvent from '../dom/DomEvent';
10
+ import * as DomUtil from '../dom/DomUtil';
11
+ import {PosAnimation} from '../dom/PosAnimation';
12
+
13
+ /*
14
+ * @class Map
15
+ * @aka L.Map
16
+ * @inherits Evented
17
+ *
18
+ * The central class of the API — it is used to create a map on a page and manipulate it.
19
+ *
20
+ * @example
21
+ *
22
+ * ```js
23
+ * // initialize the map on the "map" div with a given center and zoom
24
+ * var map = L.map('map', {
25
+ * center: [51.505, -0.09],
26
+ * zoom: 13
27
+ * });
28
+ * ```
29
+ *
30
+ */
31
+
32
+ export var Map = Evented.extend({
33
+
34
+ options: {
35
+ // @section Map State Options
36
+ // @option crs: CRS = L.CRS.EPSG3857
37
+ // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
38
+ // sure what it means.
39
+ crs: EPSG3857,
40
+
41
+ // @option center: LatLng = undefined
42
+ // Initial geographic center of the map
43
+ center: undefined,
44
+
45
+ // @option zoom: Number = undefined
46
+ // Initial map zoom level
47
+ zoom: undefined,
48
+
49
+ // @option minZoom: Number = *
50
+ // Minimum zoom level of the map.
51
+ // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
52
+ // the lowest of their `minZoom` options will be used instead.
53
+ minZoom: undefined,
54
+
55
+ // @option maxZoom: Number = *
56
+ // Maximum zoom level of the map.
57
+ // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
58
+ // the highest of their `maxZoom` options will be used instead.
59
+ maxZoom: undefined,
60
+
61
+ // @option layers: Layer[] = []
62
+ // Array of layers that will be added to the map initially
63
+ layers: [],
64
+
65
+ // @option maxBounds: LatLngBounds = null
66
+ // When this option is set, the map restricts the view to the given
67
+ // geographical bounds, bouncing the user back if the user tries to pan
68
+ // outside the view. To set the restriction dynamically, use
69
+ // [`setMaxBounds`](#map-setmaxbounds) method.
70
+ maxBounds: undefined,
71
+
72
+ // @option renderer: Renderer = *
73
+ // The default method for drawing vector layers on the map. `L.SVG`
74
+ // or `L.Canvas` by default depending on browser support.
75
+ renderer: undefined,
76
+
77
+
78
+ // @section Animation Options
79
+ // @option zoomAnimation: Boolean = true
80
+ // Whether the map zoom animation is enabled. By default it's enabled
81
+ // in all browsers that support CSS3 Transitions except Android.
82
+ zoomAnimation: true,
83
+
84
+ // @option zoomAnimationThreshold: Number = 4
85
+ // Won't animate zoom if the zoom difference exceeds this value.
86
+ zoomAnimationThreshold: 4,
87
+
88
+ // @option fadeAnimation: Boolean = true
89
+ // Whether the tile fade animation is enabled. By default it's enabled
90
+ // in all browsers that support CSS3 Transitions except Android.
91
+ fadeAnimation: true,
92
+
93
+ // @option markerZoomAnimation: Boolean = true
94
+ // Whether markers animate their zoom with the zoom animation, if disabled
95
+ // they will disappear for the length of the animation. By default it's
96
+ // enabled in all browsers that support CSS3 Transitions except Android.
97
+ markerZoomAnimation: true,
98
+
99
+ // @option transform3DLimit: Number = 2^23
100
+ // Defines the maximum size of a CSS translation transform. The default
101
+ // value should not be changed unless a web browser positions layers in
102
+ // the wrong place after doing a large `panBy`.
103
+ transform3DLimit: 8388608, // Precision limit of a 32-bit float
104
+
105
+ // @section Interaction Options
106
+ // @option zoomSnap: Number = 1
107
+ // Forces the map's zoom level to always be a multiple of this, particularly
108
+ // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
109
+ // By default, the zoom level snaps to the nearest integer; lower values
110
+ // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
111
+ // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
112
+ zoomSnap: 1,
113
+
114
+ // @option zoomDelta: Number = 1
115
+ // Controls how much the map's zoom level will change after a
116
+ // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
117
+ // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
118
+ // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
119
+ zoomDelta: 1,
120
+
121
+ // @option trackResize: Boolean = true
122
+ // Whether the map automatically handles browser window resize to update itself.
123
+ trackResize: true
124
+ },
125
+
126
+ initialize: function (id, options) { // (HTMLElement or String, Object)
127
+ options = Util.setOptions(this, options);
128
+
129
+ // Make sure to assign internal flags at the beginning,
130
+ // to avoid inconsistent state in some edge cases.
131
+ this._handlers = [];
132
+ this._layers = {};
133
+ this._zoomBoundLayers = {};
134
+ this._sizeChanged = true;
135
+
136
+ this._initContainer(id);
137
+ this._initLayout();
138
+
139
+ // hack for https://github.com/Leaflet/Leaflet/issues/1980
140
+ this._onResize = Util.bind(this._onResize, this);
141
+
142
+ this._initEvents();
143
+
144
+ if (options.maxBounds) {
145
+ this.setMaxBounds(options.maxBounds);
146
+ }
147
+
148
+ if (options.zoom !== undefined) {
149
+ this._zoom = this._limitZoom(options.zoom);
150
+ }
151
+
152
+ if (options.center && options.zoom !== undefined) {
153
+ this.setView(toLatLng(options.center), options.zoom, {reset: true});
154
+ }
155
+
156
+ this.callInitHooks();
157
+
158
+ // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
159
+ this._zoomAnimated = DomUtil.TRANSITION && Browser.any3d && !Browser.mobileOpera &&
160
+ this.options.zoomAnimation;
161
+
162
+ // zoom transitions run with the same duration for all layers, so if one of transitionend events
163
+ // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
164
+ if (this._zoomAnimated) {
165
+ this._createAnimProxy();
166
+ DomEvent.on(this._proxy, DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
167
+ }
168
+
169
+ this._addLayers(this.options.layers);
170
+ },
171
+
172
+
173
+ // @section Methods for modifying map state
174
+
175
+ // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
176
+ // Sets the view of the map (geographical center and zoom) with the given
177
+ // animation options.
178
+ setView: function (center, zoom, options) {
179
+
180
+ zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
181
+ center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
182
+ options = options || {};
183
+
184
+ this._stop();
185
+
186
+ if (this._loaded && !options.reset && options !== true) {
187
+
188
+ if (options.animate !== undefined) {
189
+ options.zoom = Util.extend({animate: options.animate}, options.zoom);
190
+ options.pan = Util.extend({animate: options.animate, duration: options.duration}, options.pan);
191
+ }
192
+
193
+ // try animating pan or zoom
194
+ var moved = (this._zoom !== zoom) ?
195
+ this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
196
+ this._tryAnimatedPan(center, options.pan);
197
+
198
+ if (moved) {
199
+ // prevent resize handler call, the view will refresh after animation anyway
200
+ clearTimeout(this._sizeTimer);
201
+ return this;
202
+ }
203
+ }
204
+
205
+ // animation didn't start, just reset the map view
206
+ this._resetView(center, zoom, options.pan && options.pan.noMoveStart);
207
+
208
+ return this;
209
+ },
210
+
211
+ // @method setZoom(zoom: Number, options?: Zoom/pan options): this
212
+ // Sets the zoom of the map.
213
+ setZoom: function (zoom, options) {
214
+ if (!this._loaded) {
215
+ this._zoom = zoom;
216
+ return this;
217
+ }
218
+ return this.setView(this.getCenter(), zoom, {zoom: options});
219
+ },
220
+
221
+ // @method zoomIn(delta?: Number, options?: Zoom options): this
222
+ // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
223
+ zoomIn: function (delta, options) {
224
+ delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
225
+ return this.setZoom(this._zoom + delta, options);
226
+ },
227
+
228
+ // @method zoomOut(delta?: Number, options?: Zoom options): this
229
+ // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
230
+ zoomOut: function (delta, options) {
231
+ delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
232
+ return this.setZoom(this._zoom - delta, options);
233
+ },
234
+
235
+ // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
236
+ // Zooms the map while keeping a specified geographical point on the map
237
+ // stationary (e.g. used internally for scroll zoom and double-click zoom).
238
+ // @alternative
239
+ // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
240
+ // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
241
+ setZoomAround: function (latlng, zoom, options) {
242
+ var scale = this.getZoomScale(zoom),
243
+ viewHalf = this.getSize().divideBy(2),
244
+ containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
245
+
246
+ centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
247
+ newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
248
+
249
+ return this.setView(newCenter, zoom, {zoom: options});
250
+ },
251
+
252
+ _getBoundsCenterZoom: function (bounds, options) {
253
+
254
+ options = options || {};
255
+ bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
256
+
257
+ var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
258
+ paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
259
+
260
+ zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
261
+
262
+ zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
263
+
264
+ if (zoom === Infinity) {
265
+ return {
266
+ center: bounds.getCenter(),
267
+ zoom: zoom
268
+ };
269
+ }
270
+
271
+ var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
272
+
273
+ swPoint = this.project(bounds.getSouthWest(), zoom),
274
+ nePoint = this.project(bounds.getNorthEast(), zoom),
275
+ center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
276
+
277
+ return {
278
+ center: center,
279
+ zoom: zoom
280
+ };
281
+ },
282
+
283
+ // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
284
+ // Sets a map view that contains the given geographical bounds with the
285
+ // maximum zoom level possible.
286
+ fitBounds: function (bounds, options) {
287
+
288
+ bounds = toLatLngBounds(bounds);
289
+
290
+ if (!bounds.isValid()) {
291
+ throw new Error('Bounds are not valid.');
292
+ }
293
+
294
+ var target = this._getBoundsCenterZoom(bounds, options);
295
+ return this.setView(target.center, target.zoom, options);
296
+ },
297
+
298
+ // @method fitWorld(options?: fitBounds options): this
299
+ // Sets a map view that mostly contains the whole world with the maximum
300
+ // zoom level possible.
301
+ fitWorld: function (options) {
302
+ return this.fitBounds([[-90, -180], [90, 180]], options);
303
+ },
304
+
305
+ // @method panTo(latlng: LatLng, options?: Pan options): this
306
+ // Pans the map to a given center.
307
+ panTo: function (center, options) { // (LatLng)
308
+ return this.setView(center, this._zoom, {pan: options});
309
+ },
310
+
311
+ // @method panBy(offset: Point, options?: Pan options): this
312
+ // Pans the map by a given number of pixels (animated).
313
+ panBy: function (offset, options) {
314
+ offset = toPoint(offset).round();
315
+ options = options || {};
316
+
317
+ if (!offset.x && !offset.y) {
318
+ return this.fire('moveend');
319
+ }
320
+ // If we pan too far, Chrome gets issues with tiles
321
+ // and makes them disappear or appear in the wrong place (slightly offset) #2602
322
+ if (options.animate !== true && !this.getSize().contains(offset)) {
323
+ this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
324
+ return this;
325
+ }
326
+
327
+ if (!this._panAnim) {
328
+ this._panAnim = new PosAnimation();
329
+
330
+ this._panAnim.on({
331
+ 'step': this._onPanTransitionStep,
332
+ 'end': this._onPanTransitionEnd
333
+ }, this);
334
+ }
335
+
336
+ // don't fire movestart if animating inertia
337
+ if (!options.noMoveStart) {
338
+ this.fire('movestart');
339
+ }
340
+
341
+ // animate pan unless animate: false specified
342
+ if (options.animate !== false) {
343
+ DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
344
+
345
+ var newPos = this._getMapPanePos().subtract(offset).round();
346
+ this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
347
+ } else {
348
+ this._rawPanBy(offset);
349
+ this.fire('move').fire('moveend');
350
+ }
351
+
352
+ return this;
353
+ },
354
+
355
+ // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
356
+ // Sets the view of the map (geographical center and zoom) performing a smooth
357
+ // pan-zoom animation.
358
+ flyTo: function (targetCenter, targetZoom, options) {
359
+
360
+ options = options || {};
361
+ if (options.animate === false || !Browser.any3d) {
362
+ return this.setView(targetCenter, targetZoom, options);
363
+ }
364
+
365
+ this._stop();
366
+
367
+ var from = this.project(this.getCenter()),
368
+ to = this.project(targetCenter),
369
+ size = this.getSize(),
370
+ startZoom = this._zoom;
371
+
372
+ targetCenter = toLatLng(targetCenter);
373
+ targetZoom = targetZoom === undefined ? startZoom : targetZoom;
374
+
375
+ var w0 = Math.max(size.x, size.y),
376
+ w1 = w0 * this.getZoomScale(startZoom, targetZoom),
377
+ u1 = (to.distanceTo(from)) || 1,
378
+ rho = 1.42,
379
+ rho2 = rho * rho;
380
+
381
+ function r(i) {
382
+ var s1 = i ? -1 : 1,
383
+ s2 = i ? w1 : w0,
384
+ t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
385
+ b1 = 2 * s2 * rho2 * u1,
386
+ b = t1 / b1,
387
+ sq = Math.sqrt(b * b + 1) - b;
388
+
389
+ // workaround for floating point precision bug when sq = 0, log = -Infinite,
390
+ // thus triggering an infinite loop in flyTo
391
+ var log = sq < 0.000000001 ? -18 : Math.log(sq);
392
+
393
+ return log;
394
+ }
395
+
396
+ function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
397
+ function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
398
+ function tanh(n) { return sinh(n) / cosh(n); }
399
+
400
+ var r0 = r(0);
401
+
402
+ function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
403
+ function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
404
+
405
+ function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
406
+
407
+ var start = Date.now(),
408
+ S = (r(1) - r0) / rho,
409
+ duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
410
+
411
+ function frame() {
412
+ var t = (Date.now() - start) / duration,
413
+ s = easeOut(t) * S;
414
+
415
+ if (t <= 1) {
416
+ this._flyToFrame = Util.requestAnimFrame(frame, this);
417
+
418
+ this._move(
419
+ this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
420
+ this.getScaleZoom(w0 / w(s), startZoom),
421
+ {flyTo: true});
422
+
423
+ } else {
424
+ this
425
+ ._move(targetCenter, targetZoom)
426
+ ._moveEnd(true);
427
+ }
428
+ }
429
+
430
+ this._moveStart(true, options.noMoveStart);
431
+
432
+ frame.call(this);
433
+ return this;
434
+ },
435
+
436
+ // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
437
+ // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
438
+ // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
439
+ flyToBounds: function (bounds, options) {
440
+ var target = this._getBoundsCenterZoom(bounds, options);
441
+ return this.flyTo(target.center, target.zoom, options);
442
+ },
443
+
444
+ // @method setMaxBounds(bounds: LatLngBounds): this
445
+ // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
446
+ setMaxBounds: function (bounds) {
447
+ bounds = toLatLngBounds(bounds);
448
+
449
+ if (this.listens('moveend', this._panInsideMaxBounds)) {
450
+ this.off('moveend', this._panInsideMaxBounds);
451
+ }
452
+
453
+ if (!bounds.isValid()) {
454
+ this.options.maxBounds = null;
455
+ return this;
456
+ }
457
+
458
+ this.options.maxBounds = bounds;
459
+
460
+ if (this._loaded) {
461
+ this._panInsideMaxBounds();
462
+ }
463
+
464
+ return this.on('moveend', this._panInsideMaxBounds);
465
+ },
466
+
467
+ // @method setMinZoom(zoom: Number): this
468
+ // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
469
+ setMinZoom: function (zoom) {
470
+ var oldZoom = this.options.minZoom;
471
+ this.options.minZoom = zoom;
472
+
473
+ if (this._loaded && oldZoom !== zoom) {
474
+ this.fire('zoomlevelschange');
475
+
476
+ if (this.getZoom() < this.options.minZoom) {
477
+ return this.setZoom(zoom);
478
+ }
479
+ }
480
+
481
+ return this;
482
+ },
483
+
484
+ // @method setMaxZoom(zoom: Number): this
485
+ // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
486
+ setMaxZoom: function (zoom) {
487
+ var oldZoom = this.options.maxZoom;
488
+ this.options.maxZoom = zoom;
489
+
490
+ if (this._loaded && oldZoom !== zoom) {
491
+ this.fire('zoomlevelschange');
492
+
493
+ if (this.getZoom() > this.options.maxZoom) {
494
+ return this.setZoom(zoom);
495
+ }
496
+ }
497
+
498
+ return this;
499
+ },
500
+
501
+ // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
502
+ // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
503
+ panInsideBounds: function (bounds, options) {
504
+ this._enforcingBounds = true;
505
+ var center = this.getCenter(),
506
+ newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
507
+
508
+ if (!center.equals(newCenter)) {
509
+ this.panTo(newCenter, options);
510
+ }
511
+
512
+ this._enforcingBounds = false;
513
+ return this;
514
+ },
515
+
516
+ // @method panInside(latlng: LatLng, options?: padding options): this
517
+ // Pans the map the minimum amount to make the `latlng` visible. Use
518
+ // padding options to fit the display to more restricted bounds.
519
+ // If `latlng` is already within the (optionally padded) display bounds,
520
+ // the map will not be panned.
521
+ panInside: function (latlng, options) {
522
+ options = options || {};
523
+
524
+ var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
525
+ paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
526
+ pixelCenter = this.project(this.getCenter()),
527
+ pixelPoint = this.project(latlng),
528
+ pixelBounds = this.getPixelBounds(),
529
+ paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
530
+ paddedSize = paddedBounds.getSize();
531
+
532
+ if (!paddedBounds.contains(pixelPoint)) {
533
+ this._enforcingBounds = true;
534
+ var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
535
+ var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
536
+ pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
537
+ pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
538
+ this.panTo(this.unproject(pixelCenter), options);
539
+ this._enforcingBounds = false;
540
+ }
541
+ return this;
542
+ },
543
+
544
+ // @method invalidateSize(options: Zoom/pan options): this
545
+ // Checks if the map container size changed and updates the map if so —
546
+ // call it after you've changed the map size dynamically, also animating
547
+ // pan by default. If `options.pan` is `false`, panning will not occur.
548
+ // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
549
+ // that it doesn't happen often even if the method is called many
550
+ // times in a row.
551
+
552
+ // @alternative
553
+ // @method invalidateSize(animate: Boolean): this
554
+ // Checks if the map container size changed and updates the map if so —
555
+ // call it after you've changed the map size dynamically, also animating
556
+ // pan by default.
557
+ invalidateSize: function (options) {
558
+ if (!this._loaded) { return this; }
559
+
560
+ options = Util.extend({
561
+ animate: false,
562
+ pan: true
563
+ }, options === true ? {animate: true} : options);
564
+
565
+ var oldSize = this.getSize();
566
+ this._sizeChanged = true;
567
+ this._lastCenter = null;
568
+
569
+ var newSize = this.getSize(),
570
+ oldCenter = oldSize.divideBy(2).round(),
571
+ newCenter = newSize.divideBy(2).round(),
572
+ offset = oldCenter.subtract(newCenter);
573
+
574
+ if (!offset.x && !offset.y) { return this; }
575
+
576
+ if (options.animate && options.pan) {
577
+ this.panBy(offset);
578
+
579
+ } else {
580
+ if (options.pan) {
581
+ this._rawPanBy(offset);
582
+ }
583
+
584
+ this.fire('move');
585
+
586
+ if (options.debounceMoveend) {
587
+ clearTimeout(this._sizeTimer);
588
+ this._sizeTimer = setTimeout(Util.bind(this.fire, this, 'moveend'), 200);
589
+ } else {
590
+ this.fire('moveend');
591
+ }
592
+ }
593
+
594
+ // @section Map state change events
595
+ // @event resize: ResizeEvent
596
+ // Fired when the map is resized.
597
+ return this.fire('resize', {
598
+ oldSize: oldSize,
599
+ newSize: newSize
600
+ });
601
+ },
602
+
603
+ // @section Methods for modifying map state
604
+ // @method stop(): this
605
+ // Stops the currently running `panTo` or `flyTo` animation, if any.
606
+ stop: function () {
607
+ this.setZoom(this._limitZoom(this._zoom));
608
+ if (!this.options.zoomSnap) {
609
+ this.fire('viewreset');
610
+ }
611
+ return this._stop();
612
+ },
613
+
614
+ // @section Geolocation methods
615
+ // @method locate(options?: Locate options): this
616
+ // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
617
+ // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
618
+ // and optionally sets the map view to the user's location with respect to
619
+ // detection accuracy (or to the world view if geolocation failed).
620
+ // Note that, if your page doesn't use HTTPS, this method will fail in
621
+ // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
622
+ // See `Locate options` for more details.
623
+ locate: function (options) {
624
+
625
+ options = this._locateOptions = Util.extend({
626
+ timeout: 10000,
627
+ watch: false
628
+ // setView: false
629
+ // maxZoom: <Number>
630
+ // maximumAge: 0
631
+ // enableHighAccuracy: false
632
+ }, options);
633
+
634
+ if (!('geolocation' in navigator)) {
635
+ this._handleGeolocationError({
636
+ code: 0,
637
+ message: 'Geolocation not supported.'
638
+ });
639
+ return this;
640
+ }
641
+
642
+ var onResponse = Util.bind(this._handleGeolocationResponse, this),
643
+ onError = Util.bind(this._handleGeolocationError, this);
644
+
645
+ if (options.watch) {
646
+ this._locationWatchId =
647
+ navigator.geolocation.watchPosition(onResponse, onError, options);
648
+ } else {
649
+ navigator.geolocation.getCurrentPosition(onResponse, onError, options);
650
+ }
651
+ return this;
652
+ },
653
+
654
+ // @method stopLocate(): this
655
+ // Stops watching location previously initiated by `map.locate({watch: true})`
656
+ // and aborts resetting the map view if map.locate was called with
657
+ // `{setView: true}`.
658
+ stopLocate: function () {
659
+ if (navigator.geolocation && navigator.geolocation.clearWatch) {
660
+ navigator.geolocation.clearWatch(this._locationWatchId);
661
+ }
662
+ if (this._locateOptions) {
663
+ this._locateOptions.setView = false;
664
+ }
665
+ return this;
666
+ },
667
+
668
+ _handleGeolocationError: function (error) {
669
+ if (!this._container._leaflet_id) { return; }
670
+
671
+ var c = error.code,
672
+ message = error.message ||
673
+ (c === 1 ? 'permission denied' :
674
+ (c === 2 ? 'position unavailable' : 'timeout'));
675
+
676
+ if (this._locateOptions.setView && !this._loaded) {
677
+ this.fitWorld();
678
+ }
679
+
680
+ // @section Location events
681
+ // @event locationerror: ErrorEvent
682
+ // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
683
+ this.fire('locationerror', {
684
+ code: c,
685
+ message: 'Geolocation error: ' + message + '.'
686
+ });
687
+ },
688
+
689
+ _handleGeolocationResponse: function (pos) {
690
+ if (!this._container._leaflet_id) { return; }
691
+
692
+ var lat = pos.coords.latitude,
693
+ lng = pos.coords.longitude,
694
+ latlng = new LatLng(lat, lng),
695
+ bounds = latlng.toBounds(pos.coords.accuracy * 2),
696
+ options = this._locateOptions;
697
+
698
+ if (options.setView) {
699
+ var zoom = this.getBoundsZoom(bounds);
700
+ this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
701
+ }
702
+
703
+ var data = {
704
+ latlng: latlng,
705
+ bounds: bounds,
706
+ timestamp: pos.timestamp
707
+ };
708
+
709
+ for (var i in pos.coords) {
710
+ if (typeof pos.coords[i] === 'number') {
711
+ data[i] = pos.coords[i];
712
+ }
713
+ }
714
+
715
+ // @event locationfound: LocationEvent
716
+ // Fired when geolocation (using the [`locate`](#map-locate) method)
717
+ // went successfully.
718
+ this.fire('locationfound', data);
719
+ },
720
+
721
+ // TODO Appropriate docs section?
722
+ // @section Other Methods
723
+ // @method addHandler(name: String, HandlerClass: Function): this
724
+ // Adds a new `Handler` to the map, given its name and constructor function.
725
+ addHandler: function (name, HandlerClass) {
726
+ if (!HandlerClass) { return this; }
727
+
728
+ var handler = this[name] = new HandlerClass(this);
729
+
730
+ this._handlers.push(handler);
731
+
732
+ if (this.options[name]) {
733
+ handler.enable();
734
+ }
735
+
736
+ return this;
737
+ },
738
+
739
+ // @method remove(): this
740
+ // Destroys the map and clears all related event listeners.
741
+ remove: function () {
742
+
743
+ this._initEvents(true);
744
+ if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
745
+
746
+ if (this._containerId !== this._container._leaflet_id) {
747
+ throw new Error('Map container is being reused by another instance');
748
+ }
749
+
750
+ try {
751
+ // throws error in IE6-8
752
+ delete this._container._leaflet_id;
753
+ delete this._containerId;
754
+ } catch (e) {
755
+ /*eslint-disable */
756
+ this._container._leaflet_id = undefined;
757
+ /* eslint-enable */
758
+ this._containerId = undefined;
759
+ }
760
+
761
+ if (this._locationWatchId !== undefined) {
762
+ this.stopLocate();
763
+ }
764
+
765
+ this._stop();
766
+
767
+ DomUtil.remove(this._mapPane);
768
+
769
+ if (this._clearControlPos) {
770
+ this._clearControlPos();
771
+ }
772
+ if (this._resizeRequest) {
773
+ Util.cancelAnimFrame(this._resizeRequest);
774
+ this._resizeRequest = null;
775
+ }
776
+
777
+ this._clearHandlers();
778
+
779
+ if (this._loaded) {
780
+ // @section Map state change events
781
+ // @event unload: Event
782
+ // Fired when the map is destroyed with [remove](#map-remove) method.
783
+ this.fire('unload');
784
+ }
785
+
786
+ var i;
787
+ for (i in this._layers) {
788
+ this._layers[i].remove();
789
+ }
790
+ for (i in this._panes) {
791
+ DomUtil.remove(this._panes[i]);
792
+ }
793
+
794
+ this._layers = [];
795
+ this._panes = [];
796
+ delete this._mapPane;
797
+ delete this._renderer;
798
+
799
+ return this;
800
+ },
801
+
802
+ // @section Other Methods
803
+ // @method createPane(name: String, container?: HTMLElement): HTMLElement
804
+ // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
805
+ // then returns it. The pane is created as a child of `container`, or
806
+ // as a child of the main map pane if not set.
807
+ createPane: function (name, container) {
808
+ var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
809
+ pane = DomUtil.create('div', className, container || this._mapPane);
810
+
811
+ if (name) {
812
+ this._panes[name] = pane;
813
+ }
814
+ return pane;
815
+ },
816
+
817
+ // @section Methods for Getting Map State
818
+
819
+ // @method getCenter(): LatLng
820
+ // Returns the geographical center of the map view
821
+ getCenter: function () {
822
+ this._checkIfLoaded();
823
+
824
+ if (this._lastCenter && !this._moved()) {
825
+ return this._lastCenter.clone();
826
+ }
827
+ return this.layerPointToLatLng(this._getCenterLayerPoint());
828
+ },
829
+
830
+ // @method getZoom(): Number
831
+ // Returns the current zoom level of the map view
832
+ getZoom: function () {
833
+ return this._zoom;
834
+ },
835
+
836
+ // @method getBounds(): LatLngBounds
837
+ // Returns the geographical bounds visible in the current map view
838
+ getBounds: function () {
839
+ var bounds = this.getPixelBounds(),
840
+ sw = this.unproject(bounds.getBottomLeft()),
841
+ ne = this.unproject(bounds.getTopRight());
842
+
843
+ return new LatLngBounds(sw, ne);
844
+ },
845
+
846
+ // @method getMinZoom(): Number
847
+ // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
848
+ getMinZoom: function () {
849
+ return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
850
+ },
851
+
852
+ // @method getMaxZoom(): Number
853
+ // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
854
+ getMaxZoom: function () {
855
+ return this.options.maxZoom === undefined ?
856
+ (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
857
+ this.options.maxZoom;
858
+ },
859
+
860
+ // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
861
+ // Returns the maximum zoom level on which the given bounds fit to the map
862
+ // view in its entirety. If `inside` (optional) is set to `true`, the method
863
+ // instead returns the minimum zoom level on which the map view fits into
864
+ // the given bounds in its entirety.
865
+ getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
866
+ bounds = toLatLngBounds(bounds);
867
+ padding = toPoint(padding || [0, 0]);
868
+
869
+ var zoom = this.getZoom() || 0,
870
+ min = this.getMinZoom(),
871
+ max = this.getMaxZoom(),
872
+ nw = bounds.getNorthWest(),
873
+ se = bounds.getSouthEast(),
874
+ size = this.getSize().subtract(padding),
875
+ boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
876
+ snap = Browser.any3d ? this.options.zoomSnap : 1,
877
+ scalex = size.x / boundsSize.x,
878
+ scaley = size.y / boundsSize.y,
879
+ scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
880
+
881
+ zoom = this.getScaleZoom(scale, zoom);
882
+
883
+ if (snap) {
884
+ zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
885
+ zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
886
+ }
887
+
888
+ return Math.max(min, Math.min(max, zoom));
889
+ },
890
+
891
+ // @method getSize(): Point
892
+ // Returns the current size of the map container (in pixels).
893
+ getSize: function () {
894
+ if (!this._size || this._sizeChanged) {
895
+ this._size = new Point(
896
+ this._container.clientWidth || 0,
897
+ this._container.clientHeight || 0);
898
+
899
+ this._sizeChanged = false;
900
+ }
901
+ return this._size.clone();
902
+ },
903
+
904
+ // @method getPixelBounds(): Bounds
905
+ // Returns the bounds of the current map view in projected pixel
906
+ // coordinates (sometimes useful in layer and overlay implementations).
907
+ getPixelBounds: function (center, zoom) {
908
+ var topLeftPoint = this._getTopLeftPoint(center, zoom);
909
+ return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
910
+ },
911
+
912
+ // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
913
+ // the map pane? "left point of the map layer" can be confusing, specially
914
+ // since there can be negative offsets.
915
+ // @method getPixelOrigin(): Point
916
+ // Returns the projected pixel coordinates of the top left point of
917
+ // the map layer (useful in custom layer and overlay implementations).
918
+ getPixelOrigin: function () {
919
+ this._checkIfLoaded();
920
+ return this._pixelOrigin;
921
+ },
922
+
923
+ // @method getPixelWorldBounds(zoom?: Number): Bounds
924
+ // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
925
+ // If `zoom` is omitted, the map's current zoom level is used.
926
+ getPixelWorldBounds: function (zoom) {
927
+ return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
928
+ },
929
+
930
+ // @section Other Methods
931
+
932
+ // @method getPane(pane: String|HTMLElement): HTMLElement
933
+ // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
934
+ getPane: function (pane) {
935
+ return typeof pane === 'string' ? this._panes[pane] : pane;
936
+ },
937
+
938
+ // @method getPanes(): Object
939
+ // Returns a plain object containing the names of all [panes](#map-pane) as keys and
940
+ // the panes as values.
941
+ getPanes: function () {
942
+ return this._panes;
943
+ },
944
+
945
+ // @method getContainer: HTMLElement
946
+ // Returns the HTML element that contains the map.
947
+ getContainer: function () {
948
+ return this._container;
949
+ },
950
+
951
+
952
+ // @section Conversion Methods
953
+
954
+ // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
955
+ // Returns the scale factor to be applied to a map transition from zoom level
956
+ // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
957
+ getZoomScale: function (toZoom, fromZoom) {
958
+ // TODO replace with universal implementation after refactoring projections
959
+ var crs = this.options.crs;
960
+ fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
961
+ return crs.scale(toZoom) / crs.scale(fromZoom);
962
+ },
963
+
964
+ // @method getScaleZoom(scale: Number, fromZoom: Number): Number
965
+ // Returns the zoom level that the map would end up at, if it is at `fromZoom`
966
+ // level and everything is scaled by a factor of `scale`. Inverse of
967
+ // [`getZoomScale`](#map-getZoomScale).
968
+ getScaleZoom: function (scale, fromZoom) {
969
+ var crs = this.options.crs;
970
+ fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
971
+ var zoom = crs.zoom(scale * crs.scale(fromZoom));
972
+ return isNaN(zoom) ? Infinity : zoom;
973
+ },
974
+
975
+ // @method project(latlng: LatLng, zoom: Number): Point
976
+ // Projects a geographical coordinate `LatLng` according to the projection
977
+ // of the map's CRS, then scales it according to `zoom` and the CRS's
978
+ // `Transformation`. The result is pixel coordinate relative to
979
+ // the CRS origin.
980
+ project: function (latlng, zoom) {
981
+ zoom = zoom === undefined ? this._zoom : zoom;
982
+ return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
983
+ },
984
+
985
+ // @method unproject(point: Point, zoom: Number): LatLng
986
+ // Inverse of [`project`](#map-project).
987
+ unproject: function (point, zoom) {
988
+ zoom = zoom === undefined ? this._zoom : zoom;
989
+ return this.options.crs.pointToLatLng(toPoint(point), zoom);
990
+ },
991
+
992
+ // @method layerPointToLatLng(point: Point): LatLng
993
+ // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
994
+ // returns the corresponding geographical coordinate (for the current zoom level).
995
+ layerPointToLatLng: function (point) {
996
+ var projectedPoint = toPoint(point).add(this.getPixelOrigin());
997
+ return this.unproject(projectedPoint);
998
+ },
999
+
1000
+ // @method latLngToLayerPoint(latlng: LatLng): Point
1001
+ // Given a geographical coordinate, returns the corresponding pixel coordinate
1002
+ // relative to the [origin pixel](#map-getpixelorigin).
1003
+ latLngToLayerPoint: function (latlng) {
1004
+ var projectedPoint = this.project(toLatLng(latlng))._round();
1005
+ return projectedPoint._subtract(this.getPixelOrigin());
1006
+ },
1007
+
1008
+ // @method wrapLatLng(latlng: LatLng): LatLng
1009
+ // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
1010
+ // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
1011
+ // CRS's bounds.
1012
+ // By default this means longitude is wrapped around the dateline so its
1013
+ // value is between -180 and +180 degrees.
1014
+ wrapLatLng: function (latlng) {
1015
+ return this.options.crs.wrapLatLng(toLatLng(latlng));
1016
+ },
1017
+
1018
+ // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1019
+ // Returns a `LatLngBounds` with the same size as the given one, ensuring that
1020
+ // its center is within the CRS's bounds.
1021
+ // By default this means the center longitude is wrapped around the dateline so its
1022
+ // value is between -180 and +180 degrees, and the majority of the bounds
1023
+ // overlaps the CRS's bounds.
1024
+ wrapLatLngBounds: function (latlng) {
1025
+ return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
1026
+ },
1027
+
1028
+ // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1029
+ // Returns the distance between two geographical coordinates according to
1030
+ // the map's CRS. By default this measures distance in meters.
1031
+ distance: function (latlng1, latlng2) {
1032
+ return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
1033
+ },
1034
+
1035
+ // @method containerPointToLayerPoint(point: Point): Point
1036
+ // Given a pixel coordinate relative to the map container, returns the corresponding
1037
+ // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
1038
+ containerPointToLayerPoint: function (point) { // (Point)
1039
+ return toPoint(point).subtract(this._getMapPanePos());
1040
+ },
1041
+
1042
+ // @method layerPointToContainerPoint(point: Point): Point
1043
+ // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
1044
+ // returns the corresponding pixel coordinate relative to the map container.
1045
+ layerPointToContainerPoint: function (point) { // (Point)
1046
+ return toPoint(point).add(this._getMapPanePos());
1047
+ },
1048
+
1049
+ // @method containerPointToLatLng(point: Point): LatLng
1050
+ // Given a pixel coordinate relative to the map container, returns
1051
+ // the corresponding geographical coordinate (for the current zoom level).
1052
+ containerPointToLatLng: function (point) {
1053
+ var layerPoint = this.containerPointToLayerPoint(toPoint(point));
1054
+ return this.layerPointToLatLng(layerPoint);
1055
+ },
1056
+
1057
+ // @method latLngToContainerPoint(latlng: LatLng): Point
1058
+ // Given a geographical coordinate, returns the corresponding pixel coordinate
1059
+ // relative to the map container.
1060
+ latLngToContainerPoint: function (latlng) {
1061
+ return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
1062
+ },
1063
+
1064
+ // @method mouseEventToContainerPoint(ev: MouseEvent): Point
1065
+ // Given a MouseEvent object, returns the pixel coordinate relative to the
1066
+ // map container where the event took place.
1067
+ mouseEventToContainerPoint: function (e) {
1068
+ return DomEvent.getMousePosition(e, this._container);
1069
+ },
1070
+
1071
+ // @method mouseEventToLayerPoint(ev: MouseEvent): Point
1072
+ // Given a MouseEvent object, returns the pixel coordinate relative to
1073
+ // the [origin pixel](#map-getpixelorigin) where the event took place.
1074
+ mouseEventToLayerPoint: function (e) {
1075
+ return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
1076
+ },
1077
+
1078
+ // @method mouseEventToLatLng(ev: MouseEvent): LatLng
1079
+ // Given a MouseEvent object, returns geographical coordinate where the
1080
+ // event took place.
1081
+ mouseEventToLatLng: function (e) { // (MouseEvent)
1082
+ return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
1083
+ },
1084
+
1085
+
1086
+ // map initialization methods
1087
+
1088
+ _initContainer: function (id) {
1089
+ var container = this._container = DomUtil.get(id);
1090
+
1091
+ if (!container) {
1092
+ throw new Error('Map container not found.');
1093
+ } else if (container._leaflet_id) {
1094
+ throw new Error('Map container is already initialized.');
1095
+ }
1096
+
1097
+ DomEvent.on(container, 'scroll', this._onScroll, this);
1098
+ this._containerId = Util.stamp(container);
1099
+ },
1100
+
1101
+ _initLayout: function () {
1102
+ var container = this._container;
1103
+
1104
+ this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
1105
+
1106
+ DomUtil.addClass(container, 'leaflet-container' +
1107
+ (Browser.touch ? ' leaflet-touch' : '') +
1108
+ (Browser.retina ? ' leaflet-retina' : '') +
1109
+ (Browser.ielt9 ? ' leaflet-oldie' : '') +
1110
+ (Browser.safari ? ' leaflet-safari' : '') +
1111
+ (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
1112
+
1113
+ var position = DomUtil.getStyle(container, 'position');
1114
+
1115
+ if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') {
1116
+ container.style.position = 'relative';
1117
+ }
1118
+
1119
+ this._initPanes();
1120
+
1121
+ if (this._initControlPos) {
1122
+ this._initControlPos();
1123
+ }
1124
+ },
1125
+
1126
+ _initPanes: function () {
1127
+ var panes = this._panes = {};
1128
+ this._paneRenderers = {};
1129
+
1130
+ // @section
1131
+ //
1132
+ // Panes are DOM elements used to control the ordering of layers on the map. You
1133
+ // can access panes with [`map.getPane`](#map-getpane) or
1134
+ // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
1135
+ // [`map.createPane`](#map-createpane) method.
1136
+ //
1137
+ // Every map has the following default panes that differ only in zIndex.
1138
+ //
1139
+ // @pane mapPane: HTMLElement = 'auto'
1140
+ // Pane that contains all other map panes
1141
+
1142
+ this._mapPane = this.createPane('mapPane', this._container);
1143
+ DomUtil.setPosition(this._mapPane, new Point(0, 0));
1144
+
1145
+ // @pane tilePane: HTMLElement = 200
1146
+ // Pane for `GridLayer`s and `TileLayer`s
1147
+ this.createPane('tilePane');
1148
+ // @pane overlayPane: HTMLElement = 400
1149
+ // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
1150
+ this.createPane('overlayPane');
1151
+ // @pane shadowPane: HTMLElement = 500
1152
+ // Pane for overlay shadows (e.g. `Marker` shadows)
1153
+ this.createPane('shadowPane');
1154
+ // @pane markerPane: HTMLElement = 600
1155
+ // Pane for `Icon`s of `Marker`s
1156
+ this.createPane('markerPane');
1157
+ // @pane tooltipPane: HTMLElement = 650
1158
+ // Pane for `Tooltip`s.
1159
+ this.createPane('tooltipPane');
1160
+ // @pane popupPane: HTMLElement = 700
1161
+ // Pane for `Popup`s.
1162
+ this.createPane('popupPane');
1163
+
1164
+ if (!this.options.markerZoomAnimation) {
1165
+ DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
1166
+ DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
1167
+ }
1168
+ },
1169
+
1170
+
1171
+ // private methods that modify map state
1172
+
1173
+ // @section Map state change events
1174
+ _resetView: function (center, zoom, noMoveStart) {
1175
+ DomUtil.setPosition(this._mapPane, new Point(0, 0));
1176
+
1177
+ var loading = !this._loaded;
1178
+ this._loaded = true;
1179
+ zoom = this._limitZoom(zoom);
1180
+
1181
+ this.fire('viewprereset');
1182
+
1183
+ var zoomChanged = this._zoom !== zoom;
1184
+ this
1185
+ ._moveStart(zoomChanged, noMoveStart)
1186
+ ._move(center, zoom)
1187
+ ._moveEnd(zoomChanged);
1188
+
1189
+ // @event viewreset: Event
1190
+ // Fired when the map needs to redraw its content (this usually happens
1191
+ // on map zoom or load). Very useful for creating custom overlays.
1192
+ this.fire('viewreset');
1193
+
1194
+ // @event load: Event
1195
+ // Fired when the map is initialized (when its center and zoom are set
1196
+ // for the first time).
1197
+ if (loading) {
1198
+ this.fire('load');
1199
+ }
1200
+ },
1201
+
1202
+ _moveStart: function (zoomChanged, noMoveStart) {
1203
+ // @event zoomstart: Event
1204
+ // Fired when the map zoom is about to change (e.g. before zoom animation).
1205
+ // @event movestart: Event
1206
+ // Fired when the view of the map starts changing (e.g. user starts dragging the map).
1207
+ if (zoomChanged) {
1208
+ this.fire('zoomstart');
1209
+ }
1210
+ if (!noMoveStart) {
1211
+ this.fire('movestart');
1212
+ }
1213
+ return this;
1214
+ },
1215
+
1216
+ _move: function (center, zoom, data, supressEvent) {
1217
+ if (zoom === undefined) {
1218
+ zoom = this._zoom;
1219
+ }
1220
+ var zoomChanged = this._zoom !== zoom;
1221
+
1222
+ this._zoom = zoom;
1223
+ this._lastCenter = center;
1224
+ this._pixelOrigin = this._getNewPixelOrigin(center);
1225
+
1226
+ if (!supressEvent) {
1227
+ // @event zoom: Event
1228
+ // Fired repeatedly during any change in zoom level,
1229
+ // including zoom and fly animations.
1230
+ if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530
1231
+ this.fire('zoom', data);
1232
+ }
1233
+
1234
+ // @event move: Event
1235
+ // Fired repeatedly during any movement of the map,
1236
+ // including pan and fly animations.
1237
+ this.fire('move', data);
1238
+ } else if (data && data.pinch) { // Always fire 'zoom' if pinching because #3530
1239
+ this.fire('zoom', data);
1240
+ }
1241
+ return this;
1242
+ },
1243
+
1244
+ _moveEnd: function (zoomChanged) {
1245
+ // @event zoomend: Event
1246
+ // Fired when the map zoom changed, after any animations.
1247
+ if (zoomChanged) {
1248
+ this.fire('zoomend');
1249
+ }
1250
+
1251
+ // @event moveend: Event
1252
+ // Fired when the center of the map stops changing
1253
+ // (e.g. user stopped dragging the map or after non-centered zoom).
1254
+ return this.fire('moveend');
1255
+ },
1256
+
1257
+ _stop: function () {
1258
+ Util.cancelAnimFrame(this._flyToFrame);
1259
+ if (this._panAnim) {
1260
+ this._panAnim.stop();
1261
+ }
1262
+ return this;
1263
+ },
1264
+
1265
+ _rawPanBy: function (offset) {
1266
+ DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
1267
+ },
1268
+
1269
+ _getZoomSpan: function () {
1270
+ return this.getMaxZoom() - this.getMinZoom();
1271
+ },
1272
+
1273
+ _panInsideMaxBounds: function () {
1274
+ if (!this._enforcingBounds) {
1275
+ this.panInsideBounds(this.options.maxBounds);
1276
+ }
1277
+ },
1278
+
1279
+ _checkIfLoaded: function () {
1280
+ if (!this._loaded) {
1281
+ throw new Error('Set map center and zoom first.');
1282
+ }
1283
+ },
1284
+
1285
+ // DOM event handling
1286
+
1287
+ // @section Interaction events
1288
+ _initEvents: function (remove) {
1289
+ this._targets = {};
1290
+ this._targets[Util.stamp(this._container)] = this;
1291
+
1292
+ var onOff = remove ? DomEvent.off : DomEvent.on;
1293
+
1294
+ // @event click: MouseEvent
1295
+ // Fired when the user clicks (or taps) the map.
1296
+ // @event dblclick: MouseEvent
1297
+ // Fired when the user double-clicks (or double-taps) the map.
1298
+ // @event mousedown: MouseEvent
1299
+ // Fired when the user pushes the mouse button on the map.
1300
+ // @event mouseup: MouseEvent
1301
+ // Fired when the user releases the mouse button on the map.
1302
+ // @event mouseover: MouseEvent
1303
+ // Fired when the mouse enters the map.
1304
+ // @event mouseout: MouseEvent
1305
+ // Fired when the mouse leaves the map.
1306
+ // @event mousemove: MouseEvent
1307
+ // Fired while the mouse moves over the map.
1308
+ // @event contextmenu: MouseEvent
1309
+ // Fired when the user pushes the right mouse button on the map, prevents
1310
+ // default browser context menu from showing if there are listeners on
1311
+ // this event. Also fired on mobile when the user holds a single touch
1312
+ // for a second (also called long press).
1313
+ // @event keypress: KeyboardEvent
1314
+ // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
1315
+ // @event keydown: KeyboardEvent
1316
+ // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
1317
+ // the `keydown` event is fired for keys that produce a character value and for keys
1318
+ // that do not produce a character value.
1319
+ // @event keyup: KeyboardEvent
1320
+ // Fired when the user releases a key from the keyboard while the map is focused.
1321
+ onOff(this._container, 'click dblclick mousedown mouseup ' +
1322
+ 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
1323
+
1324
+ if (this.options.trackResize) {
1325
+ onOff(window, 'resize', this._onResize, this);
1326
+ }
1327
+
1328
+ if (Browser.any3d && this.options.transform3DLimit) {
1329
+ (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
1330
+ }
1331
+ },
1332
+
1333
+ _onResize: function () {
1334
+ Util.cancelAnimFrame(this._resizeRequest);
1335
+ this._resizeRequest = Util.requestAnimFrame(
1336
+ function () { this.invalidateSize({debounceMoveend: true}); }, this);
1337
+ },
1338
+
1339
+ _onScroll: function () {
1340
+ this._container.scrollTop = 0;
1341
+ this._container.scrollLeft = 0;
1342
+ },
1343
+
1344
+ _onMoveEnd: function () {
1345
+ var pos = this._getMapPanePos();
1346
+ if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
1347
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
1348
+ // a pixel offset on very high values, see: https://jsfiddle.net/dg6r5hhb/
1349
+ this._resetView(this.getCenter(), this.getZoom());
1350
+ }
1351
+ },
1352
+
1353
+ _findEventTargets: function (e, type) {
1354
+ var targets = [],
1355
+ target,
1356
+ isHover = type === 'mouseout' || type === 'mouseover',
1357
+ src = e.target || e.srcElement,
1358
+ dragging = false;
1359
+
1360
+ while (src) {
1361
+ target = this._targets[Util.stamp(src)];
1362
+ if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
1363
+ // Prevent firing click after you just dragged an object.
1364
+ dragging = true;
1365
+ break;
1366
+ }
1367
+ if (target && target.listens(type, true)) {
1368
+ if (isHover && !DomEvent.isExternalTarget(src, e)) { break; }
1369
+ targets.push(target);
1370
+ if (isHover) { break; }
1371
+ }
1372
+ if (src === this._container) { break; }
1373
+ src = src.parentNode;
1374
+ }
1375
+ if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
1376
+ targets = [this];
1377
+ }
1378
+ return targets;
1379
+ },
1380
+
1381
+ _isClickDisabled: function (el) {
1382
+ while (el && el !== this._container) {
1383
+ if (el['_leaflet_disable_click']) { return true; }
1384
+ el = el.parentNode;
1385
+ }
1386
+ },
1387
+
1388
+ _handleDOMEvent: function (e) {
1389
+ var el = (e.target || e.srcElement);
1390
+ if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
1391
+ return;
1392
+ }
1393
+
1394
+ var type = e.type;
1395
+
1396
+ if (type === 'mousedown') {
1397
+ // prevents outline when clicking on keyboard-focusable element
1398
+ DomUtil.preventOutline(el);
1399
+ }
1400
+
1401
+ this._fireDOMEvent(e, type);
1402
+ },
1403
+
1404
+ _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
1405
+
1406
+ _fireDOMEvent: function (e, type, canvasTargets) {
1407
+
1408
+ if (e.type === 'click') {
1409
+ // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
1410
+ // @event preclick: MouseEvent
1411
+ // Fired before mouse click on the map (sometimes useful when you
1412
+ // want something to happen on click before any existing click
1413
+ // handlers start running).
1414
+ var synth = Util.extend({}, e);
1415
+ synth.type = 'preclick';
1416
+ this._fireDOMEvent(synth, synth.type, canvasTargets);
1417
+ }
1418
+
1419
+ // Find the layer the event is propagating from and its parents.
1420
+ var targets = this._findEventTargets(e, type);
1421
+
1422
+ if (canvasTargets) {
1423
+ var filtered = []; // pick only targets with listeners
1424
+ for (var i = 0; i < canvasTargets.length; i++) {
1425
+ if (canvasTargets[i].listens(type, true)) {
1426
+ filtered.push(canvasTargets[i]);
1427
+ }
1428
+ }
1429
+ targets = filtered.concat(targets);
1430
+ }
1431
+
1432
+ if (!targets.length) { return; }
1433
+
1434
+ if (type === 'contextmenu') {
1435
+ DomEvent.preventDefault(e);
1436
+ }
1437
+
1438
+ var target = targets[0];
1439
+ var data = {
1440
+ originalEvent: e
1441
+ };
1442
+
1443
+ if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
1444
+ var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
1445
+ data.containerPoint = isMarker ?
1446
+ this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
1447
+ data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
1448
+ data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
1449
+ }
1450
+
1451
+ for (i = 0; i < targets.length; i++) {
1452
+ targets[i].fire(type, data, true);
1453
+ if (data.originalEvent._stopped ||
1454
+ (targets[i].options.bubblingMouseEvents === false && Util.indexOf(this._mouseEvents, type) !== -1)) { return; }
1455
+ }
1456
+ },
1457
+
1458
+ _draggableMoved: function (obj) {
1459
+ obj = obj.dragging && obj.dragging.enabled() ? obj : this;
1460
+ return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
1461
+ },
1462
+
1463
+ _clearHandlers: function () {
1464
+ for (var i = 0, len = this._handlers.length; i < len; i++) {
1465
+ this._handlers[i].disable();
1466
+ }
1467
+ },
1468
+
1469
+ // @section Other Methods
1470
+
1471
+ // @method whenReady(fn: Function, context?: Object): this
1472
+ // Runs the given function `fn` when the map gets initialized with
1473
+ // a view (center and zoom) and at least one layer, or immediately
1474
+ // if it's already initialized, optionally passing a function context.
1475
+ whenReady: function (callback, context) {
1476
+ if (this._loaded) {
1477
+ callback.call(context || this, {target: this});
1478
+ } else {
1479
+ this.on('load', callback, context);
1480
+ }
1481
+ return this;
1482
+ },
1483
+
1484
+
1485
+ // private methods for getting map state
1486
+
1487
+ _getMapPanePos: function () {
1488
+ return DomUtil.getPosition(this._mapPane) || new Point(0, 0);
1489
+ },
1490
+
1491
+ _moved: function () {
1492
+ var pos = this._getMapPanePos();
1493
+ return pos && !pos.equals([0, 0]);
1494
+ },
1495
+
1496
+ _getTopLeftPoint: function (center, zoom) {
1497
+ var pixelOrigin = center && zoom !== undefined ?
1498
+ this._getNewPixelOrigin(center, zoom) :
1499
+ this.getPixelOrigin();
1500
+ return pixelOrigin.subtract(this._getMapPanePos());
1501
+ },
1502
+
1503
+ _getNewPixelOrigin: function (center, zoom) {
1504
+ var viewHalf = this.getSize()._divideBy(2);
1505
+ return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
1506
+ },
1507
+
1508
+ _latLngToNewLayerPoint: function (latlng, zoom, center) {
1509
+ var topLeft = this._getNewPixelOrigin(center, zoom);
1510
+ return this.project(latlng, zoom)._subtract(topLeft);
1511
+ },
1512
+
1513
+ _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
1514
+ var topLeft = this._getNewPixelOrigin(center, zoom);
1515
+ return toBounds([
1516
+ this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
1517
+ this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
1518
+ this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
1519
+ this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
1520
+ ]);
1521
+ },
1522
+
1523
+ // layer point of the current center
1524
+ _getCenterLayerPoint: function () {
1525
+ return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
1526
+ },
1527
+
1528
+ // offset of the specified place to the current center in pixels
1529
+ _getCenterOffset: function (latlng) {
1530
+ return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
1531
+ },
1532
+
1533
+ // adjust center for view to get inside bounds
1534
+ _limitCenter: function (center, zoom, bounds) {
1535
+
1536
+ if (!bounds) { return center; }
1537
+
1538
+ var centerPoint = this.project(center, zoom),
1539
+ viewHalf = this.getSize().divideBy(2),
1540
+ viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
1541
+ offset = this._getBoundsOffset(viewBounds, bounds, zoom);
1542
+
1543
+ // If offset is less than a pixel, ignore.
1544
+ // This prevents unstable projections from getting into
1545
+ // an infinite loop of tiny offsets.
1546
+ if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
1547
+ return center;
1548
+ }
1549
+
1550
+ return this.unproject(centerPoint.add(offset), zoom);
1551
+ },
1552
+
1553
+ // adjust offset for view to get inside bounds
1554
+ _limitOffset: function (offset, bounds) {
1555
+ if (!bounds) { return offset; }
1556
+
1557
+ var viewBounds = this.getPixelBounds(),
1558
+ newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
1559
+
1560
+ return offset.add(this._getBoundsOffset(newBounds, bounds));
1561
+ },
1562
+
1563
+ // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
1564
+ _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
1565
+ var projectedMaxBounds = toBounds(
1566
+ this.project(maxBounds.getNorthEast(), zoom),
1567
+ this.project(maxBounds.getSouthWest(), zoom)
1568
+ ),
1569
+ minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
1570
+ maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
1571
+
1572
+ dx = this._rebound(minOffset.x, -maxOffset.x),
1573
+ dy = this._rebound(minOffset.y, -maxOffset.y);
1574
+
1575
+ return new Point(dx, dy);
1576
+ },
1577
+
1578
+ _rebound: function (left, right) {
1579
+ return left + right > 0 ?
1580
+ Math.round(left - right) / 2 :
1581
+ Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
1582
+ },
1583
+
1584
+ _limitZoom: function (zoom) {
1585
+ var min = this.getMinZoom(),
1586
+ max = this.getMaxZoom(),
1587
+ snap = Browser.any3d ? this.options.zoomSnap : 1;
1588
+ if (snap) {
1589
+ zoom = Math.round(zoom / snap) * snap;
1590
+ }
1591
+ return Math.max(min, Math.min(max, zoom));
1592
+ },
1593
+
1594
+ _onPanTransitionStep: function () {
1595
+ this.fire('move');
1596
+ },
1597
+
1598
+ _onPanTransitionEnd: function () {
1599
+ DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
1600
+ this.fire('moveend');
1601
+ },
1602
+
1603
+ _tryAnimatedPan: function (center, options) {
1604
+ // difference between the new and current centers in pixels
1605
+ var offset = this._getCenterOffset(center)._trunc();
1606
+
1607
+ // don't animate too far unless animate: true specified in options
1608
+ if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
1609
+
1610
+ this.panBy(offset, options);
1611
+
1612
+ return true;
1613
+ },
1614
+
1615
+ _createAnimProxy: function () {
1616
+
1617
+ var proxy = this._proxy = DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
1618
+ this._panes.mapPane.appendChild(proxy);
1619
+
1620
+ this.on('zoomanim', function (e) {
1621
+ var prop = DomUtil.TRANSFORM,
1622
+ transform = this._proxy.style[prop];
1623
+
1624
+ DomUtil.setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
1625
+
1626
+ // workaround for case when transform is the same and so transitionend event is not fired
1627
+ if (transform === this._proxy.style[prop] && this._animatingZoom) {
1628
+ this._onZoomTransitionEnd();
1629
+ }
1630
+ }, this);
1631
+
1632
+ this.on('load moveend', this._animMoveEnd, this);
1633
+
1634
+ this._on('unload', this._destroyAnimProxy, this);
1635
+ },
1636
+
1637
+ _destroyAnimProxy: function () {
1638
+ DomUtil.remove(this._proxy);
1639
+ this.off('load moveend', this._animMoveEnd, this);
1640
+ delete this._proxy;
1641
+ },
1642
+
1643
+ _animMoveEnd: function () {
1644
+ var c = this.getCenter(),
1645
+ z = this.getZoom();
1646
+ DomUtil.setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
1647
+ },
1648
+
1649
+ _catchTransitionEnd: function (e) {
1650
+ if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
1651
+ this._onZoomTransitionEnd();
1652
+ }
1653
+ },
1654
+
1655
+ _nothingToAnimate: function () {
1656
+ return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
1657
+ },
1658
+
1659
+ _tryAnimatedZoom: function (center, zoom, options) {
1660
+
1661
+ if (this._animatingZoom) { return true; }
1662
+
1663
+ options = options || {};
1664
+
1665
+ // don't animate if disabled, not supported or zoom difference is too large
1666
+ if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
1667
+ Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
1668
+
1669
+ // offset is the pixel coords of the zoom origin relative to the current center
1670
+ var scale = this.getZoomScale(zoom),
1671
+ offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
1672
+
1673
+ // don't animate if the zoom origin isn't within one screen from the current center, unless forced
1674
+ if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
1675
+
1676
+ Util.requestAnimFrame(function () {
1677
+ this
1678
+ ._moveStart(true, options.noMoveStart || false)
1679
+ ._animateZoom(center, zoom, true);
1680
+ }, this);
1681
+
1682
+ return true;
1683
+ },
1684
+
1685
+ _animateZoom: function (center, zoom, startAnim, noUpdate) {
1686
+ if (!this._mapPane) { return; }
1687
+
1688
+ if (startAnim) {
1689
+ this._animatingZoom = true;
1690
+
1691
+ // remember what center/zoom to set after animation
1692
+ this._animateToCenter = center;
1693
+ this._animateToZoom = zoom;
1694
+
1695
+ DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
1696
+ }
1697
+
1698
+ // @section Other Events
1699
+ // @event zoomanim: ZoomAnimEvent
1700
+ // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
1701
+ this.fire('zoomanim', {
1702
+ center: center,
1703
+ zoom: zoom,
1704
+ noUpdate: noUpdate
1705
+ });
1706
+
1707
+ if (!this._tempFireZoomEvent) {
1708
+ this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
1709
+ }
1710
+
1711
+ this._move(this._animateToCenter, this._animateToZoom, undefined, true);
1712
+
1713
+ // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
1714
+ setTimeout(Util.bind(this._onZoomTransitionEnd, this), 250);
1715
+ },
1716
+
1717
+ _onZoomTransitionEnd: function () {
1718
+ if (!this._animatingZoom) { return; }
1719
+
1720
+ if (this._mapPane) {
1721
+ DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
1722
+ }
1723
+
1724
+ this._animatingZoom = false;
1725
+
1726
+ this._move(this._animateToCenter, this._animateToZoom, undefined, true);
1727
+
1728
+ if (this._tempFireZoomEvent) {
1729
+ this.fire('zoom');
1730
+ }
1731
+ delete this._tempFireZoomEvent;
1732
+
1733
+ this.fire('move');
1734
+
1735
+ this._moveEnd(true);
1736
+ }
1737
+ });
1738
+
1739
+ // @section
1740
+
1741
+ // @factory L.map(id: String, options?: Map options)
1742
+ // Instantiates a map object given the DOM ID of a `<div>` element
1743
+ // and optionally an object literal with `Map options`.
1744
+ //
1745
+ // @alternative
1746
+ // @factory L.map(el: HTMLElement, options?: Map options)
1747
+ // Instantiates a map object given an instance of a `<div>` HTML element
1748
+ // and optionally an object literal with `Map options`.
1749
+ export function createMap(id, options) {
1750
+ return new Map(id, options);
1751
+ }