PageSourceSearch

https://ryokoukankou.com/singapore/js/map-leaflet-compat.js?v=20260928-desktop1

js ryokoukankou.com collected 2026-10-02 11:36:07 UTC 25,786 bytes, 827 lines download raw bytes

1(function (window, document) {
2	"use strict";
3
4	if (window.ryokouMapCompatLoaded) {
5		return;
6	}
7	window.ryokouMapCompatLoaded = true;
8
9	var LEAFLET_CSS = "https://unpkg.com/[email protected]/dist/leaflet.css";
10	var LEAFLET_JS = "https://unpkg.com/[email protected]/dist/leaflet.js";
11	var MAPLIBRE_CSS = "https://unpkg.com/[email protected]/dist/maplibre-gl.css";
12	var MAPLIBRE_JS = "https://unpkg.com/[email protected]/dist/maplibre-gl.js";
13	var MAPLIBRE_LEAFLET_JS = "https://unpkg.com/@maplibre/[email protected]/leaflet-maplibre-gl.js";
14	var OPENFREEMAP_STYLE = "https://tiles.openfreemap.org/styles/bright";
15	var OSM_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>';
16	var OPENFREEMAP_ATTRIBUTION = '<a href="https://openfreemap.org/">OpenFreeMap</a> &copy; <a href="https://openmaptiles.org/">OpenMapTiles</a> Data from <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>';
17	var MAP_LOAD_TIMEOUT = 12000;
18
19	var loaderState = "idle";
20	var loaderCallbacks = [];
21	var selectedProvider = "openfreemap";
22	var activeMaps = [];
23
24	function getStatusElement() {
25		return document.getElementById("map-load-status");
26	}
27
28	function showStatus(message, isError) {
29		var element = getStatusElement();
30		if (!element) {
31			return;
32		}
33		element.style.display = message ? "block" : "none";
34		element.style.color = isError ? "#b00020" : "#555";
35		element.innerHTML = "";
36		element.appendChild(document.createTextNode(message || ""));
37	}
38
39	function setLoadingState(isLoading) {
40		var button = document.getElementById("mapfirstimg");
41		var fullscreen = document.getElementById("menumapclick");
42
43		if (button) {
44			button.disabled = isLoading;
45			button.setAttribute("aria-busy", isLoading ? "true" : "false");
46		}
47		if (fullscreen) {
48			fullscreen.style.pointerEvents = isLoading ? "none" : "";
49			fullscreen.setAttribute("aria-disabled", isLoading ? "true" : "false");
50		}
51	}
52
53	function addStylesheet(id, url) {
54		if (document.getElementById(id)) {
55			return;
56		}
57		var link = document.createElement("link");
58		link.id = id;
59		link.rel = "stylesheet";
60		link.href = url;
61		document.head.appendChild(link);
62	}
63
64	function loadScript(id, url, readyTest, callback, timeoutMs) {
65		var existing;
66		var script;
67		var settled = false;
68		var timeout;
69
70		function finish(error) {
71			if (settled) {
72				return;
73			}
74			settled = true;
75			window.clearTimeout(timeout);
76			callback(error);
77		}
78
79		if (readyTest()) {
80			callback(null);
81			return;
82		}
83
84		timeout = window.setTimeout(function () {
85			if (script && script.parentNode) {
86				script.parentNode.removeChild(script);
87			}
88			finish(new Error("library load timed out"));
89		}, timeoutMs || MAP_LOAD_TIMEOUT);
90
91		existing = document.getElementById(id);
92		if (existing) {
93			existing.addEventListener("load", function () {
94				finish(readyTest() ? null : new Error("library unavailable"));
95			});
96			existing.addEventListener("error", function () {
97				finish(new Error("library load failed"));
98			});
99			return;
100		}
101
102		script = document.createElement("script");
103		script.id = id;
104		script.src = url;
105		script.async = true;
106		script.onload = function () {
107			if (readyTest()) {
108				finish(null);
109				return;
110			}
111			if (script.parentNode) {
112				script.parentNode.removeChild(script);
113			}
114			finish(new Error("library unavailable"));
115		};
116		script.onerror = function () {
117			if (script.parentNode) {
118				script.parentNode.removeChild(script);
119			}
120			finish(new Error("library load failed"));
121		};
122		document.head.appendChild(script);
123	}
124
125	function ensureLeaflet(callback) {
126		addStylesheet("ryokou-leaflet-css", LEAFLET_CSS);
127		loadScript("ryokou-leaflet-js", LEAFLET_JS, function () {
128			return !!window.L;
129		}, function (error) {
130			if (error || !window.L.Browser.canvas) { callback(error); return; }
131			loadScript("ryokou-canvas-markers-js", "/singapore/js/map-canvas-markers.js?v=20260927-explorer", function () {
132				return !!window.ryokouCanvasMarker;
133			}, function () { callback(null); }, 1500);
134		});
135	}
136
137	function ensureOpenFreeMap(callback) {
138		addStylesheet("ryokou-maplibre-css", MAPLIBRE_CSS);
139		loadScript("ryokou-maplibre-js", MAPLIBRE_JS, function () {
140			return !!window.maplibregl;
141		}, function (maplibreError) {
142			if (maplibreError) {
143				callback(maplibreError);
144				return;
145			}
146			loadScript("ryokou-maplibre-leaflet-js", MAPLIBRE_LEAFLET_JS, function () {
147				return !!(window.L && window.L.maplibreGL);
148			}, callback);
149		});
150	}
151
152	function finishLibraryLoad(error) {
153		var callbacks = loaderCallbacks.slice(0);
154		loaderCallbacks = [];
155
156		if (error) {
157			loaderState = "failed";
158			setLoadingState(false);
159			showStatus("地図を表示できません。下の一覧をご利用ください。", true);
160			if (typeof window.flgsight !== "undefined") {
161				window.flgsight = 0;
162			}
163			if (typeof window.flgmapfirst !== "undefined") {
164				window.flgmapfirst = 0;
165			}
166			return;
167		}
168
169		loaderState = "ready";
170		for (var i = 0; i < callbacks.length; i += 1) {
171			try {
172				callbacks[i]();
173			} catch (callbackError) {
174				showStatus("地図の初期化に失敗しました。下の一覧をご利用ください。", true);
175			}
176		}
177		setLoadingState(false);
178	}
179
180	function maplibreFailure(map, layer) {
181		if (!map || map._ryokouSwitching || (
182			map._ryokouProvider !== "openfreemap" &&
183			map._ryokouProvider !== "openfreemap-loading"
184		)) {
185			return;
186		}
187		map._ryokouSwitching = true;
188		if (layer && map.hasLayer(layer)) {
189			map.removeLayer(layer);
190		}
191		map._ryokouBaseLayer = null;
192		map._ryokouProvider = "osm-loading";
193		showStatus("地図表示を切り替えています…", false);
194		addOsmLayer(map);
195		map._ryokouSwitching = false;
196	}
197
198	function addOpenFreeMapLayer(map) {
199		var layer;
200		try {
201			layer = window.L.maplibreGL({
202				attributionControl: {
203					customAttribution: OPENFREEMAP_ATTRIBUTION
204				},
205				style: OPENFREEMAP_STYLE
206			}).addTo(map);
207		} catch (error) {
208			maplibreFailure(map, null);
209			return;
210		}
211
212		map._ryokouBaseLayer = layer;
213		map._ryokouProvider = "openfreemap";
214
215		var glMap = layer.getMaplibreMap ? layer.getMaplibreMap() : null;
216		if (!glMap) {
217			return;
218		}
219
220		var loaded = false;
221		var failed = false;
222		var errorTimes = [];
223
224		function recentErrorCount(now) {
225			var cutoff = now - 30000;
226			while (errorTimes.length && errorTimes[0] < cutoff) {
227				errorTimes.shift();
228			}
229			return errorTimes.length;
230		}
231
232		function fail() {
233			if (failed) {
234				return;
235			}
236			failed = true;
237			window.clearTimeout(loadTimer);
238			maplibreFailure(map, layer);
239		}
240
241		var loadTimer = window.setTimeout(function () {
242			if (!loaded && !failed) {
243				fail();
244			}
245		}, MAP_LOAD_TIMEOUT);
246
247		glMap.once("load", function () {
248			if (failed) {
249				return;
250			}
251			loaded = true;
252			window.clearTimeout(loadTimer);
253			showStatus("", false);
254		});
255		glMap.on("idle", function () {
256			if (loaded && !failed) {
257				errorTimes = [];
258			}
259		});
260		glMap.on("error", function () {
261			var errorCount;
262			if (failed) {
263				return;
264			}
265			errorTimes.push(Date.now());
266			errorCount = recentErrorCount(Date.now());
267			if ((!loaded && errorCount >= 3) || (loaded && errorCount >= 5)) {
268				fail();
269			}
270		});
271	}
272
273	function addOsmLayer(map) {
274		var startedAt = Date.now();
275		var loadedTiles = 0;
276		var errorTimes = [];
277		var consecutiveErrors = 0;
278		var failed = false;
279		var layer = window.L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
280			attribution: OSM_ATTRIBUTION,
281			detectRetina: false,
282			maxZoom: 19,
283			referrerPolicy: "strict-origin-when-cross-origin"
284		});
285
286		function recentErrorCount(now) {
287			var cutoff = now - 30000;
288			while (errorTimes.length && errorTimes[0] < cutoff) {
289				errorTimes.shift();
290			}
291			return errorTimes.length;
292		}
293
294		function fail() {
295			if (failed) {
296				return;
297			}
298			failed = true;
299			window.clearTimeout(loadTimer);
300			if (map.hasLayer(layer)) {
301				map.removeLayer(layer);
302			}
303			map._ryokouBaseLayer = null;
304			map._ryokouProvider = "unavailable";
305			showStatus("地図を表示できません。下の一覧をご利用ください。", true);
306		}
307
308		var loadTimer = window.setTimeout(function () {
309			if (loadedTiles === 0) {
310				fail();
311			}
312		}, MAP_LOAD_TIMEOUT);
313
314		layer.on("tileload", function () {
315			loadedTiles += 1;
316			consecutiveErrors = 0;
317			window.clearTimeout(loadTimer);
318			showStatus("", false);
319		});
320		layer.on("tileerror", function () {
321			var now = Date.now();
322			var recentErrors;
323			var total;
324
325			errorTimes.push(now);
326			consecutiveErrors += 1;
327			recentErrors = recentErrorCount(now);
328			total = loadedTiles + recentErrors;
329
330			if (!failed && (
331				(now - startedAt <= 8000 && loadedTiles === 0 && recentErrors >= 2) ||
332				consecutiveErrors >= 3 ||
333				(recentErrors >= 5 && total > 0 && (recentErrors / total) >= 0.25)
334			)) {
335				fail();
336			}
337		});
338
339		map._ryokouProvider = "osm";
340		map._ryokouBaseLayer = layer;
341		layer.addTo(map);
342	}
343
344	function addBaseLayer(map) {
345		if (selectedProvider === "osm") {
346			addOsmLayer(map);
347		} else {
348			addOpenFreeMapLayer(map);
349		}
350	}
351
352	function iconSettings(iconValue) {
353		var url = typeof iconValue === "string" ? iconValue : (iconValue && iconValue.url);
354		var size = iconValue && typeof iconValue === "object" ? iconValue.size : null;
355		var anchor = iconValue && typeof iconValue === "object" ? iconValue.anchor : null;
356		var cleanUrl = (url || "").split("?")[0];
357
358		if (!size && /marker-star(?:-big)?\.png$/.test(cleanUrl)) {
359			size = {width: 37, height: 35};
360		} else if (!size && /marker-(?:sightseeing|restaurant|hotel)(?:-big)?(?:BK2?|)\.png$/.test(cleanUrl)) {
361			size = {width: 22, height: 35};
362		}
363		if (!size) {
364			size = {width: 25, height: 41};
365		}
366		if (!anchor) {
367			anchor = {x: Math.round(size.width / 2), y: size.height};
368		}
369
370		return window.L.icon({
371			iconUrl: url,
372			iconSize: [size.width, size.height],
373			iconAnchor: [anchor.x, anchor.y],
374			popupAnchor: /marker-(?:sightseeing|restaurant|hotel)(?:-big)?(?:BK2?|)\.png$/.test(cleanUrl)
375				? [size.width / 2 - anchor.x, size.height * 9 / 35 - anchor.y]
376				: /marker-star(?:-big)?\.png$/.test(cleanUrl)
377					? [size.width / 2 - anchor.x, size.height * 10 / 35 - anchor.y]
378					: [0, -Math.max(10, Math.round(size.height * 0.8))]
379		});
380	}
381
382	function CompatMarker(options) {
383		options = options || {};
384		this._position = options.position || null;
385		this._icon = options.icon || null;
386		this._map = null;
387		this._visible = options.visible !== false;
388		this._zIndex = Number(options.zIndex) || 0;
389		this._numberLabel = options.canvasNumberLabel == null ? "" : String(options.canvasNumberLabel);
390		this._leafletMarker = null;
391		this._listeners = [];
392		this._ensureMarker();
393		if (options.map) {
394			this.setMap(options.map);
395		}
396	}
397
398	CompatMarker.MAX_ZINDEX = 1000000;
399
400	CompatMarker.prototype._canUseCanvas = function () {
401		return !!window.ryokouCanvasMarker && (this._numberLabel ? window.ryokouCanvasMarker.supportsNumberLabels === true : this._zIndex === 0) && !(this._map && this._map._ryokouKeyboardMarkers);
402	};
403
404	CompatMarker.prototype._ensureMarker = function () {
405		var i;
406		var markerOptions;
407		if (this._leafletMarker || !this._position) {
408			return;
409		}
410		markerOptions = {numberLabel: this._numberLabel, numberLabelColor: this._numberColor || "#123", zIndexOffset: this._zIndex, icon: new window.L.Icon.Default()};
411		if (this._icon) {
412			markerOptions.icon = iconSettings(this._icon);
413		}
414		this._leafletMarker = this._canUseCanvas()
415			? window.ryokouCanvasMarker(this._position, markerOptions)
416			: window.L.marker(this._position, markerOptions);
417		this._leafletMarker._ryokouCanvasIcon = this._canUseCanvas();
418		this._leafletMarker._ryokouCompatMarker = this;
419		if (this._numberLabel && !this._leafletMarker._ryokouCanvasIcon) {
420			var label = document.createElement("span");
421			label.textContent = this._numberLabel;
422			var size = markerOptions.icon.options.iconSize || [25, 41];
423			this._leafletMarker.bindTooltip(label, {permanent: true, direction: "center", offset: [0, -size[1] + size[0] / 2], className: "playplan-pin-number", interactive: false});
424		}
425		for (i = 0; i < this._listeners.length; i += 1) {
426			this._leafletMarker.on(this._listeners[i].eventName, this._listeners[i].handler);
427		}
428	};
429
430	CompatMarker.prototype.setNumberLabel = function (value, color) {
431		this._numberLabel = value == null ? "" : String(value);
432		this._numberColor = color || "#123";
433		var marker = this._leafletMarker;
434		if (!marker) return;
435		marker.options.numberLabel = this._numberLabel;
436		marker.options.numberLabelColor = this._numberColor;
437		if (marker._ryokouCanvasIcon) { marker.redraw(); return; }
438		if (!this._numberLabel) { marker.unbindTooltip(); return; }
439		var label = document.createElement("span"); label.textContent = this._numberLabel;
440		var size = marker.options.icon.options.iconSize || [25, 41];
441		marker.bindTooltip(label, {permanent: true, direction: "center", offset: [0, -size[1] + size[0] / 2], className: "playplan-pin-number", interactive: false});
442	};
443
444	CompatMarker.prototype.setPosition = function (position) {
445		this._position = position;
446		this._ensureMarker();
447		if (this._leafletMarker) {
448			this._leafletMarker.setLatLng(position);
449		}
450	};
451
452	CompatMarker.prototype.setIcon = function (iconValue) {
453		this._icon = iconValue;
454		// Some generated pages create an empty marker and assign its image later.
455		if (this._canUseCanvas() && iconValue && this._leafletMarker && !this._leafletMarker._ryokouCanvasIcon) {
456			if (this._map && this._map.hasLayer(this._leafletMarker)) { this._map.removeLayer(this._leafletMarker); }
457			this._leafletMarker = null;
458			this._ensureMarker();
459			if (this._map && this._visible && this._leafletMarker) { this._leafletMarker.addTo(this._map); }
460			return;
461		}
462		this._ensureMarker();
463		if (this._leafletMarker) {
464			this._leafletMarker.setIcon(iconSettings(iconValue));
465		}
466	};
467
468	CompatMarker.prototype.setMap = function (map) {
469		if (this._map && this._leafletMarker && this._map.hasLayer(this._leafletMarker)) {
470			this._map.removeLayer(this._leafletMarker);
471		}
472		this._map = map || null;
473		if (map && map._ryokouKeyboardMarkers && this._leafletMarker && this._leafletMarker._ryokouCanvasIcon) {
474			this._leafletMarker = null;
475		}
476		this._ensureMarker();
477		if (this._map && this._visible && this._leafletMarker) {
478			this._leafletMarker.addTo(this._map);
479		}
480	};
481
482	CompatMarker.prototype.setVisible = function (visible) {
483		this._visible = !!visible;
484		if (this._visible && this._map && this._map._ryokouKeyboardMarkers) {
485			this.setMap(this._map);
486			return;
487		}
488		this._ensureMarker();
489		if (!this._map || !this._leafletMarker) {
490			return;
491		}
492		if (this._visible && !this._map.hasLayer(this._leafletMarker)) {
493			this._leafletMarker.addTo(this._map);
494		} else if (!this._visible && this._map.hasLayer(this._leafletMarker)) {
495			this._map.removeLayer(this._leafletMarker);
496		}
497	};
498
499	CompatMarker.prototype.setZIndex = function (zIndex) {
500		this._zIndex = Number(zIndex) || 0;
501		if (this._leafletMarker) {
502			if (this._leafletMarker._ryokouCanvasIcon && this._canUseCanvas()) {
503				if (this._leafletMarker.setCanvasOrder) { this._leafletMarker.setCanvasOrder(this._zIndex); }
504			} else if (!this._leafletMarker._ryokouCanvasIcon) {
505				this._leafletMarker.setZIndexOffset(this._zIndex);
506			} else if (this._zIndex !== 0) {
507				// Selected stars stay in the normal marker pane above the Canvas pins.
508				if (this._map && this._map.hasLayer(this._leafletMarker)) { this._map.removeLayer(this._leafletMarker); }
509				this._leafletMarker = null;
510				this._ensureMarker();
511				if (this._map && this._visible) { this._leafletMarker.addTo(this._map); }
512			}
513		}
514	};
515
516	CompatMarker.prototype._addListener = function (eventName, handler) {
517		this._listeners.push({eventName: eventName, handler: handler});
518		if (this._leafletMarker) {
519			this._leafletMarker.on(eventName, handler);
520		}
521	};
522
523	function shapeStyle(options) {
524		options = options || {};
525		return {
526			color: options.strokeColor || "#3388ff",
527			opacity: typeof options.strokeOpacity === "number" ? options.strokeOpacity : 1,
528			weight: typeof options.strokeWeight === "number" ? options.strokeWeight : 3,
529			fillColor: options.fillColor || options.strokeColor || "#3388ff",
530			fillOpacity: typeof options.fillOpacity === "number" ? options.fillOpacity : 0.2
531		};
532	}
533
534	function CompatShape(options, isPolygon) {
535		options = options || {};
536		this._map = null;
537		this._options = {};
538		for (var optionName in options) {
539			if (Object.prototype.hasOwnProperty.call(options, optionName)) {
540				this._options[optionName] = options[optionName];
541			}
542		}
543		this._layer = isPolygon ?
544			window.L.polygon(options.paths || options.path || [], shapeStyle(this._options)) :
545			window.L.polyline(options.path || [], shapeStyle(this._options));
546		if (options.map) {
547			this.setMap(options.map);
548		}
549	}
550
551	CompatShape.prototype.setMap = function (map) {
552		if (this._map && this._map.hasLayer(this._layer)) {
553			this._map.removeLayer(this._layer);
554		}
555		this._map = map || null;
556		if (this._map) {
557			this._layer.addTo(this._map);
558		}
559	};
560
561	CompatShape.prototype.setOptions = function (options) {
562		options = options || {};
563		for (var optionName in options) {
564			if (Object.prototype.hasOwnProperty.call(options, optionName)) {
565				this._options[optionName] = options[optionName];
566			}
567		}
568		if (options.path || options.paths) {
569			this._layer.setLatLngs(options.paths || options.path);
570		}
571		this._layer.setStyle(shapeStyle(this._options));
572	};
573
574	CompatShape.prototype.setZIndex = function () {
575		if (this._layer && typeof this._layer.bringToFront === "function") {
576			this._layer.bringToFront();
577		}
578	};
579
580	CompatShape.prototype._addListener = function (eventName, handler) {
581		this._layer.on(eventName, handler);
582	};
583
584	function CompatPolyline(options) {
585		CompatShape.call(this, options, false);
586	}
587
588	CompatPolyline.prototype = Object.create(CompatShape.prototype);
589	CompatPolyline.prototype.constructor = CompatPolyline;
590
591	function CompatPolygon(options) {
592		CompatShape.call(this, options, true);
593	}
594
595	CompatPolygon.prototype = Object.create(CompatShape.prototype);
596	CompatPolygon.prototype.constructor = CompatPolygon;
597
598	function CompatInfoWindow(options) {
599		options = options || {};
600		this._content = options.content || "";
601		this._position = options.position || null;
602		this._maxWidth = options.maxWidth || 400;
603		this._map = null;
604		this._popup = null;
605	}
606
607	CompatInfoWindow.prototype._fitToMap = function () {
608		if (!this._map || !this._popup || !this._map.getContainer().closest('.rk-map-explorer')) return;
609		var map = this._map, size = map.getSize(), container = map.getContainer();
610		var root = container.closest('.rk-map-explorer'), frame = container.getBoundingClientRect();
611		var controls = root && root.querySelector('.rk-map-controls');
612		var sheet = root && root.querySelector('.rk-map-sheet');
613		var panel = sheet && sheet.getBoundingClientRect();
614		var attribution = container.querySelector('.leaflet-top .leaflet-control-attribution');
615		var top = controls ? Math.max(0, controls.getBoundingClientRect().bottom - frame.top + 8) : 0;
616		if (attribution) top = Math.max(top, attribution.getBoundingClientRect().bottom - frame.top);
617		// Desktop maps can have a floating list on the left, embedded or fullscreen.
618		var left = panel && panel.width < size.x && panel.top < frame.bottom ? Math.max(0, panel.right - frame.left + 8) : 0;
619		var width = Math.min(this._maxWidth, 300, Math.max(80, size.x - left - 80));
620		this._popup.options.maxWidth = width;
621		this._popup.options.minWidth = Math.min(120, width);
622		this._popup.options.maxHeight = Math.max(40, size.y - top - 64);
623		this._popup.options.autoPanPaddingTopLeft = window.L.point(left + 12, top + 12);
624		this._popup.options.autoPanPaddingBottomRight = window.L.point(12, 16);
625		container.style.setProperty('--rk-map-popup-width', width + 'px');
626	};
627
628	CompatInfoWindow.prototype.open = function (map, marker) {
629		var position = marker && marker._position ? marker._position : this._position;
630		if (!map || !position) return;
631		var self = this;
632		if (!this._popup) {
633			var explorer = !!map.getContainer().closest('.rk-map-explorer');
634			this._popup = window.L.popup({autoPan: true, maxWidth: this._maxWidth, className: explorer ? 'ryokou-map-popup' : ''}).setContent(this._content);
635			this._popup.on('remove', function () {
636				if (self._map && self._resizePopup) self._map.off('resize', self._resizePopup);
637			});
638		}
639		if (!this._resizePopup) this._resizePopup = function () {
640			if (self._popup.isOpen()) { self._fitToMap(); self._popup.update(); }
641		};
642		if (this._map) this._map.off('resize', this._resizePopup);
643		this._map = map;
644		if (map.getContainer().closest('.rk-map-explorer')) {
645			map.on('resize', this._resizePopup);
646			this._fitToMap();
647		}
648		// Standalone popups do not inherit a marker icon popupAnchor.
649		var icon = marker && marker._leafletMarker && marker._leafletMarker.options.icon;
650		var anchor = icon && icon.options.popupAnchor;
651		this._popup.options.offset = anchor ? window.L.point(anchor).add([0, 8]) : window.L.point(0, 7);
652		this._popup.setLatLng(position).openOn(map);
653	};
654
655	CompatInfoWindow.prototype.close = function () {
656		if (this._map && this._popup) {
657			this._map.closePopup(this._popup);
658		}
659	};
660
661	function CompatLatLng(latitude, longitude) {
662		return window.L.latLng(Number(latitude), Number(longitude));
663	}
664
665	function CompatLatLngBounds(southWest, northEast) {
666		var bounds = window.L.latLngBounds([]);
667		if (southWest) {
668			bounds.extend(southWest);
669		}
670		if (northEast) {
671			bounds.extend(northEast);
672		}
673		return bounds;
674	}
675
676	function CompatMap(element, options) {
677		var center;
678		var zoom;
679		options = options || {};
680		center = options.center || window.L.latLng(1.3521, 103.8198);
681		zoom = Number(options.zoom) || 14;
682
683		element.innerHTML = "";
684		var map = window.L.map(element, {
685			attributionControl: true,
686			maxBounds: [[-85, -Infinity], [85, Infinity]],
687			maxBoundsViscosity: 1,
688			minZoom: 1,
689			scrollWheelZoom: true
690		}).setView(center, zoom);
691
692		map._ryokouProvider = selectedProvider;
693		map._ryokouBaseLayer = null;
694		map._ryokouSwitching = false;
695		// Preserve Leaflet marker tab navigation when the map is used with a keyboard.
696		function enableKeyboardMarkers(event) {
697			if (event.key !== "Tab" || map._ryokouKeyboardMarkers) { return; }
698			map._ryokouKeyboardMarkers = true;
699			var markers = [];
700			map.eachLayer(function (layer) {
701				if (layer._ryokouCompatMarker) { markers.push(layer._ryokouCompatMarker); }
702			});
703			markers.forEach(function (marker) { marker.setMap(map); });
704		}
705		element.addEventListener("keydown", enableKeyboardMarkers);
706		map.once("unload", function () { element.removeEventListener("keydown", enableKeyboardMarkers); });
707		activeMaps.push(map);
708		addBaseLayer(map);
709		return map;
710	}
711
712	function CompatSize(width, height) {
713		this.width = Number(width);
714		this.height = Number(height);
715	}
716
717	function CompatPoint(x, y) {
718		this.x = Number(x);
719		this.y = Number(y);
720	}
721
722	function CompatMarkerImage(url, size, origin, anchor) {
723		this.url = url;
724		this.size = size || null;
725		this.origin = origin || null;
726		this.anchor = anchor || null;
727	}
728
729	function installCompatibilityLayer() {
730		var google = window.google || {};
731		google.maps = {
732			InfoWindow: CompatInfoWindow,
733			LatLng: CompatLatLng,
734			LatLngBounds: CompatLatLngBounds,
735			Map: CompatMap,
736			MapTypeId: {ROADMAP: "roadmap"},
737			Marker: CompatMarker,
738			MarkerImage: CompatMarkerImage,
739			Polygon: CompatPolygon,
740			Polyline: CompatPolyline,
741			Point: CompatPoint,
742			Size: CompatSize,
743			ZoomControlStyle: {SMALL: "small"},
744			event: {
745				addListener: function (target, eventName, handler) {
746					if (target && typeof target._addListener === "function") {
747						target._addListener(eventName, handler);
748					} else if (target && typeof target.on === "function") {
749						target.on(eventName, handler);
750					}
751				},
752				addListenerOnce: function (target, eventName, handler) {
753					if (eventName === "idle") {
754						window.setTimeout(handler, 0);
755					} else if (target && typeof target.once === "function") {
756						target.once(eventName, handler);
757					} else if (target && typeof target._addListener === "function") {
758						var called = false;
759						target._addListener(eventName, function () {
760							if (!called) {
761								called = true;
762								handler();
763							}
764						});
765					}
766				},
767				trigger: function (target, eventName) {
768					if (target && eventName === "resize" && typeof target.invalidateSize === "function") {
769						window.setTimeout(function () {
770							target.invalidateSize({pan: false});
771						}, 0);
772					} else if (target && typeof target.fire === "function") {
773						target.fire(eventName);
774					}
775				}
776			}
777		};
778		window.google = google;
779	}
780
781	window.ryokouMapIsReady = function () {
782		return loaderState === "ready";
783	};
784
785	window.ryokouLoadMapLibraries = function (callback) {
786		if (loaderState === "ready") {
787			callback();
788			return;
789		}
790		loaderCallbacks.push(callback);
791		if (loaderState === "loading") {
792			return;
793		}
794
795		loaderState = "loading";
796		setLoadingState(true);
797		showStatus("地図を読み込んでいます…", false);
798
799		selectedProvider = "openfreemap";
800		ensureLeaflet(function (leafletError) {
801			if (leafletError) {
802				finishLibraryLoad(leafletError);
803				return;
804			}
805			installCompatibilityLayer();
806			ensureOpenFreeMap(function (openFreeMapError) {
807				if (openFreeMapError) {
808					selectedProvider = "osm";
809					showStatus("地図表示を切り替えています…", false);
810				}
811				finishLibraryLoad(null);
812			});
813		});
814	};
815
816	window.ryokouMapDebugState = function () {
817		var providers = [];
818		for (var i = 0; i < activeMaps.length; i += 1) {
819			providers.push(activeMaps[i]._ryokouProvider);
820		}
821		return {
822			loaderState: loaderState,
823			selectedProvider: selectedProvider,
824			activeProviders: providers
825		};
826	};
827}(window, document));

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.