public function Field($properties = array())
    {
        Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.min.js');
        Requirements::javascript('geoform/javascript/jquery.geocomplete.js');
        if (GoogleMaps::getApiKey()) {
            Requirements::javascript('//maps.googleapis.com/maps/api/js?sensor=false&libraries=places&language=' . i18n::get_tinymce_lang() . '&key=' . GoogleMaps::getApiKey());
        } else {
            Requirements::javascript('//maps.googleapis.com/maps/api/js?sensor=false&libraries=places&language=' . i18n::get_tinymce_lang());
        }
        $name = $this->getName();
        $this->fieldAddress->setPlaceholder(_t('GeoLocationField.ADDRESSPLACEHOLDER', 'Address'));
        // set caption if required
        $js = <<<JS
(function(\$){
    \$(function(){
        \$("#{$name}-Address").geocomplete().bind("geocode:result", function(event, result){
            \$("#{$name}-Latitude").val(result.geometry.location.lat());
            \$("#{$name}-Longditude").val(result.geometry.location.lng());
        });
    });
})(jQuery);
JS;
        Requirements::customScript($js, 'BootstrapGeoLocationField_Js_' . $this->ID());
        $css = <<<CSS
/* make the location suggest dropdown appear above dialog */
.pac-container {
    z-index: 2000 !important;
}
CSS;
        Requirements::customCSS($css, 'BootstrapGeoLocationField_Css_' . $this->ID());
        return $this->fieldLatitude->Field() . $this->fieldLongditude->Field() . '<div class="row">' . '<div class="col-sm-12">' . $this->fieldAddress->Field() . '</div>' . '</div>';
    }
 /**
  * Validates PostCodeLocation against GoogleMaps Serverside
  * 
  * @return String
  */
 public function validate($validator)
 {
     $name = $this->name;
     $postcodeField = $this->fieldPostcode;
     $countryField = $this->fieldCountry;
     $latitudeField = $this->fieldLatitude;
     $longditudeField = $this->fieldLongditude;
     $postcodeField->setValue($_POST[$name]['Postcode']);
     $countryField->setValue($_POST[$name]['Country']);
     $latitudeField->setValue($_POST[$name]['Latitude']);
     $longditudeField->setValue($_POST[$name]['Longditude']);
     // Result was unique
     if ($latitudeField->Value() != '' && is_numeric($latitudeField->Value()) && $longditudeField->Value() != '' && is_numeric($longditudeField->Value())) {
         return true;
     }
     // postcode and country are still placeholders
     if (trim($postcodeField->Value()) == '' || trim($countryField->Value()) == '') {
         $validator->validationError($name, _t('PostCodeLocationField.VALIDATIONJS', 'Please enter an accurate ZIP and City/Country.'), "validation");
         return false;
     }
     if (stristr(trim(_t('PostCodeLocationField.ZIPCODEPLACEHOLDER', 'ZIP/Postcode')), trim($postcodeField->Value())) && stristr(trim(_t('PostCodeLocationField.CITYCOUNTRYPLACEHOLDER', 'City/Country')), trim($countryField->Value()))) {
         $validator->validationError($name, _t('PostCodeLocationField.VALIDATIONJS', 'Please enter an accurate ZIP and City/Country.'), "validation");
         return false;
     }
     // fetch result from google (serverside)
     $myPostcode = stristr(trim(_t('PostCodeLocationField.ZIPCODEPLACEHOLDER', 'ZIP/Postcode')), trim($postcodeField->Value())) ? '' : trim($postcodeField->Value());
     $myCountry = stristr(trim(_t('PostCodeLocationField.CITYCOUNTRYPLACEHOLDER', 'City/Country')), trim($countryField->Value())) ? '' : trim($countryField->Value());
     // Update to v3 API
     $googleUrl = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode($myPostcode . ', ' . $myCountry) . '&language=' . i18n::get_tinymce_lang();
     if (GoogleMaps::getApiKey()) {
         $googleUrl .= '&key=' . GoogleMaps::getApiKey();
     }
     $result = json_decode(file_get_contents($googleUrl), true);
     // if result unique
     if ($result['status'] == 'OK' && count($result['results']) == 1) {
         $latitudeField->setValue($result['results'][0]['geometry']['location']['lat']);
         $longditudeField->setValue($result['results'][0]['geometry']['location']['lng']);
         return true;
     } else {
         $tmpCounter = 0;
         $tmpLocality = null;
         for ($i = 0; $i < count($result['results']); $i++) {
             // check if type is locality political
             if ($result['results'][$i]['types'][0] == 'locality' && $result['results'][$i]['types'][1] == 'political') {
                 $tmpLocality = $i;
                 $tmpCounter++;
             }
         }
         if ($tmpCounter == 1) {
             $latitudeField->setValue($result['results'][$tmpLocality]['geometry']['location']['lat']);
             $longditudeField->setValue($result['results'][$tmpLocality]['geometry']['location']['lng']);
             return true;
         } else {
             // result not unique
             $validator->validationError($name, _t('PostCodeLocationField.VALIDATIONUNIQUEJS', 'ZIP and City/Country are not unique, please specify.'), "validation");
             return false;
         }
     }
 }
    public function Field($properties = array())
    {
        Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.min.js');
        if (GoogleMaps::getApiKey()) {
            Requirements::javascript('//maps.googleapis.com/maps/api/js?sensor=false&libraries=places&language=' . i18n::get_tinymce_lang() . '&key=' . GoogleMaps::getApiKey());
        } else {
            Requirements::javascript('//maps.googleapis.com/maps/api/js?sensor=false&libraries=places&language=' . i18n::get_tinymce_lang());
        }
        $name = $this->getName();
        $this->fieldPostcode->setPlaceholder(_t('PostCodeLocationField.ZIPCODEPLACEHOLDER', 'ZIP/Postcode'));
        $this->fieldCountry->setPlaceholder(_t('PostCodeLocationField.CITYCOUNTRYPLACEHOLDER', 'City/Country'));
        // set caption if required
        $js = <<<JS
jQuery(document).ready(function() {
    // bind PostCodeLocationChanged to Postcode and Country Fields
    jQuery('#{$name}-Postcode').keyup({$name}PostCodeLocationChanged);
    jQuery('#{$name}-Country').keyup({$name}PostCodeLocationChanged);
});

var {$name}PostcodeTypeTimer = null;

// react on typing
function {$name}PostCodeLocationChanged(){
    // check typeTimer and delete
    if({$name}PostcodeTypeTimer){
        clearTimeout({$name}PostcodeTypeTimer);
    }
                        
    // trim Postcode value
    var postcode = jQuery('#{$name}-Postcode').val().replace(/\\s+\$/,"").replace(/^\\s+/,"");
    // trim Country value
    var country = jQuery('#{$name}-Country').val().replace(/\\s+\$/,"").replace(/^\\s+/,"");
    
    // Postcode or Country at least more than 2 digits and not placeholster is stristr of value
    if(postcode.length >= 2 || country.length >= 2){
        {$name}PostcodeTypeTimer = setTimeout('{$name}PostCodeLocationFetch()', 500); // execute googlemaps request after 1/2 second of not typing
    }
}

var {$name}PostcodeGeocoder = null;

// fetch google data and update lat, lng
function {$name}PostCodeLocationFetch(){
    // clear Lat + Lng
    jQuery('#{$name}-Latitude').val('');
    jQuery('#{$name}-Longditude').val('');
    
    // trim Postcode value
    var postcode = jQuery('#{$name}-Postcode').val().replace(/\\s+\$/,"").replace(/^\\s+/,"");
    // trim Country value
    var country = jQuery('#{$name}-Country').val().replace(/\\s+\$/,"").replace(/^\\s+/,"");
    
    // create request
    var Request = {
        address: postcode+', '+country
    };
    
    // create geocoder
    {$name}PostcodeGeocoder = new google.maps.Geocoder();
    {$name}PostcodeGeocoder.geocode(Request, {$name}PostcodeGeocoderCallback);
}

function {$name}PostcodeGeocoderCallback(Response, Status){
    // Status OK
    if(Status == 'OK'){
        if(Response.length == 1){
            jQuery('#{$name}-Latitude').val(Response[0]['geometry']['location'].lat());
            jQuery('#{$name}-Longditude').val(Response[0]['geometry']['location'].lng());
            //alert(\$('#{$name}-Latitude').val()+','+\$('#{$name}-Longditude').val());
        }else{
            // check if there is only one locality, while all others are places of interest
            var id = PostcodeIsSingleLocality(Response);
            if(id != null){
                jQuery('#{$name}-Latitude').val(Response[id]['geometry']['location'].lat());
                jQuery('#{$name}-Longditude').val(Response[id]['geometry']['location'].lng());
            }
        }
    }
}

function PostcodeIsSingleLocality(Response){
    // check if Response has only one locality->Political
    var counter = 0;
    var locality = null;
    for(var i=0; i<Response.length; i++){
        // check if type is locality political
        if(Response[i]['types'][0] == 'locality' && Response[i]['types'][1] == 'political'){
            locality = i;
            counter++;
        }
    }
    
    return (counter == 1) ? locality : null;
}
JS;
        Requirements::customScript($js, 'BootstrapPostCodeLocationField_Css_' . $this->ID());
        return $this->fieldLatitude->Field() . $this->fieldLongditude->Field() . '<div class="row">' . '<div class="col-sm-6">' . $this->fieldPostcode->Field() . '</div>' . '<div class="col-sm-6">' . $this->fieldCountry->Field() . '</div>' . '</div>';
    }
 /**
  * Validates PostCodeLocation against GoogleMaps Serverside
  * 
  * @return String
  */
 public function validate($validator)
 {
     $name = $this->name;
     $addressField = $this->fieldAddress;
     $latitudeField = $this->fieldLatitude;
     $longditudeField = $this->fieldLongditude;
     $addressField->setValue($_POST[$name]['Address']);
     $latitudeField->setValue($_POST[$name]['Latitude']);
     $longditudeField->setValue($_POST[$name]['Longditude']);
     // Result was unique
     if ($latitudeField->Value() != '' && is_numeric($latitudeField->Value()) && $longditudeField->Value() != '' && is_numeric($longditudeField->Value())) {
         return true;
     }
     if (trim($addressField->Value()) == '') {
         if (!$validator->fieldIsRequired($this->name)) {
             return true;
         } else {
             $validator->validationError($name, _t('GeoLocationField.VALIDATION', 'Please enter an accurate address!'), "validation");
             return false;
         }
     }
     // fetch result from google (serverside)
     $myAddress = trim($addressField->Value());
     // Update to v3 API
     $googleUrl = 'https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode($myAddress) . '&language=' . i18n::get_tinymce_lang();
     if (GoogleMaps::getApiKey()) {
         $googleUrl .= '&key=' . GoogleMaps::getApiKey();
     }
     $result = json_decode(file_get_contents($googleUrl), true);
     // if result unique
     if ($result['status'] == 'OK' && count($result['results']) == 1) {
         $latitudeField->setValue($result['results'][0]['geometry']['location']['lat']);
         $longditudeField->setValue($result['results'][0]['geometry']['location']['lng']);
         return true;
     } else {
         $tmpCounter = 0;
         $tmpLocality = null;
         for ($i = 0; $i < count($result['results']); $i++) {
             // check if type is locality political
             if ($result['results'][$i]['types'][0] == 'locality' && $result['results'][$i]['types'][1] == 'political') {
                 $tmpLocality = $i;
                 $tmpCounter++;
             }
         }
         if ($tmpCounter == 1) {
             $latitudeField->setValue($result['results'][$tmpLocality]['geometry']['location']['lat']);
             $longditudeField->setValue($result['results'][$tmpLocality]['geometry']['location']['lng']);
             return true;
         } else {
             // result not unique
             $validator->validationError($name, _t('GeoLocationField.VALIDATIONUNIQUE', 'The address is not unique, please specify.'), "validation");
             return false;
         }
     }
 }