1/** 2 * This file relies on prefilled data globally available 3 */ 4(function(data) { 5 let $win = $(window), 6 $body = $('body'), 7 mapsAllowed = $('#map').hasClass('maps-allowed'), 8 mapGlobalSettings = { 9 startZoom: 15, 10 maxZoom: 17, 11 // startLng: 6.083611, 12 startLng: 6.083880928779806, 13 // startLat: 50.775555, 14 startLat: 50.77534719439608, 15 cityZooms: { 16 'Aachen': 15, 17 'Berlin': 14, 18 'Datteln': 15, 19 'Dülmen': 15, 20 'Würselen': 15 21 } 22 }, 23 map, 24 infoWindows = new Array(window.app.facilities.length), 25 poisInfoWindows = [], 26 poisMarkers = [], 27 // pf list 28 $mapWrapper = $('#map'), 29 $mapControls = $('#map-controls'), 30 $cityUl = $mapControls.find('#pills-cities'), 31 $capacityTypeUl = $mapControls.find('#pills-capacity-types'), 32 $cityItems = $cityUl.find('button'), 33 $capacityTypeItems = $capacityTypeUl.find('button'), 34 $facilityListWrapper = $('#map-facility-list-wrapper'), 35 $facilityListToggles = $('#facility-list-toggle, #map-facility-list-wrapper .close'), 36 $facilityListCapacityTypeButtons = $('#map-facility-list-wrapper nav button'), 37 $facilityListSearch = $('#map-facility-list-wrapper .facility-list-search'), 38 $facilityList = $('#map-facility-list-wrapper ul.map-facility-list'), 39 $facilityListItems = $('#map-facility-list-wrapper ul.map-facility-list li'), 40 $facilityListSearchHint = $('#map-facility-list-wrapper .search-hint'), 41 panInfoWindowWhenListOpenWidth = 1599, 42 windowWidthMapPanThreshold = 1900, 43 _capacityTypes = [ 44 'parking', 45 'charging', 46 'bikes' 47 ], 48 _capacityTypesLabelMapping = { 49 'parking' : '.availability-car-parking', 50 'charging' : '.availability-car-charging', 51 'bikes' : '.availability-bike-parking' 52 }, 53 _capacityTypesServiceMapping = { 54 'parking' : 'car_parking', 55 'charging' : 'car_charging', 56 'bikes' : 'bike_parking' 57 }; 58 59 60 // add parking facilities interaction, add map functions if allowed 61 62 window.app.facilities.forEach(function(facility, index) { 63 (function() { 64 let $facilityListItem = $facilityListItems.filter(function($element) { 65 return facility.uuid === $(this).attr('data-facility-uuid'); 66 }); 67 68 facility['facilityListItem'] = $facilityListItem; 69 $facilityListItem.data('facility', facility); 70 71 /** 72 * Update a facility and all its references 73 */ 74 facility.update = function() { 75 let available = getAvailableForType(facility), 76 // @todo get trend from api 77 trend = getTrendForType(facility); 78 79 let that = this; 80 81 // // update facility list item labels 82 // $.each(_capacityTypes, function(key, name) { 83 84 // if (facility.availableCapacityTypesString.indexOf( _capacityTypesServiceMapping[name] ) > -1) { 85 // that.facilityListItem.find(_capacityTypesLabelMapping[name]).text( 86 // 'available_' + name in facility 87 // ? facility['available_' + name] 88 // : window.app.overviewMap.settings.notAvailableString 89 // ); 90 91 // that.facilityListItem 92 // .find(_capacityTypesLabelMapping[name]) 93 // .removeClass('trend-easy trend-medium trend-difficult') 94 // .addClass( 95 // 'trend-' + ( 96 // 'trend_' + name in facility 97 // ? facility['trend_' + name] 98 // : 'not-available' 99 100 // ) 101 // ); 102 // } 103 // }); 104 }; 105 106 /** 107 * Test 108 */ 109 facility.hasMaintenanceFor = function(capacityType) { 110 if ( 111 this['maintenance_' + capacityType] 112 && true == this['maintenance_' + capacityType] 113 ) { 114 return true; 115 } 116 return false;
117 }; 118 119 window.app.facilities[index] = facility; 120 })(); 121 }); // data loop end 122 123 124 /** 125 * Calculate a trend (which is rather a capacity stepping) 126 */ 127 function getTrendForType(facility) { 128 let keyCapacity = getFilterKeyFor(data.capacityTypeShowing), 129 keyTrend = keyCapacity.replace('capacity', 'trend'); 130 131 return facility[keyTrend] || null; 132 } 133 134 /** 135 * Get the availability for the currently selected capacity type 136 */ 137 function getAvailableForType(facility) { 138 let keyCapacity = getFilterKeyFor(data.capacityTypeShowing), 139 keyAvailable = keyCapacity.replace('capacity', 'available'); 140 141 if (null !== facility[keyAvailable] && undefined !== facility[keyAvailable]) { 142 return facility[keyAvailable]; 143 } 144 else { 145 return null; 146 } 147 } 148 149 /** 150 * Return all facilities that are in a city 151 */ 152 function getFacilitiesByCity(city) { 153 let facilities = []; 154 window.app.facilities.forEach(function(facility) { 155 if (city == facility.address_city) { 156 facilities.push(facility); 157 } 158 }); 159 return facilities; 160 } 161 162 /** 163 * @todo 164 */ 165 function getFilterKeyFor(capacityType) { 166 switch (capacityType) { 167 case 'car_charging': 168 return 'capacity_charging'; 169 break; 170 case 'bike_parking': 171 return 'capacity_bikes'; 172 break; 173 case 'car_parking': 174 return 'capacity_parking'; 175 break; 176 } 177 } 178 179 /** 180 * Get facilities for a specific capacity type within a city 181 */ 182 function getFacilitiesByCityAndCapacityType(city, capacityType) { 183 let facilities = getFacilitiesByCity(city); 184 // filterKey = getFilterKeyFor(capacityType); 185 186 return facilities.filter(function(facility) { 187 return facility.availableCapacityTypesString.indexOf(capacityType) > -1; 188 }); 189 } 190 191 /** 192 * Add pf list capacity type button event listener 193 */ 194 $facilityListCapacityTypeButtons.on('click', function() { 195 var capacityType = $(this).data('capacitytype'); 196 197 $facilityList.removeClass('showing-car_parking showing-car_charging showing-bike_parking') 198 .addClass('showing-' + capacityType); 199 200 $facilityListSearch.trigger('input'); 201 }); 202 203 /** 204 * Add pf search input event listener 205 */ 206 $facilityListSearch.on('input', function() { 207 var input = $.trim($facilityListSearch.val()); 208 209 $facilityListItems.removeClass('d-none'); 210 211 if ('' == input) { 212 $facilityListItems.removeClass('d-none'); 213 } 214 else { 215 $facilityListItems.not( function(index, element) { 216 return $(element).text().toLowerCase().indexOf(input.toLowerCase()) > -1; 217 } ).not('.city-label').addClass('d-none'); 218 219 $facilityListItems.filter('.city-label').each(function() { 220 if (0 === $(this).nextUntil('.city-label').filter(':visible').length) { 221 $(this).addClass('d-none'); 222 } 223 }); 224 } 225 226 setTimeout(function() { 227 if (0 === $facilityListItems.filter(':visible').length) { 228 $facilityListSearchHint.removeClass('d-none'); 229 } 230 else { 231 $facilityListSearchHint.addClass('d-none'); 232 } 233 }, 50); 234 235 }); 236 237 /** 238 * Add pf toggle event listener 239 */ 240 $facilityListToggles.on('click', function() { 241 $mapWrapper.toggleClass('facility-list-open'); 242 243 if ($win.width() < windowWidthMapPanThreshold) { 244 if ($mapWrapper.hasClass('facility-list-open')) { 245 map.panBy(-.5 * $facilityListWrapper.width(), 0); 246 } 247 else { 248 map.panBy(.5 * $facilityListWrapper.width(), 0); 249 } 250 } 251 }); 252 253 /** 254 * maps will call this function after loaded successfully 255 */ 256 function initMap() { 257 // make the custom overlays available we need in our map 258 window.addMapClasses(); 259 260 // create the map 261 map = new google.maps.Map(document.getElementById('map-wrapper'), { 262 center: { lat: mapGlobalSettings.startLat, lng: mapGlobalSettings.startLng }, 263 mapTypeControl: false, 264 zoom: mapGlobalSettings.startZoom, 265 zoomControl: true, 266 mapTypeControl: false, 267 scaleControl: true, 268 streetViewControl: false, 269 rotateControl: false, 270 fullscreenControl: false 271 }); 272 273 // when the map rendered and list is open, add pan shift 274 google.maps.event.addListenerOnce(map, 'idle', function() { 275 if ($mapWrapper.hasClass('facility-list-open') && $win.width() < windowWidthMapPanThreshold) { 276 map.panBy(-.5 * $facilityListWrapper.width(), 0); 277 } 278 }); 279 280 // close open info windows on blur 281 map.addListener('click', closeAllInfoWindows); 282 283 let poiStyles = data.mapStyles; 284 map.setOptions({styles: poiStyles}); 285 286 // draw custom pois 287 288 if (window.app.overviewMap.settings.pois) { 289 /** 290 * @todo 291 */ 292 function buildPoiInfowWindow(poi) { 293 return poi.infoWindow || '<div class="poi-info-window"><h5 class="iw-title">' + poi.title + '</h5></div>'; 294 } 295 296 $.each(window.app.overviewMap.settings.pois, function(city, data) { 297 $.each(data, function(index, poi) { 298 var marker = new google.maps.Marker({ 299 position: { lat: poi.lat, lng: poi.lng }, 300 map, 301 icon: { 302 url: window.app.overviewMap.settings.icons[poi.icon], 303 scaledSize: new google.maps.Size(24, 32) 304 }, 305 title: poi.title 306 }), 307 infoWindowContent = buildPoiInfowWindow(poi), 308 infoWindow = new PoiInfoWindow( 309 marker, 310 infoWindowContent 311 ); 312 313 poisInfoWindows.push(infoWindow); 314 poisMarkers.push(marker); 315 316 let shadow = new MarkerShadow(marker, 'poi-shadow-marker');
317 shadow.setMap(map); 318 319 marker.shadow = shadow; 320 321 /** 322 * Add marker click event listener 323 */ 324 marker.addListener('click', function() { 325 closeAllInfoWindows(); 326 infoWindow.setMap(map); 327 }); 328 329 }); 330 }); 331 } 332 333 /** 334 * https://stackoverflow.com/a/10666030 335 */ 336 function getLatLngWithOffset(latLng, offsetX, offsetY, zoom) { 337 // latLng is the apparent centre-point 338 // offsetX is the distance you want that point to move to the right, in pixels 339 // offsetY is the distance you want that point to move upwards, in pixels 340 // offset can be negative 341 // offsetX and offsetY are both optional 342 343 let scale = Math.pow(2, zoom || map.getZoom()), 344 worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latLng), 345 pixelOffset = new google.maps.Point( 346 (offsetX / scale) || 0, 347 (offsetY / scale) || 0 348 ), 349 worldCoordinateNewCenter = new google.maps.Point( 350 worldCoordinateCenter.x - pixelOffset.x, 351 worldCoordinateCenter.y + pixelOffset.y 352 ); 353 354 return map.getProjection().fromPointToLatLng(worldCoordinateNewCenter); 355 } 356 357 // add maps functions, markers, badges, etc 358 359 $.each(window.app.facilities, function(index, facility) { 360 (function() { 361 let iconKey = ''; 362 363 if (facility.type === 'ParkingFacility') { 364 if (facility.facility_type && data.icons[facility.facility_type.toLowerCase()]) { 365 iconKey = facility.facility_type.toLowerCase(); 366 } 367 else { 368 iconKey = 'parking_facility_default'; 369 } 370 } 371 else { 372 iconKey = 'bike_parking'; 373 } 374 375 // add the marker 376 let marker = new google.maps.Marker({ 377 animation: google.maps.Animation.DROP, 378 position: { lat: facility.lat, lng: facility.lng }, 379 map, 380 icon: { 381 url: data.icons[iconKey], 382 scaledSize: new google.maps.Size(49, 68) 383 }, 384 title: facility.label 385 }); 386 387 facility['marker'] = marker; 388 facility['marker']['facility'] = facility; 389 390 // add pf list link 391 392 let $facilityListItem = $facilityListItems.filter(function($element) { 393 return facility.uuid === $(this).attr('data-facility-uuid'); 394 }); 395 396 facility['facilityListItem'] = $facilityListItem; 397 $facilityListItem.data('facility', facility); 398 399 // add occupancy badge 400 401 let badge = new FacilityAvailabilityBadge( 402 facility, 403 data.capacityTypeShowing, 404 new google.maps.LatLng(facility.lat, facility.lng), 405 facility.available_parking || null, 406 facility.trend_parking || null 407 );
408 badge.setMap(map); 409 facility['badge'] = badge; 410 411 // add a pin shadow 412 413 let shadow = new MarkerShadow(marker); 414 shadow.setMap(map); 415 416 marker.shadow = shadow; 417 facility.shadow = shadow; 418 419 // add the info window 420 421 let infoWindowContent = buildInfoWindowContent(window.app.overviewMap.settings.facilities[index]), 422 infoWindow = new FacilityInfoWindow( 423 marker, 424 infoWindowContent 425 ); 426 infoWindows[index] = infoWindow; 427 428 // add maintenance badge 429 430 let maintenanceBadge = new MaintenanceBadge( 431 facility, 432 new google.maps.LatLng(facility.lat, facility.lng) 433 ); 434 maintenanceBadge.setMap(map); 435 facility['maintenanceBadge'] = maintenanceBadge; 436 437 // $.each(facility.availableCapacityTypesString.split(','), function(index, capacityType) { 438 // let maintenanceBadge = new MaintenanceBadge( 439 // new google.maps.LatLng(facility.lat, facility.lng), 440 // capacityType, 441 // facility.uuid 442 // );
443 // maintenanceBadge.setMap(map); 444 // facility['maintenance_badge_' + capacityType] = maintenanceBadge; 445 // }); 446 447 /** 448 * Add marker click event listener 449 */ 450 marker.addListener('click', function() { 451 facility.focus(); 452 }); 453 454 /** 455 * Pan the map to a facility 456 */ 457 facility.focus = function() { 458 closeAllInfoWindows(); 459 460 let currentMapZoom = map.getZoom(), 461 $infoWindow = $(infoWindow.containerDiv), 462 $wrapper = $('<div class="if-calc-wrapper"></div>').appendTo($body), 463 $infoWindowClone = $(infoWindow.containerDiv) 464 .clone() 465 .appendTo($wrapper), 466 panOffsetX = $mapWrapper.hasClass('facility-list-open') && $win.width() > ($facilityListWrapper.width() * 2) && $win.width() < windowWidthMapPanThreshold 467 ? .5 * $facilityListWrapper.width() 468 : 0, 469 offsetLatLng = getLatLngWithOffset( 470 this.marker.getPosition(), 471 panOffsetX, 472 $infoWindowClone.height() * -.5, 473 facility.address_city === data.cityShowing 474 ? currentMapZoom 475 : mapGlobalSettings.cityZooms[facility.address_city] 476 ); 477 478 // this is buggy without a timeout 479 setTimeout(function() { 480 map.panTo( 481 offsetLatLng 482 ); 483 }, 30); 484 485 $wrapper.remove(); 486 487 setTimeout(function() { 488 facility.openInfoWindow(); 489 }, 250); 490 }; 491 492 /** 493 * Open a facility's info window 494 */ 495 facility.openInfoWindow = function(capacityType) { 496 closeAllInfoWindows(); 497 infoWindow.setMap(map); 498 }; 499 500 /** 501 * Update a facility and all its references 502 */ 503 facility.update = function() { 504 let available = getAvailableForType(facility), 505 // @todo get trend from api 506 trend = getTrendForType(facility); 507 508 // update google maps marker badge 509 this.badge.update(data.capacityTypeShowing, available, trend); 510 this.maintenanceBadge.update(data.capacityTypeShowing); 511 512 let that = this; 513 514 // // update facility list item labels 515 // $.each(_capacityTypes, function(key, name) { 516 517 // if (facility.availableCapacityTypesString.indexOf( _capacityTypesServiceMapping[name] ) > -1) { 518 // that.facilityListItem.find(_capacityTypesLabelMapping[name]).text( 519 // 'available_' + name in facility 520 // ? facility['available_' + name] 521 // : window.app.overviewMap.settings.notAvailableString 522 // ); 523 524 // that.facilityListItem 525 // .find(_capacityTypesLabelMapping[name]) 526 // .removeClass('trend-easy trend-medium trend-difficult') 527 // .addClass( 528 // 'trend-' + ( 529 // 'trend_' + name in facility 530 // ? facility['trend_' + name] 531 // : 'not-available' 532 533 // ) 534 // ); 535 // } 536 // }); 537 }; 538 539 // when on a detail page or preselection is set 540 541 if (window.app.overviewMap.settings.preSelected === facility.uuid) { 542 var preselectedCity = facility.address_city; 543 544 if ($win.width() < 768 && $mapWrapper.hasClass('facility-list-open')) { 545 $mapWrapper.removeClass('facility-list-open') 546 } 547 548 setTimeout(function() { 549 if (data.cityShowing !== preselectedCity) { 550 $cityItems.filter(function() { 551 return $(this).data('city') === preselectedCity; 552 }).trigger('click'); 553 } 554 }, 400); 555 556 // when the map rendered and list is open, add pan shift 557 google.maps.event.addListenerOnce(map, 'idle', function() { 558 setTimeout(function() { 559 // new google.maps.event.trigger(marker, 'click'); 560 facility.focus(); 561 }, 120); 562 }); 563 } 564 565 window.app.facilities[index] = facility; 566 })(); 567 }); // data loop end 568 569 /** 570 * Close all info windows 571 */ 572 function closeAllInfoWindows() { 573 infoWindows.forEach(function(iw) { 574 iw.setMap(null); 575 }); 576 poisInfoWindows.forEach(function(iw) { 577 iw.setMap(null); 578 }); 579 } 580 581 /** 582 * @todo https://pineco.de/using-laravels-localization-js/ 583 */ 584 function buildInfoWindowContent(facility) { 585 return facility.infoWindow; 586 } 587 588 /** 589 * Get all facilities that are currently visible in map bounds 590 */ 591 function getVisibleFacilities() { 592 let bounds = map.getBounds(), 593 facilities = []; 594 595 if (undefined == bounds) return facilities; 596
597 window.app.facilities.forEach(function(facility) { 598 if (bounds.contains(facility.marker.getPosition())) { 599 facilities.push(facility); 600 } 601 }); 602 return facilities; 603 } 604 605 /** 606 * Disable/ enable capacity filters based on the map 607 */ 608 function updateCapacityTypeItems() { 609 let facilities = getVisibleFacilities(), 610 types = Object.keys(data.capacityTypes); 611 612 types.shift(); // the first type will always be available 613 types.forEach(function(capacityType) { 614 615 if (facilities.filter(function(facility) { 616 return facility.availableCapacityTypesString.indexOf(capacityType) > -1; 617 }).length) { 618 // @todo rewrite in jquery 619 Array.from($capacityTypeItems) 620 .filter($item => $item.matches('[data-capacitytype="' + capacityType + '"]')) 621 [0].classList.remove('disabled'); 622 } 623 else { 624 // @todo rewrite in jquery 625 Array.from($capacityTypeItems) 626 .filter($item => $item.matches('[data-capacitytype="' + capacityType + '"]')) 627 [0].classList.add('disabled'); 628 } 629 }); 630 } 631 632 /** 633 * Set the current city after clicking the filter 634 */ 635 function setCity(city) { 636 if ('Aachen' == city) { 637 map.setCenter({ lat: mapGlobalSettings.startLat, lng: mapGlobalSettings.startLng }); 638 map.setZoom(mapGlobalSettings.startZoom); 639 } 640 else { 641 let bounds = new google.maps.LatLngBounds(); 642 let facilities = getFacilitiesByCity(city); 643 facilities.forEach(function(facility) { 644 bounds.extend(facility.marker.position); 645 }); 646 map.fitBounds(bounds); 647 map.setZoom( mapGlobalSettings.cityZooms[city] ); 648 } 649 data.cityShowing = city; 650 651 if ( 652 $mapWrapper.hasClass('facility-list-open') 653 && $win.width() <= panInfoWindowWhenListOpenWidth 654 && $win.width() < windowWidthMapPanThreshold 655 ) { 656 map.panBy( 657 -.5 * $facilityListWrapper.width(), 658 0 659 ); 660 } 661 } 662 663 /** 664 * Set the current capacity type after clicking the filter 665 */ 666 function setCapacityType(capacityType) { 667 data.capacityTypeShowing = capacityType; 668 669 $.each(window.app.facilities, function(index, facility) { 670 if (facility.availableCapacityTypesString.indexOf(capacityType) > -1) { 671 let icon = facility.marker.getIcon(); 672 673 icon.url = ( 674 'car_parking' == capacityType 675 ? data.icons[facility['facility_type'].toLowerCase()] || data.icons['parking_facility_default'] 676 : data.icons[capacityType] 677 ); 678 679 facility.marker.setMap(null); 680 facility.marker.setIcon(icon); 681 facility.marker.setAnimation(google.maps.Animation.DROP); 682 facility.marker.setMap(map); 683 684 // facility.badge.hide(); 685 // // facility.badge.setMap(null);
686 // // facility.badge.setMap(map); 687 // facility.badge.setCapacityType( 688 // capacityType, 689 // getAvailableForType(facility), 690 // getTrendForType(facility) 691 // ); 692 // facility.badge.show(); 693 694 // facility.badge.hide(); 695 // facility.badge.setMap(null); 696 // facility.badge.setMap(map); 697 facility.badge.setCapacityType( 698 capacityType, 699 getAvailableForType(facility), 700 getTrendForType(facility) 701 ); 702 703 facility.shadow.hide(); 704 facility.shadow.setMap(null); 705 facility.shadow.setMap(map); 706 707 // facility.maintenanceBadge.update(capacityType); 708 709 // if ( 710 // facility['maintenance_' + capacityType] 711 // && true == facility['maintenance_' + capacityType] 712 // ) { 713 // facility['maintenanceBadge'].show(); 714 // } 715 // else { 716 // facility['maintenanceBadge'].hide(); 717 // } 718 719 // setTimeout(function() { 720 facility.update(); 721 // }, 200); 722 } 723 else { 724 facility.marker.setMap(null); 725 // facility.badge.setMap(null); 726 facility.shadow.setMap(null); 727 // facility.maintenanceBadge.setMap(null); 728 facility.badge.hide(); 729 facility.maintenanceBadge.hide(); 730 } 731 }); 732 733 // check if cities even have facilities with the currently selected capacity type 734 735 data.cities.forEach(function(city) { 736 // if (data.cityShowing == city) return true; 737 738 let facilities = getFacilitiesByCityAndCapacityType(city, capacityType); 739 740 if (facilities.length) { 741 $cityUl.find('[data-city="' + city + '"]').removeClass('disabled'); 742 } 743 else { 744 $cityUl.find('[data-city="' + city + '"]').addClass('disabled'); 745 } 746 }); 747 } 748 749 /** 750 * close info window buttons 751 */ 752 $mapWrapper.on('click', '.facility-info-window .close', closeAllInfoWindows); 753 754 // add events to navigation 755 756 /** 757 * Add city button event listener 758 */ 759 $cityItems.on('click', function() { 760 if ($(this).hasClass('disabled')) return false; 761 762 if (1 === +$cityUl.css('--drop-active') && !$cityUl.hasClass('open')) return true; 763 764 setCity($(this).data('city')); 765 closeAllInfoWindows(); 766 }); 767 768 /** 769 * Add capacity type button event listener 770 */ 771 $capacityTypeItems.on('click', function() { 772 if ($(this).hasClass('disabled')) return true; 773 setCapacityType($(this).data('capacitytype')); 774 closeAllInfoWindows(); 775 }); 776 777 /** 778 * remove the maps scroll hint 779 */ 780 // $('#map-wrapper').on('mouse.hintremove mousewheel.hintremove wheel.hintremove DOMMouseScroll.hintremove scroll.hintremove', function() { 781 // var $hint = $(this).find(':contains("Halte die Taste"):not(:has(*))'); 782 // if ($hint.length) { 783 // $hint.parent().remove(); 784 // $('#map-wrapper').off('mouse.hintremove mousewheel.hintremove wheel.hintremove DOMMouseScroll.hintremove scroll.hintremove'); 785 // } 786 // }); 787 788 map.addListener('drag', updateCapacityTypeItems); 789 map.addListener('center_changed', updateCapacityTypeItems); 790 map.addListener('zoom_changed', updateCapacityTypeItems); 791 map.addListener('zoom_changed', function() { 792 var zoomLvl = map.getZoom(); 793 794 if (zoomLvl < 15) { 795 // small pois 796 $.each(poisMarkers, function(index, marker) { 797 var icon = marker.getIcon(); 798 icon.size = new google.maps.Size(16, 21); 799 icon.scaledSize = new google.maps.Size(16, 21); 800 marker.setIcon(icon); 801 }); 802 } 803 else if (zoomLvl >= 16) { 804 $.each(poisMarkers, function(index, marker) { 805 var icon = marker.getIcon(); 806 icon.size = new google.maps.Size(32, 42); 807 icon.scaledSize = new google.maps.Size(32, 42); 808 marker.setIcon(icon); 809 }); 810 } 811 else { 812 $.each(poisMarkers, function(index, marker) { 813 var icon = marker.getIcon(); 814 icon.size = new google.maps.Size(24, 32); 815 icon.scaledSize = new google.maps.Size(24, 32); 816 marker.setIcon(icon); 817 }); 818 } 819 }); 820 821 // when ready 822 google.maps.event.addListenerOnce(map, 'idle', function() { 823 setTimeout(function() { 824 updateCapacityTypeItems(); 825 }, 30); 826 }); 827 828 // pf list controls 829 830 /** 831 * @todo 832 */ 833 $facilityListWrapper.on('click', '.facility-list-link', function(event) { 834 event.preventDefault(); 835 }); 836 837 /** 838 * Add pf list item event listener 839 */ 840 $facilityListItems.on('click', function() { 841 var $item = $(this); 842 843 if ($item.hasClass('city-label')) return true; 844 845 $item.data('facility').focus(); 846 847 // close list when screen too small 848 if ( 849 $win.width() < ($facilityListWrapper.width() * 2) 850 && $mapWrapper.hasClass('facility-list-open') 851 && $win.width() < windowWidthMapPanThreshold 852 ) { 853 $mapWrapper.toggleClass('facility-list-open'); 854 } 855 856 var capacityType = $facilityListCapacityTypeButtons.filter('.active').first().data('capacitytype'), 857 city = $item.data('city'); 858 859 if (data.capacityTypeShowing !== capacityType) { 860 let $capacityTypeItem = $capacityTypeItems.filter('[data-capacitytype="' + $facilityListCapacityTypeButtons.filter('.active').first().data('capacitytype') + '"]'); 861 862 $capacityTypeItem.removeClass('disabled').click(); 863 } 864 865 if (data.cityShowing !== city) { 866 $cityItems.filter('[data-city="' + city + '"]').removeClass('disabled').click(); 867 } 868 }); 869 870 // trigger 871 872 setCapacityType(data.capacityTypeShowing); 873 $facilityListCapacityTypeButtons.filter('[data-capacitytype="' + data.capacityTypeShowing
873+ '"]').click(); 874 875 } // init map 876 877 window.initMap = initMap; // make this globally available for Google Maps callback 878 879})(window.app.overviewMap.settings); 880 881// init classes needed for gmap 882(function() { 883 /** 884 * @todo 885 */ 886 function addMapClasses() { 887 /** 888 * Add the availability badge 889 */ 890 class FacilityAvailabilityBadge extends google.maps.OverlayView { 891 constructor(facility, capacityType, position, available, trend) { 892 super(); 893 this.facility = facility; 894 this.capacityType = capacityType; 895 this.position = position; 896 this.trend = trend; 897 this.available = available; 898 this.containerDiv = $('<div/>', { 899 text: this.formatAvailable(available), 900 'class': [ 901 'badge-container', 902 'availability-label', 903 // 'availability-car-parking', 904 'trend-' + (trend || 'not-available'), 905 'transition-delay' 906 ].join(' '), 907 'data-facility-uuid': facility.uuid, 908 'data-max': 99 909 }).get(0); 910 this.hidden = false; 911 912 FacilityAvailabilityBadge.preventMapHitsAndGesturesFrom(this.containerDiv); 913 } 914 915 /** Called when the popup is added to the map. */ 916 onAdd() { 917 this.getPanes().floatPane.appendChild(this.containerDiv); 918 let that = this; 919 setTimeout(function() { 920 if (that.facility.availableCapacityTypesString.indexOf(window.app.overviewMap.settings.capacityTypeShowing) > -1) { 921 that.show(); 922 } 923 else { 924 that.hide(); 925 } 926 }, 20); 927 } 928 929 /** Called when the popup is removed from the map. */ 930 onRemove() { 931 if (this.containerDiv.parentElement) { 932 this.containerDiv.parentElement.removeChild(this.containerDiv); 933 } 934 } 935 936 /** Called each frame when the popup needs to draw itself. */ 937 draw() { 938 let divPosition = this.getProjection().fromLatLngToDivPixel(this.position); 939 // Hide the popup when it is far out of view. 940 let display = 941 Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 942 ? "inline-block" 943 : "none"; 944 945 if (display === "inline-block") { 946 this.containerDiv.style.left = divPosition.x + "px"; 947 this.containerDiv.style.top = divPosition.y + "px"; 948 } 949 950 if (this.containerDiv.style.display !== display) { 951 this.containerDiv.style.display = display; 952 } 953 } 954 955 /** Custom: Change the availability text and color */ 956 update(capacityType, available, trend) { 957 this.checkForMaintenance(); 958 this.capacityType = capacityType; 959 this.available = available; 960 this.trend = trend; 961 962 if (!this.hidden) { 963 $(this.containerDiv) 964 .text(this.formatAvailable(available)) 965 .removeClass('trend-easy trend-medium trend-difficult trend-not-available') 966 .addClass('trend-' + (trend || 'not-available')); 967 } 968 969 } 970 971 /** Custom: @todo */ 972 setCapacityType(capacityType, available, trend) { 973 // this.checkForMaintenance(); 974 this.capacityType = capacityType; 975 976 $(this.containerDiv) 977 .removeClass('availability-car-parking availability-car-charging availability-bike-parking') 978 .addClass('availability-' + capacityType.replaceAll('_', '-')); 979 980 // this.update(capacityType, available, trend); 981 } 982 983 checkForMaintenance() { 984 if ( 985 this.facility.hasMaintenanceFor(this.capacityType) 986 || this.capacityType != window.app.overviewMap.settings.capacityTypeShowing 987 ) { 988 this.hide(); 989 } 990 else if ( 991 this.capacityType == window.app.overviewMap.settings.capacityTypeShowing 992 && this.facility.availableCapacityTypesString.indexOf(window.app.overviewMap.settings.capacityTypeShowing) > -1 993 ) { 994 this.show(); 995 } 996 } 997 998 /** Custom: format text for available */ 999 formatAvailable(available) { 1000 if (null === available || undefined === available) { 1001 return window.app.overviewMap.settings.notAvailableString; 1002 } 1003 1004 if (+available > 99) return '99+'; 1005 return available; 1006 } 1007 1008 hide() { 1009 $(this.containerDiv).removeClass('transition-delay'); 1010 1011 let that = this; 1012 1013 setTimeout(function() { 1014 $(that.containerDiv).removeClass('show'); 1015 }, 20); 1016 this.hidden = true; 1017 } 1018 1019 show() { 1020 $(this.containerDiv).addClass('transition-delay'); 1021 1022 let that = this; 1023 1024 setTimeout(function() { 1025 $(that.containerDiv).addClass('show'); 1026 }, 20); 1027 this.hidden = false;
1028 } 1029 } 1030 1031 window.FacilityAvailabilityBadge = FacilityAvailabilityBadge; 1032 1033 /** 1034 * Add the maintenance badge 1035 */ 1036 class MaintenanceBadge extends google.maps.OverlayView { 1037 constructor(facility, position) { 1038 super(); 1039 this.facility = facility; 1040 this.position = position; 1041 this.containerDiv = $('<div/>', { 1042 'html': $('#template-maintenance-icon').html(), 1043 'class': [ 1044 'maintenance-badge-container' 1045 ].join(' ') 1046 }).get(0); 1047 this.hidden = false; 1048 1049 MaintenanceBadge.preventMapHitsAndGesturesFrom(this.containerDiv); 1050 } 1051 1052 /** Called when the popup is added to the map. */ 1053 onAdd() { 1054 this.getPanes().floatPane.appendChild(this.containerDiv); 1055 let that = this; 1056 setTimeout(function() { 1057 that.update(window.app.overviewMap.settings.capacityTypeShowing); 1058 // if ( 1059 // that.facility.availableCapacityTypesString.indexOf(window.app.overviewMap.settings.capacityTypeShowing) > -1 1060 // && 1061 // ) { 1062 // that.show(); 1063 // } 1064 // else { 1065 // that.hide(); 1066 // } 1067 }, 20); 1068 } 1069 1070 /** Called when the popup is removed from the map. */ 1071 onRemove() { 1072 if (this.containerDiv.parentElement) { 1073 this.containerDiv.parentElement.removeChild(this.containerDiv); 1074 } 1075 } 1076 1077 /** Called each frame when the popup needs to draw itself. */ 1078 draw() { 1079 let divPosition = this.getProjection().fromLatLngToDivPixel(this.position); 1080 // Hide the popup when it is far out of view. 1081 let display = 1082 Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 1083 ? "inline-block" 1084 : "none"; 1085 1086 if (display === "inline-block") { 1087 this.containerDiv.style.left = divPosition.x + "px"; 1088 this.containerDiv.style.top = divPosition.y + "px"; 1089 } 1090 1091 if (this.containerDiv.style.display !== display) { 1092 this.containerDiv.style.display = display; 1093 } 1094 } 1095 1096 update(capacityType) { 1097 if ( 1098 capacityType == window.app.overviewMap.settings.capacityTypeShowing 1099 && this.facility.availableCapacityTypesString.indexOf(window.app.overviewMap.settings.capacityTypeShowing) > -1 1100 && this.facility.hasMaintenanceFor(capacityType) 1101 ) { 1102 this.show(); 1103 } 1104 else { 1105 this.hide(); 1106 } 1107 } 1108 1109 hide() { 1110 $(this.containerDiv).removeClass('transition-delay'); 1111 1112 let that = this; 1113 1114 setTimeout(function() { 1115 $(that.containerDiv).removeClass('show'); 1116 }, 20); 1117 this.hidden = true; 1118 } 1119 1120 show() { 1121 $(this.containerDiv).addClass('transition-delay'); 1122 1123 let that = this; 1124 1125 setTimeout(function() { 1126 $(that.containerDiv).addClass('show'); 1127 }, 20); 1128 this.hidden = false; 1129 } 1130 } 1131 1132 window.MaintenanceBadge = MaintenanceBadge; 1133 1134 1135 /** 1136 * Add the availability badge 1137 */ 1138 class MarkerShadow extends google.maps.OverlayView { 1139 constructor(marker, additionalClasses) { 1140 super(); 1141 this.marker = marker; 1142 this.position = marker.getPosition();
1143 this.containerDiv = $('<div/>', { 1144 'class': 'marker-shadow ' + (additionalClasses || '') 1145 }).get(0); 1146 1147 MarkerShadow.preventMapHitsAndGesturesFrom(this.containerDiv); 1148 } 1149 1150 /** Called when the popup is added to the map. */ 1151 onAdd() { 1152 this.getPanes().overlayShadow.appendChild(this.containerDiv); 1153 let that = this; 1154 setTimeout(function() { 1155 $(that.containerDiv).addClass('added'); 1156 }, 100); 1157 } 1158 1159 /** Called when the popup is removed from the map. */ 1160 onRemove() { 1161 if (this.containerDiv.parentElement) { 1162 this.containerDiv.parentElement.removeChild(this.containerDiv); 1163 } 1164 } 1165 1166 /** Called each frame when the popup needs to draw itself. */ 1167 draw() { 1168 let divPosition = this.getProjection().fromLatLngToDivPixel(this.position); 1169 // Hide the popup when it is far out of view. 1170 let display = 1171 Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 1172 ? "inline-block" 1173 : "none"; 1174 1175 if (display === "inline-block") { 1176 this.containerDiv.style.left = divPosition.x + "px"; 1177 this.containerDiv.style.top = divPosition.y + "px"; 1178 } 1179 1180 if (this.containerDiv.style.display !== display) { 1181 this.containerDiv.style.display = display; 1182 } 1183 } 1184 1185 hide() { 1186 $(this.containerDiv).removeClass('added'); 1187 } 1188 } 1189 1190 window.MarkerShadow = MarkerShadow; 1191 1192 /** 1193 * Custom Info Window for future proof styling 1194 */ 1195 class FacilityInfoWindow extends google.maps.OverlayView { 1196 constructor(marker, content) { 1197 super(); 1198 this.marker = marker; 1199 this.position = marker.getPosition(); 1200 this.containerDiv = $(content).get(0); 1201 1202 FacilityInfoWindow.preventMapHitsAndGesturesFrom(this.containerDiv); 1203 } 1204 1205 /** Called when the popup is added to the map. */ 1206 onAdd() { 1207 this.getPanes().floatPane.appendChild(this.containerDiv); 1208 } 1209 1210 /** Called when the popup is removed from the map. */ 1211 onRemove() { 1212 if (this.containerDiv.parentElement) { 1213 this.containerDiv.parentElement.removeChild(this.containerDiv); 1214 } 1215 } 1216 1217 /** Called each frame when the popup needs to draw itself. */ 1218 draw() { 1219 let divPosition = this.getProjection().fromLatLngToDivPixel(this.position); 1220 // Hide the popup when it is far out of view. 1221 var display = 1222 Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 1223 ? "inline-block" 1224 : "none"; 1225 1226 if (display === "inline-block") { 1227 this.containerDiv.style.left = divPosition.x + "px"; 1228 this.containerDiv.style.top = divPosition.y + "px"; 1229 } 1230 1231 if (this.containerDiv.style.display !== display) { 1232 this.containerDiv.style.display = display; 1233 } 1234 } 1235 } 1236 1237 window.FacilityInfoWindow = FacilityInfoWindow; 1238 1239 /** 1240 * Custom Info Window for future proof styling 1241 */ 1242 class PoiInfoWindow extends google.maps.OverlayView { 1243 constructor(marker, content) { 1244 super(); 1245 this.marker = marker; 1246 this.position = marker.getPosition(); 1247 this.containerDiv = $(content).get(0); 1248 1249 PoiInfoWindow.preventMapHitsAndGesturesFrom(this.containerDiv); 1250 } 1251 1252 /** Called when the popup is added to the map. */ 1253 onAdd() { 1254 this.getPanes().floatPane.appendChild(this.containerDiv); 1255 } 1256 1257 /** Called when the popup is removed from the map. */ 1258 onRemove() { 1259 if (this.containerDiv.parentElement) { 1260 this.containerDiv.parentElement.removeChild(this.containerDiv); 1261 } 1262 } 1263 1264 /** Called each frame when the popup needs to draw itself. */ 1265 draw() { 1266 let divPosition = this.getProjection().fromLatLngToDivPixel(this.position); 1267 // Hide the popup when it is far out of view. 1268 var display = 1269 Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000 1270 ? "inline-block" 1271 : "none"; 1272 1273 if (display === "inline-block") { 1274 this.containerDiv.style.left = divPosition.x + "px"; 1275 this.containerDiv.style.top = divPosition.y + "px"; 1276 } 1277 1278 if (this.containerDiv.style.display !== display) { 1279 this.containerDiv.style.display = display; 1280 } 1281 } 1282 } 1283 1284 window.PoiInfoWindow = PoiInfoWindow; 1285 } 1286 1287 window.addMapClasses = addMapClasses; 1288})();
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.