PageSourceSearch

https://www.climatechangetheatreaction.com/wp-content/plugins/si-c…/Leaflet.GoogleMutant.js?ver=1790212359

js climatechangetheatreaction.com collected 2026-09-24 18:53:21 UTC 22,775 bytes, 771 lines download raw bytes

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