Files
wordpress-preseed/wp-content/plugins/advanced-custom-fields/assets/build/js/_acf-field-google-map.js
2019-09-11 19:08:46 +02:00

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);