/* globals jQuery, google, sowb */ var sowb = window.sowb || {}; sowb.SiteOriginGoogleMap = function($) { return { // So that we can always display something, even if no location or address was entered. DEFAULT_LOCATIONS: [ 'Addo Elephant National Park, R335, Addo', 'Cape Town, Western Cape, South Africa', 'San Francisco Bay Area, CA, United States', 'New York, NY, United States', ], showMap: function(element, location, options) { var zoom = Number(options.zoom); if ( !zoom ) zoom = 14; var userMapTypeId = 'user_map_style'; var mapOptions = { zoom: zoom, scrollwheel: options.scrollZoom, draggable: options.draggable, disableDefaultUI: options.disableUi, zoomControl: options.zoomControl, panControl: options.panControl, center: location, mapTypeControlOptions: { mapTypeIds: [google.maps.MapTypeId.ROADMAP, userMapTypeId] } }; var map = new google.maps.Map(element, mapOptions); var userMapOptions = { name: options.mapName }; var userMapStyles = options.mapStyles; if ( userMapStyles ) { var userMapType = new google.maps.StyledMapType(userMapStyles, userMapOptions); map.mapTypes.set(userMapTypeId, userMapType); map.setMapTypeId(userMapTypeId); } if (options.markerAtCenter) { this.centerMarker = new google.maps.Marker({ position: location, map: map, draggable: options.markersDraggable, icon: options.markerIcon, title: '' }); } if(options.keepCentered) { var center; google.maps.event.addDomListener(map, 'idle', function () { center = map.getCenter(); }); google.maps.event.addDomListener(window, 'resize', function () { map.setCenter(center); }); } this.linkAutocompleteField(options.autocomplete, options.autocompleteElement, map, options); this.showMarkers(options.markerPositions, map, options); this.showDirections(options.directions, map, options); // If the Google Maps element is hidden it won't display properly. This is an attempt to make it display by // calling resize when a custom 'show' event is fired. The 'show' event is something we fire in a few widgets // like Accordion and Tabs and in future any widgets which might show and hide content using `display:none;`. if ( $( element ).is( ':hidden' ) ) { var $visParent = $( element ).closest( ':visible' ); $visParent.find( '> :hidden' ).on( 'show', function () { google.maps.event.trigger(map, 'resize'); map.setCenter(location); } ); } }, linkAutocompleteField: function (autocomplete, autocompleteElement, map, options) { if( autocomplete && autocompleteElement ) { var updateMapLocation = function ( address ) { if ( this.inputAddress !== address ) { this.inputAddress = address; this.getLocation( this.inputAddress ).done( function ( location ) { map.setZoom( 15 ); map.setCenter( location ); if( this.centerMarker ) { this.centerMarker.setPosition( location ); this.centerMarker.setTitle( this.inputAddress ); } }.bind( this ) ); } }.bind( this ); var $autocompleteElement = $( autocompleteElement ); autocomplete.addListener( 'place_changed', function () { var place = autocomplete.getPlace(); map.setZoom( 15 ); if ( place.geometry ) { map.setCenter( place.geometry.location ); if( this.centerMarker ) { this.centerMarker.setPosition(place.geometry.location); } } }.bind( this ) ); google.maps.event.addDomListener( autocompleteElement, 'keypress', function ( event ) { var key = event.keyCode || event.which; if ( key === '13' ) { event.preventDefault(); } } ); $autocompleteElement.focusin( function () { if ( !this.resultsObserver ) { var autocompleteResultsContainer = document.querySelector( '.pac-container' ); this.resultsObserver = new MutationObserver( function () { var $topResult = $( $( '.pac-item' ).get( 0 ) ); var queryPartA = $topResult.find( '.pac-item-query' ).text(); var queryPartB = $topResult.find( 'span' ).not( '[class]' ).text(); var topQuery = queryPartA + ( queryPartB ? (', ' + queryPartB) : '' ); if ( topQuery ) { updateMapLocation( topQuery ); } } ); var config = { attributes: true, childList: true, characterData: true }; this.resultsObserver.observe( autocompleteResultsContainer, config ); } }.bind( this ) ); var revGeocode = function ( latLng ) { this.getGeocoder().geocode( { location: latLng }, function ( results, status ) { if ( status === google.maps.GeocoderStatus.OK ) { if ( results.length > 0 ) { var addr = results[ 0 ].formatted_address; $autocompleteElement.val( addr ); if( this.centerMarker ) { this.centerMarker.setPosition(latLng); this.centerMarker.setTitle(addr); } } } }.bind( this ) ); }.bind( this ); map.addListener( 'click', function ( event ) { revGeocode( event.latLng ); } ); this.centerMarker.addListener( 'dragend', function ( event ) { revGeocode( event.latLng ); } ); } }, showMarkers: function(markerPositions, map, options) { if ( markerPositions && markerPositions.length ) { this.infoWindows = []; var markerBatches = []; var BATCH_SIZE = 10; // Group markers into batches of 10 in attempt to avoid query limits for ( var i = 0; i < markerPositions.length; i++ ) { var batchIndex = parseInt( i / BATCH_SIZE ); // truncate decimals if ( markerBatches.length === batchIndex ) { markerBatches[ batchIndex ] = []; } markerBatches[ batchIndex ][ i % BATCH_SIZE ] = markerPositions[ i ]; } var geocodeMarkerBatch = function ( markerBatchHead, markerBatchTail ) { var doneCount = 0; markerBatchHead.forEach( function ( mrkr ) { this.getLocation( mrkr.place ).done( function ( location ) { var mrkerIcon = options.markerIcon; if(mrkr.custom_marker_icon) { mrkerIcon = mrkr.custom_marker_icon; } var marker = new google.maps.Marker( { position: location, map: map, draggable: options.markersDraggable, icon: mrkerIcon, title: '' } ); if ( mrkr.hasOwnProperty( 'info' ) && mrkr.info ) { var infoWindowOptions = { content: mrkr.info }; if ( mrkr.hasOwnProperty( 'info_max_width' ) && mrkr.info_max_width ) { infoWindowOptions.maxWidth = mrkr.info_max_width; } var infoDisplay = options.markerInfoDisplay; infoWindowOptions.disableAutoPan = infoDisplay === 'always'; var infoWindow = new google.maps.InfoWindow( infoWindowOptions ); this.infoWindows.push( infoWindow ); var openEvent = infoDisplay; if ( infoDisplay === 'always' ) { openEvent = 'click'; infoWindow.open( map, marker ); } marker.addListener( openEvent, function () { infoWindow.open( map, marker ); if ( infoDisplay !== 'always' && !options.markerInfoMultiple ) { this.infoWindows.forEach( function ( iw ) { if ( iw !== infoWindow ) { iw.close(); } } ); } }.bind( this ) ); if ( infoDisplay === 'mouseover' ) { marker.addListener( 'mouseout', function () { setTimeout( function () { infoWindow.close(); }, 100 ); } ); } } if ( ++doneCount === markerBatchHead.length && markerBatchTail.length ) { geocodeMarkerBatch( markerBatchTail.shift(), markerBatchTail ); } }.bind( this ) ); }.bind( this ) ); }.bind( this ); geocodeMarkerBatch( markerBatches.shift(), markerBatches ); } }, showDirections: function(directions, map) { if ( directions ) { if ( directions.waypoints && directions.waypoints.length ) { directions.waypoints.map( function (wypt) { wypt.stopover = Boolean(wypt.stopover); } ); } var directionsRenderer = new google.maps.DirectionsRenderer(); directionsRenderer.setMap(map); var directionsService = new google.maps.DirectionsService(); directionsService.route({ origin: directions.origin, destination: directions.destination, travelMode: directions.travelMode.toUpperCase(), avoidHighways: directions.avoidHighways, avoidTolls: directions.avoidTolls, waypoints: directions.waypoints, optimizeWaypoints: directions.optimizeWaypoints, }, function(result, status) { if (status === google.maps.DirectionsStatus.OK) { directionsRenderer.setOptions( { preserveViewport: directions.preserveViewport } ); directionsRenderer.setDirections(result); } }); } }, initMaps: function() { // Init any autocomplete fields first. var $autoCompleteFields = $( '.sow-google-map-autocomplete' ); var autoCompleteInit = new $.Deferred(); if( $autoCompleteFields.length === 0 ) { autoCompleteInit.resolve(); } else { $autoCompleteFields.each(function (index, element) { if ( typeof google.maps.places === 'undefined' ) { autoCompleteInit.reject('Sorry, we couldn\'t load the "places" library due to another plugin, so the autocomplete feature is not available.'); return; } var autocomplete = new google.maps.places.Autocomplete( element, {types: ['address']} ); var $mapField = $(element).siblings('.sow-google-map-canvas'); if ($mapField.length > 0) { var options = $mapField.data('options'); options.autocomplete = autocomplete; options.autocompleteElement = element; this.getLocation(options.address).done( function (location) { this.showMap($mapField.get(0), location, options); $mapField.data('initialized', true); autoCompleteInit.resolve(); }.bind(this) ).fail(function () { $mapField.append('
' + soWidgetsGoogleMap.geocode.noResults + '
' + soWidgetsGoogleMap.geocode.noResults + '