]> git.openstreetmap.org Git - rails.git/commitdiff
Update leaflet to 1.5.1
authorTom Hughes <tom@compton.nu>
Wed, 8 May 2019 18:00:15 +0000 (19:00 +0100)
committerTom Hughes <tom@compton.nu>
Wed, 8 May 2019 18:00:15 +0000 (19:00 +0100)
Vendorfile
vendor/assets/leaflet/leaflet.css
vendor/assets/leaflet/leaflet.js

index 22a6ae422b4355efb15e4edbb5fa8bea337a6cc1..ad59f91ebea6aef14b73f8295c84aeeb14993c05 100644 (file)
@@ -11,13 +11,13 @@ folder 'vendor/assets' do
   end
 
   folder 'leaflet' do
   end
 
   folder 'leaflet' do
-    file 'leaflet.js', 'https://unpkg.com/leaflet@1.4.0/dist/leaflet-src.js'
-    file 'leaflet.css', 'https://unpkg.com/leaflet@1.4.0/dist/leaflet.css'
+    file 'leaflet.js', 'https://unpkg.com/leaflet@1.5.1/dist/leaflet-src.js'
+    file 'leaflet.css', 'https://unpkg.com/leaflet@1.5.1/dist/leaflet.css'
 
     [ 'layers.png', 'layers-2x.png',
       'marker-icon.png', 'marker-icon-2x.png',
       'marker-shadow.png' ].each do |image|
 
     [ 'layers.png', 'layers-2x.png',
       'marker-icon.png', 'marker-icon-2x.png',
       'marker-shadow.png' ].each do |image|
-      file "images/#{image}", "https://unpkg.com/leaflet@1.4.0/dist/images/#{image}"
+      file "images/#{image}", "https://unpkg.com/leaflet@1.5.1/dist/images/#{image}"
     end
 
     from 'git://github.com/aratcliffe/Leaflet.contextmenu.git', :tag => 'v1.5.0' do
     end
 
     from 'git://github.com/aratcliffe/Leaflet.contextmenu.git', :tag => 'v1.5.0' do
index 70802f36874bffb5d8fee4b57a8c7d8f93a3aa1a..609a662453604e9a8a7be0355dced6f671e56d80 100644 (file)
                user-select: none;
          -webkit-user-drag: none;
        }
                user-select: none;
          -webkit-user-drag: none;
        }
