1/** 2 * Maps v5 3 * ! 5.0.0 - 09/22/20 4 * * Vanilla rewrite 5 * Maps v4 6 * ! 4.2.0 - 01/13/20 7 * * Added custom map makers based on tags 8 * ! 4.1.0 - 09/10/18 9 * * Added disableDynamicTrigger option for static map only version 10 * ! 4.0.0 - 08/22/18 11 * * Merged with staticmaps-v1.js 12 * * Removed Geo 13 * * Separated Zip search from dynamic map 14 * ! 3.4.0 - 02/20/18 15 * * Added enableSpeedBumps feature 16 * ! 3.3.0 - 12/5/17 17 * * Added check for zipCodeSearch query string 18 * ! 3.2.1 - 10/24/17 19 * * Updated Delay Distance Matrix requests to .25 secs 20 * ! 3.2 - 10/06/17 21 * * Added fedMaps.zipFormRegex - Allows for anything other than a zip code match - defaults to zip code numbers 22 * * Added fedMaps.zipFormAppendToSearchValue - Appends a string to zip search value for GeoCoding search - defaults to an empty string 23 * * Added fedMaps.zipInputPlaceholder - Customize zip form input placeholder - defaults to 'zip code' 24 * * Added fedMapsApi.markersIconUrl - Optional custom map marker icon 25 * * Added fedMapsApi.userMarkerIconUrl - Optional custom user map marker icon 26 * ! 3.1 - 08/02/17 27 * * Delay Distance Matrix requests by 1.0 secs 28 * * Added Geo Button 29 * * Geo on Page Load disabled by default 30 * * Multiple seeMap click events fix 31 * * Distance Matrix Max Tries Default = 5 32 **/ 33 34// fedMapsApi should only handle tasks that would require 3rd-party API calls. 35// It shouldn't care about the dom or the user, only the map itself and any task that would require an API call. 36var fedMapsApi = { 37 dms: new google.maps.DistanceMatrixService(), 38 geocoder: new google.maps.Geocoder(), 39 locationMarkers: [], 40 maxRequests: 20, // This is the max google allows 41 map: null, 42 maxTries: 5, 43 numTries: 0, 44 userMarker: null, 45 distanceMatrixRequestDelay: 250, 46 distanceMatrixOverLimitDelay: 1000, 47 markersIconUrl: null, 48 userMarkerIconUrl: null, 49 50 // converts a given address string to a latitude/longitude object. 51 addressToCoords: function (addressString, callback) { 52 var self = this; 53 try { 54 self.geocoder.geocode({ 55 address: addressString 56 }, 57 function (results_array, status) { 58 if (status != google.maps.GeocoderStatus.OK) { 59 console.log('GeocoderStatus error: ' + status); 60 callback(false); 61 return; 62 } 63 64 callback({ 65 lat: results_array[0].geometry.location.lat(), 66 lng: results_array[0].geometry.location.lng() 67 }); 68 }); 69 } catch (err) { 70 console.log(err); 71 callback(false); 72 } 73 }, 74 75 // adds location markers to the map 76 addLocationsToMap: function (locations) { 77 var self = this; 78 79 // we're just creating one info window and reusing for all the locations. 80 var infoWindow = new google.maps.InfoWindow(); 81 82 // remove any previous markers from map 83 for (var i = 0; i < self.locationMarkers.length; i++) { 84 self.locationMarkers[i].setMap(null); 85 } 86 87 self.locationMarkers = []; 88 89 // add new markers 90 for (var i = 0; i < locations.length; i++) { 91 var marker = new google.maps.Marker({ 92 map: self.map, 93 position: locations[i].coords, 94 visible: true, 95 }); 96 97 // Set global marker image 98 if (self.markersIconUrl !== null) { 99 marker.setIcon(self.markersIconUrl); 100 } 101 102 // Individual marker image/icon if needed 103 if (locations[i].customMarker !== null) { 104 // Find custom marker image or path 105 if (fedMaps.customLocationTagMarkers !== null) { 106 // Find key in object and set value of key to as marker on location 107 if (fedMaps.customLocationTagMarkers.hasOwnProperty(locations[i].customMarker)) { 108 marker.setIcon(fedMaps.customLocationTagMarkers[locations[i].customMarker]); 109 } 110 } 111 } 112 113 // keep track of the markers 114 self.locationMarkers.push(marker); 115 self.setupInfoWindow(infoWindow, locations[i], marker, self.map); 116 } 117 118 console.log(self.locationMarkers); 119 self.zoomToFitMarkers(); 120 }, 121 122 customIcon: function (opts) { 123 /* 124 * Use: self.customIcon({ 125 fillColor: '#fff', 126 strokeColor: '#000' 127 }) 128 */ 129 130 return Object.assign({ 131 path: 'M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30 a 2,2 0 1,1 4,0 2,2 0 1,1 -4,0', 132 fillColor: '#34495e', 133 fillOpacity: 1, 134 strokeColor: '#000', 135 strokeWeight: 2, 136 scale: 1, 137 }, opts); 138 }, 139 140 // adds the user marker to the map. 141 addUserToMap: function (coords) { 142 var self = this; 143 144 // if we already added the user marker then just update the position. 145 if (self.userMarker != null) { 146 self.userMarker.setPosition(coords); 147 self.zoomToFitMarkers(); 148 return; 149 } 150 151 var defaultIcon = { 152 path: google.maps.SymbolPath.CIRCLE, 153 fillOpacity: 1.0, 154 fillColor: '#1FAEE3', 155 strokeOpacity: 1.0, 156 strokeColor: '#3788C0', 157 strokeWeight: 1.0, 158 scale: 6 //pixels 159 }; 160 161 // create a user marker at the given position. 162 self.userMarker = new google.maps.Marker({ 163 map: self.map, 164 position: coords, 165 icon: (self.userMarkerIconUrl !== null) ? self.userMarkerIconUrl : defaultIcon 166 }); 167 168 self.zoomToFitMarkers(); 169 }, 170 171 // get distances between an origin and destinations. 172 getDistanceMatrix: function (destinations, origins, callback, curIndex, responses) { 173 174 var self = this; 175 176 if (typeof curIndex === 'undefined') { 177 curIndex = 0; 178 responses = []; 179 } 180 181 // set the endIndex to either the next full set or the end of the array, whichever is smallest. 182 var endIndex = Math.min(curIndex + self.maxRequests - 1, destinations.length - 1); 183 184 // create the request to send to google. 185 var request = { 186 destinations: destinations.slice(curIndex, endIndex + 1), 187 origins: origins, 188 travelMode: google.maps.TravelMode.DRIVING, 189 unitSystem: google.maps.UnitSystem.IMPERIAL 190 }; 191 192 // send the request. 193 self.dms.getDistanceMatrix(request, function (response, status) { 194 195 if (status == google.maps.DistanceMatrixStatus.OVER_QUERY_LIMIT && self.numTries < self.maxTries) { 196 console.log('google.maps.DistanceMatrixStatus.OVER_QUERY_LIMIT'); 197 console.log('Over query limit. ' + self.numTries + ' of ' + self.maxTries + ' tries remaining.'); 198 self.numTries += 1; 199 200 // wait and try again. 201 setTimeout(function () { 202 self.getDistanceMatrix(destinations, origins, callback, curIndex, responses) 203 }, self.distanceMatrixOverLimitDelay); 204 return; 205 } else if (status != google.maps.DistanceMatrixStatus.OK) { 206 console.log('DistanceMatrixStatus error: ' + status); 207 return; 208 } 209 210 // append the response 211 responses.push(response); 212 213 // figure out if we have more to request 214 if (endIndex < destinations.length - 1) { 215 //GmapsMgr.sendRequest( destinations, origins, callback, endIndex + 1, responses ); 216 217 // Delay Distance Matrix requests by 1 secs - jweber 20170719 218 setTimeout(function () { 219 self.getDistanceMatrix(destinations, origins, callback, endIndex + 1, responses); 220 }, self.distanceMatrixRequestDelay); 221 } 222 // if there's no more to request then call the original callback function. 223 else { 224 callback(responses); 225 } 226 227 }); 228 229 }, 230 231 // draws the map on the page. 232 renderMap: function (domId) { 233 var self = this; 234 235 // Set the map options. 236 var mapOptions = { 237 draggable: true, 238 mapTypeId: google.maps.MapTypeId.ROADMAP, 239 zoom: 9 240 }; 241 242 // we need to keep track of the map so we can add/remove markers, zoom, etc. 243 self.map = new google.maps.Map(document.getElementById(domId), mapOptions); 244 245 return self.map; 246 }, 247 248 // sets the center position of the map. 249 setCenter: function (coords) { 250 var self = this; 251 252 if (self.map !== null) { 253 self.map.setCenter(coords); 254 } 255 }, 256 257 // sets up the info window for a location 258 setupInfoWindow: function (infoWindow, location, marker) { 259 var self = this; 260 261 // If map locations are reset after initialization, we need to unbind initial click event listener to avoid multiple seeMap click events 262 if ( location.el.querySelector(fedMaps.seeMap) !== null ) { 263 location.el.querySelector(fedMaps.seeMap).removeEventListener('click', function () {}, true); 264 265 location.el.querySelector(fedMaps.seeMap).addEventListener('click', function () { 266 self.showInfoWindow(infoWindow, location.infoWindowMarkup, marker.getPosition(), self.map); 267 }); 268 } 269 270 google.maps.event.addListener(marker, 'click', function () { 271 self.showInfoWindow(infoWindow, location.infoWindowMarkup, marker.getPosition(), self.map); 272 }); 273 274 }, 275 276 // sets the zoom level of the map. 277 setZoom: function (intValue) { 278 var self = this; 279 if (self.map !== null) { 280 self.map.setZoom(intValue); 281 } 282 }, 283 284 // shows an info window with the given content at the given position 285 showInfoWindow: function (infoWindow, content, position) { 286 var self = this; 287 infoWindow.close(); 288 infoWindow.setPosition(position); 289 infoWindow.setContent(content); 290 infoWindow.setOptions({ 291 pixelOffset: new google.maps.Size(0, -26) 292 }) 293 infoWindow.open(self.map); 294 }, 295 296 // returns a list of locations ordered by distance from the given origin 297 sortLocations: function (locations, origin, callback) { 298 var self = this; 299 300 // the first argument uses the core javascript map function, which has nothing to do with google maps or geography, to return the 301 // coords property of each location as an array.
302 self.getDistanceMatrix(locations.map(function (a) { 303 return a.coords 304 }), new Array(origin), function (response) { 305 306 307 // The DistanceMatrixResponse object contains one row for each origin that was passed in the request. It contains other stuff too,b 308 // the rows are what we care about. 309 // In our case there's only one origin, the user's current position, either via HTML5 geo services or an entered zipcode. 310 // Each row contains an element field for each pairing of that origin with the provided destination(s). 311 312 //var distances = response[0].rows[0].elements; // we only need the first row because we only have 1 origin. 313 var distances = self.getRows(response); 314 distances = distances[0].elements; 315 316 //var distancesAvailable = 0; 317 318 for (var j = 0; j < distances.length; j++) { 319 if (distances[j].status !== 'ZERO_RESULTS') { 320 locations[j].distance = distances[j].distance; 321 //distancesAvailable++; 322 } else { 323 console.log('no-results'); 324 locations[j].distance = ""; 325 } 326 } 327 328 // Sort locations by distance 329 locations.sort(function (a, b) { 330 331 if (a.distance.value < b.distance.value) { 332 return -1; 333 } 334 335 if (a.distance.value > b.distance.value) { 336 return 1; 337 } 338 339 return 0; 340 }); 341 342 callback(locations); 343 }); 344 }, 345 346 // zooms out the map so that all markers are visible 347 zoomToFitMarkers: function () { 348 var self = this; 349 350 if (self.locationMarkers.length == 1) { 351 fedMapsApi.setZoom(fedMaps.mapZoom); 352 fedMapsApi.setCenter(self.locationMarkers[0].getPosition()); 353 return; 354 } 355 356 var bounds = new google.maps.LatLngBounds(); 357 358 for (var i = 0; i < self.locationMarkers.length; i++) { 359 bounds.extend(self.locationMarkers[i].getPosition()); 360 } 361 362 if (self.userMarker) { 363 364 bounds.extend(self.userMarker.getPosition()); 365 } 366 367 self.map.fitBounds(bounds); 368 }, 369 370 getRows: function (responses) { 371 var rows = []; 372 rows.push({ 373 elements: [] 374 }); 375 376 for (var i = 0; i < responses.length; i++) { 377 var elems = responses[i].rows[0].elements; 378 379 for (var j = 0; j < elems.length; j++) { 380 rows[0].elements.push(elems[j]); 381 } 382 383 } 384 return rows; 385 }, 386 387}; 388 389// fedMaps should handle any dom related tasks or anything that has to the user. 390// anything that requires interaction with a 3rd-party API should be offloaded to fedMapsApi. 391var fedMaps = { 392 clickFirstLocation: true, 393 locListSelector: '#locList > li', 394 locations: [], // list of location objects, which includes the jquery element, along with any other properties we care about. 395 mapZoom: 15, 396 numLocations: 10, // The number of locations to show after a zip search. 397 onSortComplete: null, // optional callback function called after locations on the page have finisned sorting, incase a site needs it. 398 seeMap: '.seeMap', 399 seeMapClicked: null, // Keeps track of clicked .seeMap on static map 400 userMarker: null, 401 zipForm: false, 402 zipFormLabel: 'Find a branch location near you:', 403 zipInputPlaceholder: 'zip code', 404 zipFormRegex: /(^\d{5}$)|(^\d{5}-\d{4}$)/, // Default value only accepts zip codes 405 zipFormAppendToSearchValue: '', // Appends string to search value under handleZipSearch 406 allLocations: false, 407 allZipLocations: true, 408 enableSpeedBumps: true, 409 version: '5.0.0', 410 dynamicMapTrigger: true, 411 /* Example object: { 412 'sba': fedMapsApi.customIcon({ 413 fillColor: '#fff', 414 strokeColor: '#000' 415 }), 416 'atm': '//maps.google.com/mapfiles/kml/pal2/icon2.png' 417 } 418 */ 419 customLocationTagMarkers: null, 420 421 // Get fedMaps version 422 getVersion: function () { 423 return 'fedMaps: ' + this.version; 424 }, 425 426 // this creates a list of location objects. 427 // each object will have a reference to the jquery object, content to populate the info window, and anything else we might care about. 428 getLocations: function () { 429 var self = this; 430 431 document.querySelectorAll(self.locListSelector).forEach(function (locItem) { 432 433 var locationData = locItem.dataset; 434 435 // get the content for the info window 436 var title = locationData.title; 437 var address1 = locationData.address1; 438 var address2 = locationData.address2; 439 var address = address2 ? address1 + '<br/>' + address2 : address1; 440 var gmapAddress = address2 ? address1 + ' ' + address2 : address1; 441 var city = locationData.city; 442 var state = locationData.state; 443 var zip = locationData.zip; 444 var hasAtmString = ""; 445 if (locationData.hasAtm == 0) { 446 hasAtmString = "Has Onsite ATM"; 447 } 448 449 var locationMarkerTag = null; 450 if (typeof locationData.tags !== 'undefined' && locationData.tags.length) { 451 // Find custom marker icons 452 locationMarkerTag = self.findCustomLocationMarker(locationData.tags); 453 } 454 455 var link = 'http://maps.google.com/?q=' + encodeURIComponent(title + ' ' + gmapAddress + ' ' + city + ', ' + state
455+ ' ' + zip); 456 var infoWindowMarkup = '<div class="mapInfoWindow"><strong>' + title + '</strong><br/>' + address + '<br/>' + city + ', ' + state + ' ' + zip + '<br/><br/>' + hasAtmString + '</div>'; 457 458 // add to list of locations 459 // Sanitation FIX: 10/16/16 460 // Lat and Long Strings are converted to numbers - IE FIX 461 self.locations.push({ 462 el: locItem, 463 distance: -1, 464 coords: { 465 lat: parseFloat(locationData.latitude), 466 lng: parseFloat(locationData.longitude) 467 }, 468 infoWindowMarkup: infoWindowMarkup, 469 customMarker: locationMarkerTag 470 }); 471 }); 472 473 return document.querySelectorAll(self.locListSelector); 474 }, 475 476 // initial call for the location pages. 477 setupLocationsPage: function () { 478 var self = this; 479 480 console.log(this.getVersion()); 481 482 loadStaticMap(); 483 484 staticSeeMapEvent(); 485 486 if (fedMaps.zipForm === true) { 487 self.setupZipSearch(); 488 } 489 }, 490 491 findCustomLocationMarker: function (tagString) { 492 // Example string from tags = 'sba, marker:sba,lender'; 493 var tagArray = tagString.toLowerCase().replace(/ /g, "").split(','); 494 495 var customMarker = tagArray.filter(function (item) { 496 return item.indexOf('marker:') !== -1; 497 }); 498 499 if (customMarker.length > 0) { 500 return customMarker[0].split('marker:')[1]; 501 } 502 return null; 503 }, 504 505 setupZipSearch: function (noMap) { 506 var self = this; 507 // Get location dom elements. 508 self.getLocations(); 509 510 if (noMap === true) { 511 document.querySelector("#map").style.display = 'none'; 512 } 513 514 // Add zipcode form 515 document.querySelector("#map").insertAdjacentHTML('beforebegin', '<form class="findBranch" id="t" onSubmit="return handleZipSubmit();"><label> ' + fedMaps.zipFormLabel + ' </label><input name="zip" type="text" placeholder="' + fedMaps.zipInputPlaceholder + '" /> <input type="submit" value="Search" style=""/><img class="loading" style="" src="//cms.bancvue.com/custom/fed-library/production/maps/130.gif" /></form>'); 516 document.querySelector("#map").style.marginTop = '12px'; 517 518 // Hide animation until search 519 document.querySelector('img.loading').style.visibility = 'hidden'; 520 521 // Check for zip code query string if available and search based on value 522 fedMaps.handleZipCodeUrlQuery(); 523 }, 524 525 setupDynamicLocationsPage: function () { 526 var self = this; 527 528 // Reset Locations 529 self.locations = []; 530 531 // Get location dom elements. 532 self.getLocations(); 533 534 // Show the map 535 map = fedMapsApi.renderMap('map'); 536 537 // Handle post map render options 538 self.handlePostRender(); 539 540 // Show the locations on the map. 541 fedMapsApi.addLocationsToMap(self.locations); 542 543 // Handles final map setup ** 544 fedMaps.handleNoGeo(); 545 fedMapsApi.zoomToFitMarkers(self.locations); 546 547 // If .seeMap click from static map, trigger info window 548 if (this.seeMapClicked !== null) { 549 this.seeMapClicked.click(); 550 this.seeMapClicked = null; 551 } 552 }, 553 554 // sets the center of the map 555 setCenter: function (options) { 556 fedMapsApi.setCenter({ 557 lat: options.lat, 558 lng: options.lng 559 }); 560 fedMapsApi.setZoom(options.zoom); 561 }, 562 563 // returns the user coordinates 564 getUserPosition: function (callback) { 565 566 // check if we've already got the user position 567 if (typeof fedMaps.userPosition != 'undefined') { 568 callback(fedMaps.userPosition); 569 return; 570 } 571 572 // check if we even know where the user is 573 if (fedMaps.userMarker == null) { 574 callback(false); 575 return false; 576 } 577 578 // get the user position 579 var userPosition = fedMaps.userMarker.getPosition(); 580 581 // ask google where the user is 582 try { 583 fedMapsApi.geocoder.geocode({ 584 location: userPosition 585 }, 586 function (results_array, status) { 587 588 if (status != google.maps.GeocoderStatus.OK) { 589 console.log('GeocoderStatus error: ' + status); 590 fedMapsApi.zoomToFitMarkers(locations); 591 return; 592 } 593 // store the result for future reference 594 fedMaps.userPosition = results_array; 595 callback(results_array); 596 }); 597 } catch (err) { 598 console.log(err); 599 callback(false); 600 } 601 }, 602 603 getUserState: function (callback) { 604 605 fedMaps.getUserPosition(function (userPosition) { 606 607 var userState = false; 608 if (userPosition) { 609 userState = userPosition[0].address_components[5].short_name; 610 } 611 612 callback(userState); 613 }); 614 }, 615 616 handlePostRender: function () { 617 var self = this; 618 619 // Enable warn speed bumps to Google Map 620 if (self.enableSpeedBumps === true) { 621 self.addSpeedBumpsToMap(); 622 } 623 }, 624 625 addSpeedBumpsToMap: function () { 626 var observerObject = new MutationObserver(function (mutationRecords) { 627 var numberOfMutations = 0; 628 mutationRecords.forEach(function (mutationRecord) { 629 numberOfMutations++; 630 }); 631 if (numberOfMutations > 0) { 632 var links = document.querySelectorAll('#map a'); 633 if (links.length) { 634 for (i = 0; i < links.length; ++i) { 635 links[i].classList.add('warn'); 636 } 637 if (typeof fiName !== 'undefined') { 638 if (typeof setSpeedBumps === 'function') { 639 setSpeedBumps(); 640 } else if (typeof confirmAlert === 'function') { 641 confirmAlert('#map a.warn', warnText); 642 } 643 } 644 observerObject.disconnect(); 645 } 646 } 647 }); 648 649 var target = document.querySelector('#map'); 650 var config = { 651 childList: true, 652 subtree: true, 653 characterData: true 654 }; 655 observerObject.observe(target, config); 656 }, 657 658 handleNoGeo: function () { 659 var self = this; 660 661 // center map on first location position 662 fedMapsApi.map.setCenter(self.locations[0].coords); 663 664 if (fedMaps.clickFirstLocation === true && fedMaps.seeMapClicked === null) { 665 if ( document.querySelector('#locList .togglesNext') !== null ) { 666 document.querySelector('#locList .togglesNext').click(); 667 } else { 668 if ( document.querySelector('#locList .loc') !== null ) { 669 document.querySelector('#locList .loc').click(); 670 } 671 } 672 } 673 }, 674 675 handleZipCodeUrlQuery: function () { 676 var urlQueryString = window.location.search; 677 678 if (urlQueryString.indexOf('zipCodeSearch') !== -1) { 679 var queryStringObject = fedMapsUtils.parseQueryString(urlQueryString); 680 681 if (queryStringObject.hasOwnProperty('zipCodeSearch')) { 682 document.querySelector('form.findBranch input[name="zip"]').value = queryStringObject.zipCodeSearch; 683 document.querySelector('form.findBranch input[type="submit"]').click(); 684 } 685 } 686 }, 687 688 renderLocationsPage: function (locationsToRender) { 689 690 // remove current locations 691 document.querySelectorAll(self.locListSelector).forEach(function (locItem) { 692 locItem.parentNode.removeChild(locItem); 693 }); 694 695 if (fedMapsApi.map !== null) { 696 // update map 697 fedMapsApi.addLocationsToMap(locationsToRender); 698 } 699 700 for (var i = 0; i < locationsToRender.length; i++) { 701 document.querySelector('#locList').appendChild(locationsToRender[i].el); 702 } 703 704 // Add distances to each location. 705 var distances = document.querySelectorAll('#locList li .distance'); 706 if (distances.length) { 707 console.log(distances.length);
708 distances.forEach(function (distanceItem, index) { 709 console.log(index); 710 if ( typeof locationsToRender[index] !== "undefined" ) { 711 var distanceText = locationsToRender[index].distance.text; 712 if (typeof distanceText == "undefined") { 713 distanceText = ""; 714 } 715 distanceItem.textContent = distanceText; 716 } else { 717 distanceItem.textContent = ''; 718 } 719 720 721 }); 722 } 723 if (fedMaps.clickFirstLocation === true) { 724 if ( document.querySelector('#locList .togglesNext') !== null ) { 725 document.querySelector('#locList .togglesNext').click(); 726 } else { 727 if ( document.querySelector('#locList .loc') !== null ) { 728 document.querySelector('#locList .loc').click(); 729 } 730 } 731 } 732 if (fedMaps.onSortComplete != null) { 733 fedMaps.onSortComplete(); 734 } 735 736 } 737}; 738 739// updates the user position on locations list based on zipcode 740function handleZipSubmit() { 741 var searchValue = document.querySelector('input[name="zip"]').value.toLowerCase().trim(); 742 743 if (fedMaps.zipFormRegex.test(searchValue) == false) { 744 return false; 745 } 746 747 // In the situtation that locations array is still empty, grab all locations 748 if (fedMaps.locations.length === 0) { 749 fedMaps.getLocations(); 750 } 751 752 // Append fedMaps.zipFormAppendToSearchValue to end of searchValue string 753 searchValue = searchValue + fedMaps.zipFormAppendToSearchValue; 754 755 document.querySelector('#locList').style.visibility = 'hidden'; 756 document.querySelector('#locList').style.height = '0'; 757 document.querySelector('#map').style.visibility = 'hidden'; 758 document.querySelector('img.loading').style.visibility = 'visible'; 759 760 // Turn searchValue into a latLng object 761 try { 762 fedMapsApi.addressToCoords(searchValue, function (coords) { 763 if (false === coords) { 764 document.querySelector('img.loading').style.visibility = 'hidden'; 765 document.querySelector('.findBranch input[type="text"]').value = 'No matches found'; 766 return; 767 } 768 769 // Sort locations by distance. 770 document.querySelectorAll(self.locListSelector).forEach(function (locItem) { 771 locItem.parentNode.removeChild(locItem); 772 }); 773 774 if ( fedMapsApi.map !== null ) { 775 fedMapsApi.addUserToMap({ 776 lat: coords.lat, 777 lng: coords.lng 778 }); 779 } 780 781 fedMapsApi.sortLocations(fedMaps.locations, coords, function (sortedLocations) { 782 783 // All Locations fix -- 10/17/16 - jweber 784 var numLocations = fedMaps.numLocations; 785 786 if (fedMaps.allZipLocations) { 787 numLocations = sortedLocations.length; 788 } 789 790 var locationsToAdd = sortedLocations.slice(0, numLocations); 791 792 fedMaps.renderLocationsPage(locationsToAdd); 793 794 document.querySelector('img.loading').style.visibility = 'hidden'; 795 document.querySelector('#locList').classList.add('all-sorted'); 796 document.querySelector('#locList').style.visibility = 'visible'; 797 document.querySelector('#locList').style.height = 'auto'; 798 document.querySelector('#map').style.visibility = 'visible'; 799 }); 800 }); 801 } catch (err) { 802 console.log(err); 803 } 804 return false; 805} 806 807// Utility Methods 808var fedMapsUtils = { 809 // Builds object from query string 810 parseQueryString: function (queryString) { 811 var query = {}; 812 var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&'); 813 for (var i = 0; i < pairs.length; i++) { 814 var pair = pairs[i].split('='); 815 query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); 816 } 817 return query; 818 } 819}; 820 821// Static map dimensions object 822var staticMapDimensions = { 823 width: '', 824 height: '' 825}; 826 827// Initialize static map 828function loadStaticMap() { 829 var map = document.querySelector('#map'); 830 if (!isHidden(map)) { 831 staticMapDimensions.width = getMapWidth(); 832 staticMapDimensions.height = getMapHeight(); 833 var mapImageSrc = getStaticMap(); 834 var mapImageAlt = 'Locations Map'; 835 var mapImage = '<img id="staticMapImage" src="' + mapImageSrc + '" alt="' + mapImageAlt + '">'; 836 map.innerHTML = mapImage; 837 838 // Site specific remediations 839 map.style.position = 'relative'; 840 map.style.clear = 'both'; 841 map.style.lineHeight = '0'; 842 map.style.overflow = 'hidden'; 843 map.style.height = 'auto'; 844 845 if (fedMaps.dynamicMapTrigger === true) { 846 // Add dyanmic map trigger to static map image 847 addDynamicMapTrigger(); 848 } else { 849 // No dynamic map trigger means hidden .seeMap links
850 document.querySelectorAll(fedMaps.seeMap).forEach(function (seeMapItem) { 851 seeMapItem.style.display = 'none'; 852 }); 853 } 854 } 855} 856 857// Add dynamic map trigger to #map container 858function addDynamicMapTrigger() { 859 // Using b64 image because not all sites have fontawesome 860 var mousePointerIconB64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABZklEQVQ4jaWUsYoUURBFzx2WDQyGRQYZRAxlEXPBf9B4lHUDA4P9ADMDWQyMFhER/AFzQ0O/QM1MTDSWCQRFxjom3Uvv2D2zM1YF3e+96nNvdxUdkdOQKbAPvCcUW8Sou1BvAW+Ae5Y7/w1MsgtMgZdJjvDs+TYO29sx8Bg4QHa3Bnah6gR4ATzYxOk/hUlQ2+tYfbKJ017lLjTJJfV5A13rdLCghTaxBzxVD5GV3V+pmKS7nCY5AWarnA4edJpzClb3gGfq4dCc9gI7r7rsEvVykpMkd/qe7VXpgZDke5Jvait4k+JtRlmsBQ4IfFDvJvndCPwgLJZrVzXlF/AOmDeubiS5TphnlHkfbBVwAbxW7wOfmnmcqLfXDrjd1FlV/ayqY3Xc7B1U1Z+qsqq+VtUVGc5l4DX1oXqh3auqq1X12SaqanZ+YF/qjvpI/ai+WucwZ/7Yw99lAlxUvyyPyXKcD7hB/AVl1h+Jvjf9cgAAAABJRU5ErkJggg=='; 861 862 // Build HTML for overlay and trigger 863 var dynamicTrigger = '<div id="makeDynamic" style="opacity: 0;transition: all .3s ease;height: ' + staticMapDimensions.height + 'px;width: 100%;background: rgba(0,0,0,0.5);position: absolute;top: 0;display: flex;align-items: center;justify-content: center;cursor: pointer;"><span style="color: #fff;font-weight: 600;font-size: 20px;"><img style="vertical-align: middle;display: inline;" src="' + mousePointerIconB64 + '" alt="Mouse Pointer" aria-hidden="true" />Click to Interact</span></div>'; 864 865 // Add trigger to map 866 document.querySelector('#map').insertAdjacentHTML('beforeend', dynamicTrigger); 867 setupDynamicMapEvent(); 868} 869 870// Setup events for dynamic map trigger 871function setupDynamicMapEvent() { 872 var dynamicTrigger = document.querySelector('#makeDynamic'); 873 874 // Static map hover event 875 dynamicTrigger.addEventListener("mouseover", function () { 876 dynamicTrigger.style.opacity = '1' 877 }, false); 878 dynamicTrigger.addEventListener("mouseout", function () { 879 dynamicTrigger.style.opacity = '0' 880 }, false); 881 882 // On click event for dynamic map trigger 883 dynamicTrigger.addEventListener("click", function () { 884 // If fedMaps object loaded on page, trigger dynamic maps 885 if (typeof fedMaps !== "undefined") { 886 document.getElementById('map').style.removeProperty('height'); 887 document.querySelector('#map').innerHTML = ''; 888 fedMaps.setupDynamicLocationsPage(); 889 } 890 }, false); 891} 892 893// On .seeMap click: trigger dynamic map, keep track of clicked, remove click static seeMap click event 894function staticSeeMapEvent() { 895 document.querySelectorAll(fedMaps.locListSelector).forEach(function (locItem) { 896 if ( locItem.querySelector(fedMaps.seeMap) !== null ) { 897 locItem.querySelector(fedMaps.seeMap).addEventListener('click', function (event) { 898 fedMaps.seeMapClicked = event.target; 899 this.removeEventListener('click', function () {}, true); 900 if (document.querySelector('#makeDynamic') !== null) { 901 document.querySelector('#makeDynamic').click(); 902 } 903 }); 904 } 905 }); 906} 907 908// Build static map URL 909function getStaticMap() { 910 var gStaticMapEndpoint = "https://maps.googleapis.com/maps/api/staticmap?"; 911 var maptype = "roadmap"; 912 var size = getMapUrlDimensions(); 913 var format = "png"; 914 var scale = "1"; 915 var key = getKeyFromScriptSrc(); 916 var pinColor = "0xff0000"; 917 var locations = getMarkersFromList(); 918 var markers = []; 919 var zoom = ''; 920 921 // Loop over locations and setup markers 922 locations.forEach(function (item) { 923 var latLong = item.lat + ',' + item.long; 924 if (latLong !== ',') { 925 markers.push('&markers=size:mid%7Ccolor:' + pinColor + '%7C' + latLong); 926 } 927 }); 928 929 // Locations detail zoom rate 930 if (locations.length === 1) { 931 zoom = '&zoom=16'; 932 } 933 934 // Create marker string 935 var markersString = markers.join(''); 936 937 // return static map URL 938 return gStaticMapEndpoint + 'scale=' + scale + '&maptype=' + maptype + zoom + '&size=' + size + '&format=' + format + markersString + '&key=' + key; 939} 940 941// Create array from #locList 942function getMarkersFromList() { 943 var list = document.querySelectorAll(fedMaps.locListSelector); 944 var markersArray = []; 945 Array.prototype.forEach.call(list, function (item) { 946 markersArray.push({ 947 lat: item.dataset.latitude, 948 long: item.dataset.longitude 949 }); 950 }); 951 return markersArray; 952} 953 954// Build Map dimensions variable 955function getMapUrlDimensions() { 956 return Math.round(staticMapDimensions.width) + 'x' + Math.round(staticMapDimensions.height); 957} 958 959// return container #map height 960function getMapHeight() { 961 return window.getComputedStyle(document.querySelector('#map')).height.replace('px', ''); 962} 963 964// return container #map width 965function getMapWidth() { 966 return window.getComputedStyle(document.querySelector('#map')).width.replace('px', ''); 967} 968 969// On resize update static map height 970function updateMapHeight() { 971 if (document.querySelectorAll('#makeDynamic').length) { 972 document.querySelector('#makeDynamic').style.height = window.getComputedStyle(document.querySelector('#staticMapImage')).height; 973 } 974} 975 976window.onresize = updateMapHeight; 977 978function isHidden(el) { 979 return (el.offsetParent === null); 980} 981 982function getKeyFromScriptSrc() { 983 var scriptUrl = document.querySelector('script[src*="//maps.googleapis.com/ma
983ps/api/js"]'); 984 985 if (scriptUrl !== null) { 986 var keyArray = scriptUrl.getAttribute('src').split('key='); 987 if (keyArray.length > 1) { 988 return keyArray[1]; 989 } 990 return null; 991 } 992 return null; 993}
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.