1/*jslint regexp: true, browser: true, confusion: true, unparam: true, sloppy: true, white: true, forin: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */ 2 3(function() { 4 var window = this, 5 $ = window.jQuery, 6 DDC = (window.DDC = (window.DDC || {})), 7 hide = false, 8 show = true, 9 mapCoordsList = []; 10 11 DDC.modules.googleMap.refresh = function() { 12 var google = window.google; 13 DDC.modules.googleMap["previous"] = DDC.modules.googleMap["previous"] || {}; 14 DDC.modules.googleMap["mapNumber"] = DDC.modules.googleMap["mapNumber"] || 0; 15 //use a data attr flag on this map instance to check if it has already be initialized 16 if (typeof google === 'object' && google.hasOwnProperty('load')) { 17 if (!$(this).data('google-map-initialized')) { 18 $(this).data('google-map-initialized', true); 19 var mapsApiKey = DDC.siteSettings && DDC.siteSettings.googleMapsApiKey ? DDC.siteSettings.googleMapsApiKey : ''; 20 if (!mapsApiKey) { 21 DDC.log('Google Maps API key not configured'); 22 return; 23 } 24 25 google.load('maps', '3', { 26 other_params: 'language=' + DDC.getLang() + '&key=' + mapsApiKey + '&libraries=places', 27 callback: function () { 28 initGoogleMap() 29 } 30 }); 31 } else { 32 initGoogleMap() 33 } 34 } 35 // end map exists check 36 initGoogleMap = function() { 37 $(document).trigger("google-maps-initialized"); 38 DDC.googleMaps = DDC.googleMaps || []; 39 DDC.googleMapsMarkers = DDC.googleMapsMarkers || []; 40 41 var getEvData = function() { 42 var data = []; 43 $.ajax({ 44 url: "/static/assets/map-locations/ev-charging-map-data.json", 45 dataType: 'json', 46 async: false, 47 success: function(result){ 48 data.push(result['entries']) 49 }, 50 error: function(xhr) { 51 console.log(xhr.responseText) 52 }, 53 }); 54 return data[0] 55 } 56 57 var myOptions = {}, 58 mapEl, 59 map = {}, 60 ignoreBounds = true, // Ignores the mapBounds based on multiple markers. Fix for multiple dealerships in same location. 61 directionsService = new google.maps.DirectionsService(), 62 geocoder = new google.maps.Geocoder(), 63 isCoord = function(a) { 64 return $.isArray(a) && (a.length === 2) && !isNaN(a[0]) && !isNaN(a[1]); 65 }, 66 isLatLng = function(a) { 67 return a instanceof google.maps.LatLng; 68 }, 69 isPoint = function(a) { 70 return isLatLng(a) || isCoord(a); 71 }, 72 containsLatLng = function(list, point) { 73 var res = false; 74 75 if (list && point) { 76 res = !!$.grep(list, function(e, i) { 77 return list[i].equals(point); 78 }).length; 79 } 80 81 return res; 82 }, 83 getMapBounds = function(list) { 84 var res = false; 85 86 // filter out any values that can't be calculated 87 list = $.grep(list, isPoint); 88 89 // convert all coords to LatLng points 90 $.each(list, function(i, v) { 91 if (isCoord(v)) { 92 list[i] = new google.maps.LatLng(v[0], v[1]); 93 } 94 }); 95 96 if (list.length) { 97 res = new google.maps.LatLngBounds(list[0], list[0]); 98 for (i = 1; i < list.length; i++) { 99 res.extend(list[i]); 100 if (!list[i].equals(list[i - 1])) { 101 ignoreBounds = false; // use google maps bounds based on multiple markers 102 } 103 } 104 } 105 return res; 106 }, 107 coords = function() { 108 this.list = [], 109 this.getList = function() { 110 return $.grep(this.list, function(i) { if (i) { return i } }); 111 }, 112 this.addToList = function(latlng, i) { 113 if (isLatLng(latlng) && !containsLatLng(this.list)) { 114 this.list[i] = latlng; 115 } else { 116 this.list[i] = ''; 117 } 118 }, 119 this.addCoords = function(coordsArray, map, mapCoords, it) { 120 var self = this, 121 geocodesOutstanding = coordsArray.length; 122 123 if (geocodesOutstanding) { 124 $.each(coordsArray, function(i, v) { 125 if (v && (!$.isArray(v) || v.length !== 2)) { 126 v = v.toString(); 127 128 // geocode if marker isn't a lat/lng coord 129 geocoder.geocode({'address': v}, function(results, status) { 130 if (status === google.maps.GeocoderStatus.OK) { 131 // LatLng object returned 132 self.addToList(results[0].geometry.location, i); 133 } 134 135 if (!--geocodesOutstanding) { 136 self.pushCoords(map, self, it); 137 } 138 }); 139 } else { 140 if (isCoord(v)) { 141 // convert lat/lng coords into LatLng object 142 self.addToList(new google.maps.LatLng(v[0], v[1]), i); 143 144 if (!--geocodesOutstanding) { 145 self.pushCoords(map, self, it); 146 } 147 } 148 } 149 }); 150 } 151 }, 152 this.pushCoords = function(map, self, it) { 153 mapCoordsList.push(self); 154 initializeMap(it); 155 map.data('google-maps-initialized', true); 156 }, 157 this.getCenter = function() { 158 var res = this.getBounds(this.list); 159 160 if (res) { 161 res = res.getCenter(); 162 } 163 164 return res; 165 }, 166 this.getBounds = function() { 167 return getMapBounds(this.list); 168 } 169 }, 170 initializeMap = function(x) { 171 var currentCoords = mapCoordsList[x], 172 currentCoordList = currentCoords.getList(), 173 scrollWheel = myOptions.scrollWheel != undefined; 174 175 currentCoords.center = currentCoords.getCenter(); 176 177 if (currentCoords.center) { 178 myOptions.center = currentCoords.center; 179 180 map[x] = new google.maps.Map(mapEl[0], { 181 center: myOptions.center, 182 zoom: myOptions.zoom, 183 mapTypeId: myOptions.mapTypeId, 184 streetViewControl: false, 185 navigationControl: true, 186 mapTypeControl: true, 187 scrollwheel: scrollWheel, 188 originalCenter: myOptions.center, 189 styles: DDC.checkNested(DDC, 'WidgetData', 'map_dynamic', 'styles') ? DDC.WidgetData.map_dynamic.styles : [] 190 }); 191 192 DDC.googleMaps.push(map[x]); 193 194 // Emit map interaction signals for ws-tracking to consume 195 // (mirrors the marker-clicked-google-maps trigger below). 196 // ws-tracking turns these into ddc.map.* events and the 197 // tagging layer maps them per the client spec. We pass the 198 // map instance so ws-tracking can resolve the widget via 199 // map.getDiv(). 200 (function(mapInstance) { 201 // pan: user drag only. Programmatic panTo/fitBounds do n
201ot 202 // fire dragend, so no init-time false positives. 203 google.maps.event.addListener(mapInstance, 'dragend', function() { 204 $(document).trigger('map-panned-google-maps', mapInstance); 205 }); 206 207 // zoom and view-type (Map/Satellite/Terrain) both fire during 208 // the initial fitBounds/type set, so wait for the map's first 209 // idle before listening. Debounce zoom so a single 210 // scroll-wheel/pinch gesture emits once; view-type is discrete. 211 google.maps.event.addListenerOnce(mapInstance, 'idle', function() { 212 var zoomTimer; 213 google.maps.event.addListener(mapInstance, 'zoom_changed', function() { 214 clearTimeout(zoomTimer); 215 zoomTimer = setTimeout(function() { 216 $(document).trigger('map-zoomed-google-maps', mapInstance); 217 }, 250); 218 }); 219 google.maps.event.addListener(mapInstance, 'maptypeid_changed', function() { 220 $(document).trigger('map-viewchanged-google-maps', mapInstance); 221 }); 222 }); 223 224 // fullscreen: Google requests fullscreen on the map div. Both 225 // entering and exiting are interactions. fullscreenchange is a 226 // document event, so attribute it to this map by its div and 227 // track whether this map was the one in fullscreen for the exit. 228 document.addEventListener('fullscreenchange', function() { 229 var fsEl = document.fullscreenElement, 230 div = mapInstance.getDiv(); 231 if (fsEl && (fsEl === div || div.contains(fsEl))) { 232 mapInstance.wsWasFullscreen = true; 233 $(document).trigger('map-fullscreen-google-maps', mapInstance); 234 } else if (!fsEl && mapInstance.wsWasFullscreen) { 235 mapInstance.wsWasFullscreen = false; 236 $(document).trigger('map-fullscreen-google-maps', mapInstance); 237 } 238 }); 239 })(map[x]); 240 241 if (myOptions && myOptions.circleAddress) { 242 if (myOptions.circleAddress.toString().length === 4) { 243 myOptions.circleAddress = '0' + myOptions.circleAddress.toString(); 244 } 245 246 geocoder.geocode({'address': myOptions.data.circleAddress.toString()}, function(results, status) { 247 var radius = myOptions.data.circleRadius; 248 249 if (radius && (myOptions.circleRadiusUom === 'm')) { 250 // convert miles to kilometers 251 radius = (+radius * 1.609); 252 } 253 254 if (radius && (status === google.maps.GeocoderStatus.OK)) { 255 myOptions.circle = new google.maps.Circle({ 256 center: results[0].geometry.location, 257 map: map[x], 258 radius: radius * 1000, // meters 259 clickable: false, 260 fillOpacity: 0.2, 261 fillColor: '#0056B7', 262 strokeWeight: 2, 263 strokeColor: '#0056B7', 264 zoomControl: false, 265 }); 266 map[x].circleBounds = myOptions.circle.getBounds(); 267 map[x].fitBounds(map[x].circleBounds); 268 } 269 }); 270 } 271 272 if (myOptions && myOptions.enableEvChargers) { 273 //Constraints for rendering the pins on the map 274 var evRadius = 0.1; 275 var evMarkerCap = 250; 276 277 var evData = getEvData(); 278 if (evData && evData.length != 0) { 279 map[0].setOptions({ minZoom: 12 }); // minZoom level set to prevent performance issues, page breaks if too many markers rendered 280 var svgMarker = { 281 path: "M18.88 57.82C22.0267 58.2133 37.76 29.3071 37.76 18.88C37.76 8.45286 29.3071 0 18.88 0C8.45286 0 0 8.45286 0 18.88C0 29.3071 15.7333 57.4267 18.88 57.82ZM20.6762 19.6669H25.0253C25.2035 19.6668 25.378 19.7182 25.5282 19.815C25.6785 19.9119 25.7982 20.0501 25.8732 20.2134C25.9482 20.3768 25.9754 20.5584 25.9516 20.7368C25.9279 20.9153 25.854 21.0831 25.7389 21.2206L15.464 33.4913C15.3254 33.6572 15.1345 33.7699 14.9234 33.8102C14.7122 33.8506 14.4938 33.8161 14.3049 33.7127C14.116 33.6092 13.9681 33.4431 13.8862 33.2424C13.8044 33.0416 13.7935 32.8185 13.8555 32.6106L16.2972 24.3864H11.948C11.7698 24.3865 11.5953 24.3351 11.4451 24.2383C11.2949 24.1415 11.1752 24.0032 11.1002 23.8399C11.0251 23.6766 10.9979 23.495 11.0217 23.3165C11.0455 23.1381 11.1193 22.9702 11.2344 22.8328L21.5094 10.5621C21.6479 10.3961 21.8388 10.2834 22.0499 10.2431C22.2611 10.2028 22.4795 10.2372 22.6684 10.3407C22.8574 10.4441 23.0052 10.6102 23.0871 10.8109C23.169 11.0117 23.1798 11.2348 23.1179 11.4427L20.6762 19.6669Z", 282 fillColor: "#33D0F2", 283 fillOpacity: 0.9, 284 rotation: 0, 285 scale: 0.6, 286 anchor: new google.maps.Point(15, 30), 287 }; 288 289 google.maps.event.addListener(map[x], "initialize", function(){ 290 }) 291 292 pointList = [] 293 var currentLat = currentCoords.center.lat(); 294 var currentLng = currentCoords.center.lng(); 295 296 evData.forEach(function(el) { 297 var dx = Math.abs(currentLat - el[0]); 298 var dy = Math.abs(currentLng - el[1]); 299 if(dx <= evRadius && dy <= evRadius && pointList.length < evMarkerCap){ 300 pointList.push( new google.maps.LatLng(el[0], el[1])) 301 } 302 }); 303 304 markers = [] 305 pointList.forEach(function(el) { 306 markers.push(new google.maps.Marker({ 307 position: el, 308 map: map[x], 309 icon: svgMarker, 310 optimized: true, 311 clickable: false, 312 })) 313 }) 314 new markerClusterer.MarkerClusterer({ map: map[x], markers: markers }); 315 }; 316 }; 317 318 $(currentCoordList).each(function(i) { 319 var coord = currentCoordList[i], 320 windowMaxWidth = myOptions.data.markerInfoWindowMaxWidth != 'undefined' ? myOptions.data.markerInfoWindowMaxWidth : 1000, 321 enableInfoWindow = myOptions.enableInfoWindow != undefined, 322 markerLabel = $(myOptions.infoWindows[i]).data("marker-label"), 323 setOptimized = !($(myOptions.infoWindows[0]).is(".marker-labels")); 324 325 if (coord !== undefined && isLatLng(coord) && !coord.marker) { 326 coord.marker = new google.maps.Marker({ 327 position: coord, 328 map: map[x], 329 infoClass: myOptions.infoWindows[i], 330 enableInfoWindow: enableInfoWindow, 331 optimized: setOptimized 332 }); 333 334 if(myOptions.customMarker) { 335 // %E2%80%A2 is the "dot" in the center of the marker. 336 var currentLabel = markerLabel != undefined ? markerLabel.toString() : "%E2%80%A2"; 337 coord.marker.defaultMarker = myOptions.hasOwnProperty("markerImage") ? myOptions.markerImage : myOptions.hasOwnProperty("markerColor") ? getMarker(myOptions.markerColor.replace('#',''), myOptions.markerLabelColor.replace('#',''), currentLabel) : undefined; 338 coord.marker.selectedMarker = myOptions.hasOwnProperty("markerSelectedImage") && myOptions.hasOwnProperty("markerImage") ? myOptions.markerSelectedImage : myOptions.hasOwnProperty("markerSelectedColor") && myOptions.hasOwnProperty("markerColor") && !myOptions.hasOwnProperty("markerImage") ? getMarker(myOptions.markerSelectedColor.replace('#',''), myOptions.markerSelectedLabelColor.replace('#',''), currentLabel) : undefined; 339 coord.marker.setIcon(coord.marker.defaultMarker); 340 341 if(myOptions.hasOwnProperty("markerImage") && markerLabel != undefined){ 342 coord.marker.setLabel(markerLabel.toString()) 343 } 344 }else if(markerLabel != undefined){ 345 coord.marker.setLabel(markerLabel.toString()) 346 } 347
348 DDC.googleMapsMarkers[i] = coord; 349 350 if (myOptions.infoWindows && myOptions.infoWindows[i] && !coord.infoWindow) { 351 coord.infoWindow = new google.maps.InfoWindow({ 352 content: '<div class="infowindow-wrap" style="min-height: 100px;">' + $(myOptions.infoWindows[i]).html() + '</div>', 353 maxWidth: windowMaxWidth 354 }); 355 356 if ($.getDataAttributes(myOptions.infoWindows[i]).infoWindowEvent) { 357 $(myOptions.infoWindows[i]).bind($.getDataAttributes(myOptions.infoWindows[i]).infoWindowEvent, function() { 358 google.maps.event.trigger(coord.marker, 'click'); 359 map[x].panTo(new google.maps.LatLng(coord.lat(),coord.lng())); 360 }); 361 } 362 google.maps.event.addListener(coord.marker, 'click', function () { 363 // resetting previously selected marker back to default 364 if(DDC.modules.googleMap["previous"].hasOwnProperty("marker")){ 365 DDC.modules.googleMap["previous"].marker.setIcon(DDC.modules.googleMap["previous"].marker.defaultMarker); 366 DDC.modules.googleMap["previous"].marker.setZIndex(google.maps.Marker.MAX_ZINDEX - 1); 367 } 368 if(DDC.modules.googleMap["previous"].hasOwnProperty("infoWindow")){ 369 DDC.modules.googleMap["previous"].infoWindow.close(); 370 } 371 372 // Setting current marker as previous marker (all maps have cord.marker) 373 DDC.modules.googleMap["previous"].marker = coord.marker; 374 375 if(coord.marker.selectedMarker != undefined) { 376 coord.marker.setIcon(coord.marker.selectedMarker); 377 } 378 // Moving current map marker to the top z-index 379 coord.marker.setZIndex(google.maps.Marker.MAX_ZINDEX + 1); 380 381 if(enableInfoWindow){ 382 coord.infoWindow.open(coord.marker.map, coord.marker); 383 // Setting current infoWindow as previous infoWindow onlt ifinfo windows are enabled 384 DDC.modules.googleMap["previous"].infoWindow = coord.infoWindow; 385 }else { 386 // if the marker is outside the bounds of the map, this will center the map on the marker 387 var currentMapBounds = coord.marker.map.getBounds(); 388 if (!currentMapBounds.contains(coord.marker.position)) { 389 coord.marker.map.panTo(coord.marker.position); 390 } 391 } 392 393 $(document).trigger('marker-clicked-google-maps', coord.marker); 394 }); 395 } 396 } 397 }); 398 } 399 400 if (map[x] && currentCoords.list.length > 1 && !ignoreBounds) { 401 map[x].fitBounds(currentCoords.getBounds()); 402 } 403 }, 404 toggleMarkers = function(showMarkers) { 405 // hard coding currentCoordList to use first object on directions pages 406 var coordList = mapCoordsList[0].getList(); 407 408 $(coordList).each(function(i) { 409 coordList[i].marker.setVisible(showMarkers); 410 411 if (coordList[i].infoWindow) { 412 coordList[i].infoWindow.close(); 413 } 414 }); 415 }, 416 getMarker = function(color, labelColor, label){ 417 return "https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=" + label + "|" + color + "|" + labelColor; 418 }; 419 420 $(DDC.modules.googleMap.selector).each(function() { 421 mapEl = $(this); 422 if (mapEl.attr('map-initialized') === 'true') { 423 return; 424 }; 425 myOptions = $.getDataAttributes(this); 426 mapEl.attr('map-initialized', 'true'); 427 markerAddresses = [], 428 myOptions.customMarker = myOptions.hasOwnProperty("markerColor") || myOptions.hasOwnProperty("markerImage"), 429 widgetClass = mapEl.closest('div[data-widget-id]').attr('data-widget-id'), 430 myOptions.infoWindows = []; 431 432 if ($.getDataAttributes(this).infoWindows) { 433 $.each( $.getDataAttributes(this).infoWindows.split(','), function(i, info) { 434 myOptions.infoWindows.push("[data-widget-id=" + widgetClass + "] " + info); 435 }); 436 } else { 437 myOptions.infoWindows = null; 438 } 439 440 myOptions.data = $.getDataAttributes(this); 441 if (myOptions.data.mapGetMarkersByAddress && myOptions.data.mapGetMarkersByAddress === true) { 442 myOptions.data.markersList = myOptions.data.markersList.replace(/\'/g, '"'); 443 myOptions.markersList = myOptions.markersList ? $.parseJSON(myOptions.data.markersList) : null; 444 445 for (var x = 0; x < myOptions.markersList.length; x++) { 446 markerAddresses.push($.makeArray(myOptions.markersList[x].storeLocation)); 447 } 448 449 myOptions.markersList = markerAddresses; 450 } else { 451 myOptions.markersList = myOptions.markersList ? $.parseJSON(myOptions.markersList) : null; 452 } 453 454 myOptions.mapTypeId = myOptions.mapTypeId ? google.maps.MapTypeId[$.getDataAttributes(this).mapTypeId] : google.maps.MapTypeId.ROADMAP; 455
456 var mapCoords = new coords(); 457 mapCoords.addCoords(myOptions.markersList, $(this), mapCoords, DDC.modules.googleMap.mapNumber++); 458 }); 459 460 $(document) 461 .unbind('.google-maps') 462 // set up map refresh events 463 .bind('map-refresh.google-maps', function(ev, data) { 464 var map = DDC.googleMaps[data.mapCounter], 465 zoom = data.zoom ? data.zoom : map.getZoom(); 466 467 google.maps.event.trigger(map, 'resize'); 468 469 if (map.circleBounds) { 470 map.fitBounds(map.circleBounds); 471 } else{ 472 map.setZoom(zoom); 473 map.setCenter(map.originalCenter); 474 } 475 }) 476 477 // set up directions events 478 .bind('directions-search.google-maps', function(ev, data) { 479 if (data && data.directions.origin && data.directions.destination) { 480 $.extend(data.directions, { 481 travelMode: google.maps.DirectionsTravelMode.DRIVING 482 }); 483 directionsService.route(data.directions, function(results, status) { 484 if (status === google.maps.DirectionsStatus.OK) { 485 $.extend(data, {results: results}); 486 $(document).trigger('directions-results', data); 487 } 488 }); 489 } else { 490 return data; 491 } 492 }) 493 .bind('directions-results.google-maps', function(ev, data) { 494 toggleMarkers(hide); 495 496 if (data && data.results) { 497 $.each(DDC.googleMaps, function(i, map) { 498 if ($(map.getDiv()).is(':visible')) { 499 map.directionsRenderer = map.directionsRenderer || new google.maps.DirectionsRenderer(); 500 map.directionsRenderer.setMap(map); 501 map.directionsRenderer.setDirections(data.results); 502 } 503 504 if ($('.directions-results').get(i) && 505 map.directionsRenderer) { 506 map.directionsRenderer.setPanel($('.directions-results').get(i)); 507 } 508 }); 509 } 510 }) 511 .bind('directions-reset.google-maps', function(ev) { 512 map.directionsRenderer.setMap(null); 513 toggleMarkers(show); 514 $('.directions-results').empty(); 515 }); 516 517 518 // trigger on full initialization with directions events 519 $(document).trigger('googleMapInitialized'); 520 } 521 }; 522}());
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.