map.js (7758B)
1 ( function ( $, document, window, google, rwmb, i18n ) { 2 'use strict'; 3 4 // Use function construction to store map & DOM elements separately for each instance 5 var MapField = function ( $container ) { 6 this.$container = $container; 7 }; 8 9 // Geocoder service. 10 var geocoder = new google.maps.Geocoder(); 11 12 // Use prototype for better performance 13 MapField.prototype = { 14 // Initialize everything 15 init: function () { 16 this.initDomElements(); 17 this.initMapElements(); 18 19 this.initMarkerPosition(); 20 this.addListeners(); 21 this.autocomplete(); 22 }, 23 24 // Initialize DOM elements 25 initDomElements: function () { 26 this.$canvas = this.$container.find( '.rwmb-map-canvas' ); 27 this.canvas = this.$canvas[0]; 28 this.$coordinate = this.$container.find( '.rwmb-map' ); 29 this.addressField = this.$container.data( 'address-field' ); 30 }, 31 32 // Initialize map elements 33 initMapElements: function () { 34 var defaultLoc = this.$canvas.data( 'default-loc' ), 35 latLng; 36 37 defaultLoc = defaultLoc ? defaultLoc.split( ',' ) : [53.346881, - 6.258860]; 38 latLng = new google.maps.LatLng( defaultLoc[0], defaultLoc[1] ); // Initial position for map 39 40 this.map = new google.maps.Map( this.canvas, { 41 center: latLng, 42 zoom: 14, 43 streetViewControl: 0, 44 mapTypeId: google.maps.MapTypeId.ROADMAP 45 } ); 46 this.marker = new google.maps.Marker( {position: latLng, map: this.map, draggable: true} ); 47 }, 48 49 // Initialize marker position 50 initMarkerPosition: function () { 51 var coordinate = this.$coordinate.val(), 52 location, 53 zoom; 54 55 if ( coordinate ) { 56 location = coordinate.split( ',' ); 57 this.marker.setPosition( new google.maps.LatLng( location[0], location[1] ) ); 58 59 zoom = location.length > 2 ? parseInt( location[2], 10 ) : 14; 60 61 this.map.setCenter( this.marker.position ); 62 this.map.setZoom( zoom ); 63 } else if ( this.addressField ) { 64 this.geocodeAddress( false ); 65 } 66 }, 67 68 // Add event listeners for 'click' & 'drag' 69 addListeners: function () { 70 var that = this; 71 72 /* 73 * Auto change the map when there's change in address fields. 74 * Works only for multiple address fields as single address field has autocomplete functionality. 75 */ 76 if ( this.addressField.split( ',' ).length > 1 ) { 77 var geocodeAddress = that.geocodeAddress.bind( that ); 78 var addressFields = this.addressField.split( ',' ).forEach( function( part ) { 79 var $field = that.findAddressField( part ); 80 if ( null !== $field ) { 81 $field.on( 'change', geocodeAddress ); 82 } 83 } ); 84 } 85 86 google.maps.event.addListener( this.map, 'click', function ( event ) { 87 that.marker.setPosition( event.latLng ); 88 that.updateCoordinate( event.latLng ); 89 } ); 90 91 google.maps.event.addListener( this.map, 'zoom_changed', function ( event ) { 92 that.updateCoordinate( that.marker.getPosition() ); 93 } ); 94 95 google.maps.event.addListener( this.marker, 'drag', function ( event ) { 96 that.updateCoordinate( event.latLng ); 97 } ); 98 99 /** 100 * Custom event to refresh maps when in hidden divs. 101 * @see https://developers.google.com/maps/documentation/javascript/reference ('resize' Event) 102 */ 103 var refresh = that.refresh.bind( this ); 104 $( window ).on( 'rwmb_map_refresh', refresh ); 105 106 // Refresh on meta box hide and show 107 rwmb.$document.on( 'postbox-toggled', refresh ); 108 // Refresh on sorting meta boxes 109 $( '.meta-box-sortables' ).on( 'sortstop', refresh ); 110 }, 111 112 refresh: function () { 113 if ( ! this.map ) { 114 return; 115 } 116 var zoom = this.map.getZoom(), 117 center = this.map.getCenter(); 118 119 google.maps.event.trigger( this.map, 'resize' ); 120 this.map.setZoom( zoom ); 121 this.map.panTo( center ); 122 }, 123 124 // Autocomplete address 125 autocomplete: function () { 126 var that = this, 127 $address = this.getAddressField(); 128 129 if ( null === $address ) { 130 return; 131 } 132 133 // If Meta Box Geo Location installed. Do not run autocomplete. 134 if ( $( '.rwmb-geo-binding' ).length ) { 135 var geocodeAddress = that.geocodeAddress.bind( that ); 136 $address.on( 'selected_address', geocodeAddress ); 137 return false; 138 } 139 140 $address.autocomplete( { 141 source: function ( request, response ) { 142 var options = { 143 'address': request.term, 144 'region': that.$canvas.data( 'region' ) 145 }; 146 geocoder.geocode( options, function ( results ) { 147 if ( ! results.length ) { 148 response( [ { 149 value: '', 150 label: i18n.no_results_string 151 } ] ); 152 return; 153 } 154 response( results.map( function ( item ) { 155 return { 156 label: item.formatted_address, 157 value: item.formatted_address, 158 latitude: item.geometry.location.lat(), 159 longitude: item.geometry.location.lng() 160 }; 161 } ) ); 162 } ); 163 }, 164 select: function ( event, ui ) { 165 var latLng = new google.maps.LatLng( ui.item.latitude, ui.item.longitude ); 166 that.map.setCenter( latLng ); 167 that.marker.setPosition( latLng ); 168 that.updateCoordinate( latLng ); 169 } 170 } ); 171 }, 172 173 // Update coordinate to input field 174 updateCoordinate: function ( latLng ) { 175 var zoom = this.map.getZoom(); 176 this.$coordinate.val( latLng.lat() + ',' + latLng.lng() + ',' + zoom ).trigger( 'change' ); 177 }, 178 179 // Find coordinates by address 180 geocodeAddress: function ( notify ) { 181 var address = this.getAddress(), 182 that = this; 183 if ( ! address ) { 184 return; 185 } 186 187 if ( false !== notify ) { 188 notify = true; 189 } 190 geocoder.geocode( {'address': address}, function ( results, status ) { 191 if ( status !== google.maps.GeocoderStatus.OK ) { 192 if ( notify ) { 193 alert( i18n.no_results_string ); 194 } 195 return; 196 } 197 that.map.setCenter( results[0].geometry.location ); 198 that.marker.setPosition( results[0].geometry.location ); 199 that.updateCoordinate( results[0].geometry.location ); 200 } ); 201 }, 202 203 // Get the address field. 204 getAddressField: function() { 205 // No address field or more than 1 address fields, ignore 206 if ( ! this.addressField || this.addressField.split( ',' ).length > 1 ) { 207 return null; 208 } 209 return this.findAddressField( this.addressField ); 210 }, 211 212 // Get the address value for geocoding. 213 getAddress: function() { 214 var that = this; 215 216 return this.addressField.split( ',' ) 217 .map( function( part ) { 218 part = that.findAddressField( part ); 219 return null === part ? '' : part.val(); 220 } ) 221 .join( ',' ).replace( /\n/g, ',' ).replace( /,,/g, ',' ); 222 }, 223 224 // Find address field based on its name attribute. Auto search inside groups when needed. 225 findAddressField: function( fieldName ) { 226 // Not in a group. 227 var $address = $( 'input[name="' + fieldName + '"]'); 228 if ( $address.length ) { 229 return $address; 230 } 231 232 // If map and address is inside a cloneable group. 233 $address = this.$container.closest( '.rwmb-group-clone' ).find( 'input[name*="[' + fieldName + ']"]' ); 234 if ( $address.length ) { 235 return $address; 236 } 237 238 // If map and address is inside a non-cloneable group. 239 $address = this.$container.closest( '.rwmb-group-wrapper' ).find( 'input[name*="[' + fieldName + ']"]' ); 240 if ( $address.length ) { 241 return $address; 242 } 243 244 return null; 245 } 246 }; 247 248 function createController() { 249 var $this = $( this ), 250 controller = $this.data( 'mapController' ); 251 if ( controller ) { 252 return; 253 } 254 255 controller = new MapField( $this ); 256 controller.init(); 257 $this.data( 'mapController', controller ); 258 } 259 260 function init( e ) { 261 $( e.target ).find( '.rwmb-map-field' ).each( createController ); 262 } 263 264 function restart() { 265 $( '.rwmb-map-field' ).each( createController ); 266 } 267 268 rwmb.$document 269 .on( 'mb_ready', init ) 270 .on( 'clone', '.rwmb-input', restart ); 271 } )( jQuery, document, window, google, rwmb, RWMB_Map );