PageSourceSearch

https://klauskomenda.net/wp-content/cache/autoptimize/js/autoptimi…_single_39a98b7920a1982a7497737f0c65cd36.js

js klauskomenda.net collected 2026-10-09 04:59:02 UTC 16,336 bytes, 37 lines download raw bytes

1class RouteMap{constructor(containerId){const version='2.9';console.log('[RouteMap] constructor, version: ',version+' ('+Date.now()+')');this.activities=[];this.activityInfoWindow=new google.maps.InfoWindow();this.$mapContainer=$('#'+containerId);this.map=false;this.photoMarkers=[];this.stats={distance:0,elevation_gain:0};this.callCount=0;this.bounds=new google.maps.LatLngBounds();this.strokeColors=['c02026','5461ac','8f59a4','faa251'];const lowFiMapStyleConfig={name:'LowFi Map',config:[{stylers:[{saturation:-80}]},{featureType:'road',elementType:'geometry',stylers:[{lightness:100},{visibility:'simplified'}]},{featureType:'road',elementType:'labels',stylers:[{visibility:'off'}]}]};const viewportWidth=$(window).width();const zoomLevels={desktopExtraLarge:6,desktopLarge:5,desktopMedium:4,mobile:3};let zoomLevel;const basePath=this.$mapContainer.data('basepath');if(!basePath){console.error('[routeMap] no base url to activity JSON config data defined');}
2if(viewportWidth>=2250){zoomLevel=zoomLevels.desktopExtraLarge;}else if(viewportWidth<2250&&viewportWidth>=1440){zoomLevel=zoomLevels.desktopLarge;}else if(viewportWidth<1440&&viewportWidth>=800){zoomLevel=zoomLevels.desktopMedium;}else{zoomLevel=zoomLevels.mobile;}
3this.config={map:{containerId,basePath,strokeOpacity:1.0,strokeWeight:(viewportWidth<2250)?3:4,styledMap:lowFiMapStyleConfig,gMap:{zoom:zoomLevel,mapTypeControl:true,mapTypeControlOptions:{style:google.maps.MapTypeControlStyle.DROPDOWN_MENU,position:google.maps.ControlPosition.LEFT_TOP,mapTypeIds:['roadmap','satellite','hybrid','terrain']},zoomControl:true,zoomControlOptions:{position:google.maps.ControlPosition.RIGHT_BOTTOM},scaleControl:true,streetViewControl:true,streetViewControlOptions:{position:google.maps.ControlPosition.RIGHT_BOTTOM},fullscreenControl:true,fullscreenControlOptions:{position:google.maps.ControlPosition.RIGHT_TOP}}}}}
4fetchServerData=async(fileName)=>{const response=await fetch(this.config.map.basePath+fileName);const responseJson=await response.json();return responseJson;}
5getActivities(){const self=this;this.fetchServerData('activities.json').then(activities=>{self.activityList=activities;let count=0;activities.forEach((activity,index,list)=>{self.fetchServerData(activity.fileName).then(data=>{if(Array.isArray(data)){const sortedActivities=data.sort((a,b)=>a.id-b.id);self.activityList=sortedActivities;sortedActivities.forEach((data,index,list)=>{count++;self.renderPolyline(data,index);self.stats.distance+=data.distance;self.stats.elevation_gain+=data.total_elevation_gain;if(index===0){self.startDate=data.start_date;}
6if(index===(list.length-1)){self.endDate=data.start_date;}
7if(count===list.length){self.renderStatistics();self.renderRouteDownloadLink();}});}else{count++;self.renderPolyline(data,index);self.stats.distance+=data.distance;self.stats.elevation_gain+=data.total_elevation_gain;if(index===0){self.startDate=data.start_date;}
8if(index===(list.length-1)){self.endDate=data.start_date;}
9if(count===list.length){self.renderStatistics();self.renderRouteDownloadLink();}}})});});}
10getPhotos(){console.log('[RouteMap] getPhotos');const self=this;this.fetchServerData('photos.json').then(photosGeoJson=>{photosGeoJson.features.forEach((photo,index,list)=>{self.renderPhotoMarker(photo);})
11$('#activity-overview-map').magnificPopup({delegate:'.photo-marker a',type:'image',gallery:{enabled:true},image:{titleSrc:'title'}});})}
12renderPinMarker(type,latlng){const startMarkerImgUrl='/wp-content/themes/spacegray/images/start.png';const endMarkerImgUrl='/wp-content/themes/spacegray/images/stop.png';const image={size:new google.maps.Size(20,28)};switch(type){case'start':image.url=startMarkerImgUrl;break;case'end':image.url=endMarkerImgUrl;break;}
13new google.maps.Marker({position:{lat:latlng[0],lng:latlng[1]},map:this.map,icon:image});}
14renderPolyline(activity,idx){this.callCount++;const self=this;let renderStartStopMarkers=this.$mapContainer.data('renderstartstop');if(renderStartStopMarkers===undefined){renderStartStopMarkers=true;}
15const route=new google.maps.Polyline({path:google.maps.geometry.encoding.decodePath(activity.map.summary_polyline),strokeColor:'#'+this.strokeColors[idx%this.strokeColors.length],strokeOpacity:this.config.map.strokeOpacity,strokeWeight:this.config.map.strokeWeight});route.setMap(this.map);
15route.index=idx;route.addListener('click',function(){const r=this;const path=r.getPath();const pathLength=path.getLength();const infowindowLocation=path.getAt(Math.floor(pathLength/2));self.activityInfoWindow.close();self.activityInfoWindow.setContent(self.generateInfoWindowContent(activity));self.activityInfoWindow.setPosition(infowindowLocation);self.activityInfoWindow.open(self.map);});const points=route.getPath().getArray();for(let n=0;n<points.length;n++){this.bounds.extend(points[n]);}
16if(renderStartStopMarkers){if(idx===0){const firstPoint=points[0];const startLatLng=[firstPoint.lat(),firstPoint.lng()];this.renderPinMarker('start',startLatLng);}
17if(idx===this.activityList.length-1){const lastPoint=points[points.length-1];const endLatLng=[lastPoint.lat(),lastPoint.lng()];this.renderPinMarker('end',endLatLng);}}
18if(this.callCount===this.activityList.length){this.map.fitBounds(this.bounds);}}
19generateInfoWindowContent(activity){const stravaUrl='https://www.strava.com/activities/'+activity.id;const unitSystem='m';const labels={distance:(unitSystem==='m')?'km':'mi',speed:(unitSystem==='m')?'km/h':'mph',elevation:(unitSystem==='m')?'m':' feet'}
20const movingTimeObj=this.utils.secondsToDaysHoursMinutes(activity.moving_time);activity.distanceKm=this.utils.round(activity.distance/1000,1);activity.distanceMiles=this.utils.metersToMiles(activity.distance,1);const avgSpeedKmh=(activity.distanceKm/activity.moving_time)*3600;activity.avgSpeedKmh=this.utils.round(avgSpeedKmh);activity.avgSpeedMph=this.utils.round(activity.avgSpeedKmh/1.609);activity.elevationGainFeet=Math.floor(this.utils.metersToFeet(activity.total_elevation_gain));const tplData={heading:activity.name,distance:(unitSystem==='m')?activity.distanceKm:activity.distanceMiles,avg_speed:(unitSystem==='m')?activity.avgSpeedKmh:activity.avgSpeedMph,elevation:(unitSystem==='m')?activity.total_elevation_gain:activity.elevationGainFeet,hours:movingTimeObj.hours,minutes:movingTimeObj.minutes,seconds:movingTimeObj.seconds,labels};const infoWindowContentTpl='<div class="activity-info-window">'+'<h3>'+activity.name+'</h3>'+'<ul>'+'<li><h4>Distance</h4><span class="value">'+tplData.distance+tplData.labels.distance+'</span></li>'+'<li><h4>Elevation Gain</h4><span class="value">'+tplData.elevation+tplData.labels.elevation+'</span></li>'+'<li><h4>Moving Time</h4><span class="value">'+tplData.hours+'h '+tplData.minutes+'m '+tplData.seconds+'s</span></li>'+'<li><h4>Avg Speed</h4><span class="value">'+tplData.avg_speed+tplData.labels.speed+'</span></li>'+'</ul>'+'<a href="'+stravaUrl+'" target="_blank">View on Strava</a>'+'</div>';return infoWindowContentTpl;}
21renderPhotoMarker(marker){const lat=marker.geometry.coordinates[1];const lng=marker.geometry.coordinates[0];const latlng=new google.maps.LatLng(lat,lng);const photoMarker=new PhotoMarker(this.config.map.basePath+'img/'+marker.properties.imageSrc,this.config.map.basePath+'img/'+marker.properties.thumbSrc,marker.properties.title,latlng,this.map,{marker_id:'marker-'+(Math.ceil(Math.random()*10000))});this.photoMarkers.push(photoMarker);$('#map').magnificPopup({delegate:'.photo-marker a',type:'image',gallery:{enabled:true},image:{titleSrc:'title'}});}
22renderStatistics(){const activityTitle=this.$mapContainer.data('title');const distanceInKm=this.utils.round(this.stats.distance/1000,1);const distanceInMiles=this.utils.round(this.utils.metersToMiles(this.stats.distance),1);const elevationInMeters=this.utils.round(this.stats.elevation_gain,0);const elevationInFeet=this.utils.round(this.utils.metersToFeet(this.stats.elevation_gain),0);let totalDays;this.startDate=this.startDate.split('T')[0];this.endDate=this.endDate.split('T')[0];const timeDiff=new Date(this.endDate).getTime()-new Date(this.startDate).getTime();totalDays=Math.floor(timeDiff/(1000*3600*24));totalDays++;const statsMarkup='<div id="activity-stats"><h3>'+activityTitle+'</h3><ul>'+'<li><span class="value-metric" title="'+distanceInMiles+' miles">'+distanceInKm+'km</span></li>'+'<li><span class="value-metric" title="'+elevationInFeet+' ft">'+elevationInMeters+' meters</span></li>'+'<li><span class="value-days">'+totalDays+' days</span></li></ul>'+'</div>';if(activityTitle){$(statsMarkup).insertBefore(this.$mapContainer.parent());}}
23renderRouteDownloadLink(){const routeDownloadUrl=this.$mapContainer.data('routedownloadurl');let cssClass;let linkText;
23if(routeDownloadUrl){const routeDownloadLinkDetails=this.getRouteDownloadLinkDetails(routeDownloadUrl);cssClass=routeDownloadLinkDetails.cssClass;linkText=routeDownloadLinkDetails.linkText;const disclaimerTextMarkup='<p>The tracks on the map represent the route as it was recorded by me on Strava. It may differ from the planned route.</p>'+'<p>Please download the official route from <a href="'+routeDownloadUrl+'" class="'+cssClass+'" target="_blank">'+linkText+'</a>.</p>';const footerMarkup='<div class="routemap-footer">'+disclaimerTextMarkup+'</div>';$(footerMarkup).insertAfter(this.$mapContainer.parent());}}
24getRouteDownloadLinkDetails(routeDownloadUrl){let cssClass;let linkText;if(routeDownloadUrl.includes('ridewithgps.com')){cssClass='ridewithgps-link';linkText='Ride With GPS';}else if(routeDownloadUrl.includes('komoot.com')){cssClass='komoot-link';linkText='komoot';}else if(routeDownloadUrl.includes('alltrails.com')){cssClass='alltrails-link';linkText='AllTrails';}else{console.warn('[RouteMap] route download URL provided, but source could not be determined from URL: ',routeDownloadUrl);cssClass='unknown-route-download';linkText='unknown source';}
25return{cssClass,linkText};}
26renderQuickFactsSection(){const self=this;$('#route-info-container').addClass('flex-container');const routeDownloadUrl=this.$mapContainer.data('routedownloadurl');const distanceInKm=this.$mapContainer.data('distance-km')||false;const elevationInMeters=this.$mapContainer.data('elevation-meters')||false;if(!routeDownloadUrl){console.warn('[RouteMap] no route download URL provided, cannot render "quick facts" section');}
27if(routeDownloadUrl.includes('ridewithgps.com')&&(distanceInKm===false||elevationInMeters===false)){const apiKey='a88d2332';const authToken='5d94aca4585c27f46200a654d26f9efe';const apiEndpoint=`https://ridewithgps.com/routes/${routeDownloadUrl.split('/').pop()}.json?api_key=${apiKey}&auth_token=${authToken}`;fetch(apiEndpoint).then(response=>response.json()).then(data=>{const distanceInKm=Number(data.route.distance/1000).toLocaleString('en-US',{minimumFractionDigits:1,maximumFractionDigits:1});const elevationInMeters=Number(data.route.elevation_gain).toLocaleString('en-US',{minimumFractionDigits:0,maximumFractionDigits:0});const distanceInMiles=Number(self.utils.metersToMiles(data.route.distance,1)).toLocaleString('en-US',{minimumFractionDigits:1,maximumFractionDigits:1});const elevationInFeet=Number(self.utils.metersToFeet(data.route.elevation_gain)).toLocaleString('en-US',{minimumFractionDigits:0,maximumFractionDigits:0});const surfaceType=data.route.surface||false;const unpavedPercentage=data.route.unpaved_pct||0;let surfaceTypeString;switch(surfaceType){case'paved':surfaceTypeString='Paved';break;case'mostly_paved':surfaceTypeString='Mostly Paved';break;case'mixed_surfaces':surfaceTypeString='Mixed Surfaces';break;case'mostly_unpaved':surfaceTypeString='Mostly Unpaved';break;default:surfaceTypeString='Unknown';}
28const routeOverview=self.$mapContainer.data('route-overview')||false;const routeOverviewMarkup=routeOverview?'<div class="route-overview"><dt>Route Overview</dt><dd>'+routeOverview+'</dd></div>':'';const routeDownloadLinkDetails=self.getRouteDownloadLinkDetails(routeDownloadUrl);const routeDownloadLinkCssClass=routeDownloadLinkDetails.cssClass;const totalDays=self.$mapContainer.data('total-days')||false;const quickFactsMarkup=self.generateQuickFactsMarkup(routeDownloadUrl,distanceInKm,distanceInMiles,elevationInMeters,elevationInFeet,totalDays,surfaceTypeString,unpavedPercentage,routeOverviewMarkup,routeDownloadLinkCssClass);$('#route-info-container').prepend(quickFactsMarkup);$('#routemap-container').addClass('column-75');}).catch(error=>{console.error('[RouteMap] error fetching route details from Ride With GPS API:',error);});}else{const distanceInMiles=Number(self.utils.metersToMiles(distanceInKm*1000,1)).toLocaleString('en-US',{minimumFractionDigits:1,maximumFractionDigits:1});const elevationInFeet=Number(self.utils.metersToFeet(elevationInMeters)).toLocaleString('en-US',{minimumFractionDigits:0,maximumFractionDigits:0});const totalDays=this.$mapContainer.data('total-days')||false;
28const surfaceTypeString=this.$mapContainer.data('surface-type')||false;const unpavedPercentage=this.$mapContainer.data('unpaved-percentage')||false;const routeOverview=this.$mapContainer.data('route-overview')||false;const routeOverviewMarkup=routeOverview?'<div class="route-overview"><dt>Route Overview</dt><dd>'+routeOverview+'</dd></div>':'';const routeDownloadLinkDetails=self.getRouteDownloadLinkDetails(routeDownloadUrl);const routeDownloadLinkCssClass=routeDownloadLinkDetails.cssClass;const quickFactsMarkup=self.generateQuickFactsMarkup(routeDownloadUrl,distanceInKm,distanceInMiles,elevationInMeters,elevationInFeet,totalDays,surfaceTypeString,unpavedPercentage,routeOverviewMarkup,routeDownloadLinkCssClass);$('#route-info-container').prepend(quickFactsMarkup);$('#routemap-container').addClass('column-75');}}
29generateQuickFactsMarkup(routeDownloadUrl,distanceInKm,distanceInMiles,elevationInMeters,elevationInFeet,totalDays,surfaceTypeString,unpavedPercentage,routeOverviewMarkup,routeDownloadLinkCssClass){let quickFactsMarkup='<section id="quick-facts" class="column-25">'+'<h2>Route Quick Facts</h2>'+'<dl class="route-facts">'+'<div><dt>Distance</dt>'+'<dd><span class="imperial-m" title="Distance in miles">'+distanceInMiles+' mi</span> (<span class="metric-km" title="Distance in kilometers">'+distanceInKm+' km</span>)</dd></div>'+'<div><dt>Elevation Gain</dt>'+'<dd><span class="imperial-ft" title="Elevation in feet">'+elevationInFeet+' ft</span> (<span class="metric-m" title="Elevation gain in meters">'+elevationInMeters+' m</span>)</dd></div>';if(totalDays!==false){quickFactsMarkup+='<div><dt>Total Days</dt>'+'<dd>'+totalDays+'</dd></div>';}
30if(surfaceTypeString&&unpavedPercentage!==false){quickFactsMarkup+='<div><dt>Surface</dt>'+'<dd><span class="surface-type">'+surfaceTypeString+'</span><span class="surface-type-distribution"> ('+unpavedPercentage.toFixed(0)+'% unpaved)</span></dd></div>';}
31if(routeOverviewMarkup){quickFactsMarkup+=routeOverviewMarkup;}
32quickFactsMarkup+='<div><dt>Route Download</dt>'+'<dd><a href="'+routeDownloadUrl+'" class="'+routeDownloadLinkCssClass+'" target="_blank" aria-label="View and download the route"></a></dd></div>'+'</dl>'+'</section>';return quickFactsMarkup;}
33init(){console.log('[RouteMap] init');const self=this;const mapConfig=this.config.map.gMap;const mapType=this.$mapContainer.data('maptype');const renderQuickFactsSection=this.$mapContainer.data('render-quick-facts')||false;let routeMapContainerCssClass=false;let styledMapType;if(renderQuickFactsSection){this.renderQuickFactsSection();}
34if(this.config.map.styledMap){styledMapType=new google.maps.StyledMapType(this.config.map.styledMap.config,{name:this.config.map.styledMap.name});mapConfig.mapTypeControlOptions.mapTypeIds.push('styled_map');}
35this.map=new google.maps.Map(document.getElementById(this.config.map.containerId),mapConfig);this.map.setTilt(45);if(this.config.map.styledMap){this.map.mapTypes.set('styled_map',styledMapType);}
36this.map.setMapTypeId(mapType||'styled_map');this.getActivities();if(renderQuickFactsSection){routeMapContainerCssClass='column-75';}
37$(this.$mapContainer.parent()).wrap('<div id="routemap-container" class="'+(routeMapContainerCssClass||'')+'"></div>');if(this.$mapContainer.data('show-photos')){$.getScript('/wp-content/themes/spacegray/js/PhotoMarker-v1.1.js').done(function(){self.getPhotos();}).fail(function(jqxhr,settings,exception){console.error('[RouteMap] could not load PhotoMarker lib');});}}}

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.