PageSourceSearch

https://www.patriot-bank.com/custom/fed-library/production/maps/maps-v5.js

js patriot-bank.com collected 2026-09-29 12:45:41 UTC 30,269 bytes, 993 lines download raw bytes

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.