1 // Following https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md
2 (function (factory
, window
) {
4 // define an AMD module that relies on 'leaflet'
5 if (typeof define
=== 'function' && define
.amd
) {
6 define(['leaflet'], factory
);
8 // define a Common JS module that relies on 'leaflet'
9 } else if (typeof exports
=== 'object') {
10 module
.exports
= factory(require('leaflet'));
13 // attach your plugin to the global 'L' variable
14 if (typeof window
!== 'undefined' && window
.L
) {
15 window
.L
.Control
.MiniMap
= factory(L
);
16 window
.L
.control
.minimap = function (layer
, options
) {
17 return new window
.L
.Control
.MiniMap(layer
, options
);
22 var MiniMap
= L
.Control
.extend({
24 position
: 'bottomright',
27 zoomLevelFixed
: false,
30 autoToggleDisplay
: false,
36 aimingRectOptions
: {color
: '#ff7800', weight
: 1, clickable
: false},
37 shadowRectOptions
: {color
: '#000000', weight
: 1, clickable
: false, opacity
: 0, fillOpacity
: 0},
38 strings
: {hideText
: 'Hide MiniMap', showText
: 'Show MiniMap'},
39 mapOptions
: {} // Allows definition / override of Leaflet map options.
42 // layer is the map layer to be shown in the minimap
43 initialize: function (layer
, options
) {
44 L
.Util
.setOptions(this, options
);
45 // Make sure the aiming rects are non-clickable even if the user tries to set them clickable (most likely by forgetting to specify them false)
46 this.options
.aimingRectOptions
.clickable
= false;
47 this.options
.shadowRectOptions
.clickable
= false;
51 onAdd: function (map
) {
55 // Creating the container and stopping events from spilling through to the main map.
56 this._container
= L
.DomUtil
.create('div', 'leaflet-control-minimap');
57 this._container
.style
.width
= this.options
.width
+ 'px';
58 this._container
.style
.height
= this.options
.height
+ 'px';
59 L
.DomEvent
.disableClickPropagation(this._container
);
60 L
.DomEvent
.on(this._container
, 'mousewheel', L
.DomEvent
.stopPropagation
);
63 attributionControl
: false,
64 dragging
: !this.options
.centerFixed
,
66 zoomAnimation
: this.options
.zoomAnimation
,
67 autoToggleDisplay
: this.options
.autoToggleDisplay
,
68 touchZoom
: this.options
.centerFixed
? 'center' : !this._isZoomLevelFixed(),
69 scrollWheelZoom
: this.options
.centerFixed
? 'center' : !this._isZoomLevelFixed(),
70 doubleClickZoom
: this.options
.centerFixed
? 'center' : !this._isZoomLevelFixed(),
71 boxZoom
: !this._isZoomLevelFixed(),
74 mapOptions
= L
.Util
.extend(this.options
.mapOptions
, mapOptions
); // merge with priority of the local mapOptions object.
76 this._miniMap
= new L
.Map(this._container
, mapOptions
);
78 this._miniMap
.addLayer(this._layer
);
80 // These bools are used to prevent infinite loops of the two maps notifying each other that they've moved.
81 this._mainMapMoving
= false;
82 this._miniMapMoving
= false;
84 // Keep a record of this to prevent auto toggling when the user explicitly doesn't want it.
85 this._userToggledDisplay
= false;
86 this._minimized
= false;
88 if (this.options
.toggleDisplay
) {
89 this._addToggleButton();
92 this._miniMap
.whenReady(L
.Util
.bind(function () {
93 this._aimingRect
= L
.rectangle(this._mainMap
.getBounds(), this.options
.aimingRectOptions
).addTo(this._miniMap
);
94 this._shadowRect
= L
.rectangle(this._mainMap
.getBounds(), this.options
.shadowRectOptions
).addTo(this._miniMap
);
95 this._mainMap
.on('moveend', this._onMainMapMoved
, this);
96 this._mainMap
.on('move', this._onMainMapMoving
, this);
97 this._miniMap
.on('movestart', this._onMiniMapMoveStarted
, this);
98 this._miniMap
.on('move', this._onMiniMapMoving
, this);
99 this._miniMap
.on('moveend', this._onMiniMapMoved
, this);
102 return this._container
;
105 addTo: function (map
) {
106 L
.Control
.prototype.addTo
.call(this, map
);
108 var center
= this.options
.centerFixed
|| this._mainMap
.getCenter();
109 this._miniMap
.setView(center
, this._decideZoom(true));
110 this._setDisplay(this.options
.minimized
);
114 onRemove: function (map
) {
115 this._mainMap
.off('moveend', this._onMainMapMoved
, this);
116 this._mainMap
.off('move', this._onMainMapMoving
, this);
117 this._miniMap
.off('moveend', this._onMiniMapMoved
, this);
119 this._miniMap
.removeLayer(this._layer
);
122 changeLayer: function (layer
) {
123 this._miniMap
.removeLayer(this._layer
);
125 this._miniMap
.addLayer(this._layer
);
128 _addToggleButton: function () {
129 this._toggleDisplayButton
= this.options
.toggleDisplay
? this._createButton(
130 '', this.options
.strings
.hideText
, ('leaflet-control-minimap-toggle-display leaflet-control-minimap-toggle-display-' +
131 this.options
.position
), this._container
, this._toggleDisplayButtonClicked
, this) : undefined;
133 this._toggleDisplayButton
.style
.width
= this.options
.collapsedWidth
+ 'px';
134 this._toggleDisplayButton
.style
.height
= this.options
.collapsedHeight
+ 'px';
137 _createButton: function (html
, title
, className
, container
, fn
, context
) {
138 var link
= L
.DomUtil
.create('a', className
, container
);
139 link
.innerHTML
= html
;
143 var stop
= L
.DomEvent
.stopPropagation
;
146 .on(link
, 'click', stop
)
147 .on(link
, 'mousedown', stop
)
148 .on(link
, 'dblclick', stop
)
149 .on(link
, 'click', L
.DomEvent
.preventDefault
)
150 .on(link
, 'click', fn
, context
);
155 _toggleDisplayButtonClicked: function () {
156 this._userToggledDisplay
= true;
157 if (!this._minimized
) {
164 _setDisplay: function (minimize
) {
165 if (minimize
!== this._minimized
) {
166 if (!this._minimized
) {
174 _minimize: function () {
176 if (this.options
.toggleDisplay
) {
177 this._container
.style
.width
= this.options
.collapsedWidth
+ 'px';
178 this._container
.style
.height
= this.options
.collapsedHeight
+ 'px';
179 this._toggleDisplayButton
.className
+= (' minimized-' + this.options
.position
);
180 this._toggleDisplayButton
.title
= this.options
.strings
.showText
;
182 this._container
.style
.display
= 'none';
184 this._minimized
= true;
187 _restore: function () {
188 if (this.options
.toggleDisplay
) {
189 this._container
.style
.width
= this.options
.width
+ 'px';
190 this._container
.style
.height
= this.options
.height
+ 'px';
191 this._toggleDisplayButton
.className
= this._toggleDisplayButton
.className
192 .replace('minimized-' + this.options
.position
, '');
193 this._toggleDisplayButton
.title
= this.options
.strings
.hideText
;
195 this._container
.style
.display
= 'block';
197 this._minimized
= false;
200 _onMainMapMoved: function (e
) {
201 if (!this._miniMapMoving
) {
202 var center
= this.options
.centerFixed
|| this._mainMap
.getCenter();
204 this._mainMapMoving
= true;
205 this._miniMap
.setView(center
, this._decideZoom(true));
206 this._setDisplay(this._decideMinimized());
208 this._miniMapMoving
= false;
210 this._aimingRect
.setBounds(this._mainMap
.getBounds());
213 _onMainMapMoving: function (e
) {
214 this._aimingRect
.setBounds(this._mainMap
.getBounds());
217 _onMiniMapMoveStarted: function (e
) {
218 if (!this.options
.centerFixed
) {
219 var lastAimingRect
= this._aimingRect
.getBounds();
220 var sw
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getSouthWest());
221 var ne
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getNorthEast());
222 this._lastAimingRectPosition
= {sw
: sw
, ne
: ne
};
226 _onMiniMapMoving: function (e
) {
227 if (!this.options
.centerFixed
) {
228 if (!this._mainMapMoving
&& this._lastAimingRectPosition
) {
229 this._shadowRect
.setBounds(new L
.LatLngBounds(this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.sw
), this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.ne
)));
230 this._shadowRect
.setStyle({opacity
: 1, fillOpacity
: 0.3});
235 _onMiniMapMoved: function (e
) {
236 if (!this._mainMapMoving
) {
237 this._miniMapMoving
= true;
238 this._mainMap
.setView(this._miniMap
.getCenter(), this._decideZoom(false));
239 this._shadowRect
.setStyle({opacity
: 0, fillOpacity
: 0});
241 this._mainMapMoving
= false;
245 _isZoomLevelFixed: function () {
246 var zoomLevelFixed
= this.options
.zoomLevelFixed
;
247 return this._isDefined(zoomLevelFixed
) && this._isInteger(zoomLevelFixed
);
250 _decideZoom: function (fromMaintoMini
) {
251 if (!this._isZoomLevelFixed()) {
252 if (fromMaintoMini
) {
253 return this._mainMap
.getZoom() + this.options
.zoomLevelOffset
;
255 var currentDiff
= this._miniMap
.getZoom() - this._mainMap
.getZoom();
256 var proposedZoom
= this._miniMap
.getZoom() - this.options
.zoomLevelOffset
;
259 if (currentDiff
> this.options
.zoomLevelOffset
&& this._mainMap
.getZoom() < this._miniMap
.getMinZoom() - this.options
.zoomLevelOffset
) {
260 // This means the miniMap is zoomed out to the minimum zoom level and can't zoom any more.
261 if (this._miniMap
.getZoom() > this._lastMiniMapZoom
) {
262 // This means the user is trying to zoom in by using the minimap, zoom the main map.
263 toRet
= this._mainMap
.getZoom() + 1;
264 // Also we cheat and zoom the minimap out again to keep it visually consistent.
265 this._miniMap
.setZoom(this._miniMap
.getZoom() - 1);
267 // Either the user is trying to zoom out past the mini map's min zoom or has just panned using it, we can't tell the difference.
268 // Therefore, we ignore it!
269 toRet
= this._mainMap
.getZoom();
272 // This is what happens in the majority of cases, and always if you configure the min levels + offset in a sane fashion.
273 toRet
= proposedZoom
;
275 this._lastMiniMapZoom
= this._miniMap
.getZoom();
279 if (fromMaintoMini
) {
280 return this.options
.zoomLevelFixed
;
282 return this._mainMap
.getZoom();
287 _decideMinimized: function () {
288 if (this._userToggledDisplay
) {
289 return this._minimized
;
292 if (this.options
.autoToggleDisplay
) {
293 if (this._mainMap
.getBounds().contains(this._miniMap
.getBounds())) {
299 return this._minimized
;
302 _isInteger: function (value
) {
303 return typeof value
=== 'number';
306 _isDefined: function (value
) {
307 return typeof value
!== 'undefined';
312 miniMapControl
: false
315 L
.Map
.addInitHook(function () {
316 if (this.options
.miniMapControl
) {
317 this.miniMapControl
= (new MiniMap()).addTo(this);