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._toggleButtonInitialTitleText(), ('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 _toggleButtonInitialTitleText: function () {
138 if (this.options
.minimized
) {
139 return this.options
.strings
.showText
;
141 return this.options
.strings
.hideText
;
145 _createButton: function (html
, title
, className
, container
, fn
, context
) {
146 var link
= L
.DomUtil
.create('a', className
, container
);
147 link
.innerHTML
= html
;
151 var stop
= L
.DomEvent
.stopPropagation
;
154 .on(link
, 'click', stop
)
155 .on(link
, 'mousedown', stop
)
156 .on(link
, 'dblclick', stop
)
157 .on(link
, 'click', L
.DomEvent
.preventDefault
)
158 .on(link
, 'click', fn
, context
);
163 _toggleDisplayButtonClicked: function () {
164 this._userToggledDisplay
= true;
165 if (!this._minimized
) {
172 _setDisplay: function (minimize
) {
173 if (minimize
!== this._minimized
) {
174 if (!this._minimized
) {
182 _minimize: function () {
184 if (this.options
.toggleDisplay
) {
185 this._container
.style
.width
= this.options
.collapsedWidth
+ 'px';
186 this._container
.style
.height
= this.options
.collapsedHeight
+ 'px';
187 this._toggleDisplayButton
.className
+= (' minimized-' + this.options
.position
);
188 this._toggleDisplayButton
.title
= this.options
.strings
.showText
;
190 this._container
.style
.display
= 'none';
192 this._minimized
= true;
195 _restore: function () {
196 if (this.options
.toggleDisplay
) {
197 this._container
.style
.width
= this.options
.width
+ 'px';
198 this._container
.style
.height
= this.options
.height
+ 'px';
199 this._toggleDisplayButton
.className
= this._toggleDisplayButton
.className
200 .replace('minimized-' + this.options
.position
, '');
201 this._toggleDisplayButton
.title
= this.options
.strings
.hideText
;
203 this._container
.style
.display
= 'block';
205 this._minimized
= false;
208 _onMainMapMoved: function (e
) {
209 if (!this._miniMapMoving
) {
210 var center
= this.options
.centerFixed
|| this._mainMap
.getCenter();
212 this._mainMapMoving
= true;
213 this._miniMap
.setView(center
, this._decideZoom(true));
214 this._setDisplay(this._decideMinimized());
216 this._miniMapMoving
= false;
218 this._aimingRect
.setBounds(this._mainMap
.getBounds());
221 _onMainMapMoving: function (e
) {
222 this._aimingRect
.setBounds(this._mainMap
.getBounds());
225 _onMiniMapMoveStarted: function (e
) {
226 if (!this.options
.centerFixed
) {
227 var lastAimingRect
= this._aimingRect
.getBounds();
228 var sw
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getSouthWest());
229 var ne
= this._miniMap
.latLngToContainerPoint(lastAimingRect
.getNorthEast());
230 this._lastAimingRectPosition
= {sw
: sw
, ne
: ne
};
234 _onMiniMapMoving: function (e
) {
235 if (!this.options
.centerFixed
) {
236 if (!this._mainMapMoving
&& this._lastAimingRectPosition
) {
237 this._shadowRect
.setBounds(new L
.LatLngBounds(this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.sw
), this._miniMap
.containerPointToLatLng(this._lastAimingRectPosition
.ne
)));
238 this._shadowRect
.setStyle({opacity
: 1, fillOpacity
: 0.3});
243 _onMiniMapMoved: function (e
) {
244 if (!this._mainMapMoving
) {
245 this._miniMapMoving
= true;
246 this._mainMap
.setView(this._miniMap
.getCenter(), this._decideZoom(false));
247 this._shadowRect
.setStyle({opacity
: 0, fillOpacity
: 0});
249 this._mainMapMoving
= false;
253 _isZoomLevelFixed: function () {
254 var zoomLevelFixed
= this.options
.zoomLevelFixed
;
255 return this._isDefined(zoomLevelFixed
) && this._isInteger(zoomLevelFixed
);
258 _decideZoom: function (fromMaintoMini
) {
259 if (!this._isZoomLevelFixed()) {
260 if (fromMaintoMini
) {
261 return this._mainMap
.getZoom() + this.options
.zoomLevelOffset
;
263 var currentDiff
= this._miniMap
.getZoom() - this._mainMap
.getZoom();
264 var proposedZoom
= this._miniMap
.getZoom() - this.options
.zoomLevelOffset
;
267 if (currentDiff
> this.options
.zoomLevelOffset
&& this._mainMap
.getZoom() < this._miniMap
.getMinZoom() - this.options
.zoomLevelOffset
) {
268 // This means the miniMap is zoomed out to the minimum zoom level and can't zoom any more.
269 if (this._miniMap
.getZoom() > this._lastMiniMapZoom
) {
270 // This means the user is trying to zoom in by using the minimap, zoom the main map.
271 toRet
= this._mainMap
.getZoom() + 1;
272 // Also we cheat and zoom the minimap out again to keep it visually consistent.
273 this._miniMap
.setZoom(this._miniMap
.getZoom() - 1);
275 // 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.
276 // Therefore, we ignore it!
277 toRet
= this._mainMap
.getZoom();
280 // This is what happens in the majority of cases, and always if you configure the min levels + offset in a sane fashion.
281 toRet
= proposedZoom
;
283 this._lastMiniMapZoom
= this._miniMap
.getZoom();
287 if (fromMaintoMini
) {
288 return this.options
.zoomLevelFixed
;
290 return this._mainMap
.getZoom();
295 _decideMinimized: function () {
296 if (this._userToggledDisplay
) {
297 return this._minimized
;
300 if (this.options
.autoToggleDisplay
) {
301 if (this._mainMap
.getBounds().contains(this._miniMap
.getBounds())) {
307 return this._minimized
;
310 _isInteger: function (value
) {
311 return typeof value
=== 'number';
314 _isDefined: function (value
) {
315 return typeof value
!== 'undefined';
320 miniMapControl
: false
323 L
.Map
.addInitHook(function () {
324 if (this.options
.miniMapControl
) {
325 this.miniMapControl
= (new MiniMap()).addTo(this);