+/* Prevents IE11 from highlighting tiles in blue */
+.leaflet-tile::selection {
+       background: transparent;
+}
 /* Safari renders non-retina tile on retina better with this, but Chrome is worse */
 .leaflet-safari .leaflet-tile {
        image-rendering: -webkit-optimize-contrast;
 /* Safari renders non-retina tile on retina better with this, but Chrome is worse */
 .leaflet-safari .leaflet-tile {
        image-rendering: -webkit-optimize-contrast;
 
 .leaflet-marker-icon.leaflet-interactive,
 .leaflet-image-layer.leaflet-interactive,
 
 .leaflet-marker-icon.leaflet-interactive,
 .leaflet-image-layer.leaflet-interactive,
-.leaflet-pane > svg path.leaflet-interactive {
+.leaflet-pane > svg path.leaflet-interactive,
+svg.leaflet-image-layer.leaflet-interactive path {
        pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
        pointer-events: auto;
        }
        pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
        pointer-events: auto;
        }
index 2327057ca7b3346903085d53557516ae03c5e08a..a845eb6aeeccd94d1cd79a2ac770d0c83c06f442 100644 (file)
@@ -1,5 +1,5 @@
 /* @preserve
 /* @preserve
- * Leaflet 1.4.0, a JS library for interactive maps. http://leafletjs.com
+ * Leaflet 1.5.1+build.2e3e0ff, a JS library for interactive maps. http://leafletjs.com
  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
  */
 
  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
  */
 
@@ -9,7 +9,7 @@
        (factory((global.L = {})));
 }(this, (function (exports) { 'use strict';
 
        (factory((global.L = {})));
 }(this, (function (exports) { 'use strict';
 
-var version = "1.4.0";
+var version = "1.5.1+build.2e3e0ffb";
 
 /*
  * @namespace Util
 
 /*
  * @namespace Util
@@ -127,8 +127,8 @@ function falseFn() { return false; }
 // @function formatNum(num: Number, digits?: Number): Number
 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
 function formatNum(num, digits) {
 // @function formatNum(num: Number, digits?: Number): Number
 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
 function formatNum(num, digits) {
-       var pow = Math.pow(10, (digits === undefined ? 6 : digits));
-       return Math.round(num * pow) / pow;
+       digits = (digits === undefined ? 6 : digits);
+       return +(Math.round(num + ('e+' + digits)) + ('e-' + digits));
 }
 
 // @function trim(str: String): String
 }
 
 // @function trim(str: String): String
@@ -468,7 +468,7 @@ var Events = {
         *
         * @alternative
         * @method off: this
         *
         * @alternative
         * @method off: this
-        * Removes all listeners to all events on the object.
+        * Removes all listeners to all events on the object. This includes implicitly attached events.
         */
        off: function (types, fn, context) {
 
         */
        off: function (types, fn, context) {
 
@@ -1648,9 +1648,11 @@ var Earth = extend({}, CRS, {
  * a sphere. Used by the `EPSG:3857` CRS.
  */
 
  * a sphere. Used by the `EPSG:3857` CRS.
  */
 
+var earthRadius = 6378137;
+
 var SphericalMercator = {
 
 var SphericalMercator = {
 
-       R: 6378137,
+       R: earthRadius,
        MAX_LATITUDE: 85.0511287798,
 
        project: function (latlng) {
        MAX_LATITUDE: 85.0511287798,
 
        project: function (latlng) {
@@ -1673,7 +1675,7 @@ var SphericalMercator = {
        },
 
        bounds: (function () {
        },
 
        bounds: (function () {
-               var d = 6378137 * Math.PI;
+               var d = earthRadius * Math.PI;
                return new Bounds([-d, -d], [d, d]);
        })()
 };
                return new Bounds([-d, -d], [d, d]);
        })()
 };
@@ -2171,6 +2173,7 @@ function addDoubleTapListener(obj, handler, id) {
                                touch$$1 = newTouch;
                        }
                        touch$$1.type = 'dblclick';
                                touch$$1 = newTouch;
                        }
                        touch$$1.type = 'dblclick';
+                       touch$$1.button = 0;
                        handler(touch$$1);
                        last = null;
                }
                        handler(touch$$1);
                        last = null;
                }
@@ -4308,9 +4311,15 @@ var Map = Evented.extend({
                // this event. Also fired on mobile when the user holds a single touch
                // for a second (also called long press).
                // @event keypress: KeyboardEvent
                // this event. Also fired on mobile when the user holds a single touch
                // for a second (also called long press).
                // @event keypress: KeyboardEvent
-               // Fired when the user presses a key from the keyboard while the map is focused.
+               // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
+               // @event keydown: KeyboardEvent
+               // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
+               // the `keydown` event is fired for keys that produce a character value and for keys
+               // that do not produce a character value.
+               // @event keyup: KeyboardEvent
+               // Fired when the user releases a key from the keyboard while the map is focused.
                onOff(this._container, 'click dblclick mousedown mouseup ' +
                onOff(this._container, 'click dblclick mousedown mouseup ' +
-                       'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
+                       'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
 
                if (this.options.trackResize) {
                        onOff(window, 'resize', this._onResize, this);
 
                if (this.options.trackResize) {
                        onOff(window, 'resize', this._onResize, this);
@@ -4374,7 +4383,7 @@ var Map = Evented.extend({
 
                var type = e.type;
 
 
                var type = e.type;
 
-               if (type === 'mousedown' || type === 'keypress') {
+               if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
                        // prevents outline when clicking on keyboard-focusable element
                        preventOutline(e.target || e.srcElement);
                }
                        // prevents outline when clicking on keyboard-focusable element
                        preventOutline(e.target || e.srcElement);
                }
@@ -4413,7 +4422,7 @@ var Map = Evented.extend({
                        originalEvent: e
                };
 
                        originalEvent: e
                };
 
-               if (e.type !== 'keypress') {
+               if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
                        var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
                        data.containerPoint = isMarker ?
                                this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
                        var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
                        data.containerPoint = isMarker ?
                                this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
@@ -4666,7 +4675,7 @@ var Map = Evented.extend({
                }
 
                // @event zoomanim: ZoomAnimEvent
                }
 
                // @event zoomanim: ZoomAnimEvent
-               // Fired at least once per zoom animation. For continous zoom, like pinch zooming, fired once per frame during zoom.
+               // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
                this.fire('zoomanim', {
                        center: center,
                        zoom: zoom,
                this.fire('zoomanim', {
                        center: center,
                        zoom: zoom,
@@ -4784,6 +4793,8 @@ var Control = Class.extend({
                        corner.appendChild(container);
                }
 
                        corner.appendChild(container);
                }
 
+               this._map.on('unload', this.remove, this);
+
                return this;
        },
 
                return this;
        },
 
@@ -4800,6 +4811,7 @@ var Control = Class.extend({
                        this.onRemove(this._map);
                }
 
                        this.onRemove(this._map);
                }
 
+               this._map.off('unload', this.remove, this);
                this._map = null;
 
                return this;
                this._map = null;
 
                return this;
@@ -5197,7 +5209,7 @@ var Layers = Control.extend({
                        input.className = 'leaflet-control-layers-selector';
                        input.defaultChecked = checked;
                } else {
                        input.className = 'leaflet-control-layers-selector';
                        input.defaultChecked = checked;
                } else {
-                       input = this._createRadioElement('leaflet-base-layers', checked);
+                       input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
                }
 
                this._layerControlInputs.push(input);
                }
 
                this._layerControlInputs.push(input);
@@ -5582,7 +5594,7 @@ var Attribution = Control.extend({
 
                // @option prefix: String = 'Leaflet'
                // The HTML text shown before the attributions. Pass `false` to disable.
 
                // @option prefix: String = 'Leaflet'
                // The HTML text shown before the attributions. Pass `false` to disable.
-               prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
+               prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
        },
 
        initialize: function (options) {
        },
 
        initialize: function (options) {
@@ -6327,7 +6339,7 @@ var LonLat = {
  * @namespace Projection
  * @projection L.Projection.Mercator
  *
  * @namespace Projection
  * @projection L.Projection.Mercator
  *
- * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
+ * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
  */
 
 var Mercator = {
  */
 
 var Mercator = {
@@ -6487,7 +6499,7 @@ CRS.Simple = Simple;
  * @example
  *
  * ```js
  * @example
  *
  * ```js
- * var layer = L.Marker(latlng).addTo(map);
+ * var layer = L.marker(latlng).addTo(map);
  * layer.addTo(map);
  * layer.remove();
  * ```
  * layer.addTo(map);
  * layer.remove();
  * ```
@@ -7418,6 +7430,10 @@ var Marker = Layer.extend({
                // `Map pane` where the markers icon will be added.
                pane: 'markerPane',
 
                // `Map pane` where the markers icon will be added.
                pane: 'markerPane',
 
+               // @option pane: String = 'shadowPane'
+               // `Map pane` where the markers shadow will be added.
+               shadowPane: 'shadowPane',
+
                // @option bubblingMouseEvents: Boolean = false
                // When `true`, a mouse event on this marker will trigger the same event on the map
                // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
                // @option bubblingMouseEvents: Boolean = false
                // When `true`, a mouse event on this marker will trigger the same event on the map
                // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
@@ -7510,6 +7526,12 @@ var Marker = Layer.extend({
                return this.update();
        },
 
                return this.update();
        },
 
+       // @method getIcon: Icon
+       // Returns the current icon used by the marker
+       getIcon: function () {
+               return this.options.icon;
+       },
+
        // @method setIcon(icon: Icon): this
        // Changes the marker icon.
        setIcon: function (icon) {
        // @method setIcon(icon: Icon): this
        // Changes the marker icon.
        setIcon: function (icon) {
@@ -7605,7 +7627,7 @@ var Marker = Layer.extend({
                }
                this._initInteraction();
                if (newShadow && addShadow) {
                }
                this._initInteraction();
                if (newShadow && addShadow) {
-                       this.getPane('shadowPane').appendChild(this._shadow);
+                       this.getPane(options.shadowPane).appendChild(this._shadow);
                }
        },
 
                }
        },
 
@@ -7689,7 +7711,9 @@ var Marker = Layer.extend({
        _updateOpacity: function () {
                var opacity = this.options.opacity;
 
        _updateOpacity: function () {
                var opacity = this.options.opacity;
 
-               setOpacity(this._icon, opacity);
+               if (this._icon) {
+                       setOpacity(this._icon, opacity);
+               }
 
                if (this._shadow) {
                        setOpacity(this._shadow, opacity);
 
                if (this._shadow) {
                        setOpacity(this._shadow, opacity);
@@ -7826,6 +7850,9 @@ var Path = Layer.extend({
                setOptions(this, style);
                if (this._renderer) {
                        this._renderer._updateStyle(this);
                setOptions(this, style);
                if (this._renderer) {
                        this._renderer._updateStyle(this);
+                       if (this.options.stroke && style.hasOwnProperty('weight')) {
+                               this._updateBounds();
+                       }
                }
                return this;
        },
                }
                return this;
        },
@@ -8267,16 +8294,21 @@ var Polyline = Path.extend({
                this._rings = [];
                this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
                this._rings = [];
                this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
-               var w = this._clickTolerance(),
-                   p = new Point(w, w);
-
                if (this._bounds.isValid() && pxBounds.isValid()) {
                if (this._bounds.isValid() && pxBounds.isValid()) {
-                       pxBounds.min._subtract(p);
-                       pxBounds.max._add(p);
-                       this._pxBounds = pxBounds;
+                       this._rawPxBounds = pxBounds;
+                       this._updateBounds();
                }
        },
 
                }
        },
 
+       _updateBounds: function () {
+               var w = this._clickTolerance(),
+                   p = new Point(w, w);
+               this._pxBounds = new Bounds([
+                       this._rawPxBounds.min.subtract(p),
+                       this._rawPxBounds.max.add(p)
+               ]);
+       },
+
        // recursively turns latlngs into a set of rings with projected coordinates
        _projectLatlngs: function (latlngs, result, projectedBounds) {
                var flat = latlngs[0] instanceof LatLng,
        // recursively turns latlngs into a set of rings with projected coordinates
        _projectLatlngs: function (latlngs, result, projectedBounds) {
                var flat = latlngs[0] instanceof LatLng,
@@ -8704,10 +8736,10 @@ var GeoJSON = FeatureGroup.extend({
        },
 
        _setLayerStyle: function (layer, style) {
        },
 
        _setLayerStyle: function (layer, style) {
-               if (typeof style === 'function') {
-                       style = style(layer.feature);
-               }
                if (layer.setStyle) {
                if (layer.setStyle) {
+                       if (typeof style === 'function') {
+                               style = style(layer.feature);
+                       }
                        layer.setStyle(style);
                }
        }
                        layer.setStyle(style);
                }
        }
@@ -8857,19 +8889,25 @@ var PointToGeoJSON = {
 };
 
 // @namespace Marker
 };
 
 // @namespace Marker
-// @method toGeoJSON(): Object
+// @method toGeoJSON(precision?: Number): Object
+// `precision` is the number of decimal places for coordinates.
+// The default value is 6 places.
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 Marker.include(PointToGeoJSON);
 
 // @namespace CircleMarker
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 Marker.include(PointToGeoJSON);
 
 // @namespace CircleMarker
-// @method toGeoJSON(): Object
+// @method toGeoJSON(precision?: Number): Object
+// `precision` is the number of decimal places for coordinates.
+// The default value is 6 places.
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 Circle.include(PointToGeoJSON);
 CircleMarker.include(PointToGeoJSON);
 
 
 // @namespace Polyline
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 Circle.include(PointToGeoJSON);
 CircleMarker.include(PointToGeoJSON);
 
 
 // @namespace Polyline
-// @method toGeoJSON(): Object
+// @method toGeoJSON(precision?: Number): Object
+// `precision` is the number of decimal places for coordinates.
+// The default value is 6 places.
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 Polyline.include({
        toGeoJSON: function (precision) {
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 Polyline.include({
        toGeoJSON: function (precision) {
@@ -8885,7 +8923,9 @@ Polyline.include({
 });
 
 // @namespace Polygon
 });
 
 // @namespace Polygon
-// @method toGeoJSON(): Object
+// @method toGeoJSON(precision?: Number): Object
+// `precision` is the number of decimal places for coordinates.
+// The default value is 6 places.
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 Polygon.include({
        toGeoJSON: function (precision) {
 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 Polygon.include({
        toGeoJSON: function (precision) {
@@ -8921,7 +8961,9 @@ LayerGroup.include({
                });
        },
 
                });
        },
 
-       // @method toGeoJSON(): Object
+       // @method toGeoJSON(precision?: Number): Object
+       // `precision` is the number of decimal places for coordinates.
+       // The default value is 6 places.
        // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
        toGeoJSON: function (precision) {
 
        // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
        toGeoJSON: function (precision) {
 
@@ -9266,7 +9308,12 @@ var VideoOverlay = ImageOverlay.extend({
 
                // @option loop: Boolean = true
                // Whether the video will loop back to the beginning when played.
 
                // @option loop: Boolean = true
                // Whether the video will loop back to the beginning when played.
-               loop: true
+               loop: true,
+
+               // @option keepAspectRatio: Boolean = true
+               // Whether the video will save aspect ratio after the projection.
+               // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
+               keepAspectRatio: true
        },
 
        _initImage: function () {
        },
 
        _initImage: function () {
@@ -9296,6 +9343,7 @@ var VideoOverlay = ImageOverlay.extend({
 
                if (!isArray(this._url)) { this._url = [this._url]; }
 
 
                if (!isArray(this._url)) { this._url = [this._url]; }
 
+               if (!this.options.keepAspectRatio && vid.style.hasOwnProperty('objectFit')) { vid.style['objectFit'] = 'fill'; }
                vid.autoplay = !!this.options.autoplay;
                vid.loop = !!this.options.loop;
                for (var i = 0; i < this._url.length; i++) {
                vid.autoplay = !!this.options.autoplay;
                vid.loop = !!this.options.loop;
                for (var i = 0; i < this._url.length; i++) {
@@ -9319,6 +9367,49 @@ function videoOverlay(video, bounds, options) {
        return new VideoOverlay(video, bounds, options);
 }
 
        return new VideoOverlay(video, bounds, options);
 }
 
+/*
+ * @class SVGOverlay
+ * @aka L.SVGOverlay
+ * @inherits ImageOverlay
+ *
+ * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
+ *
+ * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
+ *
+ * @example
+ *
+ * ```js
+ * var element = '<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" height="200" width="200"/></svg>',
+ *              elementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
+ * L.svgOverlay(element, elementBounds).addTo(map);
+ * ```
+ */
+
+var SVGOverlay = ImageOverlay.extend({
+       _initImage: function () {
+               var el = this._image = this._url;
+
+               addClass(el, 'leaflet-image-layer');
+               if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
+
+               el.onselectstart = falseFn;
+               el.onmousemove = falseFn;
+       }
+
+       // @method getElement(): SVGElement
+       // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
+       // used by this overlay.
+});
+
+
+// @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
+// Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
+// A viewBox attribute is required on the SVG element to zoom in and out properly.
+
+function svgOverlay(el, bounds, options) {
+       return new SVGOverlay(el, bounds, options);
+}
+
 /*
  * @class DivOverlay
  * @inherits Layer
 /*
  * @class DivOverlay
  * @inherits Layer
@@ -9473,6 +9564,38 @@ var DivOverlay = Layer.extend({
                return this;
        },
 
                return this;
        },
 
+       _prepareOpen: function (parent, layer, latlng) {
+               if (!(layer instanceof Layer)) {
+                       latlng = layer;
+                       layer = parent;
+               }
+
+               if (layer instanceof FeatureGroup) {
+                       for (var id in parent._layers) {
+                               layer = parent._layers[id];
+                               break;
+                       }
+               }
+
+               if (!latlng) {
+                       if (layer.getCenter) {
+                               latlng = layer.getCenter();
+                       } else if (layer.getLatLng) {
+                               latlng = layer.getLatLng();
+                       } else {
+                               throw new Error('Unable to get source layer LatLng.');
+                       }
+               }
+
+               // set overlay source to this layer
+               this._source = layer;
+
+               // update the overlay (content, layout, ect...)
+               this.update();
+
+               return latlng;
+       },
+
        _updateContent: function () {
                if (!this._content) { return; }
 
        _updateContent: function () {
                if (!this._content) { return; }
 
@@ -9927,28 +10050,8 @@ Layer.include({
        // @method openPopup(latlng?: LatLng): this
        // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
        openPopup: function (layer, latlng) {
        // @method openPopup(latlng?: LatLng): this
        // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
        openPopup: function (layer, latlng) {
-               if (!(layer instanceof Layer)) {
-                       latlng = layer;
-                       layer = this;
-               }
-
-               if (layer instanceof FeatureGroup) {
-                       for (var id in this._layers) {
-                               layer = this._layers[id];
-                               break;
-                       }
-               }
-
-               if (!latlng) {
-                       latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
-               }
-
                if (this._popup && this._map) {
                if (this._popup && this._map) {
-                       // set popup source to this layer
-                       this._popup._source = layer;
-
-                       // update the popup (content, layout, ect...)
-                       this._popup.update();
+                       latlng = this._popup._prepareOpen(this, layer, latlng);
 
                        // open the popup on the map
                        this._map.openPopup(this._popup, latlng);
 
                        // open the popup on the map
                        this._map.openPopup(this._popup, latlng);
@@ -10345,29 +10448,8 @@ Layer.include({
        // @method openTooltip(latlng?: LatLng): this
        // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
        openTooltip: function (layer, latlng) {
        // @method openTooltip(latlng?: LatLng): this
        // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
        openTooltip: function (layer, latlng) {
-               if (!(layer instanceof Layer)) {
-                       latlng = layer;
-                       layer = this;
-               }
-
-               if (layer instanceof FeatureGroup) {
-                       for (var id in this._layers) {
-                               layer = this._layers[id];
-                               break;
-                       }
-               }
-
-               if (!latlng) {
-                       latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
-               }
-
                if (this._tooltip && this._map) {
                if (this._tooltip && this._map) {
-
-                       // set tooltip source to this layer
-                       this._tooltip._source = layer;
-
-                       // update the tooltip (content, layout, ect...)
-                       this._tooltip.update();
+                       latlng = this._tooltip._prepareOpen(this, layer, latlng);
 
                        // open the tooltip on the map
                        this._map.openTooltip(this._tooltip, latlng);
 
                        // open the tooltip on the map
                        this._map.openTooltip(this._tooltip, latlng);
@@ -10478,8 +10560,9 @@ var DivIcon = Icon.extend({
                // iconAnchor: (Point),
                // popupAnchor: (Point),
 
                // iconAnchor: (Point),
                // popupAnchor: (Point),
 
-               // @option html: String = ''
-               // Custom HTML code to put inside the div element, empty by default.
+               // @option html: String|HTMLElement = ''
+               // Custom HTML code to put inside the div element, empty by default. Alternatively,
+               // an instance of `HTMLElement`.
                html: false,
 
                // @option bgPos: Point = [0, 0]
                html: false,
 
                // @option bgPos: Point = [0, 0]
@@ -10493,7 +10576,12 @@ var DivIcon = Icon.extend({
                var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
                    options = this.options;
 
                var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
                    options = this.options;
 
-               div.innerHTML = options.html !== false ? options.html : '';
+               if (options.html instanceof Element) {
+                       empty(div);
+                       div.appendChild(options.html);
+               } else {
+                       div.innerHTML = options.html !== false ? options.html : '';
+               }
 
                if (options.bgPos) {
                        var bgPos = toPoint(options.bgPos);
 
                if (options.bgPos) {
                        var bgPos = toPoint(options.bgPos);
@@ -13883,6 +13971,8 @@ exports.ImageOverlay = ImageOverlay;
 exports.imageOverlay = imageOverlay;
 exports.VideoOverlay = VideoOverlay;
 exports.videoOverlay = videoOverlay;
 exports.imageOverlay = imageOverlay;
 exports.VideoOverlay = VideoOverlay;
 exports.videoOverlay = videoOverlay;
+exports.SVGOverlay = SVGOverlay;
+exports.svgOverlay = svgOverlay;
 exports.DivOverlay = DivOverlay;
 exports.Popup = Popup;
 exports.popup = popup;
 exports.DivOverlay = DivOverlay;
 exports.Popup = Popup;
 exports.popup = popup;