leaflet-rails 1.0.0.pre.rc1 → 1.0.0.pre.rc2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d66ece8341a5d7a5253b4d31691fcaeb80eeccf
4
- data.tar.gz: 8d9fdd1150bf20b83b41ed57471a4b77bfac6e20
3
+ metadata.gz: b92eef4dc68d876290568325017e802bbf6a0f11
4
+ data.tar.gz: d4167cf1724cb85dff40b7148f31af902dbfb6dc
5
5
  SHA512:
6
- metadata.gz: d5402fe07724c9f282508e9a277d9a0874a555cdbddc9291184a1029f499a56f0dca89fcdf579b8c4a537d2058dd0903e9a89b0db029ff4fd12b16d45c88920c
7
- data.tar.gz: 20d7f7cbc861252597048b844cc46b4614fec28b62c2cc8ec43d723aeba04235fd5869cf29f97c865635b218531ceda24f1beacf7ebb4af4c2102bd436ea8514
6
+ metadata.gz: 4b86c672ff65df137a82cc98887a28a87918d6e65fe5cdddfeae322fc4c95214dab7eb8338c999516a2280598308e01397473b9265ad4455dce5a71017d5f21a
7
+ data.tar.gz: 8d21a8ab52b96e3d3d15013eaec15802609506f10432d6fd8702bf40a5301702e72f19a4d68500a4cead7816e2a2f0112f93371c0e43aff382e262731b55bcc5
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
9
9
  s.email = ["joshi.a@gmail.com"]
10
10
  s.license = "BSD"
11
11
  s.homepage = ""
12
- s.summary = %q{Use leaflet.js with Rails 4.}
13
- s.description = %q{This gem provides the leaflet.js map display library for your Rails 4 application.}
12
+ s.summary = %q{Use leaflet.js with Rails 4/5.}
13
+ s.description = %q{This gem provides the leaflet.js map display library for your Rails 4/5 application.}
14
14
 
15
15
  s.rubyforge_project = "leaflet-rails"
16
16
 
@@ -1,5 +1,5 @@
1
1
  module Leaflet
2
2
  module Rails
3
- VERSION = "1.0.0-rc1"
3
+ VERSION = "1.0.0-rc2"
4
4
  end
5
5
  end
@@ -40,7 +40,7 @@ module Leaflet
40
40
  output << "marker = L.marker([#{marker[:latlng][0]}, #{marker[:latlng][1]}]).addTo(map);"
41
41
  end
42
42
  if marker[:popup]
43
- output << "marker.bindPopup('#{marker[:popup]}');"
43
+ output << "marker.bindPopup('#{escape_javascript marker[:popup]}');"
44
44
  end
45
45
  end
46
46
  end
@@ -72,9 +72,8 @@ module Leaflet
72
72
  attribution: '#{attribution}',
73
73
  maxZoom: #{max_zoom},"
74
74
 
75
- if options[:subdomains]
76
- output << " subdomains: #{options[:subdomains]},"
77
- options.delete( :subdomains )
75
+ if subdomains = options.delete(:subdomains)
76
+ output << " subdomains: #{subdomains},"
78
77
  end
79
78
 
80
79
  options.each do |key, value|
@@ -27,6 +27,22 @@ describe Leaflet::ViewHelpers do
27
27
  expect(result).to match(/attribution: 'Some attribution statement'/)
28
28
  expect(result).to match(/maxZoom: 18/)
29
29
  end
30
+
31
+ it 'should set subdomains if present' do
32
+ result = @view.map(:center => {
33
+ :latlng => [51.52238797921441, -0.08366235665359283],
34
+ :zoom => 18
35
+ }, :subdomains => ['otile1', 'otile2'])
36
+ expect(result).to match(/subdomains: \["otile1", "otile2"\]/)
37
+ end
38
+
39
+ it 'should not set subdomains if nil' do
40
+ result = @view.map(:center => {
41
+ :latlng => [51.52238797921441, -0.08366235665359283],
42
+ :zoom => 18
43
+ }, :subdomains => nil)
44
+ expect(result).not_to match(/subdomains:/)
45
+ end
30
46
 
31
47
  it 'should generate a basic map with the correct latitude, longitude and zoom' do
