[PLUGINS] ~maj globale
[lhc/web/www.git] / www / plugins / gis / lib / leaflet / plugins / Control.MiniMap.js
1 // Following https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md
2 (function (factory, window) {
3
4 // define an AMD module that relies on 'leaflet'
5 if (typeof define === 'function' && define.amd) {
6 define(['leaflet'], factory);
7
8 // define a Common JS module that relies on 'leaflet'
9 } else if (typeof exports === 'object') {
10 module.exports = factory(require('leaflet'));
11 }
12
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);
18 };
19 }
20 }(function (L) {
21
22 var MiniMap = L.Control.extend({
23 options: {
24 position: 'bottomright',
25 toggleDisplay: false,
26 zoomLevelOffset: -5,
27 zoomLevelFixed: false,
28 centerFixed: false,
29 zoomAnimation: false,
30 autoToggleDisplay: false,
31 minimized: false,
32 width: 150,
33 height: 150,
34 collapsedWidth: 19,
35 collapsedHeight: 19,
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.
40 },
41
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;
48 this._layer = layer;
49 },
50
51 onAdd: function (map) {
52
53 this._mainMap = map;
54
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);
61
62 var mapOptions = {
63 attributionControl: false,
64 dragging: !this.options.centerFixed,
65 zoomControl: false,
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(),
72 crs: map.options.crs
73 };
74 mapOptions = L.Util.extend(this.options.mapOptions, mapOptions); // merge with priority of the local mapOptions object.
75
76 this._miniMap = new L.Map(this._container, mapOptions);
77
78 this._miniMap.addLayer(this._layer);
79
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;
83
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;
87
88 if (this.options.toggleDisplay) {
89 this._addToggleButton();
90 }
91
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);
100 }, this));
101
102 return this._container;
103 },
104
105 addTo: function (map) {
106 L.Control.prototype.addTo.call(this, map);
107
108 var center = this.options.centerFixed || this._mainMap.getCenter();
109 this._miniMap.setView(center, this._decideZoom(true));
110 this._setDisplay(this.options.minimized);
111 return this;
112 },
113
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);
118
119 this._miniMap.removeLayer(this._layer);
120 },
121
122 changeLayer: function (layer) {
123 this._miniMap.removeLayer(this._layer);
124 this._layer = layer;
125 this._miniMap.addLayer(this._layer);
126 },
127
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;
132
133 this._toggleDisplayButton.style.width = this.options.collapsedWidth + 'px';
134 this._toggleDisplayButton.style.height = this.options.collapsedHeight + 'px';
135 },
136
137 _createButton: function (html, title, className, container, fn, context) {
138 var link = L.DomUtil.create('a', className, container);
139 link.innerHTML = html;
140 link.href = '#';
141 link.title = title;
142
143 var stop = L.DomEvent.stopPropagation;
144
145 L.DomEvent
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);
151
152 return link;
153 },
154
155 _toggleDisplayButtonClicked: function () {
156 this._userToggledDisplay = true;
157 if (!this._minimized) {
158 this._minimize();
159 } else {
160 this._restore();
161 }
162 },
163
164 _setDisplay: function (minimize) {
165 if (minimize !== this._minimized) {
166 if (!this._minimized) {
167 this._minimize();
168 } else {
169 this._restore();
170 }
171 }
172 },
173
174 _minimize: function () {
175 // hide the minimap
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;
181 } else {
182 this._container.style.display = 'none';
183 }
184 this._minimized = true;
185 },
186
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;
194 } else {
195 this._container.style.display = 'block';
196 }
197 this._minimized = false;
198 },
199
200 _onMainMapMoved: function (e) {
201 if (!this._miniMapMoving) {
202 var center = this.options.centerFixed || this._mainMap.getCenter();
203
204 this._mainMapMoving = true;
205 this._miniMap.setView(center, this._decideZoom(true));
206 this._setDisplay(this._decideMinimized());
207 } else {
208 this._miniMapMoving = false;
209 }
210 this._aimingRect.setBounds(this._mainMap.getBounds());
211 },
212
213 _onMainMapMoving: function (e) {
214 this._aimingRect.setBounds(this._mainMap.getBounds());
215 },
216
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};
223 }
224 },
225
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});
231 }
232 }
233 },
234
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});
240 } else {
241 this._mainMapMoving = false;
242 }
243 },
244
245 _isZoomLevelFixed: function () {
246 var zoomLevelFixed = this.options.zoomLevelFixed;
247 return this._isDefined(zoomLevelFixed) && this._isInteger(zoomLevelFixed);
248 },
249
250 _decideZoom: function (fromMaintoMini) {
251 if (!this._isZoomLevelFixed()) {
252 if (fromMaintoMini) {
253 return this._mainMap.getZoom() + this.options.zoomLevelOffset;
254 } else {
255 var currentDiff = this._miniMap.getZoom() - this._mainMap.getZoom();
256 var proposedZoom = this._miniMap.getZoom() - this.options.zoomLevelOffset;
257 var toRet;
258
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);
266 } else {
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();
270 }
271 } else {
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;
274 }
275 this._lastMiniMapZoom = this._miniMap.getZoom();
276 return toRet;
277 }
278 } else {
279 if (fromMaintoMini) {
280 return this.options.zoomLevelFixed;
281 } else {
282 return this._mainMap.getZoom();
283 }
284 }
285 },
286
287 _decideMinimized: function () {
288 if (this._userToggledDisplay) {
289 return this._minimized;
290 }
291
292 if (this.options.autoToggleDisplay) {
293 if (this._mainMap.getBounds().contains(this._miniMap.getBounds())) {
294 return true;
295 }
296 return false;
297 }
298
299 return this._minimized;
300 },
301
302 _isInteger: function (value) {
303 return typeof value === 'number';
304 },
305
306 _isDefined: function (value) {
307 return typeof value !== 'undefined';
308 }
309 });
310
311 L.Map.mergeOptions({
312 miniMapControl: false
313 });
314
315 L.Map.addInitHook(function () {
316 if (this.options.miniMapControl) {
317 this.miniMapControl = (new MiniMap()).addTo(this);
318 }
319 });
320
321 return MiniMap;
322
323 }, window));