553 lines
12 KiB
JavaScript
553 lines
12 KiB
JavaScript
(function($, undefined){
|
|
|
|
var Field = acf.Field.extend({
|
|
|
|
type: 'google_map',
|
|
|
|
map: false,
|
|
|
|
wait: 'load',
|
|
|
|
events: {
|
|
'click a[data-name="clear"]': 'onClickClear',
|
|
'click a[data-name="locate"]': 'onClickLocate',
|
|
'click a[data-name="search"]': 'onClickSearch',
|
|
'keydown .search': 'onKeydownSearch',
|
|
'keyup .search': 'onKeyupSearch',
|
|
'focus .search': 'onFocusSearch',
|
|
'blur .search': 'onBlurSearch',
|
|
'showField': 'onShow'
|
|
},
|
|
|
|
$control: function(){
|
|
return this.$('.acf-google-map');
|
|
},
|
|
|
|
$input: function( name ){
|
|
return this.$('input[data-name="' + (name || 'address') + '"]');
|
|
},
|
|
|
|
$search: function(){
|
|
return this.$('.search');
|
|
},
|
|
|
|
$canvas: function(){
|
|
return this.$('.canvas');
|
|
},
|
|
|
|
addClass: function( name ){
|
|
this.$control().addClass( name );
|
|
},
|
|
|
|
removeClass: function( name ){
|
|
this.$control().removeClass( name );
|
|
},
|
|
|
|
getValue: function(){
|
|
|
|
// defaults
|
|
var val = {
|
|
lat: '',
|
|
lng: '',
|
|
address: ''
|
|
};
|
|
|
|
// loop
|
|
this.$('input[type="hidden"]').each(function(){
|
|
val[ $(this).data('name') ] = $(this).val();
|
|
});
|
|
|
|
// return false if no lat/lng
|
|
if( !val.lat || !val.lng ) {
|
|
val = false;
|
|
}
|
|
|
|
// return
|
|
return val;
|
|
},
|
|
|
|
setValue: function( val ){
|
|
|
|
// defaults
|
|
val = acf.parseArgs(val, {
|
|
lat: '',
|
|
lng: '',
|
|
address: ''
|
|
});
|
|
|
|
// loop
|
|
for( var name in val ) {
|
|
acf.val( this.$input(name), val[name] );
|
|
}
|
|
|
|
// return false if no lat/lng
|
|
if( !val.lat || !val.lng ) {
|
|
val = false;
|
|
}
|
|
|
|
// render
|
|
this.renderVal( val );
|
|
|
|
// action
|
|
var latLng = this.newLatLng( val.lat, val.lng );
|
|
acf.doAction('google_map_change', latLng, this.map, this);
|
|
},
|
|
|
|
renderVal: function( val ){
|
|
|
|
// has value
|
|
if( val ) {
|
|
this.addClass('-value');
|
|
this.setPosition( val.lat, val.lng );
|
|
this.map.marker.setVisible( true );
|
|
|
|
// no value
|
|
} else {
|
|
this.removeClass('-value');
|
|
this.map.marker.setVisible( false );
|
|
}
|
|
|
|
// search
|
|
this.$search().val( val.address );
|
|
},
|
|
|
|
setPosition: function( lat, lng ){
|
|
|
|
// vars
|
|
var latLng = this.newLatLng( lat, lng );
|
|
|
|
// update marker
|
|
this.map.marker.setPosition( latLng );
|
|
|
|
// show marker
|
|
this.map.marker.setVisible( true );
|
|
|
|
// center
|
|
this.center();
|
|
|
|
// return
|
|
return this;
|
|
},
|
|
|
|
center: function(){
|
|
|
|
// vars
|
|
var position = this.map.marker.getPosition();
|
|
var lat = this.get('lat');
|
|
var lng = this.get('lng');
|
|
|
|
// if marker exists, center on the marker
|
|
if( position ) {
|
|
lat = position.lat();
|
|
lng = position.lng();
|
|
}
|
|
|
|
// latlng
|
|
var latLng = this.newLatLng( lat, lng );
|
|
|
|
// set center of map
|
|
this.map.setCenter( latLng );
|
|
},
|
|
|
|
getSearchVal: function(){
|
|
return this.$search().val();
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// Ensure Google API is loaded and then initialize map.
|
|
withAPI( this.initializeMap.bind(this) );
|
|
},
|
|
|
|
newLatLng: function( lat, lng ){
|
|
return new google.maps.LatLng( parseFloat(lat), parseFloat(lng) );
|
|
},
|
|
|
|
initializeMap: function(){
|
|
|
|
// vars
|
|
var zoom = this.get('zoom');
|
|
var lat = this.get('lat');
|
|
var lng = this.get('lng');
|
|
|
|
// Create Map.
|
|
var mapArgs = {
|
|
scrollwheel: false,
|
|
zoom: parseInt( zoom ),
|
|
center: this.newLatLng(lat, lng),
|
|
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
|
marker: {
|
|
draggable: true,
|
|
raiseOnDrag: true
|
|
},
|
|
autocomplete: {}
|
|
};
|
|
mapArgs = acf.applyFilters('google_map_args', mapArgs, this);
|
|
var map = new google.maps.Map( this.$canvas()[0], mapArgs );
|
|
|
|
// Create Marker.
|
|
var markerArgs = acf.parseArgs(mapArgs.marker, {
|
|
draggable: true,
|
|
raiseOnDrag: true,
|
|
map: map
|
|
});
|
|
markerArgs = acf.applyFilters('google_map_marker_args', markerArgs, this);
|
|
var marker = new google.maps.Marker( markerArgs );
|
|
|
|
// Maybe Create Autocomplete.
|
|
var autocomplete = false;
|
|
if( acf.isset(google, 'maps', 'places', 'Autocomplete') ) {
|
|
var autocompleteArgs = mapArgs.autocomplete || {};
|
|
autocompleteArgs = acf.applyFilters('google_map_autocomplete_args', autocompleteArgs, this);
|
|
autocomplete = new google.maps.places.Autocomplete( this.$search()[0], autocompleteArgs );
|
|
autocomplete.bindTo('bounds', map);
|
|
}
|
|
|
|
// Add map events.
|
|
this.addMapEvents( this, map, marker, autocomplete );
|
|
|
|
// Append references.
|
|
map.acf = this;
|
|
map.marker = marker;
|
|
map.autocomplete = autocomplete;
|
|
this.map = map;
|
|
|
|
// action for 3rd party customization
|
|
acf.doAction('google_map_init', map, marker, this);
|
|
|
|
// set position
|
|
var val = this.getValue();
|
|
this.renderVal( val );
|
|
},
|
|
|
|
addMapEvents: function( field, map, marker, autocomplete ){
|
|
|
|
// Click map.
|
|
google.maps.event.addListener( map, 'click', function( e ) {
|
|
|
|
// vars
|
|
var lat = e.latLng.lat();
|
|
var lng = e.latLng.lng();
|
|
|
|
// search
|
|
field.searchPosition( lat, lng );
|
|
});
|
|
|
|
// Drag marker.
|
|
google.maps.event.addListener( marker, 'dragend', function(){
|
|
|
|
// vars
|
|
var position = this.getPosition();
|
|
var lat = position.lat();
|
|
var lng = position.lng();
|
|
|
|
// search
|
|
field.searchPosition( lat, lng );
|
|
});
|
|
|
|
// Autocomplete search.
|
|
if( autocomplete ) {
|
|
|
|
// autocomplete event place_changed is triggered each time the input changes
|
|
// customize the place object with the current "search value" to allow users controll over the address text
|
|
google.maps.event.addListener(autocomplete, 'place_changed', function() {
|
|
var place = this.getPlace();
|
|
place.address = field.getSearchVal();
|
|
field.setPlace( place );
|
|
});
|
|
}
|
|
},
|
|
|
|
searchPosition: function( lat, lng ){
|
|
|
|
// vars
|
|
var latLng = this.newLatLng( lat, lng );
|
|
var $wrap = this.$control();
|
|
|
|
// set position
|
|
this.setPosition( lat, lng );
|
|
|
|
// add class
|
|
$wrap.addClass('-loading');
|
|
|
|
// callback
|
|
var callback = $.proxy(function( results, status ){
|
|
|
|
// remove class
|
|
$wrap.removeClass('-loading');
|
|
|
|
// vars
|
|
var address = '';
|
|
|
|
// validate
|
|
if( status != google.maps.GeocoderStatus.OK ) {
|
|
console.log('Geocoder failed due to: ' + status);
|
|
} else if( !results[0] ) {
|
|
console.log('No results found');
|
|
} else {
|
|
address = results[0].formatted_address;
|
|
}
|
|
|
|
// update val
|
|
this.val({
|
|
lat: lat,
|
|
lng: lng,
|
|
address: address
|
|
});
|
|
|
|
}, this);
|
|
|
|
// query
|
|
geocoder.geocode({ 'latLng' : latLng }, callback);
|
|
},
|
|
|
|
setPlace: function( place ){
|
|
|
|
// bail if no place
|
|
if( !place ) return this;
|
|
|
|
// search name if no geometry
|
|
// - possible when hitting enter in search address
|
|
if( place.name && !place.geometry ) {
|
|
this.searchAddress(place.name);
|
|
return this;
|
|
}
|
|
|
|
// vars
|
|
var lat = place.geometry.location.lat();
|
|
var lng = place.geometry.location.lng();
|
|
var address = place.address || place.formatted_address;
|
|
|
|
// update
|
|
this.setValue({
|
|
lat: lat,
|
|
lng: lng,
|
|
address: address
|
|
});
|
|
|
|
// return
|
|
return this;
|
|
},
|
|
|
|
searchAddress: function( address ){
|
|
|
|
// is address latLng?
|
|
var latLng = address.split(',');
|
|
if( latLng.length == 2 ) {
|
|
|
|
// vars
|
|
var lat = latLng[0];
|
|
var lng = latLng[1];
|
|
|
|
// check
|
|
if( $.isNumeric(lat) && $.isNumeric(lng) ) {
|
|
return this.searchPosition( lat, lng );
|
|
}
|
|
}
|
|
|
|
// vars
|
|
var $wrap = this.$control();
|
|
|
|
// add class
|
|
$wrap.addClass('-loading');
|
|
|
|
// callback
|
|
var callback = this.proxy(function( results, status ){
|
|
|
|
// remove class
|
|
$wrap.removeClass('-loading');
|
|
|
|
// vars
|
|
var lat = '';
|
|
var lng = '';
|
|
|
|
// validate
|
|
if( status != google.maps.GeocoderStatus.OK ) {
|
|
console.log('Geocoder failed due to: ' + status);
|
|
} else if( !results[0] ) {
|
|
console.log('No results found');
|
|
} else {
|
|
lat = results[0].geometry.location.lat();
|
|
lng = results[0].geometry.location.lng();
|
|
//address = results[0].formatted_address;
|
|
}
|
|
|
|
// update val
|
|
this.val({
|
|
lat: lat,
|
|
lng: lng,
|
|
address: address
|
|
});
|
|
|
|
//acf.doAction('google_map_geocode_results', results, status, this.$el, this);
|
|
|
|
});
|
|
|
|
// query
|
|
geocoder.geocode({ 'address' : address }, callback);
|
|
},
|
|
|
|
searchLocation: function(){
|
|
|
|
// Try HTML5 geolocation
|
|
if( !navigator.geolocation ) {
|
|
return alert( acf.__('Sorry, this browser does not support geolocation') );
|
|
}
|
|
|
|
// vars
|
|
var $wrap = this.$control();
|
|
|
|
// add class
|
|
$wrap.addClass('-loading');
|
|
|
|
// callback
|
|
var onSuccess = $.proxy(function( results, status ){
|
|
|
|
// remove class
|
|
$wrap.removeClass('-loading');
|
|
|
|
// vars
|
|
var lat = results.coords.latitude;
|
|
var lng = results.coords.longitude;
|
|
|
|
// search;
|
|
this.searchPosition( lat, lng );
|
|
|
|
}, this);
|
|
|
|
var onFailure = function( error ){
|
|
$wrap.removeClass('-loading');
|
|
}
|
|
|
|
// try query
|
|
navigator.geolocation.getCurrentPosition( onSuccess, onFailure );
|
|
},
|
|
|
|
onClickClear: function( e, $el ){
|
|
this.val( false );
|
|
},
|
|
|
|
onClickLocate: function( e, $el ){
|
|
this.searchLocation();
|
|
},
|
|
|
|
onClickSearch: function( e, $el ){
|
|
this.searchAddress( this.$search().val() );
|
|
},
|
|
|
|
onFocusSearch: function( e, $el ){
|
|
this.removeClass('-value');
|
|
this.onKeyupSearch.apply(this, arguments);
|
|
},
|
|
|
|
onBlurSearch: function( e, $el ){
|
|
|
|
// timeout to allow onClickLocate event
|
|
this.setTimeout(function(){
|
|
this.removeClass('-search');
|
|
if( $el.val() ) {
|
|
this.addClass('-value');
|
|
}
|
|
}, 100);
|
|
},
|
|
|
|
onKeyupSearch: function( e, $el ){
|
|
if( $el.val() ) {
|
|
this.addClass('-search');
|
|
} else {
|
|
this.removeClass('-search');
|
|
}
|
|
},
|
|
|
|
onKeydownSearch: function( e, $el ){
|
|
|
|
// prevent form from submitting
|
|
if( e.which == 13 ) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
|
|
onMousedown: function(){
|
|
|
|
/*
|
|
// clear timeout in 1ms (onMousedown will run before onBlurSearch)
|
|
this.setTimeout(function(){
|
|
clearTimeout( this.get('timeout') );
|
|
}, 1);
|
|
*/
|
|
},
|
|
|
|
onShow: function(){
|
|
|
|
// bail early if no map
|
|
// - possible if JS API was not loaded
|
|
if( !this.map ) {
|
|
return false;
|
|
}
|
|
|
|
// center map when it is shown (by a tab / collapsed row)
|
|
// - use delay to avoid rendering issues with browsers (ensures div is visible)
|
|
this.setTimeout( this.center, 10 );
|
|
}
|
|
});
|
|
|
|
acf.registerFieldType( Field );
|
|
|
|
// Vars.
|
|
var loading = false;
|
|
var geocoder = false;
|
|
|
|
/**
|
|
* withAPI
|
|
*
|
|
* Loads the Google Maps API library and troggers callback.
|
|
*
|
|
* @date 28/3/19
|
|
* @since 5.7.14
|
|
*
|
|
* @param function callback The callback to excecute.
|
|
* @return void
|
|
*/
|
|
|
|
function withAPI( callback ) {
|
|
|
|
// Check if geocoder exists.
|
|
if( geocoder ) {
|
|
return callback();
|
|
}
|
|
|
|
// Check if geocoder API exists.
|
|
if( acf.isset(window, 'google', 'maps', 'Geocoder') ) {
|
|
geocoder = new google.maps.Geocoder();
|
|
return callback();
|
|
}
|
|
|
|
// Geocoder will need to be loaded. Hook callback to action.
|
|
acf.addAction( 'google_map_api_loaded', callback );
|
|
|
|
// Bail early if already loading API.
|
|
if( loading ) {
|
|
return;
|
|
}
|
|
|
|
// load api
|
|
var url = acf.get('google_map_api');
|
|
if( url ) {
|
|
|
|
// Set loading status.
|
|
loading = true;
|
|
|
|
// Load API
|
|
$.ajax({
|
|
url: url,
|
|
dataType: 'script',
|
|
cache: true,
|
|
success: function(){
|
|
geocoder = new google.maps.Geocoder();
|
|
acf.doAction('google_map_api_loaded');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
})(jQuery); |