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

Sign up to get free protection for your applications and to get access to all the features.
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') %>