PageSourceSearch

https://cafes.cyclingmaps.net/js/cafe.js

js cyclingmaps.net collected 2026-10-05 22:36:07 UTC 11,225 bytes, 369 lines download raw bytes

1
2function genMarkerIcon_dot(url) {
3	return L.icon({
4		iconUrl: url,
5		className: "marker-cafePin",
6		popupAnchor: [0, -20],
7		iconSize: [25, 25],
8		iconAnchor: [12, 25],
9	});
10}
11
12var green_pin = genMarkerIcon_dot('leaflet/images/green-pin.png');
13var blue_pin = genMarkerIcon_dot('leaflet/images/blue-pin.png');
14var grey_pin = genMarkerIcon_dot('leaflet/images/grey-pin.png');
15
16var pinMapping = {
17	"New": green_pin,
18	"default": blue_pin,
19	"Take": blue_pin,
20	"TempClosed": grey_pin,
21};
22
23function updateMapSize()
24{
25	var c = $(map.getContainer());
26	var newHeight = (window.innerHeight-c.offset().top-2);
27	if (c.height()!=newHeight)
28	{
29		c.height(newHeight+ "px");
30		map.invalidateSize();
31	}
32}
33
34function initMap()
35{
36    var tileLayers = {
37        "OpenStreetMap": L.tileLayer( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
38            attribution: ' &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
39           
40            maxZoom: 19,
41            minZoom: 5,
42        }),
43        "OpenCycleMap": L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey={apikey}', {
44            attribution: '&copy; <a href="http://www.thunderforest.com/">Thunderforest</a>, &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
45            apikey: 'f3e5c8ab7dc9440faf167ccd39c826e0',
46			bounds:[[35, -11],[62, 18]],
47                maxZoom: 22,minZoom: 6
48        }),
49         "Esri": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
50         	maxZoom: 19,  minZoom: 5,
51			 bounds:[[35, -11],[62, 18]],
52            attribution: 'Tiles &copy; Esri &mdash; Source: Esri, DeLorme, <br>NAVTEQ, USGS, Intermap, iPC, NRCAN, METI, TomTom',
53                       }),
54					   
55		"CyclOSM": L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', {
56		attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', 
57		maxZoom: 20,minZoom: 6,
58		
59		}),
60        
61    	"Open MTB": L.tileLayer('https://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png', {
62		attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &amp; USGS',
63            maxZoom: 18, minZoom: 6,
64			bounds:[[35, -11],[62, 18]],
65        }),
66        
67		"Top Plus" : L.tileLayer('https://sgx.geodatenzentrum.de/wmts_topplus_open/tile/1.0.0/web/default/WEBMERCATOR/{z}/{y}/{x}.png', {
68			maxZoom: 16, minZoom: 5,
69			attribution: 'Map data: &copy; <a href="http://www.govdata.de/dl-de/by-2-0">dl-de/by-2-0</a>',
70		}),
71
72		"OpenTopoMap": L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
73            maxZoom: 17, minZoom: 5,
74            attribution: 'Map data: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
75			bounds:[[35, -11],[62, 18]],
76        }),
77
78        "Satellite": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
79        maxZoom: 19, minZoom: 6,
80        attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, <br>USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, <br>IGP, UPR-EGP, and the GIS User Community'
81        })
82    };
83
84	
85	map = new L.map('cafemap', {
86		trackResize: true,
87	});
88	ciLayer = L.canvasIconLayer({}).addTo(map);
89
90    tileLayers["OpenStreetMap"].addTo( map );
91
92    L.control.layers(tileLayers).addTo(map);
93	
94	L.Control.FileLayerLoad.LABEL = '<i class="fa fa-route fa"></i>',
95   L.Control.fileLayerLoad({
96	
97		position: 'topright',
98        layerOptions: {style: {color:'fuchsia'}},
99        fileSizeLimit: 4096,
100        formats: [
101			/*'.gpx',
102            '.geojson',
103			'.json',
104            '.kml',
105			'application/gpx+xml',
106			'application/json',
107			'application/vnd.google-earth.kml+xml'*/
108			''
109        ]
110    }).addTo(map);
111
112
113	if (L.Browser.mobile) {
114		map.removeControl(map.zoomControl);
115	}
116
117
118	map.setView(new L.LatLng(54.4700378,-4.3945308),6);
119	
120	var doneFirstLocation = false;
121
122	function onLocationFound(e) {
123		var minZoom = 11;
124		var radius = e.accuracy / 2;
125		var newZoom = map.getZoom();
126		if (newZoom<minZoom)
127			newZoom = minZoom;	
128		map.flyTo(e.latlng, newZoom, {animate:doneFirstLocation});
129		doneFirstLocation = true;
130	}
131	map.on('locationfound', onLocationFound);
132	map.locate({timeout:60000, maximumAge:30000});
133
134function formatJSON(rawjson) {	//callback that remap fields name
135		var json = {},
136			key, loc, disp = [];
137			
138		for(var i in rawjson)
139		{	
140			disp = rawjson[i].display_name.split(',');	
141			key = disp[0] +', '+ disp[1];	
142			loc = L.latLng( rawjson[i].lat, rawjson[i].lon );	
143			json[ key ]= loc;	//key,value format
144		}
145		
146		return json;
147	}
148
149	var SearchOpts = {
150		url: 'https://nominatim.openstreetmap.org/search?format=json&countrycodes=ie%2Cgb&q={s}',
151		jsonpParam: 'json_callback',
152		formatData: formatJSON,		
153		textPlaceholder: 'Town, Place, Post Code..',
154		autoType: true,
155		tipAutoSubmit: true,
156		firstTipSubmit: true,
157		//tooltipLimit: 25,
158		textErr: "Location Not Found",
159		position: "topleft",
160		autoCollapse: true,
161		//autoCollapseTime: 5,
162		zoom: 14,
163		delayType: 50,	//with mobile device typing is more slow
164		marker: {
165			icon: false
166		}		
167	};
168		
169	map.addControl( new L.Control.Search(SearchOpts) );
170
171var lc = L.control.locate({
172	position: 'bottomright',
173	strings: {
174        title: "Show Location"
175	},
176	locateOptions: {
177		enableHighAccuracy: true
178	},
179	keepCurrentZoomLevel : true,
180});
181lc.addTo(map)
182
183	var legend = L.control({position: 'bottomleft'});
184	
185    legend.onAdd = function (map) {
186        var div = L.DomUtil.create('div', 'legend');
187		div.innerHTML +=  '<img src="leaflet/images/blue-pin.png">'  + 'Cafe' + '<br>'
188        div.innerHTML +=  '<img src="leaflet/images/green-pin.png">' + 'New cafe' + '<br>'
189		div.innerHTML +=  '<img src="leaflet/images/grey-pin.png">' + 'Temp closed' + '<br>'
190		div.innerHTML +=   '<u><a href="https://cafes.cyclingmaps.net/help.php#q-Legend"target="_blank">Maps Legend</a></u>' 	 
191        return div;
192    };
193
194    legend.addTo(map);
195
196}
197
198function genPopupContent(c)
199{
200	var x;
201	var popupContent = document.createElement('div');
202
203	x = document.createElement('b');
204	var titleTxt;
205	if (typeof c.location==="string" && c.location!="")
206		titleTxt = c.name + ", " + c.location;
207	else
208		titleTxt = c.name;
209	x.appendChild(document.createTextNode(titleTxt));
210	popupContent.appendChild(x);
211
212	popupContent.appendChild(document.createElement('br'));
213	popupContent.appendChild(document.createTextNode(c.address));
214	popupContent.appendChild(document.createElement('br'));
215	popupContent.appendChild(document.createTextNode(c.details));
216	popupContent.appendChild(document.createElement('br'));
217
218	if (typeof c.phone==="string" && c.phone!="")
219	{
220		popupContent.appendChild(document.createTextNode("Tel: "));
221		x = document.createElement('a');
222		x.appendChild(document.createTextNode(c.phone));
223		x.setAttribute("target", "_blank");
224		x.setAttribute("href", "tel:"+c.phone);
225		popupContent.appendChild(x);
226		popupContent.appendChild(document.createElement('br'));
227		
228	}
229	if (typeof c.email==="string" && c.email!="")
230	{
231		x = document.createElement('a');
232		x.appendChild(document.createTextNode("Email"));
233		x.setAttribute("target", "_blank");
234		x.setAttribute("href", "mailto:"+c.email);
235		popupContent.appendChild(x);
236		popupContent.appendChild(document.createElement('br'));
237	}
238	if (typeof c.website==="string" && c.website!="")
239	{
240		x = document.createElement('a');
241		x.appendChild(document.createTextNode("Website"));
242		x.setAttribute("target", "_blank");
243		x.setAttribute("href", c.website);
244		x.className = "cafe-webLink";
245		popupContent.appendChild(x);
246		popupContent.appendChild(document.createElement('br'));
247	}
248		// Streetview
249		x = document.createElement('a');
250		x.appendChild(document.createTextNode("Street View"));
251		x.setAttribute("target", "_blank");
252		x.setAttribute("href", "https://www.google.com/maps/@?api=1&map_action=pano&viewpoint="+c.lat+","+c.lng);
253		x.className = "cafe-webLink";
254		popupContent.appendChild(x);
255	if (typeof c.photo==="string" && c.photo!="")
256	{
257		x = document.createElement('img');
258		x.className = "cafe-photo";
259		x.setAttribute("alt", "Image");
260		x.setAttribute("src", c.photo);
261		x.setAttribute('onclick', 'this.requestFullscreen()');
262		popupContent.appendChild(x);
263		popupContent.appendChild(document.createElement('br'));
264	}
265
266	return popupContent;
267}
268function onCafeMarkerClick()
269{
270	var sizeLimit = map.getSize();
271	var margin = {
272		top:50,
273		left:36,
274		bottom:70,
275		right:4,
276	}
277		
278	sizeLimit.y -= margin.top+margin.bottom +50;
279	sizeLimit.x -= margin.left+margin.right +50;
280
281	var popupContent = genPopupContent(this.options.cafe);
282	var popup = L.popup({
283		hasTip: true,
284		autoPan: true,
285		autoPanPaddingTopLeft: L.point(margin.left,margin.top),
286		autoPanPaddingBottomRight: L.point(margin.right,margin.bottom),
287		maxWidth:(sizeLimit.x<500 ? sizeLimit.x : 500),
288		minWidth:(sizeLimit.x<200 ? sizeLimit.x : 200),
289		maxHeight:(sizeLimit.y>100 ? sizeLimit.y : 100),
290		offset: this.options.icon.options.popupAnchor,
291	})
292	.setLatLng(this.getLatLng())
293
294	$(popupContent).find("img").on("load error", function(){
295		setTimeout(function(){
296			popup.update();
297		}, 1);
298	});
299	popup.setContent(popupContent);
300
301	popup.openOn(map);
302	popup.update();
303}
304
305
306function addCafeMarkers(cafes)
307{
308	var n = cafes.length;
309	var cafes_total = cafes.length;
310    var newMarkers = [];
311	document.getElementById("cafecount").innerHTML = cafes_total;
312	
313	for (var i=0; i<n; i++)
314	{
315		var c = cafes[i];
316		var cafeIcon = pinMapping["default"];
317	if (c.status!==null && typeof pinMapping[c.status] != "undefined")
318		//	if (c.status!==null  && typeof pinMapping[c.status] != "Closed")		
319			cafeIcon = pinMapping[c.status];
320		var m = L.marker([c.lat,c.lng], {icon: cafeIcon});
321		m.options.cafe = c;
322		m.on('click', L.bind(onCafeMarkerClick, m));
323		newMarkers.push(m);
324	}
325	ciLayer.addMarkers(newMarkers);
326    var redrawFunc = function(){
327        ciLayer._redraw();
328    };
329    setTimeout(redrawFunc, 1);
330    setTimeout(redrawFunc, 1000);
331}
332
333document.onclick = (event) => {
334	if (document.fullscreenElement) {
335	  document
336		.exitFullscreen()
337		.catch((err) => console.error(err));
338	} 
339  };
340
341$(document).ready(function(){
342	$("#cafemapLoading").html("<div>Loading cafes...</div><div id=\"cafemapLoadingProgress\"></div>");
343	initMap();
344	$.get({
345		type:"GET",
346		//url:"/data/cafes.json",
347		url:"/data/cafes.json?mtime="+cafeMTime,
348		dataType:"json",
349		//cache:false,
350		cache:true,
351		xhrFields:{
352			onprogress:function(e){
353				if (e.lengthComputable)
354				{
355					$("#cafemapLoadingProgress").text(Math.round(e.loaded/e.total *100)+"%");
356				}
357			},
358		},
359	})
360	.done(function(data){
361		addCafeMarkers(data);
362		$("#cafemapLoading").remove();
363	})
364	.fail(function(){
365		alert("Failed to load cafes data");
366	});
367	setTimeout(updateMapSize,1);
368	$(window).on("orientationchange resize scroll", updateMapSize);
369});

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.