1 | import {DivOverlay} from './DivOverlay';
|
2 | import * as DomEvent from '../dom/DomEvent';
|
3 | import * as DomUtil from '../dom/DomUtil';
|
4 | import {Point, toPoint} from '../geometry/Point';
|
5 | import {Map} from '../map/Map';
|
6 | import {Layer} from './Layer';
|
7 | import {Path} from './vector/Path';
|
8 | import {FeatureGroup} from './FeatureGroup';
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | export var Popup = DivOverlay.extend({
|
45 |
|
46 |
|
47 |
|
48 | options: {
|
49 |
|
50 |
|
51 | pane: 'popupPane',
|
52 |
|
53 |
|
54 |
|
55 | offset: [0, 7],
|
56 |
|
57 |
|
58 |
|
59 | maxWidth: 300,
|
60 |
|
61 |
|
62 |
|
63 | minWidth: 50,
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | maxHeight: null,
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | autoPan: true,
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | autoPanPaddingTopLeft: null,
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | autoPanPaddingBottomRight: null,
|
86 |
|
87 |
|
88 |
|
89 | autoPanPadding: [5, 5],
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | keepInView: false,
|
95 |
|
96 |
|
97 |
|
98 | closeButton: true,
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | autoClose: true,
|
104 |
|
105 |
|
106 |
|
107 |
|
108 | closeOnEscapeKey: true,
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | className: ''
|
117 | },
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | openOn: function (map) {
|
124 | map = arguments.length ? map : this._source._map;
|
125 |
|
126 | if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) {
|
127 | map.removeLayer(map._popup);
|
128 | }
|
129 | map._popup = this;
|
130 |
|
131 | return DivOverlay.prototype.openOn.call(this, map);
|
132 | },
|
133 |
|
134 | onAdd: function (map) {
|
135 | DivOverlay.prototype.onAdd.call(this, map);
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | map.fire('popupopen', {popup: this});
|
142 |
|
143 | if (this._source) {
|
144 |
|
145 |
|
146 |
|
147 |
|
148 | this._source.fire('popupopen', {popup: this}, true);
|
149 |
|
150 |
|
151 | if (!(this._source instanceof Path)) {
|
152 | this._source.on('preclick', DomEvent.stopPropagation);
|
153 | }
|
154 | }
|
155 | },
|
156 |
|
157 | onRemove: function (map) {
|
158 | DivOverlay.prototype.onRemove.call(this, map);
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | map.fire('popupclose', {popup: this});
|
165 |
|
166 | if (this._source) {
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | this._source.fire('popupclose', {popup: this}, true);
|
172 | if (!(this._source instanceof Path)) {
|
173 | this._source.off('preclick', DomEvent.stopPropagation);
|
174 | }
|
175 | }
|
176 | },
|
177 |
|
178 | getEvents: function () {
|
179 | var events = DivOverlay.prototype.getEvents.call(this);
|
180 |
|
181 | if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
|
182 | events.preclick = this.close;
|
183 | }
|
184 |
|
185 | if (this.options.keepInView) {
|
186 | events.moveend = this._adjustPan;
|
187 | }
|
188 |
|
189 | return events;
|
190 | },
|
191 |
|
192 | _initLayout: function () {
|
193 | var prefix = 'leaflet-popup',
|
194 | container = this._container = DomUtil.create('div',
|
195 | prefix + ' ' + (this.options.className || '') +
|
196 | ' leaflet-zoom-animated');
|
197 |
|
198 | var wrapper = this._wrapper = DomUtil.create('div', prefix + '-content-wrapper', container);
|
199 | this._contentNode = DomUtil.create('div', prefix + '-content', wrapper);
|
200 |
|
201 | DomEvent.disableClickPropagation(container);
|
202 | DomEvent.disableScrollPropagation(this._contentNode);
|
203 | DomEvent.on(container, 'contextmenu', DomEvent.stopPropagation);
|
204 |
|
205 | this._tipContainer = DomUtil.create('div', prefix + '-tip-container', container);
|
206 | this._tip = DomUtil.create('div', prefix + '-tip', this._tipContainer);
|
207 |
|
208 | if (this.options.closeButton) {
|
209 | var closeButton = this._closeButton = DomUtil.create('a', prefix + '-close-button', container);
|
210 | closeButton.setAttribute('role', 'button');
|
211 | closeButton.setAttribute('aria-label', 'Close popup');
|
212 | closeButton.href = '#close';
|
213 | closeButton.innerHTML = '<span aria-hidden="true">×</span>';
|
214 |
|
215 | DomEvent.on(closeButton, 'click', function (ev) {
|
216 | DomEvent.preventDefault(ev);
|
217 | this.close();
|
218 | }, this);
|
219 | }
|
220 | },
|
221 |
|
222 | _updateLayout: function () {
|
223 | var container = this._contentNode,
|
224 | style = container.style;
|
225 |
|
226 | style.width = '';
|
227 | style.whiteSpace = 'nowrap';
|
228 |
|
229 | var width = container.offsetWidth;
|
230 | width = Math.min(width, this.options.maxWidth);
|
231 | width = Math.max(width, this.options.minWidth);
|
232 |
|
233 | style.width = (width + 1) + 'px';
|
234 | style.whiteSpace = '';
|
235 |
|
236 | style.height = '';
|
237 |
|
238 | var height = container.offsetHeight,
|
239 | maxHeight = this.options.maxHeight,
|
240 | scrolledClass = 'leaflet-popup-scrolled';
|
241 |
|
242 | if (maxHeight && height > maxHeight) {
|
243 | style.height = maxHeight + 'px';
|
244 | DomUtil.addClass(container, scrolledClass);
|
245 | } else {
|
246 | DomUtil.removeClass(container, scrolledClass);
|
247 | }
|
248 |
|
249 | this._containerWidth = this._container.offsetWidth;
|
250 | },
|
251 |
|
252 | _animateZoom: function (e) {
|
253 | var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
|
254 | anchor = this._getAnchor();
|
255 | DomUtil.setPosition(this._container, pos.add(anchor));
|
256 | },
|
257 |
|
258 | _adjustPan: function () {
|
259 | if (!this.options.autoPan) { return; }
|
260 | if (this._map._panAnim) { this._map._panAnim.stop(); }
|
261 |
|
262 |
|
263 |
|
264 | if (this._autopanning) {
|
265 | this._autopanning = false;
|
266 | return;
|
267 | }
|
268 |
|
269 | var map = this._map,
|
270 | marginBottom = parseInt(DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
|
271 | containerHeight = this._container.offsetHeight + marginBottom,
|
272 | containerWidth = this._containerWidth,
|
273 | layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
|
274 |
|
275 | layerPos._add(DomUtil.getPosition(this._container));
|
276 |
|
277 | var containerPos = map.layerPointToContainerPoint(layerPos),
|
278 | padding = toPoint(this.options.autoPanPadding),
|
279 | paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
|
280 | paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
|
281 | size = map.getSize(),
|
282 | dx = 0,
|
283 | dy = 0;
|
284 |
|
285 | if (containerPos.x + containerWidth + paddingBR.x > size.x) {
|
286 | dx = containerPos.x + containerWidth - size.x + paddingBR.x;
|
287 | }
|
288 | if (containerPos.x - dx - paddingTL.x < 0) {
|
289 | dx = containerPos.x - paddingTL.x;
|
290 | }
|
291 | if (containerPos.y + containerHeight + paddingBR.y > size.y) {
|
292 | dy = containerPos.y + containerHeight - size.y + paddingBR.y;
|
293 | }
|
294 | if (containerPos.y - dy - paddingTL.y < 0) {
|
295 | dy = containerPos.y - paddingTL.y;
|
296 | }
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 | if (dx || dy) {
|
303 |
|
304 | if (this.options.keepInView) {
|
305 | this._autopanning = true;
|
306 | }
|
307 |
|
308 | map
|
309 | .fire('autopanstart')
|
310 | .panBy([dx, dy]);
|
311 | }
|
312 | },
|
313 |
|
314 | _getAnchor: function () {
|
315 |
|
316 | return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
|
317 | }
|
318 |
|
319 | });
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 | export var popup = function (options, source) {
|
328 | return new Popup(options, source);
|
329 | };
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 | Map.mergeOptions({
|
338 | closePopupOnClick: true
|
339 | });
|
340 |
|
341 |
|
342 |
|
343 |
|
344 | Map.include({
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 | openPopup: function (popup, latlng, options) {
|
351 | this._initOverlay(Popup, popup, latlng, options)
|
352 | .openOn(this);
|
353 |
|
354 | return this;
|
355 | },
|
356 |
|
357 |
|
358 |
|
359 | closePopup: function (popup) {
|
360 | popup = arguments.length ? popup : this._popup;
|
361 | if (popup) {
|
362 | popup.close();
|
363 | }
|
364 | return this;
|
365 | }
|
366 | });
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 | Layer.include({
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 | bindPopup: function (content, options) {
|
391 | this._popup = this._initOverlay(Popup, this._popup, content, options);
|
392 | if (!this._popupHandlersAdded) {
|
393 | this.on({
|
394 | click: this._openPopup,
|
395 | keypress: this._onKeyPress,
|
396 | remove: this.closePopup,
|
397 | move: this._movePopup
|
398 | });
|
399 | this._popupHandlersAdded = true;
|
400 | }
|
401 |
|
402 | return this;
|
403 | },
|
404 |
|
405 |
|
406 |
|
407 | unbindPopup: function () {
|
408 | if (this._popup) {
|
409 | this.off({
|
410 | click: this._openPopup,
|
411 | keypress: this._onKeyPress,
|
412 | remove: this.closePopup,
|
413 | move: this._movePopup
|
414 | });
|
415 | this._popupHandlersAdded = false;
|
416 | this._popup = null;
|
417 | }
|
418 | return this;
|
419 | },
|
420 |
|
421 |
|
422 |
|
423 | openPopup: function (latlng) {
|
424 | if (this._popup) {
|
425 | if (!(this instanceof FeatureGroup)) {
|
426 | this._popup._source = this;
|
427 | }
|
428 | if (this._popup._prepareOpen(latlng || this._latlng)) {
|
429 |
|
430 | this._popup.openOn(this._map);
|
431 | }
|
432 | }
|
433 | return this;
|
434 | },
|
435 |
|
436 |
|
437 |
|
438 | closePopup: function () {
|
439 | if (this._popup) {
|
440 | this._popup.close();
|
441 | }
|
442 | return this;
|
443 | },
|
444 |
|
445 |
|
446 |
|
447 | togglePopup: function () {
|
448 | if (this._popup) {
|
449 | this._popup.toggle(this);
|
450 | }
|
451 | return this;
|
452 | },
|
453 |
|
454 |
|
455 |
|
456 | isPopupOpen: function () {
|
457 | return (this._popup ? this._popup.isOpen() : false);
|
458 | },
|
459 |
|
460 |
|
461 |
|
462 | setPopupContent: function (content) {
|
463 | if (this._popup) {
|
464 | this._popup.setContent(content);
|
465 | }
|
466 | return this;
|
467 | },
|
468 |
|
469 |
|
470 |
|
471 | getPopup: function () {
|
472 | return this._popup;
|
473 | },
|
474 |
|
475 | _openPopup: function (e) {
|
476 | if (!this._popup || !this._map) {
|
477 | return;
|
478 | }
|
479 |
|
480 | DomEvent.stop(e);
|
481 |
|
482 | var target = e.layer || e.target;
|
483 | if (this._popup._source === target && !(target instanceof Path)) {
|
484 |
|
485 |
|
486 | if (this._map.hasLayer(this._popup)) {
|
487 | this.closePopup();
|
488 | } else {
|
489 | this.openPopup(e.latlng);
|
490 | }
|
491 | return;
|
492 | }
|
493 | this._popup._source = target;
|
494 | this.openPopup(e.latlng);
|
495 | },
|
496 |
|
497 | _movePopup: function (e) {
|
498 | this._popup.setLatLng(e.latlng);
|
499 | },
|
500 |
|
501 | _onKeyPress: function (e) {
|
502 | if (e.originalEvent.keyCode === 13) {
|
503 | this._openPopup(e);
|
504 | }
|
505 | }
|
506 | });
|