32
48
  result = @view.map(:center => {
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,10 +1,10 @@
1
1
  /*
2
- Leaflet 1.0.0-rc.1 (a626826), a JS library for interactive maps. http://leafletjs.com
2
+ Leaflet 1.0.0-rc.2, a JS library for interactive maps. http://leafletjs.com
3
3
  (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4
4
  */
5
5
  (function (window, document, undefined) {
6
6
  var L = {
7
- version: '1.0.0-rc.1'
7
+ version: "1.0.0-rc.2"
8
8
  };
9
9
 
10
10
  function expose() {
@@ -98,7 +98,11 @@ L.Util = {
98
98
 
99
99
  // @function throttle(fn: Function, time: Number, context: Object): Function
100
100
  // Returns a function which executes function `fn` with the given scope `context`
101
- // (so that the `this` keyword refers to `context` inside `fn`'s code). The arguments received by the bound function will be any arguments passed when binding the function, followed by any arguments passed when invoking the bound function. Has an `L.bind` shortcut.
101
+ // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
102
+ // `fn` will be called no more than one time per given amount of `time`. The arguments
103
+ // received by the bound function will be any arguments passed when binding the
104
+ // function, followed by any arguments passed when invoking the bound function.
105
+ // Has an `L.bind` shortcut.
102
106
  throttle: function (fn, time, context) {
103
107
  var lock, args, wrapperFn, later;
104
108
 
@@ -161,7 +165,7 @@ L.Util = {
161
165
  return L.Util.trim(str).split(/\s+/);
162
166
  },
163
167
 
164
- // @function setOptions(obj: Object: options: Object): Object
168
+ // @function setOptions(obj: Object, options: Object): Object
165
169
  // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
166
170
  setOptions: function (obj, options) {
167
171
  if (!obj.hasOwnProperty('options')) {
@@ -186,7 +190,7 @@ L.Util = {
186
190
  return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
187
191
  },
188
192
 
189
- // @template (str: String, data: Object)
193
+ // @function template(str: String, data: Object): String
190
194
  // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
191
195
  // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
192
196
  // `('Hello foo, bar')`. You can also specify functions instead of strings for
@@ -213,7 +217,7 @@ L.Util = {
213
217
  return (Object.prototype.toString.call(obj) === '[object Array]');
214
218
  },
215
219
 
216
- // @function indexOf: Number
220
+ // @function indexOf(array: Array, el: Object): Number
217
221
  // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
218
222
  indexOf: function (array, el) {
219
223
  for (var i = 0; i < array.length; i++) {
@@ -252,12 +256,12 @@ L.Util = {
252
256
  getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
253
257
 
254
258
 
255
- // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): requestId: Number
259
+ // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
256
260
  // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
257
261
  // `context` if given. When `immediate` is set, `fn` is called immediately if
258
262
  // the browser doesn't have native support for
259
263
  // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
260
- // otherwise it's delayed. Returns an id that can be used to cancel the request.
264
+ // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
261
265
  L.Util.requestAnimFrame = function (fn, context, immediate) {
262
266
  if (immediate && requestFn === timeoutDefer) {
263
267
  fn.call(context);
@@ -266,7 +270,7 @@ L.Util = {
266
270
  }
267
271
  };
268
272
 
269
- // @function cancelAnimFrame(id: Number)
273
+ // @function cancelAnimFrame(id: Number): undefined
270
274
  // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
271
275
  L.Util.cancelAnimFrame = function (id) {
272
276
  if (id) {
@@ -366,21 +370,21 @@ L.Class.extend = function (props) {
366
370
  };
367
371
 
368
372
 
369
- // @function include(properties: Object)
373
+ // @function include(properties: Object): this
370
374
  // [Includes a mixin](#class-includes) into the current class.
371
375
  L.Class.include = function (props) {
372
376
  L.extend(this.prototype, props);
373
377
  return this;
374
378
  };
375
379
 
376
- // @function mergeOptions(options: Object)
380
+ // @function mergeOptions(options: Object): this
377
381
  // [Merges `options`](#class-options) into the defaults of the class.
378
382
  L.Class.mergeOptions = function (options) {
379
383
  L.extend(this.prototype.options, options);
380
384
  return this;
381
385
  };
382
386
 
383
- // @function addInitHook(fn: Function)
387
+ // @function addInitHook(fn: Function): this
384
388
  // Adds a [constructor hook](#class-constructor-hooks) to the class.
385
389
  L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
386
390
  var args = Array.prototype.slice.call(arguments, 1);
@@ -488,81 +492,110 @@ L.Evented = L.Class.extend({
488
492
 
489
493
  // attach listener (without syntactic sugar now)
490
494
  _on: function (type, fn, context) {
495
+ this._events = this._events || {};
496
+
497
+ /* get/init listeners for type */
498
+ var typeListeners = this._events[type];
499
+ if (!typeListeners) {
500
+ typeListeners = {
501
+ listeners: {},
502
+ count: 0
503
+ };
504
+ this._events[type] = typeListeners;
505
+ }
491
506
 
492
- var events = this._events = this._events || {},
493
- contextId = context && context !== this && L.stamp(context);
494
-
495
- if (contextId) {
496
- // store listeners with custom context in a separate hash (if it has an id);
497
- // gives a major performance boost when firing and removing events (e.g. on map object)
507
+ var contextId = context && context !== this && L.stamp(context),
508
+ newListener = {fn: fn, ctx: context};
498
509
 
499
- var indexKey = type + '_idx',
500
- indexLenKey = type + '_len',
501
- typeIndex = events[indexKey] = events[indexKey] || {},
502
- id = L.stamp(fn) + '_' + contextId;
510
+ if (!contextId) {
511
+ contextId = 'no_context';
512
+ newListener.ctx = undefined;
513
+ }
503
514
 
504
- if (!typeIndex[id]) {
505
- typeIndex[id] = {fn: fn, ctx: context};
515
+ // fn array for context
516
+ var listeners = typeListeners.listeners[contextId];
517
+ if (!listeners) {
518
+ listeners = [];
519
+ typeListeners.listeners[contextId] = listeners;
520
+ }
506
521
 
507
- // keep track of the number of keys in the index to quickly check if it's empty
508
- events[indexLenKey] = (events[indexLenKey] || 0) + 1;
522
+ // check if fn already there
523
+ for (var i = 0, len = listeners.length; i < len; i++) {
524
+ if (listeners[i].fn === fn) {
525
+ return;
509
526
  }
510
-
511
- } else {
512
- // individual layers mostly use "this" for context and don't fire listeners too often
513
- // so simple array makes the memory footprint better while not degrading performance
514
-
515
- events[type] = events[type] || [];
516
- events[type].push({fn: fn});
517
527
  }
528
+
529
+ listeners.push(newListener);
530
+ typeListeners.count++;
518
531
  },
519
532
 
520
533
  _off: function (type, fn, context) {
521
- var events = this._events,
522
- indexKey = type + '_idx',
523
- indexLenKey = type + '_len';
534
+ var typeListeners,
535
+ contextId,
536
+ listeners,
537
+ i,
538
+ len;
524
539
 
525
- if (!events) { return; }
540
+ if (!this._events) { return; }
526
541
 
527
542
  if (!fn) {
528
- // clear all listeners for a type if function isn't specified
529
- delete events[type];
530
- delete events[indexKey];
531
- delete events[indexLenKey];
543
+ // Set all removed listeners to noop so they are not called if remove happens in fire
544
+ typeListeners = this._events[type];
545
+ if (typeListeners) {
546
+ for (contextId in typeListeners.listeners) {
547
+ listeners = typeListeners.listeners[contextId];
548
+ for (i = 0, len = listeners.length; i < len; i++) {
549
+ listeners[i].fn = L.Util.falseFn;
550
+ }
551
+ }
552
+ // clear all listeners for a type if function isn't specified
553
+ delete this._events[type];
554
+ }
532
555
  return;
533
556
  }
534
557
 
535
- var contextId = context && context !== this && L.stamp(context),
536
- listeners, i, len, listener, id;
558
+ typeListeners = this._events[type];
559
+ if (!typeListeners) {
560
+ return;
561
+ }
537
562
 
538
- if (contextId) {
539
- id = L.stamp(fn) + '_' + contextId;
540
- listeners = events[indexKey];
563
+ contextId = context && context !== this && L.stamp(context);
564
+ if (!contextId) {
565
+ contextId = 'no_context';
566
+ }
541
567
 
542
- if (listeners && listeners[id]) {
543
- listener = listeners[id];
544
- delete listeners[id];
545
- events[indexLenKey]--;
546
- }
568
+ listeners = typeListeners.listeners[contextId];
569
+ if (listeners) {
547
570
 
548
- } else {
549
- listeners = events[type];
550
-
551
- if (listeners) {
552
- for (i = 0, len = listeners.length; i < len; i++) {
553
- if (listeners[i].fn === fn) {
554
- listener = listeners[i];
555
- listeners.splice(i, 1);
556
- break;
571
+ // find fn and remove it
572
+ for (i = 0, len = listeners.length; i < len; i++) {
573
+ var l = listeners[i];
574
+ if (l.fn === fn) {
575
+
576
+ // set the removed listener to noop so that's not called if remove happens in fire
577
+ l.fn = L.Util.falseFn;
578
+ typeListeners.count--;
579
+
580
+ if (len > 1) {
581
+ if (!this._isFiring) {
582
+ listeners.splice(i, 1);
583
+ } else {
584
+ /* copy array in case events are being fired */
585
+ typeListeners.listeners[contextId] = listeners.slice();
586
+ typeListeners.listeners[contextId].splice(i, 1);
587
+ }
588
+ } else {
589
+ delete typeListeners.listeners[contextId];
557
590
  }
591
+
592
+ return;
593
+ }
594
+ if (listeners.length === 0) {
595
+ delete typeListeners.listeners[contextId];
558
596
  }
559
597
  }
560
598
  }
561
-
562
- // set the removed listener to noop so that's not called if remove happens in fire
563
- if (listener) {
564
- listener.fn = L.Util.falseFn;
565
- }
566
599
  },
567
600
 
568
601
  // @method fire(type: String, data?: Object, propagate?: Boolean): this
@@ -572,25 +605,26 @@ L.Evented = L.Class.extend({
572
605
  fire: function (type, data, propagate) {
573
606
  if (!this.listens(type, propagate)) { return this; }
574
607
 
575
- var event = L.Util.extend({}, data, {type: type, target: this}),
576
- events = this._events;
608
+ var event = L.Util.extend({}, data, {type: type, target: this});
609
+
610
+ if (this._events) {
611
+ var typeListeners = this._events[type];
577
612
 
578
- if (events) {
579
- var typeIndex = events[type + '_idx'],
580
- i, len, listeners, id;
613
+ if (typeListeners) {
614
+ this._isFiring = true;
581
615
 
582
- if (events[type]) {
583
- // make sure adding/removing listeners inside other listeners won't cause infinite loop
584
- listeners = events[type].slice();
616
+ // each context
617
+ for (var contextId in typeListeners.listeners) {
618
+ var listeners = typeListeners.listeners[contextId];
585
619
 
586
- for (i = 0, len = listeners.length; i < len; i++) {
587
- listeners[i].fn.call(this, event);
620
+ // each fn in context
621
+ for (var i = 0, len = listeners.length; i < len; i++) {
622
+ var l = listeners[i];
623
+ l.fn.call(l.ctx || this, event);
624
+ }
588
625
  }
589
- }
590
626
 
591
- // fire event for the context-indexed listeners as well
592
- for (id in typeIndex) {
593
- typeIndex[id].fn.call(typeIndex[id].ctx, event);
627
+ this._isFiring = false;
594
628
  }
595
629
  }
596
630
 
@@ -605,9 +639,8 @@ L.Evented = L.Class.extend({
605
639
  // @method listens(type: String): Boolean
606
640
  // Returns `true` if a particular event type has any listeners attached to it.
607
641
  listens: function (type, propagate) {
608
- var events = this._events;
609
-
610
- if (events && (events[type] || events[type + '_len'])) { return true; }
642
+ var typeListeners = this._events && this._events[type];
643
+ if (typeListeners && typeListeners.count) { return true; }
611
644
 
612
645
  if (propagate) {
613
646
  // also check parents for listeners if event propagates
@@ -724,6 +757,8 @@ L.Mixin = {Events: proto};
724
757
  chrome = ua.indexOf('chrome') !== -1,
725
758
  gecko = ua.indexOf('gecko') !== -1 && !webkit && !window.opera && !ie,
726
759
 
760
+ win = navigator.platform.indexOf('Win') === 0,
761
+
727
762
  mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
728
763
  msPointer = !window.PointerEvent && window.MSPointerEvent,
729
764
  pointer = window.PointerEvent || msPointer,
@@ -733,6 +768,7 @@ L.Mixin = {Events: proto};
733
768
  gecko3d = 'MozPerspective' in doc.style,
734
769
  opera12 = 'OTransition' in doc.style;
735
770
 
771
+
736
772
  var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
737
773
  (window.DocumentTouch && document instanceof window.DocumentTouch));
738
774
 
@@ -775,6 +811,11 @@ L.Mixin = {Events: proto};
775
811
  safari: !chrome && ua.indexOf('safari') !== -1,
776
812
 
777
813
 
814
+ // @property win: Boolean
815
+ // `true` when the browser is running in a Windows platform
816
+ win: win,
817
+
818
+
778
819
  // @property ie3d: Boolean
779
820
  // `true` for all Internet Explorer versions supporting CSS transforms.
780
821
  ie3d: ie3d,
@@ -1018,6 +1059,10 @@ L.Point.prototype = {
1018
1059
  // @alternative
1019
1060
  // @factory L.point(coords: Number[])
1020
1061
  // Expects an array of the form `[x, y]` instead.
1062
+
1063
+ // @alternative
1064
+ // @factory L.point(coords: Object)
1065
+ // Expects a plain object of the form `{x: Number, y: Number}` instead.
1021
1066
  L.point = function (x, y, round) {
1022
1067
  if (x instanceof L.Point) {
1023
1068
  return x;
@@ -1028,6 +1073,9 @@ L.point = function (x, y, round) {
1028
1073
  if (x === undefined || x === null) {
1029
1074
  return x;
1030
1075
  }
1076
+ if (typeof x === 'object' && 'x' in x && 'y' in x) {
1077
+ return new L.Point(x.x, x.y);
1078
+ }
1031
1079
  return new L.Point(x, y, round);
1032
1080
  };
1033
1081
 
@@ -1116,7 +1164,7 @@ L.Bounds.prototype = {
1116
1164
  // Returns `true` if the rectangle contains the given one.
1117
1165
  // @alternative
1118
1166
  // @method contains(point: Point): Boolean
1119
- // Returns `true` if the rectangle contains the given poing.
1167
+ // Returns `true` if the rectangle contains the given point.
1120
1168
  contains: function (obj) {
1121
1169
  var min, max;
1122
1170
 
@@ -1220,7 +1268,7 @@ L.Transformation = function (a, b, c, d) {
1220
1268
  };
1221
1269
 
1222
1270
  L.Transformation.prototype = {
1223
- // @method transform(point: Point, scale?: Number)
1271
+ // @method transform(point: Point, scale?: Number): Point
1224
1272
  // Returns a transformed point, optionally multiplied by the given scale.
1225
1273
  // Only accepts real `L.Point` instances, not arrays.
1226
1274
  transform: function (point, scale) { // (Point, Number) -> Point
@@ -1235,7 +1283,7 @@ L.Transformation.prototype = {
1235
1283
  return point;
1236
1284
  },
1237
1285
 
1238
- // @method untransform(point: Point, scale?: Number)
1286
+ // @method untransform(point: Point, scale?: Number): Point
1239
1287
  // Returns the reverse transformation of the given point, optionally divided
1240
1288
  // by the given scale. Only accepts real `L.Point` instances, not arrays.
1241
1289
  untransform: function (point, scale) {
@@ -1288,7 +1336,7 @@ L.DomUtil = {
1288
1336
  create: function (tagName, className, container) {
1289
1337
 
1290
1338
  var el = document.createElement(tagName);
1291
- el.className = className;
1339
+ el.className = className || '';
1292
1340
 
1293
1341
  if (container) {
1294
1342
  container.appendChild(el);
@@ -2022,7 +2070,7 @@ L.Projection.SphericalMercator = {
2022
2070
  * @aka L.CRS
2023
2071
  * Abstract class that defines coordinate reference systems for projecting
2024
2072
  * geographical points into pixel (screen) coordinates and back (and to
2025
- * coordinates in other units for WMS services). See
2073
+ * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
2026
2074
  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
2027
2075
  *
2028
2076
  * Leaflet defines the most usual CRSs by default. If you want to use a
@@ -2092,6 +2140,9 @@ L.CRS = {
2092
2140
  return L.bounds(min, max);
2093
2141
  },
2094
2142
 
2143
+ // @method distance(latlng1: LatLng, latlng1: LatLng): Number
2144
+ // Returns the distance between two geographical coordinates.
2145
+
2095
2146
  // @property code: String
2096
2147
  // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
2097
2148
  //
@@ -2130,7 +2181,8 @@ L.CRS = {
2130
2181
  *
2131
2182
  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
2132
2183
  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
2133
- * axis should still be inverted (going from bottom to top).
2184
+ * axis should still be inverted (going from bottom to top). `distance()` returns
2185
+ * simple euclidean distance.
2134
2186
  */
2135
2187
 
2136
2188
  L.CRS.Simple = L.extend({}, L.CRS, {
@@ -2163,7 +2215,8 @@ L.CRS.Simple = L.extend({}, L.CRS, {
2163
2215
  *
2164
2216
  * Serves as the base for CRS that are global such that they cover the earth.
2165
2217
  * Can only be used as the base for other CRS and cannot be used directly,
2166
- * since it does not have a `code`, `projection` or `transformation`.
2218
+ * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
2219
+ * meters.
2167
2220
  */
2168
2221
 
2169
2222
  L.CRS.Earth = L.extend({}, L.CRS, {
@@ -2364,7 +2417,7 @@ L.Map = L.Evented.extend({
2364
2417
 
2365
2418
  // @section Methods for modifying map state
2366
2419
 
2367
- // @method setView(center: LatLnt, zoom: Number, options?: Zoom/Pan options): this
2420
+ // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
2368
2421
  // Sets the view of the map (geographical center and zoom) with the given
2369
2422
  // animation options.
2370
2423
  setView: function (center, zoom) {
@@ -2374,7 +2427,7 @@ L.Map = L.Evented.extend({
2374
2427
  return this;
2375
2428
  },
2376
2429
 
2377
- // @method setZoom(zoom: Number, options: Zoom/Pan options): this
2430
+ // @method setZoom(zoom: Number, options: Zoom/pan options): this
2378
2431
  // Sets the zoom of the map.
2379
2432
  setZoom: function (zoom, options) {
2380
2433
  if (!this._loaded) {
@@ -2484,7 +2537,8 @@ L.Map = L.Evented.extend({
2484
2537
  setMaxBounds: function (bounds) {
2485
2538
  bounds = L.latLngBounds(bounds);
2486
2539
 
2487
- if (!bounds) {
2540
+ if (!bounds.isValid()) {
2541
+ this.options.maxBounds = null;
2488
2542
  return this.off('moveend', this._panInsideMaxBounds);
2489
2543
  } else if (this.options.maxBounds) {
2490
2544
  this.off('moveend', this._panInsideMaxBounds);
@@ -2589,7 +2643,7 @@ L.Map = L.Evented.extend({
2589
2643
  }
2590
2644
 
2591
2645
  // @section Map state change events
2592
- // @event resize: Event
2646
+ // @event resize: ResizeEvent
2593
2647
  // Fired when the map is resized.
2594
2648
  return this.fire('resize', {
2595
2649
  oldSize: oldSize,
@@ -2665,7 +2719,7 @@ L.Map = L.Evented.extend({
2665
2719
 
2666
2720
  // @section Other Methods
2667
2721
  // @method createPane(name: String, container?: HTMLElement): HTMLElement
2668
- // Creates a new map pane with the given name if it doesn't exist already,
2722
+ // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
2669
2723
  // then returns it. The pane is created as a children of `container`, or
2670
2724
  // as a children of the main map pane if not set.
2671
2725
  createPane: function (name, container) {
@@ -2735,8 +2789,8 @@ L.Map = L.Evented.extend({
2735
2789
  max = this.getMaxZoom(),
2736
2790
  nw = bounds.getNorthWest(),
2737
2791
  se = bounds.getSouthEast(),
2738
- size = this.getSize(),
2739
- boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding),
2792
+ size = this.getSize().subtract(padding),
2793
+ boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)),
2740
2794
  snap = L.Browser.any3d ? this.options.zoomSnap : 1;
2741
2795
 
2742
2796
  var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
@@ -2792,13 +2846,13 @@ L.Map = L.Evented.extend({
2792
2846
  // @section Other Methods
2793
2847
 
2794
2848
  // @method getPane(pane: String|HTMLElement): HTMLElement
2795
- // Returns a map pane, given its name or its HTML element (its identity).
2849
+ // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
2796
2850
  getPane: function (pane) {
2797
2851
  return typeof pane === 'string' ? this._panes[pane] : pane;
2798
2852
  },
2799
2853
 
2800
2854
  // @method getPanes(): Object
2801
- // Returns a plain object containing the names of all panes as keys and
2855
+ // Returns a plain object containing the names of all [panes](#map-pane) as keys and
2802
2856
  // the panes as values.
2803
2857
  getPanes: function () {
2804
2858
  return this._panes;
@@ -2993,20 +3047,23 @@ L.Map = L.Evented.extend({
2993
3047
  this._mapPane = this.createPane('mapPane', this._container);
2994
3048
  L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
2995
3049
 
2996
- // @pane tilePane: HTMLElement = 2
2997
- // Pane for tile layers
3050
+ // @pane tilePane: HTMLElement = 200
3051
+ // Pane for `GridLayer`s and `TileLayer`s
2998
3052
  this.createPane('tilePane');
2999
- // @pane overlayPane: HTMLElement = 4
3000
- // Pane for overlays like polylines and polygons
3053
+ // @pane overlayPane: HTMLElement = 400
3054
+ // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
3001
3055
  this.createPane('shadowPane');
3002
- // @pane shadowPane: HTMLElement = 5
3003
- // Pane for overlay shadows (e.g. marker shadows)
3056
+ // @pane shadowPane: HTMLElement = 500
3057
+ // Pane for overlay shadows (e.g. `Marker` shadows)
3004
3058
  this.createPane('overlayPane');
3005
- // @pane markerPane: HTMLElement = 6
3006
- // Pane for marker icons
3059
+ // @pane markerPane: HTMLElement = 600
3060
+ // Pane for `Icon`s of `Marker`s
3007
3061
  this.createPane('markerPane');
3008
- // @pane popupPane: HTMLElement = 7
3009
- // Pane for popups.
3062
+ // @pane tooltipPane: HTMLElement = 650
3063
+ // Pane for tooltip.
3064
+ this.createPane('tooltipPane');
3065
+ // @pane popupPane: HTMLElement = 700
3066
+ // Pane for `Popup`s.
3010
3067
  this.createPane('popupPane');
3011
3068
 
3012
3069
  if (!this.options.markerZoomAnimation) {
@@ -3219,17 +3276,6 @@ L.Map = L.Evented.extend({
3219
3276
 
3220
3277
  var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
3221
3278
 
3222
- if (e.type === 'click') {
3223
- // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
3224
- // @event preclick: MouseEvent
3225
- // Fired before mouse click on the map (sometimes useful when you
3226
- // want something to happen on click before any existing click
3227
- // handlers start running).
3228
- var synth = L.Util.extend({}, e);
3229
- synth.type = 'preclick';
3230
- this._handleDOMEvent(synth);
3231
- }
3232
-
3233
3279
  if (type === 'mousedown') {
3234
3280
  // prevents outline when clicking on keyboard-focusable element
3235
3281
  L.DomUtil.preventOutline(e.target || e.srcElement);
@@ -3240,6 +3286,17 @@ L.Map = L.Evented.extend({
3240
3286
 
3241
3287
  _fireDOMEvent: function (e, type, targets) {
3242
3288
 
3289
+ if (e.type === 'click') {
3290
+ // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
3291
+ // @event preclick: MouseEvent
3292
+ // Fired before mouse click on the map (sometimes useful when you
3293
+ // want something to happen on click before any existing click
3294
+ // handlers start running).
3295
+ var synth = L.Util.extend({}, e);
3296
+ synth.type = 'preclick';
3297
+ this._fireDOMEvent(synth, synth.type, targets);
3298
+ }
3299
+
3243
3300
  if (e._stopped) { return; }
3244
3301
 
3245
3302
  // Find the layer the event is propagating from and its parents.
@@ -3272,7 +3329,7 @@ L.Map = L.Evented.extend({
3272
3329
  },
3273
3330
 
3274
3331
  _draggableMoved: function (obj) {
3275
- obj = obj.options.draggable ? obj : this;
3332
+ obj = obj.dragging && obj.dragging.enabled() ? obj : this;
3276
3333
  return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
3277
3334
  },
3278
3335
 
@@ -3502,7 +3559,11 @@ L.Layer = L.Evented.extend({
3502
3559
  this._zoomAnimated = map._zoomAnimated;
3503
3560
 
3504
3561
  if (this.getEvents) {
3505
- map.on(this.getEvents(), this);
3562
+ var events = this.getEvents();
3563
+ map.on(events, this);
3564
+ this.once('remove', function () {
3565
+ map.off(events, this);
3566
+ }, this);
3506
3567
  }
3507
3568
 
3508
3569
  this.onAdd(map);
@@ -3528,7 +3589,7 @@ L.Layer = L.Evented.extend({
3528
3589
  * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
3529
3590
  *
3530
3591
  * @method getEvents(): Object
3531
- * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#event-addeventlistener). These events will be automatically added and removed from the map with your layer.
3592
+ * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
3532
3593
  *
3533
3594
  * @method getAttribution(): String
3534
3595
  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
@@ -3554,7 +3615,7 @@ L.Map.include({
3554
3615
  // Adds the given layer to the map
3555
3616
  addLayer: function (layer) {
3556
3617
  var id = L.stamp(layer);
3557
- if (this._layers[id]) { return layer; }
3618
+ if (this._layers[id]) { return this; }
3558
3619
  this._layers[id] = layer;
3559
3620
 
3560
3621
  layer._mapToAdd = this;
@@ -3583,10 +3644,6 @@ L.Map.include({
3583
3644
  this.attributionControl.removeAttribution(layer.getAttribution());
3584
3645
  }
3585
3646
 
3586
- if (layer.getEvents) {
3587
- this.off(layer.getEvents(), layer);
3588
- }
3589
-
3590
3647
  delete this._layers[id];
3591
3648
 
3592
3649
  if (this._loaded) {
@@ -3744,13 +3801,13 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
3744
3801
  * @aka L.GridLayer
3745
3802
  *
3746
3803
  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
3747
- * GridLayer can be extended to create a tiled grid of HTML Elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
3804
+ * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
3748
3805
  *
3749
3806
  *
3750
3807
  * @section Synchronous usage
3751
3808
  * @example
3752
3809
  *
3753
- * To create a custom layer, extend GridLayer and impliment the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
3810
+ * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
3754
3811
  *
3755
3812
  * ```js
3756
3813
  * var CanvasLayer = L.GridLayer.extend({
@@ -3764,7 +3821,7 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
3764
3821
  * tile.height = size.y;
3765
3822
  *
3766
3823
  * // get a canvas context and draw something on it using coords.x, coords.y and coords.z
3767
- * var ctx = canvas.getContext('2d');
3824
+ * var ctx = tile.getContext('2d');
3768
3825
  *
3769
3826
  * // return the tile so it can be rendered on screen
3770
3827
  * return tile;
@@ -3772,10 +3829,10 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
3772
3829
  * });
3773
3830
  * ```
3774
3831
  *
3775
- * @section Asynchrohous usage
3832
+ * @section Asynchronous usage
3776
3833
  * @example
3777
3834
  *
3778
- * Tile creation can also be asyncronous, this is useful when using a third-party drawing library. Once the tile is finsihed drawing it can be passed to the done() callback.
3835
+ * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
3779
3836
  *
3780
3837
  * ```js
3781
3838
  * var CanvasLayer = L.GridLayer.extend({
@@ -3790,8 +3847,12 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
3790
3847
  * tile.width = size.x;
3791
3848
  * tile.height = size.y;
3792
3849
  *
3793
- * // draw something and pass the tile to the done() callback
3794
- * done(error, tile);
3850
+ * // draw something asynchronously and pass the tile to the done() callback
3851
+ * setTimeout(function() {
3852
+ * done(error, tile);
3853
+ * }, 1000);
3854
+ *
3855
+ * return tile;
3795
3856
  * }
3796
3857
  * });
3797
3858
  * ```
@@ -3802,6 +3863,8 @@ L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
3802
3863
 
3803
3864
  L.GridLayer = L.Layer.extend({
3804
3865
 
3866
+ // @section
3867
+ // @aka GridLayer options
3805
3868
  options: {
3806
3869
  // @option tileSize: Number|Point = 256
3807
3870
  // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
@@ -3815,8 +3878,12 @@ L.GridLayer = L.Layer.extend({
3815
3878
  // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
3816
3879
  updateWhenIdle: L.Browser.mobile,
3817
3880
 
3881
+ // @option updateWhenZooming: Boolean = true
3882
+ // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
3883
+ updateWhenZooming: true,
3884
+
3818
3885
  // @option updateInterval: Number = 200
3819
- // Tiles will not update more than once every `updateInterval` milliseconds.
3886
+ // Tiles will not update more than once every `updateInterval` milliseconds when panning.
3820
3887
  updateInterval: 200,
3821
3888
 
3822
3889
  // @option attribution: String = null
@@ -3828,7 +3895,7 @@ L.GridLayer = L.Layer.extend({
3828
3895
  zIndex: 1,
3829
3896
 
3830
3897
  // @option bounds: LatLngBounds = undefined
3831
- // If set, tiles will only be loaded inside inside the set `LatLngBounds`.
3898
+ // If set, tiles will only be loaded inside the set `LatLngBounds`.
3832
3899
  bounds: null,
3833
3900
 
3834
3901
  // @option minZoom: Number = 0
@@ -3837,7 +3904,7 @@ L.GridLayer = L.Layer.extend({
3837
3904
 
3838
3905
  // @option maxZoom: Number = undefined
3839
3906
  // The maximum zoom level that tiles will be loaded at.
3840
- // maxZoom: undefined,
3907
+ maxZoom: undefined,
3841
3908
 
3842
3909
  // @option noWrap: Boolean = false
3843
3910
  // Whether the layer is wrapped around the antimeridian. If `true`, the
@@ -3846,7 +3913,15 @@ L.GridLayer = L.Layer.extend({
3846
3913
 
3847
3914
  // @option pane: String = 'tilePane'
3848
3915
  // `Map pane` where the grid layer will be added.
3849
- pane: 'tilePane'
3916
+ pane: 'tilePane',
3917
+
3918
+ // @option className: String = ''
3919
+ // A custom class name to assign to the tile layer. Empty by default.
3920
+ className: '',
3921
+
3922
+ // @option keepBuffer: Number = 2
3923
+ // When panning the map, keep this many rows and columns of tiles before unloading them.
3924
+ keepBuffer: 2
3850
3925
  },
3851
3926
 
3852
3927
  initialize: function (options) {
@@ -4047,7 +4122,7 @@ L.GridLayer = L.Layer.extend({
4047
4122
  _initContainer: function () {
4048
4123
  if (this._container) { return; }
4049
4124
 
4050
- this._container = L.DomUtil.create('div', 'leaflet-layer');
4125
+ this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
4051
4126
  this._updateZIndex();
4052
4127
 
4053
4128
  if (this.options.opacity < 1) {
@@ -4225,7 +4300,7 @@ L.GridLayer = L.Layer.extend({
4225
4300
  tileZoom = undefined;
4226
4301
  }
4227
4302
 
4228
- var tileZoomChanged = (tileZoom !== this._tileZoom);
4303
+ var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
4229
4304
 
4230
4305
  if (!noUpdate || tileZoomChanged) {
4231
4306
 
@@ -4296,7 +4371,7 @@ L.GridLayer = L.Layer.extend({
4296
4371
  _onMoveEnd: function () {
4297
4372
  if (!this._map || this._map._animatingZoom) { return; }
4298
4373
 
4299
- this._resetView();
4374
+ this._update();
4300
4375
  },
4301
4376
 
4302
4377
  _getTiledPixelBounds: function (center) {
@@ -4321,10 +4396,16 @@ L.GridLayer = L.Layer.extend({
4321
4396
  var pixelBounds = this._getTiledPixelBounds(center),
4322
4397
  tileRange = this._pxBoundsToTileRange(pixelBounds),
4323
4398
  tileCenter = tileRange.getCenter(),
4324
- queue = [];
4399
+ queue = [],
4400
+ margin = this.options.keepBuffer,
4401
+ noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
4402
+ tileRange.getTopRight().add([margin, -margin]));
4325
4403
 
4326
4404
  for (var key in this._tiles) {
4327
- this._tiles[key].current = false;
4405
+ var c = this._tiles[key].coords;
4406
+ if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
4407
+ this._tiles[key].current = false;
4408
+ }
4328
4409
  }
4329
4410
 
4330
4411
  // _update just loads more tiles. If the tile zoom level differs too much
@@ -4358,7 +4439,7 @@ L.GridLayer = L.Layer.extend({
4358
4439
  if (!this._loading) {
4359
4440
  this._loading = true;
4360
4441
  // @event loading: Event
4361
- // Fired when the grid layer starts loading tiles
4442
+ // Fired when the grid layer starts loading tiles.
4362
4443
  this.fire('loading');
4363
4444
  }
4364
4445
 
@@ -4497,7 +4578,7 @@ L.GridLayer = L.Layer.extend({
4497
4578
  if (!this._map) { return; }
4498
4579
 
4499
4580
  if (err) {
4500
- // @event tileerror: TileEvent
4581
+ // @event tileerror: TileErrorEvent
4501
4582
  // Fired when there is an error loading a tile.
4502
4583
  this.fire('tileerror', {
4503
4584
  error: err,
@@ -4532,7 +4613,7 @@ L.GridLayer = L.Layer.extend({
4532
4613
 
4533
4614
  if (this._noTilesToLoad()) {
4534
4615
  this._loading = false;
4535
- // @event load: TileEvent
4616
+ // @event load: Event
4536
4617
  // Fired when the grid layer loaded all visible tiles.
4537
4618
  this.fire('load');
4538
4619
 
@@ -4609,13 +4690,13 @@ L.gridLayer = function (options) {
4609
4690
  * ```
4610
4691
  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
4611
4692
  * ```
4612
- *
4613
- * @section
4614
4693
  */
4615
4694
 
4616
4695
 
4617
4696
  L.TileLayer = L.GridLayer.extend({
4618
4697
 
4698
+ // @section
4699
+ // @aka TileLayer options
4619
4700
  options: {
4620
4701
  // @option minZoom: Number = 0
4621
4702
  // Minimum zoom number.
@@ -4644,7 +4725,7 @@ L.TileLayer = L.GridLayer.extend({
4644
4725
  zoomOffset: 0,
4645
4726
 
4646
4727
  // @option tms: Boolean = false
4647
- // If `true`, inverses Y axis numbering for tiles (turn this on for TMS services).
4728
+ // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
4648
4729
  tms: false,
4649
4730
 
4650
4731
  // @option zoomReverse: Boolean = false
@@ -4670,10 +4751,16 @@ L.TileLayer = L.GridLayer.extend({
4670
4751
  if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
4671
4752
 
4672
4753
  options.tileSize = Math.floor(options.tileSize / 2);
4673
- options.zoomOffset++;
4754
+
4755
+ if (!options.zoomReverse) {
4756
+ options.zoomOffset++;
4757
+ options.maxZoom--;
4758
+ } else {
4759
+ options.zoomOffset--;
4760
+ options.minZoom++;
4761
+ }
4674
4762
 
4675
4763
  options.minZoom = Math.max(0, options.minZoom);
4676
- options.maxZoom--;
4677
4764
  }
4678
4765
 
4679
4766
  if (typeof options.subdomains === 'string') {
@@ -4819,7 +4906,7 @@ L.TileLayer = L.GridLayer.extend({
4819
4906
  });
4820
4907
 
4821
4908
 
4822
- // @factory L.tilelayer(urlTemplate: String, options? TileLayer options)
4909
+ // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
4823
4910
  // Instantiates a tile layer object given a `URL template` and optionally an options object.
4824
4911
 
4825
4912
  L.tileLayer = function (url, options) {
@@ -4832,7 +4919,7 @@ L.tileLayer = function (url, options) {
4832
4919
  * @class TileLayer.WMS
4833
4920
  * @inherits TileLayer
4834
4921
  * @aka L.TileLayer.WMS
4835
- * Used to display WMS services as tile layers on the map. Extends `TileLayer`.
4922
+ * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
4836
4923
  *
4837
4924
  * @example
4838
4925
  *
@@ -4850,6 +4937,9 @@ L.TileLayer.WMS = L.TileLayer.extend({
4850
4937
 
4851
4938
  // @section
4852
4939
  // @aka TileLayer.WMS options
4940
+ // If any custom options not documented here are used, they will be sent to the
4941
+ // WMS server as extra parameters in each request URL. This can be useful for
4942
+ // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
4853
4943
  defaultWmsParams: {
4854
4944
  service: 'WMS',
4855
4945
  request: 'GetMap',
@@ -4960,7 +5050,7 @@ L.tileLayer.wms = function (url, options) {
4960
5050
  /*
4961
5051
  * @class ImageOverlay
4962
5052
  * @aka L.ImageOverlay
4963
- * @inherits Layer
5053
+ * @inherits Interactive layer
4964
5054
  *
4965
5055
  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
4966
5056
  *
@@ -4975,6 +5065,8 @@ L.tileLayer.wms = function (url, options) {
4975
5065
 
4976
5066
  L.ImageOverlay = L.Layer.extend({
4977
5067
 
5068
+ // @section
5069
+ // @aka ImageOverlay options
4978
5070
  options: {
4979
5071
  // @option opacity: Number = 1.0
4980
5072
  // The opacity of the image overlay.
@@ -4985,17 +5077,16 @@ L.ImageOverlay = L.Layer.extend({
4985
5077
  alt: '',
4986
5078
 
4987
5079
  // @option interactive: Boolean = false
4988
- // If `true`, the image overlay will emit mouse events when clicked or hovered.
5080
+ // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
4989
5081
  interactive: false,
4990
5082
 
4991
5083
  // @option attribution: String = null
4992
5084
  // An optional string containing HTML to be shown on the `Attribution control`
4993
- attribution: null
5085
+ attribution: null,
4994
5086
 
4995
5087
  // @option crossOrigin: Boolean = false
4996
5088
  // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
4997
-
4998
- // crossOrigin: false,
5089
+ crossOrigin: false
4999
5090
  },
5000
5091
 
5001
5092
  initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
@@ -5264,8 +5355,14 @@ L.Icon = L.Class.extend({
5264
5355
  },
5265
5356
 
5266
5357
  _setIconStyles: function (img, name) {
5267
- var options = this.options,
5268
- size = L.point(options[name + 'Size']),
5358
+ var options = this.options;
5359
+ var sizeOption = options[name + 'Size'];
5360
+
5361
+ if (typeof sizeOption === 'number') {
5362
+ sizeOption = [sizeOption, sizeOption];
5363
+ }
5364
+
5365
+ var size = L.point(sizeOption),
5269
5366
  anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
5270
5367
  size && size.divideBy(2, true));
5271
5368
 
@@ -5312,6 +5409,7 @@ L.Icon.Default = L.Icon.extend({
5312
5409
  iconSize: [25, 41],
5313
5410
  iconAnchor: [12, 41],
5314
5411
  popupAnchor: [1, -34],
5412
+ tooltipAnchor: [16, -28],
5315
5413
  shadowSize: [41, 41]
5316
5414
  },
5317
5415
 
@@ -5352,7 +5450,7 @@ L.Icon.Default.imagePath = (function () {
5352
5450
 
5353
5451
  /*
5354
5452
  * @class Marker
5355
- * @inherits Layer
5453
+ * @inherits Interactive layer
5356
5454
  * @aka L.Marker
5357
5455
  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
5358
5456
  *
@@ -5365,13 +5463,14 @@ L.Icon.Default.imagePath = (function () {
5365
5463
 
5366
5464
  L.Marker = L.Layer.extend({
5367
5465
 
5466
+ // @section
5467
+ // @aka Marker options
5368
5468
  options: {
5369
- // @option icon: L.Icon = *
5469
+ // @option icon: Icon = *
5370
5470
  // Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. Set to new `L.Icon.Default()` by default.
5371
5471
  icon: new L.Icon.Default(),
5372
5472
 
5373
- // @option interactive: Boolean = true
5374
- // If `false`, the marker will not emit mouse events and will act as a part of the underlying map.
5473
+ // Option inherited from "Interactive layer" abstract class
5375
5474
  interactive: true,
5376
5475
 
5377
5476
  // @option draggable: Boolean = false
@@ -5414,31 +5513,6 @@ L.Marker = L.Layer.extend({
5414
5513
  nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
5415
5514
  },
5416
5515
 
5417
- /* @section
5418
- *
5419
- * You can subscribe to the following events using [these methods](#evented-method).
5420
- *
5421
- * @event click: MouseEvent
5422
- * Fired when the user clicks (or taps) the marker.
5423
- *
5424
- * @event dblclick: MouseEvent
5425
- * Fired when the user double-clicks (or double-taps) the marker.
5426
- *
5427
- * @event mousedown: MouseEvent
5428
- * Fired when the user pushes the mouse button on the marker.
5429
- *
5430
- * @event mouseover: MouseEvent
5431
- * Fired when the mouse enters the marker.
5432
- *
5433
- * @event mouseout: MouseEvent
5434
- * Fired when the mouse leaves the marker.
5435
- *
5436
- * @event contextmenu: MouseEvent
5437
- * Fired when the user right-clicks on the marker.
5438
- */
5439
-
5440
-
5441
-
5442
5516
  /* @section
5443
5517
  *
5444
5518
  * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
@@ -5452,31 +5526,33 @@ L.Marker = L.Layer.extend({
5452
5526
  onAdd: function (map) {
5453
5527
  this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
5454
5528
 
5529
+ if (this._zoomAnimated) {
5530
+ map.on('zoomanim', this._animateZoom, this);
5531
+ }
5532
+
5455
5533
  this._initIcon();
5456
5534
  this.update();
5457
5535
  },
5458
5536
 
5459
- onRemove: function () {
5537
+ onRemove: function (map) {
5460
5538
  if (this.dragging && this.dragging.enabled()) {
5461
5539
  this.options.draggable = true;
5462
5540
  this.dragging.removeHooks();
5463
5541
  }
5464
5542
 
5543
+ if (this._zoomAnimated) {
5544
+ map.off('zoomanim', this._animateZoom, this);
5545
+ }
5546
+
5465
5547
  this._removeIcon();
5466
5548
  this._removeShadow();
5467
5549
  },
5468
5550
 
5469
5551
  getEvents: function () {
5470
- var events = {
5552
+ return {
5471
5553
  zoom: this.update,
5472
5554
  viewreset: this.update
5473
5555
  };
5474
-
5475
- if (this._zoomAnimated) {
5476
- events.zoomanim = this._animateZoom;
5477
- }
5478
-
5479
- return events;
5480
5556
  },
5481
5557
 
5482
5558
  // @method getLatLng: LatLng
@@ -5776,32 +5852,12 @@ L.divIcon = function (options) {
5776
5852
 
5777
5853
 
5778
5854
  /*
5779
- * @class Popup
5855
+ * @class DivOverlay
5780
5856
  * @inherits Layer
5781
- * @aka L.Popup
5782
- * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
5783
- * open popups while making sure that only one popup is open at one time
5784
- * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
5785
- *
5786
- * @example
5787
- *
5788
- * If you want to just bind a popup to marker click and then open it, it's really easy:
5789
- *
5790
- * ```js
5791
- * marker.bindPopup(popupContent).openPopup();
5792
- * ```
5793
- * Path overlays like polylines also have a `bindPopup` method.
5794
- * Here's a more complicated way to open a popup on a map:
5795
- *
5796
- * ```js
5797
- * var popup = L.popup()
5798
- * .setLatLng(latlng)
5799
- * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
5800
- * .openOn(map);
5801
- * ```
5857
+ * @aka L.DivOverlay
5858
+ * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
5802
5859
  */
5803
5860
 
5804
-
5805
5861
  /* @namespace Map
5806
5862
  * @section Interaction Options
5807
5863
  * @option closePopupOnClick: Boolean = true
@@ -5811,64 +5867,17 @@ L.Map.mergeOptions({
5811
5867
  closePopupOnClick: true
5812
5868
  });
5813
5869
 
5814
- // @namespace Popup
5815
- L.Popup = L.Layer.extend({
5870
+ // @namespace DivOverlay
5871
+ L.DivOverlay = L.Layer.extend({
5816
5872
 
5817
5873
  // @section
5818
- // @aka Popup options
5874
+ // @aka DivOverlay options
5819
5875
  options: {
5820
- // @option maxWidth: Number = 300
5821
- // Max width of the popup, in pixels.
5822
- maxWidth: 300,
5823
-
5824
- // @option minWidth: Number = 50
5825
- // Min width of the popup, in pixels.
5826
- minWidth: 50,
5827
-
5828
- // @option maxHeight: Number = null
5829
- // If set, creates a scrollable container of the given height
5830
- // inside a popup if its content exceeds it.
5831
- maxHeight: null,
5832
-
5833
- // @option autoPan: Boolean = true
5834
- // Set it to `false` if you don't want the map to do panning animation
5835
- // to fit the opened popup.
5836
- autoPan: true,
5837
-
5838
- // @option autoPanPaddingTopLeft: Point = null
5839
- // The margin between the popup and the top left corner of the map
5840
- // view after autopanning was performed.
5841
- autoPanPaddingTopLeft: null,
5842
-
5843
- // @option autoPanPaddingBottomRight: Point = null
5844
- // The margin between the popup and the bottom right corner of the map
5845
- // view after autopanning was performed.
5846
- autoPanPaddingBottomRight: null,
5847
-
5848
- // @option autoPanPadding: Point = Point(5, 5)
5849
- // Equivalent of setting both top left and bottom right autopan padding to the same value.
5850
- autoPanPadding: [5, 5],
5851
-
5852
- // @option keepInView: Boolean = false
5853
- // Set it to `true` if you want to prevent users from panning the popup
5854
- // off of the screen while it is open.
5855
- keepInView: false,
5856
-
5857
- // @option closeButton: Boolean = true
5858
- // Controls the presence of a close button in the popup.
5859
- closeButton: true,
5860
-
5861
5876
  // @option offset: Point = Point(0, 7)
5862
5877
  // The offset of the popup position. Useful to control the anchor
5863
5878
  // of the popup when opening it on some overlays.
5864
5879
  offset: [0, 7],
5865
5880
 
5866
- // @option autoClose: Boolean = true
5867
- // Set it to `false` if you want to override the default behavior of
5868
- // the popup closing when user clicks the map (set globally by
5869
- // the Map's [closePopupOnClick](#map-closepopuponclick) option).
5870
- autoClose: true,
5871
-
5872
5881
  // @option zoomAnimation: Boolean = true
5873
5882
  // Whether to animate the popup on zoom. Disable it if you have
5874
5883
  // problems with Flash content inside popups.
@@ -5908,28 +5917,7 @@ L.Popup = L.Layer.extend({
5908
5917
  L.DomUtil.setOpacity(this._container, 1);
5909
5918
  }
5910
5919
 
5911
- // @namespace Map
5912
- // @section Popup events
5913
- // @event popupopen: PopupEvent
5914
- // Fired when a popup is opened in the map
5915
- map.fire('popupopen', {popup: this});
5916
-
5917
- if (this._source) {
5918
- // @namespace Layer
5919
- // @section Popup events
5920
- // @event popupopen: PopupEvent
5921
- // Fired when a popup bound to this layer is opened
5922
- this._source.fire('popupopen', {popup: this}, true);
5923
- this._source.on('preclick', L.DomEvent.stopPropagation);
5924
- }
5925
- },
5926
-
5927
- // @namespace Popup
5928
- // @method openOn(map: Map): this
5929
- // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
5930
- openOn: function (map) {
5931
- map.openPopup(this);
5932
- return this;
5920
+ this.bringToFront();
5933
5921
  },
5934
5922
 
5935
5923
  onRemove: function (map) {
@@ -5939,22 +5927,6 @@ L.Popup = L.Layer.extend({
5939
5927
  } else {
5940
5928
  L.DomUtil.remove(this._container);
5941
5929
  }
5942
-
5943
- // @namespace Map
5944
- // @section Popup events
5945
- // @event popupclose: PopupEvent
5946
- // Fired when a popup in the map is closed
5947
- map.fire('popupclose', {popup: this});
5948
-
5949
- if (this._source) {
5950
- // @namespace Layer
5951
- // @section Popup events
5952
- // @event popupclose: PopupEvent
5953
- // Fired when a popup bound to this layer is closed
5954
- // @namespace Popup
5955
- this._source.fire('popupclose', {popup: this}, true);
5956
- this._source.off('preclick', L.DomEvent.stopPropagation);
5957
- }
5958
5930
  },
5959
5931
 
5960
5932
  // @namespace Popup
@@ -6020,12 +5992,6 @@ L.Popup = L.Layer.extend({
6020
5992
  if (this._zoomAnimated) {
6021
5993
  events.zoomanim = this._animateZoom;
6022
5994
  }
6023
- if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
6024
- events.preclick = this._close;
6025
- }
6026
- if (this.options.keepInView) {
6027
- events.moveend = this._adjustPan;
6028
- }
6029
5995
  return events;
6030
5996
  },
6031
5997
 
@@ -6053,6 +6019,193 @@ L.Popup = L.Layer.extend({
6053
6019
  return this;
6054
6020
  },
6055
6021
 
6022
+ _updateContent: function () {
6023
+ if (!this._content) { return; }
6024
+
6025
+ var node = this._contentNode;
6026
+ var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
6027
+
6028
+ if (typeof content === 'string') {
6029
+ node.innerHTML = content;
6030
+ } else {
6031
+ while (node.hasChildNodes()) {
6032
+ node.removeChild(node.firstChild);
6033
+ }
6034
+ node.appendChild(content);
6035
+ }
6036
+ this.fire('contentupdate');
6037
+ },
6038
+
6039
+ _updatePosition: function () {
6040
+ if (!this._map) { return; }
6041
+
6042
+ var pos = this._map.latLngToLayerPoint(this._latlng),
6043
+ offset = L.point(this.options.offset),
6044
+ anchor = this._getAnchor();
6045
+
6046
+ if (this._zoomAnimated) {
6047
+ L.DomUtil.setPosition(this._container, pos.add(anchor));
6048
+ } else {
6049
+ offset = offset.add(pos).add(anchor);
6050
+ }
6051
+
6052
+ var bottom = this._containerBottom = -offset.y,
6053
+ left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
6054
+
6055
+ // bottom position the popup in case the height of the popup changes (images loading etc)
6056
+ this._container.style.bottom = bottom + 'px';
6057
+ this._container.style.left = left + 'px';
6058
+ },
6059
+
6060
+ _getAnchor: function () {
6061
+ return [0, 0];
6062
+ }
6063
+
6064
+ });
6065
+
6066
+
6067
+
6068
+ /*
6069
+ * @class Popup
6070
+ * @inherits DivOverlay
6071
+ * @aka L.Popup
6072
+ * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
6073
+ * open popups while making sure that only one popup is open at one time
6074
+ * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
6075
+ *
6076
+ * @example
6077
+ *
6078
+ * If you want to just bind a popup to marker click and then open it, it's really easy:
6079
+ *
6080
+ * ```js
6081
+ * marker.bindPopup(popupContent).openPopup();
6082
+ * ```
6083
+ * Path overlays like polylines also have a `bindPopup` method.
6084
+ * Here's a more complicated way to open a popup on a map:
6085
+ *
6086
+ * ```js
6087
+ * var popup = L.popup()
6088
+ * .setLatLng(latlng)
6089
+ * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
6090
+ * .openOn(map);
6091
+ * ```
6092
+ */
6093
+
6094
+
6095
+ // @namespace Popup
6096
+ L.Popup = L.DivOverlay.extend({
6097
+
6098
+ // @section
6099
+ // @aka Popup options
6100
+ options: {
6101
+ // @option maxWidth: Number = 300
6102
+ // Max width of the popup, in pixels.
6103
+ maxWidth: 300,
6104
+
6105
+ // @option minWidth: Number = 50
6106
+ // Min width of the popup, in pixels.
6107
+ minWidth: 50,
6108
+
6109
+ // @option maxHeight: Number = null
6110
+ // If set, creates a scrollable container of the given height
6111
+ // inside a popup if its content exceeds it.
6112
+ maxHeight: null,
6113
+
6114
+ // @option autoPan: Boolean = true
6115
+ // Set it to `false` if you don't want the map to do panning animation
6116
+ // to fit the opened popup.
6117
+ autoPan: true,
6118
+
6119
+ // @option autoPanPaddingTopLeft: Point = null
6120
+ // The margin between the popup and the top left corner of the map
6121
+ // view after autopanning was performed.
6122
+ autoPanPaddingTopLeft: null,
6123
+
6124
+ // @option autoPanPaddingBottomRight: Point = null
6125
+ // The margin between the popup and the bottom right corner of the map
6126
+ // view after autopanning was performed.
6127
+ autoPanPaddingBottomRight: null,
6128
+
6129
+ // @option autoPanPadding: Point = Point(5, 5)
6130
+ // Equivalent of setting both top left and bottom right autopan padding to the same value.
6131
+ autoPanPadding: [5, 5],
6132
+
6133
+ // @option keepInView: Boolean = false
6134
+ // Set it to `true` if you want to prevent users from panning the popup
6135
+ // off of the screen while it is open.
6136
+ keepInView: false,
6137
+
6138
+ // @option closeButton: Boolean = true
6139
+ // Controls the presence of a close button in the popup.
6140
+ closeButton: true,
6141
+
6142
+ // @option autoClose: Boolean = true
6143
+ // Set it to `false` if you want to override the default behavior of
6144
+ // the popup closing when user clicks the map (set globally by
6145
+ // the Map's [closePopupOnClick](#map-closepopuponclick) option).
6146
+ autoClose: true
6147
+ },
6148
+
6149
+ // @namespace Popup
6150
+ // @method openOn(map: Map): this
6151
+ // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
6152
+ openOn: function (map) {
6153
+ map.openPopup(this);
6154
+ return this;
6155
+ },
6156
+
6157
+ onAdd: function (map) {
6158
+ L.DivOverlay.prototype.onAdd.call(this, map);
6159
+
6160
+ // @namespace Map
6161
+ // @section Popup events
6162
+ // @event popupopen: PopupEvent
6163
+ // Fired when a popup is opened in the map
6164
+ map.fire('popupopen', {popup: this});
6165
+
6166
+ if (this._source) {
6167
+ // @namespace Layer
6168
+ // @section Popup events
6169
+ // @event popupopen: PopupEvent
6170
+ // Fired when a popup bound to this layer is opened
6171
+ this._source.fire('popupopen', {popup: this}, true);
6172
+ this._source.on('preclick', L.DomEvent.stopPropagation);
6173
+ }
6174
+ },
6175
+
6176
+ onRemove: function (map) {
6177
+ L.DivOverlay.prototype.onRemove.call(this, map);
6178
+
6179
+ // @namespace Map
6180
+ // @section Popup events
6181
+ // @event popupclose: PopupEvent
6182
+ // Fired when a popup in the map is closed
6183
+ map.fire('popupclose', {popup: this});
6184
+
6185
+ if (this._source) {
6186
+ // @namespace Layer
6187
+ // @section Popup events
6188
+ // @event popupclose: PopupEvent
6189
+ // Fired when a popup bound to this layer is closed
6190
+ this._source.fire('popupclose', {popup: this}, true);
6191
+ this._source.off('preclick', L.DomEvent.stopPropagation);
6192
+ }
6193
+ },
6194
+
6195
+ getEvents: function () {
6196
+ var events = L.DivOverlay.prototype.getEvents.call(this);
6197
+
6198
+ if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
6199
+ events.preclick = this._close;
6200
+ }
6201
+
6202
+ if (this.options.keepInView) {
6203
+ events.moveend = this._adjustPan;
6204
+ }
6205
+
6206
+ return events;
6207
+ },
6208
+
6056
6209
  _close: function () {
6057
6210
  if (this._map) {
6058
6211
  this._map.closePopup(this);
@@ -6085,23 +6238,6 @@ L.Popup = L.Layer.extend({
6085
6238
  this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
6086
6239
  },
6087
6240
 
6088
- _updateContent: function () {
6089
- if (!this._content) { return; }
6090
-
6091
- var node = this._contentNode;
6092
- var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
6093
-
6094
- if (typeof content === 'string') {
6095
- node.innerHTML = content;
6096
- } else {
6097
- while (node.hasChildNodes()) {
6098
- node.removeChild(node.firstChild);
6099
- }
6100
- node.appendChild(content);
6101
- }
6102
- this.fire('contentupdate');
6103
- },
6104
-
6105
6241
  _updateLayout: function () {
6106
6242
  var container = this._contentNode,
6107
6243
  style = container.style;
@@ -6122,39 +6258,20 @@ L.Popup = L.Layer.extend({
6122
6258
  maxHeight = this.options.maxHeight,
6123
6259
  scrolledClass = 'leaflet-popup-scrolled';
6124
6260
 
6125
- if (maxHeight && height > maxHeight) {
6126
- style.height = maxHeight + 'px';
6127
- L.DomUtil.addClass(container, scrolledClass);
6128
- } else {
6129
- L.DomUtil.removeClass(container, scrolledClass);
6130
- }
6131
-
6132
- this._containerWidth = this._container.offsetWidth;
6133
- },
6134
-
6135
- _updatePosition: function () {
6136
- if (!this._map) { return; }
6137
-
6138
- var pos = this._map.latLngToLayerPoint(this._latlng),
6139
- offset = L.point(this.options.offset);
6140
-
6141
- if (this._zoomAnimated) {
6142
- L.DomUtil.setPosition(this._container, pos);
6261
+ if (maxHeight && height > maxHeight) {
6262
+ style.height = maxHeight + 'px';
6263
+ L.DomUtil.addClass(container, scrolledClass);
6143
6264
  } else {
6144
- offset = offset.add(pos);
6265
+ L.DomUtil.removeClass(container, scrolledClass);
6145
6266
  }
6146
6267
 
6147
- var bottom = this._containerBottom = -offset.y,
6148
- left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
6149
-
6150
- // bottom position the popup in case the height of the popup changes (images loading etc)
6151
- this._container.style.bottom = bottom + 'px';
6152
- this._container.style.left = left + 'px';
6268
+ this._containerWidth = this._container.offsetWidth;
6153
6269
  },
6154
6270
 
6155
6271
  _animateZoom: function (e) {
6156
- var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
6157
- L.DomUtil.setPosition(this._container, pos);
6272
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
6273
+ anchor = this._getAnchor();
6274
+ L.DomUtil.setPosition(this._container, pos.add(anchor));
6158
6275
  },
6159
6276
 
6160
6277
  _adjustPan: function () {
@@ -6192,7 +6309,7 @@ L.Popup = L.Layer.extend({
6192
6309
 
6193
6310
  // @namespace Map
6194
6311
  // @section Popup events
6195
- // @event autopanstart
6312
+ // @event autopanstart: Event
6196
6313
  // Fired when the map starts autopanning when opening a popup.
6197
6314
  if (dx || dy) {
6198
6315
  map
@@ -6204,12 +6321,18 @@ L.Popup = L.Layer.extend({
6204
6321
  _onCloseButtonClick: function (e) {
6205
6322
  this._close();
6206
6323
  L.DomEvent.stop(e);
6324
+ },
6325
+
6326
+ _getAnchor: function () {
6327
+ // Where should we anchor the popup on the source layer?
6328
+ return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
6207
6329
  }
6330
+
6208
6331
  });
6209
6332
 
6210
6333
  // @namespace Popup
6211
6334
  // @factory L.popup(options?: Popup options, source?: Layer)
6212
- // Instantiates a Popup object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
6335
+ // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
6213
6336
  L.popup = function (options, source) {
6214
6337
  return new L.Popup(options, source);
6215
6338
  };
@@ -6304,9 +6427,6 @@ L.Layer.include({
6304
6427
  this._popupHandlersAdded = true;
6305
6428
  }
6306
6429
 
6307
- // save the originally passed offset
6308
- this._originalPopupOffset = this._popup.options.offset;
6309
-
6310
6430
  return this;
6311
6431
  },
6312
6432
 
@@ -6345,9 +6465,6 @@ L.Layer.include({
6345
6465
  }
6346
6466
 
6347
6467
  if (this._popup && this._map) {
6348
- // set the popup offset for this layer
6349
- this._popup.options.offset = this._popupAnchor(layer);
6350
-
6351
6468
  // set popup source to this layer
6352
6469
  this._popup._source = layer;
6353
6470
 
@@ -6370,7 +6487,7 @@ L.Layer.include({
6370
6487
  return this;
6371
6488
  },
6372
6489
 
6373
- // @method closePopup(): this
6490
+ // @method togglePopup(): this
6374
6491
  // Opens or closes the popup bound to this layer depending on its current state.
6375
6492
  togglePopup: function (target) {
6376
6493
  if (this._popup) {
@@ -6383,13 +6500,13 @@ L.Layer.include({
6383
6500
  return this;
6384
6501
  },
6385
6502
 
6386
- // @method closePopup(): this
6503
+ // @method isPopupOpen(): boolean
6387
6504
  // Returns `true` if the popup bound to this layer is currently open.
6388
6505
  isPopupOpen: function () {
6389
6506
  return this._popup.isOpen();
6390
6507
  },
6391
6508
 
6392
- // @method setPopupContent(content: String|HTMLElement|Popup, options?: Popup options): this
6509
+ // @method setPopupContent(content: String|HTMLElement|Popup): this
6393
6510
  // Sets the content of the popup bound to this layer.
6394
6511
  setPopupContent: function (content) {
6395
6512
  if (this._popup) {
@@ -6415,6 +6532,9 @@ L.Layer.include({
6415
6532
  return;
6416
6533
  }
6417
6534
 
6535
+ // prevent map click
6536
+ L.DomEvent.stop(e);
6537
+
6418
6538
  // if this inherits from Path its a vector and we can just
6419
6539
  // open the popup at the new location
6420
6540
  if (layer instanceof L.Path) {
@@ -6431,17 +6551,6 @@ L.Layer.include({
6431
6551
  }
6432
6552
  },
6433
6553
 
6434
- _popupAnchor: function (layer) {
6435
- // where shold we anchor the popup on this layer?
6436
- var anchor = layer._getPopupAnchor ? layer._getPopupAnchor() : [0, 0];
6437
-
6438
- // add the users passed offset to that
6439
- var offsetToAdd = this._originalPopupOffset || L.Popup.prototype.options.offset;
6440
-
6441
- // return the final point to anchor the popup
6442
- return L.point(anchor).add(offsetToAdd);
6443
- },
6444
-
6445
6554
  _movePopup: function (e) {
6446
6555
  this._popup.setLatLng(e.latlng);
6447
6556
  }
@@ -6461,6 +6570,418 @@ L.Marker.include({
6461
6570
 
6462
6571
 
6463
6572
 
6573
+ /*
6574
+ * @class Tooltip
6575
+ * @inherits DivOverlay
6576
+ * @aka L.Tooltip
6577
+ * Used to display small texts on top of map layers.
6578
+ *
6579
+ * @example
6580
+ *
6581
+ * ```js
6582
+ * marker.bindTooltip("my tooltip text").openTooltip();
6583
+ * ```
6584
+ * Note about tooltip offset. Leaflet takes two options in consideration
6585
+ * for computing tooltip offseting:
6586
+ * - the `offset` Tooltip option: it defaults to [6, -6], because the tooltip
6587
+ * tip is 6px width and height. Remember to change this value if you override
6588
+ * the tip in CSS.
6589
+ * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
6590
+ * should adapt this value if you use a custom icon.
6591
+ */
6592
+
6593
+
6594
+ // @namespace Tooltip
6595
+ L.Tooltip = L.DivOverlay.extend({
6596
+
6597
+ // @section
6598
+ // @aka Tooltip options
6599
+ options: {
6600
+ // @option pane: String = 'tooltipPane'
6601
+ // `Map pane` where the tooltip will be added.
6602
+ pane: 'tooltipPane',
6603
+
6604
+ // @option offset: Point = Point(6, -6)
6605
+ // The offset of the tooltip position. Update it if you customize the
6606
+ // tooltip tip in CSS.
6607
+ offset: [6, -6],
6608
+
6609
+ // @option direction: String = 'auto'
6610
+ // Direction where to open the tooltip. Possible values are: `right`, `left`,
6611
+ // `top`, `bottom`, `center`, `auto`.
6612
+ // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
6613
+ // position on the map.
6614
+ direction: 'auto',
6615
+
6616
+ // @option permanent: Boolean = false
6617
+ // Whether to open the tooltip permanently or only on mouseover.
6618
+ permanent: false,
6619
+
6620
+ // @option sticky: Boolean = false
6621
+ // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
6622
+ sticky: false,
6623
+
6624
+ // @option interactive: Boolean = false
6625
+ // If true, the tooltip will listen to the feature events.
6626
+ interactive: false,
6627
+
6628
+ // @option opacity: Number = 0.9
6629
+ // Tooltip container opacity.
6630
+ opacity: 0.9
6631
+ },
6632
+
6633
+ onAdd: function (map) {
6634
+ L.DivOverlay.prototype.onAdd.call(this, map);
6635
+ this.setOpacity(this.options.opacity);
6636
+
6637
+ // @namespace Map
6638
+ // @section Tooltip events
6639
+ // @event tooltipopen: TooltipEvent
6640
+ // Fired when a tooltip is opened in the map.
6641
+ map.fire('tooltipopen', {tooltip: this});
6642
+
6643
+ if (this._source) {
6644
+ // @namespace Layer
6645
+ // @section Tooltip events
6646
+ // @event tooltipopen: TooltipEvent
6647
+ // Fired when a tooltip bound to this layer is opened.
6648
+ this._source.fire('tooltipopen', {tooltip: this}, true);
6649
+ }
6650
+ },
6651
+
6652
+ onRemove: function (map) {
6653
+ L.DivOverlay.prototype.onRemove.call(this, map);
6654
+
6655
+ // @namespace Map
6656
+ // @section Tooltip events
6657
+ // @event tooltipclose: TooltipEvent
6658
+ // Fired when a tooltip in the map is closed.
6659
+ map.fire('tooltipclose', {tooltip: this});
6660
+
6661
+ if (this._source) {
6662
+ // @namespace Layer
6663
+ // @section Tooltip events
6664
+ // @event tooltipclose: TooltipEvent
6665
+ // Fired when a tooltip bound to this layer is closed.
6666
+ this._source.fire('tooltipclose', {tooltip: this}, true);
6667
+ }
6668
+ },
6669
+
6670
+ _close: function () {
6671
+ if (this._map) {
6672
+ this._map.closeTooltip(this);
6673
+ }
6674
+ },
6675
+
6676
+ _initLayout: function () {
6677
+ var prefix = 'leaflet-tooltip',
6678
+ className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
6679
+
6680
+ this._contentNode = this._container = L.DomUtil.create('div', className);
6681
+ },
6682
+
6683
+ _updateLayout: function () {},
6684
+
6685
+ _adjustPan: function () {},
6686
+
6687
+ _updatePosition: function () {
6688
+ var map = this._map,
6689
+ pos = map.latLngToLayerPoint(this._latlng),
6690
+ container = this._container,
6691
+ centerPoint = map.latLngToContainerPoint(map.getCenter()),
6692
+ tooltipPoint = map.layerPointToContainerPoint(pos),
6693
+ direction = this.options.direction,
6694
+ tooltipWidth = container.offsetWidth,
6695
+ tooltipHeight = container.offsetHeight,
6696
+ offset = L.point(this.options.offset),
6697
+ anchor = this._getAnchor();
6698
+
6699
+ if (direction === 'top') {
6700
+ pos = pos.add(L.point(-tooltipWidth / 2, -tooltipHeight + offset.y + anchor.y));
6701
+ } else if (direction === 'bottom') {
6702
+ pos = pos.subtract(L.point(tooltipWidth / 2, offset.y));
6703
+ } else if (direction === 'center') {
6704
+ pos = pos.subtract(L.point(tooltipWidth / 2, tooltipHeight / 2 - anchor.y));
6705
+ } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
6706
+ direction = 'right';
6707
+ pos = pos.add([offset.x + anchor.x, anchor.y - tooltipHeight / 2]);
6708
+ } else {
6709
+ direction = 'left';
6710
+ pos = pos.subtract(L.point(offset.x + tooltipWidth + anchor.x, tooltipHeight / 2 - anchor.y));
6711
+ }
6712
+
6713
+ L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
6714
+ L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
6715
+ L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
6716
+ L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
6717
+ L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
6718
+ L.DomUtil.setPosition(container, pos);
6719
+ },
6720
+
6721
+ setOpacity: function (opacity) {
6722
+ this.options.opacity = opacity;
6723
+
6724
+ if (this._container) {
6725
+ L.DomUtil.setOpacity(this._container, opacity);
6726
+ }
6727
+ },
6728
+
6729
+ _animateZoom: function (e) {
6730
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), offset;
6731
+ if (this.options.offset) {
6732
+ offset = L.point(this.options.offset);
6733
+ pos = pos.add(offset);
6734
+ }
6735
+ L.DomUtil.setPosition(this._container, pos);
6736
+ },
6737
+
6738
+ _getAnchor: function () {
6739
+ // Where should we anchor the tooltip on the source layer?
6740
+ return L.point(this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
6741
+ }
6742
+
6743
+ });
6744
+
6745
+ // @namespace Tooltip
6746
+ // @factory L.tooltip(options?: Tooltip options, source?: Layer)
6747
+ // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
6748
+ L.tooltip = function (options, source) {
6749
+ return new L.Tooltip(options, source);
6750
+ };
6751
+
6752
+ // @namespace Map
6753
+ // @section Methods for Layers and Controls
6754
+ L.Map.include({
6755
+
6756
+ // @method openTooltip(tooltip: Tooltip): this
6757
+ // Opens the specified tooltip.
6758
+ // @alternative
6759
+ // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
6760
+ // Creates a tooltip with the specified content and options and open it.
6761
+ openTooltip: function (tooltip, latlng, options) {
6762
+ if (!(tooltip instanceof L.Tooltip)) {
6763
+ tooltip = new L.Tooltip(options).setContent(tooltip);
6764
+ }
6765
+
6766
+ if (latlng) {
6767
+ tooltip.setLatLng(latlng);
6768
+ }
6769
+
6770
+ if (this.hasLayer(tooltip)) {
6771
+ return this;
6772
+ }
6773
+
6774
+ return this.addLayer(tooltip);
6775
+ },
6776
+
6777
+ // @method closeTooltip(tooltip?: Tooltip): this
6778
+ // Closes the tooltip given as parameter.
6779
+ closeTooltip: function (tooltip) {
6780
+ if (tooltip) {
6781
+ this.removeLayer(tooltip);
6782
+ }
6783
+ return this;
6784
+ }
6785
+
6786
+ });
6787
+
6788
+
6789
+
6790
+ /*
6791
+ * @namespace Layer
6792
+ * @section Tooltip methods example
6793
+ *
6794
+ * All layers share a set of methods convenient for binding tooltips to it.
6795
+ *
6796
+ * ```js
6797
+ * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
6798
+ * layer.openTooltip();
6799
+ * layer.closeTooltip();
6800
+ * ```
6801
+ */
6802
+
6803
+ // @section Tooltip methods
6804
+ L.Layer.include({
6805
+
6806
+ // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
6807
+ // Binds a tooltip to the layer with the passed `content` and sets up the
6808
+ // neccessary event listeners. If a `Function` is passed it will receive
6809
+ // the layer as the first argument and should return a `String` or `HTMLElement`.
6810
+ bindTooltip: function (content, options) {
6811
+
6812
+ if (content instanceof L.Tooltip) {
6813
+ L.setOptions(content, options);
6814
+ this._tooltip = content;
6815
+ content._source = this;
6816
+ } else {
6817
+ if (!this._tooltip || options) {
6818
+ this._tooltip = L.tooltip(options, this);
6819
+ }
6820
+ this._tooltip.setContent(content);
6821
+
6822
+ }
6823
+
6824
+ this._initTooltipInteractions();
6825
+
6826
+ if (this._tooltip.options.permanent) { this.openTooltip(); }
6827
+
6828
+ return this;
6829
+ },
6830
+
6831
+ // @method unbindTooltip(): this
6832
+ // Removes the tooltip previously bound with `bindTooltip`.
6833
+ unbindTooltip: function () {
6834
+ if (this._tooltip) {
6835
+ this._initTooltipInteractions(true);
6836
+ this.closeTooltip();
6837
+ this._tooltip = null;
6838
+ }
6839
+ return this;
6840
+ },
6841
+
6842
+ _initTooltipInteractions: function (remove) {
6843
+ if (!remove && this._tooltipHandlersAdded) { return; }
6844
+ var onOff = remove ? 'off' : 'on',
6845
+ events = {
6846
+ remove: this.closeTooltip,
6847
+ move: this._moveTooltip
6848
+ };
6849
+ if (!this._tooltip.options.permanent) {
6850
+ events.mouseover = this._openTooltip;
6851
+ events.mouseout = this.closeTooltip;
6852
+ if (this._tooltip.options.sticky) {
6853
+ events.mousemove = this._moveTooltip;
6854
+ }
6855
+ if (L.Browser.touch) {
6856
+ events.click = this._openTooltip;
6857
+ }
6858
+ }
6859
+ this[onOff](events);
6860
+ this._tooltipHandlersAdded = !remove;
6861
+ },
6862
+
6863
+ // @method openTooltip(latlng?: LatLng): this
6864
+ // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
6865
+ openTooltip: function (layer, latlng) {
6866
+ if (!(layer instanceof L.Layer)) {
6867
+ latlng = layer;
6868
+ layer = this;
6869
+ }
6870
+
6871
+ if (layer instanceof L.FeatureGroup) {
6872
+ for (var id in this._layers) {
6873
+ layer = this._layers[id];
6874
+ break;
6875
+ }
6876
+ }
6877
+
6878
+ if (!latlng) {
6879
+ latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
6880
+ }
6881
+
6882
+ if (this._tooltip && this._map) {
6883
+
6884
+ // set tooltip source to this layer
6885
+ this._tooltip._source = layer;
6886
+
6887
+ // update the tooltip (content, layout, ect...)
6888
+ this._tooltip.update();
6889
+
6890
+ // open the tooltip on the map
6891
+ this._map.openTooltip(this._tooltip, latlng);
6892
+
6893
+ // Tooltip container may not be defined if not permanent and never
6894
+ // opened.
6895
+ if (this._tooltip.options.interactive && this._tooltip._container) {
6896
+ L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
6897
+ this.addInteractiveTarget(this._tooltip._container);
6898
+ }
6899
+ }
6900
+
6901
+ return this;
6902
+ },
6903
+
6904
+ // @method closeTooltip(): this
6905
+ // Closes the tooltip bound to this layer if it is open.
6906
+ closeTooltip: function () {
6907
+ if (this._tooltip) {
6908
+ this._tooltip._close();
6909
+ if (this._tooltip.options.interactive) {
6910
+ L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
6911
+ this.removeInteractiveTarget(this._tooltip._container);
6912
+ }
6913
+ }
6914
+ return this;
6915
+ },
6916
+
6917
+ // @method toggleTooltip(): this
6918
+ // Opens or closes the tooltip bound to this layer depending on its current state.
6919
+ toggleTooltip: function (target) {
6920
+ if (this._tooltip) {
6921
+ if (this._tooltip._map) {
6922
+ this.closeTooltip();
6923
+ } else {
6924
+ this.openTooltip(target);
6925
+ }
6926
+ }
6927
+ return this;
6928
+ },
6929
+
6930
+ // @method isTooltipOpen(): boolean
6931
+ // Returns `true` if the tooltip bound to this layer is currently open.
6932
+ isTooltipOpen: function () {
6933
+ return this._tooltip.isOpen();
6934
+ },
6935
+
6936
+ // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
6937
+ // Sets the content of the tooltip bound to this layer.
6938
+ setTooltipContent: function (content) {
6939
+ if (this._tooltip) {
6940
+ this._tooltip.setContent(content);
6941
+ }
6942
+ return this;
6943
+ },
6944
+
6945
+ // @method getTooltip(): Tooltip
6946
+ // Returns the tooltip bound to this layer.
6947
+ getTooltip: function () {
6948
+ return this._tooltip;
6949
+ },
6950
+
6951
+ _openTooltip: function (e) {
6952
+ var layer = e.layer || e.target;
6953
+
6954
+ if (!this._tooltip || !this._map) {
6955
+ return;
6956
+ }
6957
+ this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
6958
+ },
6959
+
6960
+ _moveTooltip: function (e) {
6961
+ var latlng = e.latlng, containerPoint, layerPoint;
6962
+ if (this._tooltip.options.sticky && e.originalEvent) {
6963
+ containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
6964
+ layerPoint = this._map.containerPointToLayerPoint(containerPoint);
6965
+ latlng = this._map.layerPointToLatLng(layerPoint);
6966
+ }
6967
+ this._tooltip.setLatLng(latlng);
6968
+ }
6969
+ });
6970
+
6971
+
6972
+
6973
+ /*
6974
+ * Tooltip extension to L.Marker, adding tooltip-related methods.
6975
+ */
6976
+
6977
+ L.Marker.include({
6978
+ _getTooltipAnchor: function () {
6979
+ return this.options.icon.options.tooltipAnchor || [0, 0];
6980
+ }
6981
+ });
6982
+
6983
+
6984
+
6464
6985
  /*
6465
6986
  * @class LayerGroup
6466
6987
  * @aka L.LayerGroup
@@ -6539,7 +7060,7 @@ L.LayerGroup = L.Layer.extend({
6539
7060
  return this;
6540
7061
  },
6541
7062
 
6542
- // @method invoke(methodName: string, …): this
7063
+ // @method invoke(methodName: String, …): this
6543
7064
  // Calls `methodName` on every layer contained in this group, passing any
6544
7065
  // additional parameters. Has no effect if the layers contained do not
6545
7066
  // implement `methodName`.
@@ -6727,6 +7248,8 @@ L.featureGroup = function (layers) {
6727
7248
 
6728
7249
  L.Renderer = L.Layer.extend({
6729
7250
 
7251
+ // @section
7252
+ // @aka Renderer options
6730
7253
  options: {
6731
7254
  // @option padding: Number = 0.1
6732
7255
  // How much to extend the clip area around the map view (relative to its size)
@@ -6855,7 +7378,7 @@ L.Map.include({
6855
7378
  /*
6856
7379
  * @class Path
6857
7380
  * @aka L.Path
6858
- * @inherits Layer
7381
+ * @inherits Interactive layer
6859
7382
  *
6860
7383
  * An abstract class that contains options and constants shared between vector
6861
7384
  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
@@ -6891,11 +7414,11 @@ L.Path = L.Layer.extend({
6891
7414
  lineJoin: 'round',
6892
7415
 
6893
7416
  // @option dashArray: String = null
6894
- // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on canvas-powered layers (e.g. Android 2).
7417
+ // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
6895
7418
  dashArray: null,
6896
7419
 
6897
7420
  // @option dashOffset: String = null
6898
- // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on canvas-powered layers
7421
+ // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
6899
7422
  dashOffset: null,
6900
7423
 
6901
7424
  // @option fill: Boolean = depends
@@ -6916,8 +7439,7 @@ L.Path = L.Layer.extend({
6916
7439
 
6917
7440
  // className: '',
6918
7441
 
6919
- // @option interactive: Boolean = true
6920
- // If `false`, the vector will not emit mouse events and will act as a part of the underlying map.
7442
+ // Option inherited from "Interactive layer" abstract class
6921
7443
  interactive: true
6922
7444
  },
6923
7445
 
@@ -7109,6 +7631,7 @@ L.LineUtil = {
7109
7631
  },
7110
7632
 
7111
7633
 
7634
+ // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
7112
7635
  // Clips the segment a to b by rectangular bounds with the
7113
7636
  // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
7114
7637
  // (modifying the segment points directly!). Used by Leaflet to only show polyline
@@ -7124,10 +7647,14 @@ L.LineUtil = {
7124
7647
 
7125
7648
  while (true) {
7126
7649
  // if a,b is inside the clip window (trivial accept)
7127
- if (!(codeA | codeB)) { return [a, b]; }
7650
+ if (!(codeA | codeB)) {
7651
+ return [a, b];
7652
+ }
7128
7653
 
7129
7654
  // if a,b is outside the clip window (trivial reject)
7130
- if (codeA & codeB) { return false; }
7655
+ if (codeA & codeB) {
7656
+ return false;
7657
+ }
7131
7658
 
7132
7659
  // other cases
7133
7660
  codeOut = codeA || codeB;
@@ -7266,13 +7793,15 @@ L.LineUtil = {
7266
7793
 
7267
7794
  L.Polyline = L.Path.extend({
7268
7795
 
7796
+ // @section
7797
+ // @aka Polyline options
7269
7798
  options: {
7270
7799
  // @option smoothFactor: Number = 1.0
7271
7800
  // How much to simplify the polyline on each zoom level. More means
7272
7801
  // better performance and smoother look, and less means more accurate representation.
7273
7802
  smoothFactor: 1.0,
7274
7803
 
7275
- // @option noClip: Boolean: false
7804
+ // @option noClip: Boolean = false
7276
7805
  // Disable polyline clipping.
7277
7806
  noClip: false
7278
7807
  },
@@ -7503,7 +8032,7 @@ L.Polyline = L.Path.extend({
7503
8032
  }
7504
8033
  });
7505
8034
 
7506
- // @factory L.polyline(latlngs: LatLng[], options?: Path options)
8035
+ // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
7507
8036
  // Instantiates a polyline object given an array of geographical points and
7508
8037
  // optionally an options object. You can create a `Polyline` object with
7509
8038
  // multiple separate lines (`MultiPolyline`) by passing an array of arrays
@@ -7794,6 +8323,8 @@ L.rectangle = function (latLngBounds, options) {
7794
8323
 
7795
8324
  L.CircleMarker = L.Path.extend({
7796
8325
 
8326
+ // @section
8327
+ // @aka CircleMarker options
7797
8328
  options: {
7798
8329
  fill: true,
7799
8330
 
@@ -7871,8 +8402,8 @@ L.CircleMarker = L.Path.extend({
7871
8402
  });
7872
8403
 
7873
8404
 
7874
- // @factory L.circleMarker(latlng: LatLng, options? CircleMarker options)
7875
- //
8405
+ // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
8406
+ // Instantiates a circle marker object given a geographical point, and an optional options object.
7876
8407
  L.circleMarker = function (latlng, options) {
7877
8408
  return new L.CircleMarker(latlng, options);
7878
8409
  };
@@ -7907,6 +8438,8 @@ L.Circle = L.CircleMarker.extend({
7907
8438
 
7908
8439
  if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7909
8440
 
8441
+ // @section
8442
+ // @aka Circle options
7910
8443
  // @option radius: Number; Radius of the circle, in meters.
7911
8444
  this._mRadius = this.options.radius;
7912
8445
  },
@@ -7972,11 +8505,11 @@ L.Circle = L.CircleMarker.extend({
7972
8505
  }
7973
8506
  });
7974
8507
 
7975
- // @factory L.circle(latlng: LatLng, options?: Path options)
8508
+ // @factory L.circle(latlng: LatLng, options?: Circle options)
7976
8509
  // Instantiates a circle object given a geographical point, and an options object
7977
8510
  // which contains the circle radius.
7978
8511
  // @alternative
7979
- // @factory L.circle(latlng: LatLng, radius: Number, options?: Path options)
8512
+ // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
7980
8513
  // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
7981
8514
  // Do not use in new applications or plugins.
7982
8515
  L.circle = function (latlng, options, legacyOptions) {
@@ -8073,7 +8606,7 @@ L.SVG = L.Renderer.extend({
8073
8606
  var path = layer._path = L.SVG.create('path');
8074
8607
 
8075
8608
  // @namespace Path
8076
- // @option className: string = null
8609
+ // @option className: String = null
8077
8610
  // Custom class name set on an element. Only for SVG renderer.
8078
8611
  if (layer.options.className) {
8079
8612
  L.DomUtil.addClass(path, layer.options.className);
@@ -8183,7 +8716,7 @@ L.extend(L.SVG, {
8183
8716
  return document.createElementNS('http://www.w3.org/2000/svg', name);
8184
8717
  },
8185
8718
 
8186
- // @function pointsToPath(rings: [], closed: Boolean): String
8719
+ // @function pointsToPath(rings: Point[], closed: Boolean): String
8187
8720
  // Generates a SVG path string for multiple rings, with each ring turning
8188
8721
  // into "M..L..L.." instructions
8189
8722
  pointsToPath: function (rings, closed) {
@@ -8213,7 +8746,7 @@ L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect
8213
8746
 
8214
8747
 
8215
8748
  // @namespace SVG
8216
- // @factory L.svg(options?: SVG options)
8749
+ // @factory L.svg(options?: Renderer options)
8217
8750
  // Creates a SVG renderer with the given options.
8218
8751
  L.svg = function (options) {
8219
8752
  return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
@@ -8530,7 +9063,7 @@ L.Canvas = L.Renderer.extend({
8530
9063
 
8531
9064
  for (var id in this._layers) {
8532
9065
  layer = this._layers[id];
8533
- if (!bounds || layer._pxBounds.intersects(bounds)) {
9066
+ if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
8534
9067
  layer._updatePath();
8535
9068
  }
8536
9069
  if (clear && layer._removed) {
@@ -8632,7 +9165,7 @@ L.Canvas = L.Renderer.extend({
8632
9165
 
8633
9166
  for (var id in this._layers) {
8634
9167
  layer = this._layers[id];
8635
- if (layer.options.interactive && layer._containsPoint(point)) {
9168
+ if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
8636
9169
  L.DomEvent._fakeStop(e);
8637
9170
  layers.push(layer);
8638
9171
  }
@@ -8695,7 +9228,7 @@ L.Browser.canvas = (function () {
8695
9228
  }());
8696
9229
 
8697
9230
  // @namespace Canvas
8698
- // @factory L.canvas(options?: Canvas options)
9231
+ // @factory L.canvas(options?: Renderer options)
8699
9232
  // Creates a Canvas renderer with the given options.
8700
9233
  L.canvas = function (options) {
8701
9234
  return L.Browser.canvas ? new L.Canvas(options) : null;
@@ -8799,11 +9332,11 @@ L.GeoJSON = L.FeatureGroup.extend({
8799
9332
  * ```
8800
9333
  *
8801
9334
  * @option onEachFeature: Function = *
8802
- * A `Function` that will be called once for each created `Layer`, after it has
9335
+ * A `Function` that will be called once for each created `Feature`, after it has
8803
9336
  * been created and styled. Useful for attaching events and popups to features.
8804
9337
  * The default is to do nothing with the newly created layers:
8805
9338
  * ```js
8806
- * function (layer) {}
9339
+ * function (feature, layer) {}
8807
9340
  * ```
8808
9341
  *
8809
9342
  * @option filter: Function = *
@@ -8830,6 +9363,8 @@ L.GeoJSON = L.FeatureGroup.extend({
8830
9363
  }
8831
9364
  },
8832
9365
 
9366
+ // @function addData( <GeoJSON> data ): Layer
9367
+ // Adds a GeoJSON object to the layer.
8833
9368
  addData: function (geojson) {
8834
9369
  var features = L.Util.isArray(geojson) ? geojson : geojson.features,
8835
9370
  i, len, feature;
@@ -8865,6 +9400,8 @@ L.GeoJSON = L.FeatureGroup.extend({
8865
9400
  return this.addLayer(layer);
8866
9401
  },
8867
9402
 
9403
+ // @function resetStyle( <Path> layer ): Layer
9404
+ // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8868
9405
  resetStyle: function (layer) {
8869
9406
  // reset any custom styles
8870
9407
  layer.options = L.Util.extend({}, layer.defaultOptions);
@@ -8872,6 +9409,8 @@ L.GeoJSON = L.FeatureGroup.extend({
8872
9409
  return this;
8873
9410
  },
8874
9411
 
9412
+ // @function setStyle( <Function> style ): Layer
9413
+ // Changes styles of GeoJSON vector layers with the given style function.
8875
9414
  setStyle: function (style) {
8876
9415
  return this.eachLayer(function (layer) {
8877
9416
  this._setLayerStyle(layer, style);
@@ -9175,14 +9714,15 @@ L.DomEvent = {
9175
9714
  return this;
9176
9715
  },
9177
9716
 
9178
- // @function off(el: HTMLElement, types: String, fn: Function, context?: Object)
9717
+ // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
9179
9718
  // Removes a previously added listener function. If no function is specified,
9180
9719
  // it will remove all the listeners of that particular DOM event from the element.
9181
9720
  // Note that if you passed a custom context to on, you must pass the same
9182
9721
  // context to `off` in order to remove the listener.
9183
9722
 
9184
9723
  // @alternative
9185
- // @function off(el: HTMLElement, types: eventMap: Object, context?: Object): this
9724
+ // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
9725
+ // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
9186
9726
  off: function (obj, types, fn, context) {
9187
9727
 
9188
9728
  if (typeof types === 'object') {
@@ -9362,19 +9902,26 @@ L.DomEvent = {
9362
9902
  e.clientY - rect.top - container.clientTop);
9363
9903
  },
9364
9904
 
9905
+ // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
9906
+ // and Firefox scrolls device pixels, not CSS pixels
9907
+ _wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
9908
+ L.Browser.gecko ? window.devicePixelRatio :
9909
+ 1,
9910
+
9365
9911
  // @function getWheelDelta(ev: DOMEvent): Number
9366
9912
  // Gets normalized wheel delta from a mousewheel DOM event, in vertical
9367
9913
  // pixels scrolled (negative if scrolling down).
9368
9914
  // Events from pointing devices without precise scrolling are mapped to
9369
- // a best guess of between 50-60 pixels.
9915
+ // a best guess of 60 pixels.
9370
9916
  getWheelDelta: function (e) {
9371
- return (e.deltaY && e.deltaMode === 0) ? -e.deltaY : // Pixels
9372
- (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 18 : // Lines
9373
- (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 52 : // Pages
9917
+ return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
9918
+ (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
9919
+ (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
9920
+ (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
9374
9921
  (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
9375
9922
  e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
9376
- (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 18 : // Legacy Moz lines
9377
- e.detail ? e.detail / -32765 * 52 : // Legacy Moz pages
9923
+ (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
9924
+ e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
9378
9925
  0;
9379
9926
  },
9380
9927
 
@@ -9513,7 +10060,9 @@ L.Draggable = L.Evented.extend({
9513
10060
  // Ignore simulated events, since we handle both touch and
9514
10061
  // mouse explicitly; otherwise we risk getting duplicates of
9515
10062
  // touch events, see #4315.
9516
- if (e._simulated) { return; }
10063
+ // Also ignore the event if disabled; this happens in IE11
10064
+ // under some circumstances, see #3666.
10065
+ if (e._simulated || !this._enabled) { return; }
9517
10066
 
9518
10067
  this._moved = false;
9519
10068
 
@@ -9538,7 +10087,6 @@ L.Draggable = L.Evented.extend({
9538
10087
  var first = e.touches ? e.touches[0] : e;
9539
10088
 
9540
10089
  this._startPoint = new L.Point(first.clientX, first.clientY);
9541
- this._startPos = this._newPos = L.DomUtil.getPosition(this._element);
9542
10090
 
9543
10091
  L.DomEvent
9544
10092
  .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
@@ -9549,7 +10097,9 @@ L.Draggable = L.Evented.extend({
9549
10097
  // Ignore simulated events, since we handle both touch and
9550
10098
  // mouse explicitly; otherwise we risk getting duplicates of
9551
10099
  // touch events, see #4315.
9552
- if (e._simulated) { return; }
10100
+ // Also ignore the event if disabled; this happens in IE11
10101
+ // under some circumstances, see #3666.
10102
+ if (e._simulated || !this._enabled) { return; }
9553
10103
 
9554
10104
  if (e.touches && e.touches.length > 1) {
9555
10105
  this._moved = true;
@@ -9601,7 +10151,7 @@ L.Draggable = L.Evented.extend({
9601
10151
  this.fire('predrag', e);
9602
10152
  L.DomUtil.setPosition(this._element, this._newPos);
9603
10153
 
9604
- // @event predrag: Event
10154
+ // @event drag: Event
9605
10155
  // Fired continuously during dragging.
9606
10156
  this.fire('drag', e);
9607
10157
  },
@@ -9610,7 +10160,9 @@ L.Draggable = L.Evented.extend({
9610
10160
  // Ignore simulated events, since we handle both touch and
9611
10161
  // mouse explicitly; otherwise we risk getting duplicates of
9612
10162
  // touch events, see #4315.
9613
- if (e._simulated) { return; }
10163
+ // Also ignore the event if disabled; this happens in IE11
10164
+ // under some circumstances, see #3666.
10165
+ if (e._simulated || !this._enabled) { return; }
9614
10166
 
9615
10167
  L.DomUtil.removeClass(document.body, 'leaflet-dragging');
9616
10168
 
@@ -9632,7 +10184,7 @@ L.Draggable = L.Evented.extend({
9632
10184
  // ensure drag is not fired after dragend
9633
10185
  L.Util.cancelAnimFrame(this._animRequest);
9634
10186
 
9635
- // @event dragend: Event
10187
+ // @event dragend: DragEndEvent
9636
10188
  // Fired when the drag ends.
9637
10189
  this.fire('dragend', {
9638
10190
  distance: this._newPos.distanceTo(this._startPos)
@@ -9660,22 +10212,24 @@ L.Handler = L.Class.extend({
9660
10212
  this._map = map;
9661
10213
  },
9662
10214
 
9663
- // @method enable()
10215
+ // @method enable(): this
9664
10216
  // Enables the handler
9665
10217
  enable: function () {
9666
- if (this._enabled) { return; }
10218
+ if (this._enabled) { return this; }
9667
10219
 
9668
10220
  this._enabled = true;
9669
10221
  this.addHooks();
10222
+ return this;
9670
10223
  },
9671
10224
 
9672
- // @method disable()
10225
+ // @method disable(): this
9673
10226
  // Disables the handler
9674
10227
  disable: function () {
9675
- if (!this._enabled) { return; }
10228
+ if (!this._enabled) { return this; }
9676
10229
 
9677
10230
  this._enabled = false;
9678
10231
  this.removeHooks();
10232
+ return this;
9679
10233
  },
9680
10234
 
9681
10235
  // @method enabled(): Boolean
@@ -9762,7 +10316,7 @@ L.Map.Drag = L.Handler.extend({
9762
10316
  map.whenReady(this._onZoomEnd, this);
9763
10317
  }
9764
10318
  }
9765
- L.DomUtil.addClass(this._map._container, 'leaflet-grab');
10319
+ L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
9766
10320
  this._draggable.enable();
9767
10321
  this._positions = [];
9768
10322
  this._times = [];
@@ -9770,6 +10324,7 @@ L.Map.Drag = L.Handler.extend({
9770
10324
 
9771
10325
  removeHooks: function () {
9772
10326
  L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
10327
+ L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
9773
10328
  this._draggable.disable();
9774
10329
  },
9775
10330
 
@@ -9930,7 +10485,7 @@ L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
9930
10485
  // @section Interaction Options
9931
10486
 
9932
10487
  L.Map.mergeOptions({
9933
- // @option doubleClickZoom: Boolean = true
10488
+ // @option doubleClickZoom: Boolean|String = true
9934
10489
  // Whether the map can be zoomed in by double clicking on it and
9935
10490
  // zoomed out by double clicking while holding shift. If passed
9936
10491
  // `'center'`, double-click zoom will zoom to the center of the
@@ -9985,7 +10540,7 @@ L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
9985
10540
  // @section Interaction Options
9986
10541
  L.Map.mergeOptions({
9987
10542
  // @section Mousewheel options
9988
- // @option scrollWheelZoom: Boolean = true
10543
+ // @option scrollWheelZoom: Boolean|String = true
9989
10544
  // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
9990
10545
  // it will zoom to the center of the view regardless of where the mouse was.
9991
10546
  scrollWheelZoom: true,
@@ -9995,11 +10550,11 @@ L.Map.mergeOptions({
9995
10550
  // user can't zoom via wheel more often than once per 40 ms.
9996
10551
  wheelDebounceTime: 40,
9997
10552
 
9998
- // @option wheelPxPerZoomLevel: Number = 50
10553
+ // @option wheelPxPerZoomLevel: Number = 60
9999
10554
  // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
10000
10555
  // mean a change of one full zoom level. Smaller values will make wheel-zooming
10001
10556
  // faster (and vice versa).
10002
- wheelPxPerZoomLevel: 50
10557
+ wheelPxPerZoomLevel: 60
10003
10558
  });
10004
10559
 
10005
10560
  L.Map.ScrollWheelZoom = L.Handler.extend({
@@ -10300,7 +10855,7 @@ L.extend(L.DomEvent, {
10300
10855
  // @section Interaction Options
10301
10856
  L.Map.mergeOptions({
10302
10857
  // @section Touch interaction options
10303
- // @option touchZoom: Boolean = *
10858
+ // @option touchZoom: Boolean|String = *
10304
10859
  // Whether the map can be zoomed by touch-dragging with two fingers. If
10305
10860
  // passed `'center'`, it will zoom to the center of the view regardless of
10306
10861
  // where the touch events (fingers) were. Enabled for touch-capable web
@@ -10315,10 +10870,12 @@ L.Map.mergeOptions({
10315
10870
 
10316
10871
  L.Map.TouchZoom = L.Handler.extend({
10317
10872
  addHooks: function () {
10873
+ L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
10318
10874
  L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
10319
10875
  },
10320
10876
 
10321
10877
  removeHooks: function () {
10878
+ L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
10322
10879
  L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
10323
10880
  },
10324
10881
 
@@ -11062,15 +11619,31 @@ L.control = function (options) {
11062
11619
  return new L.Control(options);
11063
11620
  };
11064
11621
 
11622
+ /* @section Extension methods
11623
+ * @uninheritable
11624
+ *
11625
+ * Every control should extend from `L.Control` and (re-)implement the following methods.
11626
+ *
11627
+ * @method onAdd(map: Map): HTMLElement
11628
+ * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
11629
+ *
11630
+ * @method onRemove(map: Map)
11631
+ * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
11632
+ */
11065
11633
 
11066
- // adds control-related methods to L.Map
11067
-
11634
+ /* @namespace Map
11635
+ * @section Methods for Layers and Controls
11636
+ */
11068
11637
  L.Map.include({
11638
+ // @method addControl(control: Control): this
11639
+ // Adds the given control to the map
11069
11640
  addControl: function (control) {
11070
11641
  control.addTo(this);
11071
11642
  return this;
11072
11643
  },
11073
11644
 
11645
+ // @method removeControl(control: Control): this
11646
+ // Removes the given control from the map
11074
11647
  removeControl: function (control) {
11075
11648
  control.remove();
11076
11649
  return this;
@@ -11576,8 +12149,8 @@ L.Control.Layers = L.Control.extend({
11576
12149
  onRemove: function () {
11577
12150
  this._map.off('zoomend', this._checkDisabledLayers, this);
11578
12151
 
11579
- for (var id in this._layers) {
11580
- this._layers[id].layer.off('add remove', this._onLayerChange, this);
12152
+ for (var i = 0; i < this._layers.length; i++) {
12153
+ this._layers[i].layer.off('add remove', this._onLayerChange, this);
11581
12154
  }
11582
12155
  },
11583
12156
 
@@ -11601,7 +12174,9 @@ L.Control.Layers = L.Control.extend({
11601
12174
  layer.off('add remove', this._onLayerChange, this);
11602
12175
 
11603
12176
  var obj = this._getLayer(L.stamp(layer));
11604
- this._layers.splice(this._layers.indexOf(obj), 1);
12177
+ if (obj) {
12178
+ this._layers.splice(this._layers.indexOf(obj), 1);
12179
+ }
11605
12180
  return (this._map) ? this._update() : this;
11606
12181
  },
11607
12182
 
@@ -11681,8 +12256,9 @@ L.Control.Layers = L.Control.extend({
11681
12256
  },
11682
12257
 
11683
12258
  _getLayer: function (id) {
11684
- for (var i = 0; i <= this._layers.length; i++) {
11685
- if (L.stamp(this._layers[i].layer) === id) {
12259
+ for (var i = 0; i < this._layers.length; i++) {
12260
+
12261
+ if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
11686
12262
  return this._layers[i];
11687
12263
  }
11688
12264
  }
@@ -11711,7 +12287,7 @@ L.Control.Layers = L.Control.extend({
11711
12287
 
11712
12288
  var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
11713
12289
 
11714
- for (i in this._layers) {
12290
+ for (i = 0; i < this._layers.length; i++) {
11715
12291
  obj = this._layers[i];
11716
12292
  this._addItem(obj);
11717
12293
  overlaysPresent = overlaysPresent || obj.overlay;
@@ -11882,7 +12458,7 @@ L.control.layers = function (baseLayers, overlays, options) {
11882
12458
  * @example
11883
12459
  * ```js
11884
12460
  * var fx = new L.PosAnimation();
11885
- f x.run(el, [300, 500], 0.5);*
12461
+ * fx.run(el, [300, 500], 0.5);
11886
12462
  * ```
11887
12463
  *
11888
12464
  * @constructor L.PosAnimation()
@@ -12329,10 +12905,12 @@ L.Map.include({
12329
12905
  },
12330
12906
 
12331
12907
  // @method locate(options?: Locate options): this
12332
- // Tries to locate the user using the Geolocation API, firing a `locationfound`
12333
- // event with location data on success or a `locationerror` event on failure,
12908
+ // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
12909
+ // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
12334
12910
  // and optionally sets the map view to the user's location with respect to
12335
12911
  // detection accuracy (or to the world view if geolocation failed).
12912
+ // Note that, if your page doesn't use HTTPS, this method will fail in
12913
+ // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
12336
12914
  // See `Locate options` for more details.
12337
12915
  locate: function (options) {
12338
12916
 
@@ -12423,5 +13001,6 @@ L.Map.include({
12423
13001
  });
12424
13002
 
12425
13003
 
13004
+
12426
13005
  }(window, document));
12427
13006
  //# sourceMappingURL=<%= asset_path('leaflet-src.map') %>