1/** 2 * @namespace WPGMZA 3 * @module GoogleMap 4 * @requires WPGMZA.Map 5 * @pro-requires WPGMZA.ProMap 6 */ 7jQuery(function($) { 8 var Parent; 9 10 /** 11 * Constructor 12 * @param element to contain the map 13 */ 14 WPGMZA.GoogleMap = function(element, options) 15 { 16 var self = this; 17 18 Parent.call(this, element, options); 19 20 this.loadGoogleMap(); 21 22 if(options){ 23 this.setOptions(options, true); 24 } else { 25 this.setOptions({}, true); 26 } 27 28 google.maps.event.addListener(this.googleMap, "click", function(event) { 29 var wpgmzaEvent = new WPGMZA.Event("click"); 30 wpgmzaEvent.latLng = { 31 lat: event.latLng.lat(), 32 lng: event.latLng.lng() 33 }; 34 self.dispatchEvent(wpgmzaEvent); 35 }); 36 37 google.maps.event.addListener(this.googleMap, "rightclick", function(event) { 38 var wpgmzaEvent = new WPGMZA.Event("rightclick"); 39 wpgmzaEvent.latLng = { 40 lat: event.latLng.lat(), 41 lng: event.latLng.lng() 42 }; 43 self.dispatchEvent(wpgmzaEvent); 44 }); 45 46 google.maps.event.addListener(this.googleMap, "dragend", function(event) { 47 self.dispatchEvent("dragend"); 48 }); 49 50 google.maps.event.addListener(this.googleMap, "zoom_changed", function(event) { 51 self.dispatchEvent("zoom_changed"); 52 self.dispatchEvent("zoomchanged"); 53 }); 54 55 // Idle event 56 google.maps.event.addListener(this.googleMap, "idle", function(event) { 57 self.onIdle(event); 58 }); 59 60 if(this.googleMap.getStreetView()){ 61 // Bind streetview events 62 google.maps.event.addListener(this.googleMap.getStreetView(), "visible_changed", function(){ 63 var wpgmzaEvent = new WPGMZA.Event("streetview_visible_changed"); 64 65 wpgmzaEvent.visible = this.getVisible(); 66 67 self.dispatchEvent(wpgmzaEvent); 68 }); 69 70 google.maps.event.addListener(this.googleMap.getStreetView(), "position_changed", function(){ 71 var wpgmzaEvent = new WPGMZA.Event("streetview_position_changed"); 72 73 const position = this.getPosition(); 74 if(position){ 75 wpgmzaEvent.latLng = { 76 lat: position.lat(), 77 lng: position.lng() 78 }; 79 } 80 81 wpgmzaEvent.visible = this.getVisible(); 82 83 self.dispatchEvent(wpgmzaEvent); 84 }); 85 86 google.maps.event.addListener(this.googleMap.getStreetView(), "pov_changed", function(){ 87 var wpgmzaEvent = new WPGMZA.Event("streetview_pov_changed"); 88 89 const pov = this.getPov(); 90 if(pov){ 91 wpgmzaEvent.pov = { 92 heading: pov.heading, 93 pitch: pov.pitch 94 }; 95 } 96 97 wpgmzaEvent.visible = this.getVisible(); 98 99 self.dispatchEvent(wpgmzaEvent); 100 }); 101 } 102 103 // Dispatch event 104 if(!WPGMZA.isProVersion()) 105 { 106 this.trigger("init"); 107 108 this.dispatchEvent("created"); 109 WPGMZA.events.dispatchEvent({type: "mapcreated", map: this}); 110 111 // Legacy event 112 $(this.element).trigger("wpgooglemaps_loaded"); 113 } 114 } 115 116 // If we're running the Pro version, inherit from ProMap, otherwise, inherit from Map 117 if(WPGMZA.isProVersion()) 118 { 119 Parent = WPGMZA.ProMap; 120 WPGMZA.GoogleMap.prototype = Object.create(WPGMZA.ProMap.prototype); 121 } 122 else 123 { 124 Parent = WPGMZA.Map; 125 WPGMZA.GoogleMap.prototype = Object.create(WPGMZA.Map.prototype); 126 } 127 WPGMZA.GoogleMap.prototype.constructor = WPGMZA.GoogleMap; 128 129 WPGMZA.GoogleMap.parseThemeData = function(raw) 130 { 131 var json; 132 133 try{ 134 json = JSON.parse(raw); // Try to parse strict JSON 135 }catch(e) { 136 var str = raw; 137 138 str = str.replace(/\\'/g, '\''); 139 str = str.replace(/\\"/g, '"'); 140 str = str.replace(/\\0/g, '\0'); 141 str = str.replace(/\\\\/g, '\\'); 142 143 try{ 144 json = JSON.parse(str); 145 }catch(e) { 146 console.warn("Couldn't parse theme data"); 147 return []; 148 } 149 } 150 151 /* As of 2023-04-28 Google Maps themes must contain array with each item being a defined object. This means older theme definitions 152 * will cause initialization issues as they may contain only keyname like ["visibility", "weight"] 153 * 154 * We will attempt to catch these and return the default theme instead of breaking the map 155 */ 156 if(json instanceof Array){ 157 try{ 158 for(let data of json){ 159 if(!(data instanceof Object)){ 160 /* This key is not an object, it's safe to assume the theme has been corrupted */ 161 return []; 162 } 163 } 164 } catch (ex){ 165 /* Loop failed, for validation, return default */ 166 return []; 167 } 168 } else { 169 /* Not array, something failed, default */ 170 return []; 171 } 172 173 return json; 174 } 175 176 /** 177 * Creates the Google Maps map 178 * @return void 179 */ 180 WPGMZA.GoogleMap.prototype.loadGoogleMap = function() 181 { 182 var self = this; 183 var options = this.settings.toGoogleMapsOptions(); 184 185 options = this.extendNativeConfig(options); 186 187 this.googleMap = new google.maps.Map(this.engineElement, options); 188 189 google.maps.event.addListener(this.googleMap, "bounds_changed", function() { 190 self.onBoundsChanged(); 191 }); 192 193 if(this.settings.bicycle == 1){ 194 this.enableBicycleLayer(true); 195 } 196 197 if(this.settings.traffic == 1){ 198 this.enableTrafficLayer(true); 199 } 200 201 if(this.settings.transport_layer){ 202 this.enablePublicTransportLayer(true); 203 } 204 205 if(this.settings.enable_scale_control){ 206 this.enableScaleControl(true); 207 } 208 209 this.showPointsOfInterest(this.settings.wpgmza_show_point_of_interest); 210 211 // Move the loading wheel into the map element (it has to live outside in the HTML file because it'll be overwritten by Google otherwise) 212 $(this.engineElement).append($(this.element).find(".wpgmza-loader")); 213 } 214 215 WPGMZA.GoogleMap.prototype.setOptions = function(options, initializing) 216 { 217 Parent.prototype.setOptions.call(this, options); 218 219 if(options.scrollwheel) 220 delete options.scrollwheel; // NB: Delete this when true, scrollwheel: true breaks gesture handling 221 222 if(!initializing) 223 { 224 this.googleMap.setOptions(options); 225 return; 226 } 227 228 var converted = $.extend(options, this.settings.toGoogleMapsOptions()); 229 230 var clone = $.extend({}, converted); 231 if(!clone.center instanceof google.maps.LatLng && (clone.center instanceof WPGMZA.LatLng || typeof clone.center == "object")) 232 clone.center = { 233 lat: parseFloat(clone.center.lat), 234 lng: parseFloat(clone.center.lng) 235 }; 236 237 if(this.settings.hide_point_of_interest){ 238 var noPoi = { 239 featureType: "poi", 240 elementType: "labels", 241 stylers: [ 242 { 243 visibility: "off" 244 } 245 ] 246 }; 247 248 if(!clone.styles) 249 clone.styles = []; 250 251 clone.styles.push(noPoi); 252 } 253 254 if(WPGMZA.settings && WPGMZA.settings.googleMarkerMode && WPGMZA.settings.googleMarkerMode === WPGMZA.GoogleMarker.MARKER_MODE_ADVANCED){ 255 /* Advanced Marker Module - Disable local styles */ 256 if(clone.styles){ 257 delete clone.styles; 258 } 259 } 260 261 this.googleMap.setOptions(clone); 262 } 263 264 /** 265 * Adds the specified marker to this map 266 * @return void 267 */ 268 WPGMZA.GoogleMap.prototype.addMarker = function(marker) 269 { 270 marker.googleMarker.setMap(this.googleMap); 271 272 Parent.prototype.addMarker.call(this, marker); 273 } 274 275 /** 276 * Removes the specified marker from this map 277 * @return void 278 */ 279 WPGMZA.GoogleMap.prototype.removeMarker = function(marker) 280 { 281 marker.googleMarker.setMap(null); 282 283 Parent.prototype.removeMarker.call(this, marker); 284 } 285 286 /** 287 * Adds the specified polygon to this map 288 * @return void 289 */ 290 WPGMZA.GoogleMap.prototype.addPolygon = function(polygon) 291 { 292 polygon.googlePolygon.setMap(this.googleMap); 293 294 Parent.prototype.addPolygon.call(this, polygon); 295 } 296 297 /** 298 * Removes the specified polygon from this map 299 * @return void 300 */ 301 WPGMZA.GoogleMap.prototype.removePolygon = function(polygon) 302 { 303 polygon.googlePolygon.setMap(null); 304 305 Parent.prototype.removePolygon.call(this, polygon); 306 } 307 308 /** 309 * Adds the specified polyline to this map 310 * @return void 311 */ 312 WPGMZA.GoogleMap.prototype.addPolyline = function(polyline) 313 { 314 polyline.googlePolyline.setMap(this.googleMap); 315 316 Parent.prototype.addPolyline.call(this, polyline); 317 } 318 319 /** 320 * Removes the specified polygon from this map 321 * @return void 322 */ 323 WPGMZA.GoogleMap.prototype.removePolyline = function(polyline) 324 { 325 polyline.googlePolyline.setMap(null); 326 327 Parent.prototype.removePolyline.call(this, polyline); 328 } 329 330 WPGMZA.GoogleMap.prototype.addCircle = function(circle) 331 { 332 circle.googleCircle.setMap(this.googleMap); 333 334 Parent.prototype.addCircle.call(this, circle); 335 } 336 337 WPGMZA.GoogleMap.prototype.removeCircle = function(circle) 338 { 339 circle.googleCircle.setMap(null); 340 341 Parent.prototype.removeCircle.call(this, circle); 342 } 343 344 WPGMZA.GoogleMap.prototype.addRectangle = function(rectangle) 345 { 346 rectangle.googleRectangle.setMap(this.googleMap); 347 348 Parent.prototype.addRectangle.call(this, rectangle); 349 } 350 351 WPGMZA.GoogleMap.prototype.removeRectangle = function(rectangle) 352 { 353 rectangle.googleRectangle.setMap(null); 354 355 Parent.prototype.removeRectangle.call(this, rectangle); 356 } 357 358 /** 359 * Delegate for google maps getCenter 360 * @return void 361 */ 362 WPGMZA.GoogleMap.prototype.getCenter = function() 363 { 364 var latLng = this.googleMap.getCenter(); 365 366 return { 367 lat: latLng.lat(), 368 lng: latLng.lng() 369 }; 370 } 371 372 /** 373 * Delegate for google maps setCenter 374 * @return void 375 */ 376 WPGMZA.GoogleMap.prototype.setCenter = function(latLng) 377 { 378 WPGMZA.Map.prototype.setCenter.call(this, latLng); 379 380 if(latLng instanceof WPGMZA.LatLng) 381 this.googleMap.setCenter({ 382 lat: latLng.lat, 383 lng: latLng.lng 384 }); 385 else 386 this.googleMap.setCenter(latLng); 387 } 388 389 /** 390 * Delegate for google maps setPan 391 * @return void 392 */ 393 WPGMZA.GoogleMap.prototype.panTo = function(latLng) 394 { 395 if(latLng instanceof WPGMZA.LatLng) 396 this.googleMap.panTo({ 397 lat: latLng.lat, 398 lng: latLng.lng 399 }); 400 else 401 this.googleMap.panTo(latLng); 402 } 403 404 /** 405 * Delegate for google maps getCenter 406 * @return void 407 */ 408 WPGMZA.GoogleMap.prototype.getZoom = function() 409 { 410 return this.googleMap.getZoom(); 411 } 412 413 /** 414 * Delegate for google maps getZoom 415 * @return void 416 */ 417 WPGMZA.GoogleMap.prototype.setZoom = function(value) 418 { 419 if(isNaN(value)) 420 throw new Error("Value must not be NaN"); 421 422 return this.googleMap.setZoom(parseInt(value)); 423 } 424 425 /** 426 * Gets the bounds 427 * @return object 428 */ 429 WPGMZA.GoogleMap.prototype.getBounds = function() { 430 431 var nativeBounds = new WPGMZA.LatLngBounds({}); 432 433 try{ 434 var bounds = this.googleMap.getBounds(); 435 var northEast = bounds.getNorthEast();
436 var southWest = bounds.getSouthWest(); 437 438 439 nativeBounds.north = northEast.lat(); 440 nativeBounds.south = southWest.lat(); 441 nativeBounds.west = southWest.lng(); 442 nativeBounds.east = northEast.lng(); 443 444 // Backward compatibility 445 nativeBounds.topLeft = { 446 lat: northEast.lat(), 447 lng: southWest.lng() 448 }; 449 450 nativeBounds.bottomRight = { 451 lat: southWest.lat(), 452 lng: northEast.lng() 453 }; 454 } catch (ex){ 455 /* Just return a default, instead of throwing an error */ 456 } 457 458 return nativeBounds; 459 } 460 461 /** 462 * Fit to given boundaries 463 * @return void 464 */ 465 WPGMZA.GoogleMap.prototype.fitBounds = function(southWest, northEast) 466 { 467 if(southWest instanceof WPGMZA.LatLng) 468 southWest = {lat: southWest.lat, lng: southWest.lng}; 469 if(northEast instanceof WPGMZA.LatLng) 470 northEast = {lat: northEast.lat, lng: northEast.lng}; 471 else if(southWest instanceof WPGMZA.LatLngBounds) 472 { 473 var bounds = southWest; 474 475 southWest = { 476 lat: bounds.south, 477 lng: bounds.west 478 }; 479 480 northEast = { 481 lat: bounds.north, 482 lng: bounds.east 483 }; 484 } 485 486 var nativeBounds = new google.maps.LatLngBounds(southWest, northEast); 487 this.googleMap.fitBounds(nativeBounds); 488 } 489 490 /** 491 * Fit the map boundaries to visible markers 492 * @return void 493 */ 494 WPGMZA.GoogleMap.prototype.fitBoundsToVisibleMarkers = function() 495 { 496 var bounds = new google.maps.LatLngBounds(); 497 for(var i = 0; i < this.markers.length; i++) 498 { 499 if(markers[i].getVisible()) 500 bounds.extend(markers[i].getPosition()); 501 } 502 this.googleMap.fitBounds(bounds); 503 } 504 505 /** 506 * Enables / disables the bicycle layer 507 * @param enable boolean, enable or not 508 * @return void 509 */ 510 WPGMZA.GoogleMap.prototype.enableBicycleLayer = function(enable) 511 { 512 if(!this.bicycleLayer) 513 this.bicycleLayer = new google.maps.BicyclingLayer(); 514 515 this.bicycleLayer.setMap( 516 enable ? this.googleMap : null 517 ); 518 } 519 520 /** 521 * Enables / disables the bicycle layer 522 * @param enable boolean, enable or not 523 * @return void 524 */ 525 WPGMZA.GoogleMap.prototype.enableTrafficLayer = function(enable) 526 { 527 if(!this.trafficLayer) 528 this.trafficLayer = new google.maps.TrafficLayer(); 529 530 this.trafficLayer.setMap( 531 enable ? this.googleMap : null 532 ); 533 } 534 535 /** 536 * Enables / disables the bicycle layer 537 * @param enable boolean, enable or not 538 * @return void 539 */ 540 WPGMZA.GoogleMap.prototype.enablePublicTransportLayer = function(enable) 541 { 542 if(!this.publicTransportLayer) 543 this.publicTransportLayer = new google.maps.TransitLayer(); 544 545 this.publicTransportLayer.setMap( 546 enable ? this.googleMap : null 547 ); 548 } 549 550 /** 551 * Enables / disables the scale control bar 552 * @param enable boolean, enable or not 553 * @return void 554 */ 555 WPGMZA.GoogleMap.prototype.enableScaleControl = function(enable) 556 { 557 const units = (this.settings.store_locator_distance == WPGMZA.Distance.MILES) 558 ? google.maps.ScaleControlStyle.IMPERIAL 559 : google.maps.ScaleControlStyle.METRIC; 560 561 this.googleMap.setOptions({ 562 scaleControl: enable, 563 scaleControlOptions: { 564 style: units, 565 position: google.maps.ControlPosition.BOTTOM_LEFT 566 } 567 }); 568 } 569 570 /** 571 * Shows / hides points of interest 572 * @param show boolean, enable or not 573 * @return void 574 */ 575 WPGMZA.GoogleMap.prototype.showPointsOfInterest = function(show) 576 { 577 // TODO: This will bug the front end because there is no textarea with theme data 578 var text = $("textarea[name='theme_data']").val(); 579 580 if(!text) 581 return; 582 583 var styles = JSON.parse(text); 584 585 styles.push({ 586 featureType: "poi", 587 stylers: [ 588 { 589 visibility: (show ? "on" : "off") 590 } 591 ] 592 }); 593 594 this.googleMap.setOptions({styles: styles}); 595 } 596 597 /** 598 * Gets the min zoom of the map 599 * @return int 600 */ 601 WPGMZA.GoogleMap.prototype.getMinZoom = function() 602 { 603 return parseInt(this.settings.min_zoom); 604 } 605 606 /** 607 * Sets the min zoom of the map 608 * @return void 609 */ 610 WPGMZA.GoogleMap.prototype.setMinZoom = function(value) 611 { 612 this.googleMap.setOptions({ 613 minZoom: value, 614 maxZoom: this.getMaxZoom() 615 }); 616 } 617 618 /** 619 * Gets the min zoom of the map 620 * @return int 621 */ 622 WPGMZA.GoogleMap.prototype.getMaxZoom = function() 623 { 624 return parseInt(this.settings.max_zoom); 625 } 626 627 /** 628 * Sets the min zoom of the map 629 * @return void 630 */ 631 WPGMZA.GoogleMap.prototype.setMaxZoom = function(value) 632 { 633 this.googleMap.setOptions({
634 minZoom: this.getMinZoom(), 635 maxZoom: value 636 }); 637 } 638 639 WPGMZA.GoogleMap.prototype.latLngToPixels = function(latLng) 640 { 641 var map = this.googleMap; 642 var nativeLatLng = new google.maps.LatLng({ 643 lat: parseFloat(latLng.lat), 644 lng: parseFloat(latLng.lng) 645 }); 646 var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast()); 647 var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest()); 648 var scale = Math.pow(2, map.getZoom()); 649 var worldPoint = map.getProjection().fromLatLngToPoint(nativeLatLng); 650 return { 651 x: (worldPoint.x - bottomLeft.x) * scale, 652 y: (worldPoint.y - topRight.y) * scale 653 }; 654 } 655 656 WPGMZA.GoogleMap.prototype.pixelsToLatLng = function(x, y) 657 { 658 if(y == undefined) 659 { 660 if("x" in x && "y" in x) 661 { 662 y = x.y; 663 x = x.x; 664 } 665 else 666 console.warn("Y coordinate undefined in pixelsToLatLng (did you mean to pass 2 arguments?)"); 667 } 668 669 var map = this.googleMap; 670 var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast()); 671 var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest
671()); 672 var scale = Math.pow(2, map.getZoom()); 673 var worldPoint = new google.maps.Point(x / scale + bottomLeft.x, y / scale + topRight.y); 674 var latLng = map.getProjection().fromPointToLatLng(worldPoint); 675 return { 676 lat: latLng.lat(), 677 lng: latLng.lng() 678 }; 679 } 680 681 /** 682 * Handle the map element resizing 683 * @return void 684 */ 685 WPGMZA.GoogleMap.prototype.onElementResized = function(event) 686 { 687 if(!this.googleMap) 688 return; 689 google.maps.event.trigger(this.googleMap, "resize"); 690 } 691 692 WPGMZA.GoogleMap.prototype.enableAllInteractions = function() 693 { 694 var options = {}; 695 696 options.scrollwheel = true; 697 options.draggable = true; 698 options.disableDoubleClickZoom = false; 699 700 this.googleMap.setOptions(options); 701 } 702 703 WPGMZA.GoogleMap.prototype.openStreetView = function(options){ 704 if(this.googleMap.getStreetView()){ 705 if(options){ 706 if(options.position && options.position instanceof WPGMZA.LatLng){ 707 this.googleMap.getStreetView().setPosition(options.position.toGoogleLatLng()); 708 } 709 710 if(options.heading || options.pitch){ 711 const pov = {}; 712 if(options.heading){ 713 pov.heading = parseFloat(options.heading); 714 } 715 716 if(options.pitch){ 717 pov.pitch = parseFloat(options.pitch); 718 } 719 720 this.googleMap.getStreetView().setPov(pov); 721 } 722 } 723 this.googleMap.getStreetView().setVisible(true); 724 } 725 } 726 727 WPGMZA.GoogleMap.prototype.closeStreetView = function(){ 728 if(this.googleMap.getStreetView()){ 729 this.googleMap.getStreetView().setVisible(false); 730 } 731 } 732 733 /** 734 * Override default map isFullScreen method for Google Maps 735 * 736 * This allows us to check the first child div's height, instead of the container. 737 * 738 * OpenLayers sets the container to fullscreen, Google sets their nested div instead 739 * 740 * @returns bool 741 */ 742 WPGMZA.GoogleMap.prototype.isFullScreen = function(){ 743 let fullscreen = WPGMZA.Map.prototype.isFullScreen.call(this); 744 745 if(!fullscreen && WPGMZA.isFullScreen()){ 746 if(parseInt(window.screen.height) === parseInt(this.element.firstChild.offsetHeight)){ 747 return true; 748 } 749 } 750 return false; 751 } 752 753 /** 754 * Override the map on Fullscreen change method, this allows us to move embedded `stacks` into the main map div to allow them to be visible 755 * as part of the fullscreen display 756 * 757 * @param bool fullscreen Is this map fullscreen 758 * 759 * @return void 760 */ 761 WPGMZA.GoogleMap.prototype.onFullScreenChange = function(fullscreen){ 762 Parent.prototype.onFullScreenChange.call(this, fullscreen); 763 764 if(fullscreen && !this._stackedComponentsMoved){ 765 if(this.element.firstChild){ 766 const innerContainer = this.element.firstChild; 767 $(this.element).find('.wpgmza-inner-stack').each(function(index, element){ 768 $(element).appendTo(innerContainer); 769 }); 770 771 this._stackedComponentsMoved = true; 772 } 773 } 774 } 775});
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.