PageSourceSearch

https://movingsimcoe.com/wp-content/plugins/ezm/XXDF/Web_Widget/DDF/lib/Leaflet.GoogleMutant.js?ver=0.0.1

js movingsimcoe.com collected 2026-10-07 21:59:28 UTC 23,895 bytes, 797 lines download raw bytes

1(function () {
2	'use strict';
3
4	/**
5	 * GoogleMutant by Iván Sánchez Ortega <[email protected]> https://ivan.sanchezortega.es
6	 * Source and issue tracking: https://gitlab.com/IvanSanchez/Leaflet.GridLayer.GoogleMutant/
7	 *
8	 * Based on techniques from https://github.com/shramov/leaflet-plugins
9	 * and https://avinmathew.com/leaflet-and-google-maps/ , but relying on MutationObserver.
10	 *
11	 * "THE BEER-WARE LICENSE":
12	 * <[email protected]> wrote this file. As long as you retain this notice you
13	 * can do whatever you want with this stuff. If we meet some day, and you think
14	 * this stuff is worth it, you can buy me a beer in return.
15	 *
16	 * Uses MIT-licensed code from https://github.com/rsms/js-lru/
17	 */
18
19	// This implementation of LRUMap is a copy of https://github.com/rsms/js-lru/ ,
20	// trivially adapted for ES6 exports.
21
22	/*
23	The MIT License
24
25	Copyright (c) 2010-2020 Rasmus Andersson <https://rsms.me/>
26
27	Permission is hereby granted, free of charge, to any person obtaining a copy
28	of this software and associated documentation files (the "Software"), to deal
29	in the Software without restriction, including without limitation the rights
30	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31	copies of the Software, and to permit persons to whom the Software is
32	furnished to do so, subject to the following conditions:
33
34	The above copyright notice and this permission notice shall be included in
35	all copies or substantial portions of the Software.
36
37	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43	THE SOFTWARE.
44	*/
45
46	/**
47	 * A doubly linked list-based Least Recently Used (LRU) cache. Will keep most
48	 * recently used items while discarding least recently used items when its limit
49	 * is reached.
50	 *
51	 * Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>
52	 * See README.md for details.
53	 *
54	 * Illustration of the design:
55	 *
56	 *       entry             entry             entry             entry
57	 *       ______            ______            ______            ______
58	 *      | head |.newer => |      |.newer => |      |.newer => | tail |
59	 *      |  A   |          |  B   |          |  C   |          |  D   |
60	 *      |______| <= older.|______| <= older.|______| <= older.|______|
61	 *
62	 *  removed  <--  <--  <--  <--  <--  <--  <--  <--  <--  <--  <--  added
63	 */
64
65	var NEWER = Symbol("newer");
66	var OLDER = Symbol("older");
67
68	var LRUMap = function LRUMap(limit, entries) {
69		if (typeof limit !== "number") {
70			// called as (entries)
71			entries = limit;
72			limit = 0;
73		}
74
75		this.size = 0;
76		this.limit = limit;
77		this.oldest = this.newest = undefined;
78		this._keymap = new Map();
79
80		if (entries) {
81			this.assign(entries);
82			if (limit < 1) {
83				this.limit = this.size;
84			}
85		}
86	};
87
88	LRUMap.prototype._markEntryAsUsed = function _markEntryAsUsed (entry) {
89		if (entry === this.newest) {
90			// Already the most recenlty used entry, so no need to update the list
91			return;
92		}
93		// HEAD--------------TAIL
94		//   <.older   .newer>
95		//  <--- add direction --
96		//   A  B  C  <D>  E
97		if (entry[NEWER]) {
98			if (entry === this.oldest) {
99				this.oldest = entry[NEWER];
100			}
101			entry[NEWER][OLDER] = entry[OLDER]; // C <-- E.
102		}
103		if (entry[OLDER]) {
104			entry[OLDER][NEWER] = entry[NEWER]; // C. --> E
105		}
106		entry[NEWER] = undefined; // D --x
107		entry[OLDER] = this.newest; // D. --> E
108		if (this.newest) {
109			this.newest[NEWER] = entry; // E. <-- D
110		}
111		this.newest = entry;
112	};
113
114	LRUMap.prototype.assign = function assign (entries) {
115		var entry,
116			limit = this.limit || Number.MAX_VALUE;
117		this._keymap.clear();
118		var it = entries[Symbol.iterator]();
119		for (var itv = it.next(); !itv.done; itv = it.next()) {
120			var e = new Entry(itv.value[0], itv.value[1]);
121			this._keymap.set(e.key, e);
122			if (!entry) {
123				this.oldest = e;
124			} else {
125				entry[NEWER] = e;
126				e[OLDER] = entry;
127			}
128			entry = e;
129			if (limit-- == 0) {
130				throw new Error("overflow");
131			}
132		}
133		this.newest = entry;
134		this.size = this._keymap.size;
135	};
136
137	LRUMap.prototype.get = function get (key) {
138		// First, find our cache entry
139		var entry = this._keymap.get(key);
140		if (!entry) { return; } // Not cached. Sorry.
141		// As <key> was found in the cache, register it as being requested recently
142		this._markEntryAsUsed(entry);
143		return entry.value;
144	};
145
146	LRUMap.prototype.set = function set (key, value) {
147		var entry = this._keymap.get(key);
148
149		if (entry) {
150			// update existing
151			entry.value = value;
152			this._markEntryAsUsed(entry);
153			return this;
154		}
155
156		// new entry
157		this._keymap.set(key, (entry = new Entry(key, value)));
158
159		if (this.newest) {
160			// link previous tail to the new tail (entry)
161			this.newest[NEWER] = entry;
162			entry[OLDER] = this.newest;
163		} else {
164			// we're first in -- yay
165			this.oldest = entry;
166		}
167
168		// add new entry to the end of the linked list -- it's now the freshest entry.
169		this.newest = entry;
170		++this.size;
171		if (this.size > this.limit) {
172			// we hit the limit -- remove the head
173			this.shift();
174		}
175
176		return this;
177	};
178
179	LRUMap.prototype.shift = function shift () {
180		// todo: handle special case when limit == 1
181		var entry = this.oldest;
182		if (entry) {
183			if (this.oldest[NEWER]) {
184				// advance the list
185				this.oldest = this.oldest[NEWER];
186				this.oldest[OLDER] = undefined;
187			} else {
188				// the cache is exhausted
189				this.oldest = undefined;
190				this.newest = undefined;
191			}
192			// Remove last strong reference to <entry> and remove links from the purged
193			// entry being returned:
194			entry[NEWER] = entry[OLDER] = undefined;
195			this._keymap.delete(entry.key);
196			--this.size;
197			return [entry.key, entry.value];
198		}
199	};
200
201	// -------------------------------------------------------------------------------------
202	// Following code (until end of class definition) is optional and can be removed without
203	// breaking the core functionality.
204
205	LRUMap.prototype.find = function find (key) {
206		var e = this._keymap.get(key);
207		return e ? e.value : undefined;
208	};
209
210	LRUMap.prototype.has = function has (key) {
211		return this._keymap.has(key);
212	};
213
214	LRUMap.prototype.delete = function delete$1 (key) {
215		var entry = this._keymap.get(key);
216		if (!entry) { return; }
217		this._keymap.delete(entry.key);
218		if (entry[NEWER] && entry[OLDER]) {
219			// relink the older entry with the newer entry
220			entry[OLDER][NEWER] = entry[NEWER];
221			entry[NEWER][OLDER] = entry[OLDER];
222		} else if (entry[NEWER]) {
223			// remove the link to us
224			entry[NEWER][OLDER] = undefined;
225			// link the newer entry to head
226			this.oldest = entry[NEWER];
227		} else if (entry[OLDER]) {
228			// remove the link to us
229			entry[OLDER][NEWER] = undefined;
230			// link the newer entry to head
231			this.newest = entry[OLDER];
232		} else {
233			// if(entry[OLDER] === undefined && entry.newer === undefined) {
234			this.oldest = this.newest = undefined;
235		}
236
237		this.size--;
238		return entry.value;
239	};
240
241	LRUMap.prototype.clear = function clear () {
242		// Not clearing links should be safe, as we don't expose live links to user
243		this.oldest = this.newest = undefined;
244		this.size = 0;
245		this._keymap.clear();
246	};
247
248	LRUMap.prototype.keys = function keys () {
249		return new KeyIterator(this.oldest);
250	};
251
252	LRUMap.prototype.values = function values () {
253		return new ValueIterator(this.oldest);
254	};
255
256	LRUMap.prototype.entries = function entries () {
257		return this;
258	};
259
260	LRUMap.prototype[Symbol.iterator] = function () {
261		return new EntryIterator(this.oldest);
262	};
263
264	LRUMap.prototype.forEach = function forEach (fun, thisObj) {
265		if (typeof thisObj !== "object") {
266			thisObj = this;
267		}
268		var entry = this.oldest;
269		while (entry) {
270			fun.call(thisObj, entry.value, entry.key, this);
271			entry = entry[NEWER];
272		}
273	};
274
275	/** Returns a JSON (array) representation */
276	LRUMap.prototype.toJSON = function toJSON () {
277		var s = new Array(this.size),
278			i = 0,
279			entry = this.oldest;
280		while (entry) {
281			s[i++] = { key: entry.key, value: entry.value };
282			entry = entry[NEWER];
283		}
284		return s;
285	};
286
287	/** Returns a String representation */
288	LRUMap.prototype.toString = function toString () {
289		var s = "",
290			entry = this.oldest;
291		while (entry) {
292			s += String(entry.key) + ":" + entry.value;
293			entry = entry[NEWER];
294			if (entry) {
295				s += " < ";
296			}
297		}
298		return s;
299	};
300
301	function Entry(key, value) {
302		this.key = key;
303		this.value = value;
304		this[NEWER] = undefined;
305		this[OLDER] = undefined;
306	}
307
308	function EntryIterator(oldestEntry) {
309		this.entry = oldestEntry;
310	}
311	EntryIterator.prototype[Symbol.iterator] = function () {
312		return this;
313	};
314	EntryIterator.prototype.next = function () {
315		var ent = this.entry;
316		if (ent) {
317			this.entry = ent[NEWER];
318			return { done: false, value: [ent.key, ent.value] };
319		} else {
320			return { done: true, value: undefined };
321		}
322	};
323
324	function KeyIterator(oldestEntry) {
325		this.entry = oldestEntry;
326	}
327	KeyIterator.prototype[Symbol.iterator] = function () {
328		return this;
329	};
330	KeyIterator.prototype.next = function () {
331		var ent = this.entry;
332		if (ent) {
333			this.entry = ent[NEWER];
334			return { done: false, value: ent.key };
335		} else {
336			return { done: true, value: undefined };
337		}
338	};
339
340	function ValueIterator(oldestEntry) {
341		this.entry = oldestEntry;
342	}
343	ValueIterator.prototype[Symbol.iterator] = function () {
344		return this;
345	};
346	ValueIterator.prototype.next = function () {
347		var ent = this.entry;
348		if (ent) {
349			this.entry = ent[NEWER];
350			return { done: false, value: ent.value };
351		} else {
352			return { done: true, value: undefined };
353		}
354	};
355
356	// GoogleMutant by Iván Sánchez Ortega <[email protected]>
357
358	function waitForAPI(callback, context) {
359		var checkCounter = 0,
360			intervalId = null;
361
362		intervalId = setInterval(function () {
363			if (checkCounter >= 20) {
364				clearInterval(intervalId);
365				throw new Error("window.google not found after 10 seconds");
366			}
367			if (!!window.google && !!window.google.maps && !!window.google.maps.Map) {
368				clearInterval(intervalId);
369				callback.call(context);
370			}
371			++checkCounter;
372		}, 500);
373	}
374
375	// 🍂class GridLayer.GoogleMutant
376	// 🍂extends GridLayer
377	L.GridLayer.GoogleMutant = L.GridLayer.extend({
378		options: {
379			minZoom: 0,
380			maxZoom: 21, // can be 23, but ugly if more than maxNativeZoom
381			tileSize: 256,
382			subdomains: "abc",
383			errorTileUrl: "",
384			attribution: "", // The mutant container will add its own attribution anyways.
385			opacity: 1,
386			continuousWorld: false,
387			noWrap: false,
388			// 🍂option type: String = 'roadmap'
389			// Google's map type. Valid values are 'roadmap', 'satellite' or 'terrain'. 'hybrid' is not really supported.
390			type: "roadmap",
391			maxNativeZoom: 21,
392		},
393
394		initialize: function (options) {
395			L.GridLayer.prototype.initialize.call(this, options);
396
397			// Couple data structures indexed by tile key
398			this._tileCallbacks = {}; // Callbacks for promises for tiles that are expected
399			this._lru = new LRUMap(100); // Tile LRU cache
400
401			this._imagesPerTile = this.options.type === "hybrid" ? 2 : 1;
402
403			this._boundOnMutatedImage = this._onMutatedImage.bind(this);
404		},
405
406		onAdd: function (map) {
407			var this$1 = this;
408
409			L.GridLayer.prototype.onAdd.call(this, map);
410			this._initMutantContainer();
411
412			// Attribution and logo nodes are not mutated a second time if the
413			// mutant is removed and re-added to the map, hence they are
414			// not cleaned up on layer removal, so they can be added here.
415			if (this._logoContainer) {
416				map._controlCorners.bottomleft.appendChild(this._logoContainer);
417			}
418			if (this._attributionContainer) {
419				map._controlCorners.bottomright.appendChild(this._attributionContainer);
420			}
421
422			waitForAPI(function () {
423				if (!this$1._map) {
424					return;
425				}
426				this$1._initMutant();
427
428				//handle layer being added to a map for which there are no Google tiles at the given zoom
429				google.maps.event.addListenerOnce(this$1._mutant, "idle", function () {
430					if (!this$1._map) {
431						return;
432					}
433					this$1._checkZoomLevels();
434					this$1._mutantIsReady = true;
435				});
436			});
437		},
438
439		onRemove: function (map) {
440			L.GridLayer.prototype.onRemove.call(this, map);
441			this._observer.disconnect();
442			map._container.removeChild(this._mutantContainer);
443			if (this._logoContainer) {
444				L.DomUtil.remove(this._logoContainer);
445			}
446			if (this._attributionContainer) {
447				L.DomUtil.remove(this._attributionContainer);
448			}
449			if (this._mutant) {
450				google.maps.event.clearListeners(this._mutant, "idle");
451			}
452		},
453
454		// 🍂method addGoogleLayer(name: String, options?: Object): this
455		// Adds layer with the given name and options to the google Map instance.
456		// `name`: one of the google maps API layers, with it's constructor available in `google.maps` object.
457		// currently following values supported: 'TrafficLayer', 'TransitLayer', 'BicyclingLayer'.
458		// `options`: see https://developers.google.com/maps/documentation/javascript/reference/map
459		addGoogleLayer: function (googleLayerName, options) {
460			var this$1 = this;
461
462			if (!this._subLayers) { this._subLayers = {}; }
463			this.whenReady(function () {
464				var Constructor = google.maps[googleLayerName];
465				var googleLayer = new Constructor(options);
466				googleLayer.setMap(this$1._mutant);
467				this$1._subLayers[googleLayerName] = googleLayer;
468			});
469			return this;
470		},
471
472		// 🍂method removeGoogleLayer(name: String): this
473		// Removes layer with the given name from the google Map instance.
474		removeGoogleLayer: function (googleLayerName) {
475			var this$1 = this;
476
477			this.whenReady(function () {
478				var googleLayer = this$1._subLayers && this$1._subLayers[googleLayerName];
479				if (googleLayer) {
480					googleLayer.setMap(null);
481					delete this$1._subLayers[googleLayerName];
482				}
483			});
484			return this;
485		},
486
487		_initMutantContainer: function () {
488			if (!this._mutantContainer) {
489				this._mutantContainer = L.DomUtil.create(
490					"div",
491					"leaflet-google-mutant leaflet-top leaflet-left"
492				);
493				this._mutantContainer.id = "_MutantContainer_" + L.Util.stamp(this._mutantContainer);
494				this._mutantContainer.style.zIndex = 800; //leaflet map pane at 400, controls at 1000
495				this._mutantContainer.style.pointerEvents = "none";
496
497				L.DomEvent.off(this._mutantContainer);
498			}
499			this._map.getContainer().appendChild(this._mutantContainer);
500
501			this.setOpacity(this.options.opacity);
502			var style = this._mutantContainer.style;
503			if (this._map.options.zoomSnap < 1) {
504				// Fractional zoom needs a bigger mutant container in order to load more (smaller) tiles
505				style.width = "180%";
506				style.height = "180%";
507			} else {
508				style.width = "100%";
509				style.height = "100%";
510			}
511			style.zIndex = -1;
512
513			this._attachObserver(this._mutantContainer);
514		},
515
516		_initMutant: function () {
517			var this$1 = this;
518
519			if (this._mutant) {
520				return;
521			}
522
523			var map = new google.maps.Map(this._mutantContainer, {
524				center: { lat: 0, lng: 0 },
525				zoom: 0,
526				tilt: 0,
527				mapTypeId: this.options.type,
528				disableDefaultUI: true,
529				keyboardShortcuts: false,
530				draggable: false,
531				disableDoubleClickZoom: true,
532				scrollwheel: false,
533				streetViewControl: false,
534				styles: this.options.styles || {},
535				backgroundColor: "transparent",
536			});
537
538			this._mutant = map;
539
540			google.maps.event.addListenerOnce(map, "idle", function () {
541				var nodes = this$1._mutantContainer.querySelectorAll("a");
542				for (var i = 0; i < nodes.length; ++i) {
543					nodes[i].style.pointerEvents = "auto";
544				}
545			});
546
547			this._update();
548
549			// 🍂event spawned
550			// Fired when the mutant has been created.
551			this.fire("spawned", { mapObject: map });
552		},
553
554		_attachObserver: function _attachObserver(node) {
555			if (!this._observer) { this._observer = new MutationObserver(this._onMutations.bind(this)); }
556
557			// pass in the target node, as well as the observer options
558			this._observer.observe(node, { childList: true, subtree: true });
559
560			// if we are reusing an old _mutantContainer, we must manually detect
561			// all existing tiles in it
562			Array.prototype.forEach.call(node.querySelectorAll("img"), this._boundOnMutatedImage);
563		},
564
565		_onMutations: function _onMutations(mutations) {
566			for (var i = 0; i < mutations.length; ++i) {
567				var mutation = mutations[i];
568				for (var j = 0; j < mutation.addedNodes.length; ++j) {
569					var node = mutation.addedNodes[j];
570
571					if (node instanceof HTMLImageElement) {
572						this._onMutatedImage(node);
573					} else if (node instanceof HTMLElement) {
574						Array.prototype.forEach.call(
575							node.querySelectorAll("img"),
576							this._boundOnMutatedImage
577						);
578
579						// Check for, and remove, the "Google Maps can't load correctly" div.
580						// You *are* loading correctly, you dumbwit.
581						if (node.style.backgroundColor === "white") {
582							L.DomUtil.remove(node);
583						}
584
585						// Check for, and remove, the "For development purposes only" divs on the aerial/hybrid tiles.
586						if (node.textContent.indexOf("For development purposes only") === 0) {
587							L.DomUtil.remove(node);
588						}
589
590						// Check for, and remove, the "Sorry, we have no imagery here"
591						// empty <div>s. The [style*="text-align: center"] selector
592						// avoids matching the attribution notice.
593						// This empty div doesn't have a reference to the tile
594						// coordinates, so it's not possible to mark the tile as
595						// failed.
596						Array.prototype.forEach.call(
597							node.querySelectorAll('div[draggable=false][style*="text-align: center"]'),
598							L.DomUtil.remove
599						);
600
601						// Move Google attributions to leaflet's bottom-right control container
602						if (
603							node.querySelectorAll(".gmnoprint").length > 0 ||
604							node.querySelectorAll('a[title="Click to see this area on Google Maps"]')
605								.length > 0
606						) {
607							var ctr = (this._attributionContainer = L.DomUtil.create(
608								"div",
609								"leaflet-control leaflet-control-attribution"
610							));
611							L.DomEvent.disableClickPropagation(ctr);
612							ctr.style.height = "14px";
613							ctr.style.background = "none";
614							this._map._controlCorners.bottomright.appendChild(ctr);
615							ctr.appendChild(node);
616						}
617
618						// Move Google logo to leaflet's bottom-left control container
619						if (node.style.zIndex == 1000000) {
620							this._map._controlCorners.bottomleft.appendChild(node);
621							this._logoContainer = node;
622						}
623					}
624				}
625			}
626		},
627
628		// Only images which 'src' attrib match this will be considered for moving around.
629		// Looks like some kind of string-based protobuf, maybe??
630		// Only the roads (and terrain, and vector-based stuff) match this pattern
631		_roadRegexp: /!1i(\d+)!2i(\d+)!3i(\d+)!/,
632
633		// On the other hand, raster imagery matches this other pattern
634		_satRegexp: /x=(\d+)&y=(\d+)&z=(\d+)/,
635
636		// On small viewports, when zooming in/out, a static image is requested
637		// This will not be moved around, just removed from the DOM.
638		_staticRegExp: /StaticMapService\.GetMapImage/,
639
640		_onMutatedImage: function _onMutatedImage(imgNode) {
641			var coords;
642			var match = imgNode.src.match(this._roadRegexp);
643			var sublayer = 0;
644
645			if (match) {
646				coords = {
647					z: match[1],
648					x: match[2],
649					y: match[3],
650				};
651				if (this._imagesPerTile > 1) {
652					imgNode.style.zIndex = 1;
653					sublayer = 1;
654				}
655			} else {
656				match = imgNode.src.match(this._satRegexp);
657				if (match) {
658					coords = {
659						x: match[1],
660						y: match[2],
661						z: match[3],
662					};
663				}
664				// imgNode.style.zIndex = 0;
665				sublayer = 0;
666			}
667
668			if (coords) {
669				var tileKey = this._tileCoordsToKey(coords);
670				imgNode.style.position = "absolute";
671				imgNode.style.visibility = "hidden";
672
673				var key = tileKey + "/" + sublayer;
674				// Cache img so it can also be used in subsequent tile requests
675				this._lru.set(key, imgNode);
676
677				if (key in this._tileCallbacks && this._tileCallbacks[key]) {
678					// Use the tile for *all* pending callbacks. They'll be cloned anyway.
679					this._tileCallbacks[key].forEach(function (callback) { return callback(imgNode); });
680					delete this._tileCallbacks[key];
681				}
682			} else if (imgNode.src.match(this._staticRegExp)) {
683				imgNode.style.visibility = "hidden";
684			}
685		},
686
687		createTile: function (coords, done) {
688			var key = this._tileCoordsToKey(coords),
689				tileContainer = L.DomUtil.create("div");
690
691			tileContainer.style.textAlign = "left";
692			tileContainer.dataset.pending = this._imagesPerTile;
693			done = done.bind(this, null, tileContainer);
694
695			for (var i = 0; i < this._imagesPerTile; ++i) {
696				var key2 = key + "/" + i,
697					imgNode = this._lru.get(key2);
698				if (imgNode) {
699					tileContainer.appendChild(this._clone(imgNode));
700					--tileContainer.dataset.pending;
701				} else {
702					this._tileCallbacks[key2] = this._tileCallbacks[key2] || [];
703					this._tileCallbacks[key2].push(
704						function (c /*, k2*/) {
705							return function (imgNode) {
706								c.appendChild(this._clone(imgNode));
707								--c.dataset.pending;
708								if (!parseInt(c.dataset.pending)) {
709									done();
710								}
711							}.bind(this);
712						}.bind(this)(tileContainer /*, key2*/)
713					);
714				}
715			}
716
717			if (!parseInt(tileContainer.dataset.pending)) {
718				L.Util.requestAnimFrame(done);
719			}
720			return tileContainer;
721		},
722
723		_clone: function (imgNode) {
724			var clonedImgNode = imgNode.cloneNode(true);
725			clonedImgNode.style.visibility = "visible";
726			return clonedImgNode;
727		},
728
729		_checkZoomLevels: function () {
730			//setting the zoom level on the Google map may result in a different zoom level than the one requested
731			//(it won't go beyond the level for which they have data).
732			var zoomLevel = this._map.getZoom(),
733				gMapZoomLevel = this._mutant.getZoom();
734
735			if (!zoomLevel || !gMapZoomLevel) { return; }
736
737			if (
738				gMapZoomLevel !== zoomLevel || //zoom levels are out of sync, Google doesn't have data
739				gMapZoomLevel > this.options.maxNativeZoom
740			) {
741				//at current location, Google does have data (contrary to maxNativeZoom)
742				//Update maxNativeZoom
743				this._setMaxNativeZoom(gMapZoomLevel);
744			}
745		},
746
747		_setMaxNativeZoom: function (zoomLevel) {
748			if (zoomLevel !== this.options.maxNativeZoom) {
749				this.options.maxNativeZoom = zoomLevel;
750				this._resetView();
751			}
752		},
753
754		_update: function (center) {
755			// zoom level check needs to happen before super's implementation (tile addition/creation)
756			// otherwise tiles may be missed if maxNativeZoom is not yet correctly determined
757			if (this._mutant) {
758				center = center || this._map.getCenter();
759				var _center = new google.maps.LatLng(center.lat, center.lng),
760					zoom = Math.round(this._map.getZoom()),
761					mutantZoom = this._mutant.getZoom();
762
763				this._mutant.setCenter(_center);
764
765				//ignore fractional zoom levels
766				if (zoom !== mutantZoom) {
767					this._mutant.setZoom(zoom);
768
769					if (this._mutantIsReady) { this._checkZoomLevels(); }
770					//else zoom level check will be done later by 'idle' handler
771				}
772			}
773
774			L.GridLayer.prototype._update.call(this, center);
775		},
776
777		// @method whenReady(fn: Function, context?: Object): this
778		// Runs the given function `fn` when the mutant gets initialized, or immediately
779		// if it's already initialized, optionally passing a function context.
780		whenReady: function (callback, context) {
781			if (this._mutant) {
782				callback.call(context || this, { target: this });
783			} else {
784				this.on("spawned", callback, context);
785			}
786			return this;
787		},
788	});
789
790	// 🍂factory gridLayer.googleMutant(options)
791	// Returns a new `GridLayer.GoogleMutant` given its options
792	L.gridLayer.googleMutant = function (options) {
793		return new L.GridLayer.GoogleMutant(options);
794	};
795
796}());
797//# sourceMappingURL=Leaflet.GoogleMutant.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.