Hotel Raxa - Advanced Booking System Implementation
🏨 Hotel Booking Enhancements: - Implemented Eagle Booking Advanced Pricing add-on - Added Booking.com-style rate management system - Created professional calendar interface for pricing - Integrated deals and discounts functionality 💰 Advanced Pricing Features: - Dynamic pricing models (per room, per person, per adult) - Base rates, adult rates, and child rates management - Length of stay discounts and early bird deals - Mobile rates and secret deals implementation - Seasonal promotions and flash sales 📅 Availability Management: - Real-time availability tracking - Stop sell and restriction controls - Closed to arrival/departure functionality - Minimum/maximum stay requirements - Automatic sold-out management 💳 Payment Integration: - Maintained Redsys payment gateway integration - Seamless integration with existing Eagle Booking - No modifications to core Eagle Booking plugin 🛠️ Technical Implementation: - Custom database tables for advanced pricing - WordPress hooks and filters integration - AJAX-powered admin interface - Data migration from existing Eagle Booking - Professional calendar view for revenue management 📊 Admin Interface: - Booking.com-style management dashboard - Visual rate and availability calendar - Bulk operations for date ranges - Statistics and analytics dashboard - Modal dialogs for quick editing 🔧 Code Quality: - WordPress coding standards compliance - Secure database operations with prepared statements - Proper input validation and sanitization - Error handling and logging - Responsive admin interface 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Redux Google Maps Extension Class
|
||||
*
|
||||
* @package Redux
|
||||
* @author Kevin Provance <kevin.provance@gmail.com>
|
||||
* @class Redux_Extension_Google_Maps
|
||||
* @version 4.4.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'Redux_Extension_Google_Maps' ) ) {
|
||||
|
||||
/**
|
||||
* Class ReduxFramework_extension_google_maps
|
||||
*/
|
||||
class Redux_Extension_Google_Maps extends Redux_Extension_Abstract {
|
||||
|
||||
/**
|
||||
* Extension version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $version = '4.4.0';
|
||||
|
||||
/**
|
||||
* Extension friendly name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $extension_name = 'Google Maps';
|
||||
|
||||
/**
|
||||
* ReduxFramework_extension_google_maps constructor.
|
||||
*
|
||||
* @param ReduxFramework $redux ReduxFramework object.
|
||||
*/
|
||||
public function __construct( $redux ) {
|
||||
parent::__construct( $redux, __FILE__ );
|
||||
|
||||
$this->add_field( 'google_maps' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
<?php
|
||||
/**
|
||||
* Redux Google Maps Field Class
|
||||
*
|
||||
* @package Redux
|
||||
* @author Kevin Provance <kevin.provance@gmail.com>
|
||||
* @class Redux_Google_Maps
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'Redux_Google_Maps' ) ) {
|
||||
|
||||
/**
|
||||
* Main ReduxFramework_google_maps class
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Redux_Google_Maps extends Redux_Field {
|
||||
|
||||
/**
|
||||
* API Key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $api_key = '';
|
||||
|
||||
/**
|
||||
* Get field defaults.
|
||||
*/
|
||||
public function set_defaults() {
|
||||
$field = array(
|
||||
'api_key' => '',
|
||||
'map_version' => 'weekly',
|
||||
);
|
||||
|
||||
$this->field = wp_parse_args( $this->field, $field );
|
||||
|
||||
$this->api_key = null;
|
||||
|
||||
$this->field['api_key'] = $this->field['api_key'] ?? '';
|
||||
|
||||
if ( empty( $this->field['api_key'] ) ) {
|
||||
$redux = get_option( $this->parent->args['opt_name'] );
|
||||
$this->api_key = $redux['google_map_api_key'] ?? '';
|
||||
} else {
|
||||
$this->api_key = $this->field['api_key'];
|
||||
}
|
||||
|
||||
// Necessary, in case the user doesn't fill out a default array.
|
||||
$def_street_number = $this->field['default']['street_number'] ?? '';
|
||||
$def_route = $this->field['default']['route'] ?? '';
|
||||
$def_locality = $this->field['default']['locality'] ?? '';
|
||||
$def_state = $this->field['default']['administrative_area_level_1'] ?? '';
|
||||
$def_postal = $this->field['default']['postal_code'] ?? '';
|
||||
$def_country = $this->field['default']['country'] ?? '';
|
||||
$def_lat = $this->field['default']['latitude'] ?? '';
|
||||
$def_long = $this->field['default']['longitude'] ?? '';
|
||||
$def_marker_info = $this->field['default']['marker_info'] ?? '';
|
||||
$def_zoom = $this->field['default']['zoom'] ?? '';
|
||||
|
||||
$defaults = array(
|
||||
'latitude' => $def_lat,
|
||||
'longitude' => $def_long,
|
||||
'street_number' => $def_street_number,
|
||||
'route' => $def_route,
|
||||
'locality' => $def_locality,
|
||||
'administrative_area_level_1' => $def_state, // <-dickheads at google. lol, srsly...wtf?
|
||||
// level_1 huh? maybe It's for multiple planets one day.
|
||||
'postal_code' => $def_postal,
|
||||
'country' => $def_country,
|
||||
'marker_info' => $def_marker_info,
|
||||
'zoom' => $def_zoom,
|
||||
);
|
||||
|
||||
$this->value = wp_parse_args( $this->value, $defaults );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Field Render Function.
|
||||
* Takes the vars and outputs the HTML for the field in the settings
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function render() {
|
||||
|
||||
// Set default or saved zoom
|
||||
// USA: 39.11676722061108,-100.47761000000003
|
||||
// Zoom far: 3
|
||||
// zoom close 17.
|
||||
if ( empty( $this->value['zoom'] ) ) {
|
||||
if ( $this->value['street_number'] ) {
|
||||
$the_zoom = 17; // make it close if the street is included.
|
||||
} else {
|
||||
$the_zoom = 3; // make it far if it's not.
|
||||
}
|
||||
} else {
|
||||
$the_zoom = $this->value['zoom'];
|
||||
}
|
||||
|
||||
// Make full address.
|
||||
$locality = ! empty( $this->value['locality'] ) ? $this->value['locality'] . ', ' : '';
|
||||
$route = ! empty( $this->value['route'] ) ? $this->value['route'] . ', ' : '';
|
||||
$country = ! empty( $this->value['country'] ) ? ', ' . $this->value['country'] : '';
|
||||
|
||||
$full = $this->value['street_number'] . ' ' . $route . ' ' . $locality . $this->value['administrative_area_level_1'] . ' ' . $this->value['postal_code'] . $country;
|
||||
$data_full = rawurlencode( $full );
|
||||
|
||||
// Hide/show various input fields.
|
||||
$show_address = $this->field['show_address'] ?? true;
|
||||
$show_city = $this->field['show_city'] ?? true;
|
||||
$show_state = $this->field['show_state'] ?? true;
|
||||
$show_postal = $this->field['show_postal'] ?? true;
|
||||
$show_country = $this->field['show_country'] ?? true;
|
||||
$show_lat = $this->field['show_latitude'] ?? true;
|
||||
$show_long = $this->field['show_longitude'] ?? true;
|
||||
$show_marker_info = $this->field['show_marker_info'] ?? true;
|
||||
$show_controls = $this->field['show_controls'] ?? true;
|
||||
|
||||
$this->field['placeholder'] = $this->field['placeholder'] ?? esc_html__( 'Enter your address', 'redux-framework' );
|
||||
$this->field['marker_tooltip'] = $this->field['marker_tooltip'] ?? esc_html__( 'Left mouse down on top of me to move me!', 'redux-framework' );
|
||||
$this->field['no_geometry_alert'] = $this->field['no_geometry_alert'] ?? esc_html__( 'The returned place contains no geometric data.', 'redux-framework' );
|
||||
$this->field['delay_render'] = $this->field['delay_render'] ?? false;
|
||||
$this->field['class'] = $this->field['class'] ?? '';
|
||||
$this->field['show_api_key'] = $this->field['show_api_key'] ?? true;
|
||||
$this->field['street_view_control'] = $this->field['street_view_control'] ?? true;
|
||||
$this->field['map_type_control'] = $this->field['map_type_control'] ?? true;
|
||||
$this->field['scroll_wheel'] = $this->field['scroll_wheel'] ?? false;
|
||||
$this->field['map_height'] = $this->field['map_height'] ?? '';
|
||||
|
||||
$map_height = '';
|
||||
|
||||
if ( ! empty( $this->field['map_height'] ) ) {
|
||||
$map_height = 'style="height:' . esc_attr( $this->field['map_height'] ) . ';"';
|
||||
}
|
||||
|
||||
$geo_alert = rawurlencode( $this->field['no_geometry_alert'] );
|
||||
|
||||
// admin defined.
|
||||
$the_lat = $this->value['latitude'];
|
||||
$the_long = $this->value['longitude'];
|
||||
$marker_tooltip = rawurlencode( $this->field['marker_tooltip'] );
|
||||
|
||||
if ( ! empty( $the_lat ) && ! empty( $the_long ) ) {
|
||||
$full = '';
|
||||
}
|
||||
|
||||
$hidden_style = ' style="display: none!important;" ';
|
||||
?>
|
||||
<div
|
||||
class="redux_framework_google_maps <?php echo esc_attr( $this->field['class'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
data-idx=""
|
||||
data-delay-render="<?php echo esc_attr( $this->field['delay_render'] ); ?>"
|
||||
data-scroll-wheel="<?php echo esc_attr( $this->field['scroll_wheel'] ); ?>"
|
||||
data-street-view="<?php echo esc_attr( $this->field['street_view_control'] ); ?>"
|
||||
data-map-type="<?php echo esc_attr( $this->field['map_type_control'] ); ?>"
|
||||
data-marker-tooltip="<?php echo esc_attr( $marker_tooltip ); ?>"
|
||||
data-geo-alert="<?php echo $geo_alert; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"
|
||||
data-address="<?php echo $data_full; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>">
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
class="google_m_zoom_input"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[zoom]"
|
||||
value="<?php echo esc_attr( $the_zoom ); ?>"
|
||||
/>
|
||||
|
||||
<?php $is_hidden = $show_controls ? '' : 'hidden'; ?>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_autocomplete"></label>
|
||||
<input
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_autocomplete"
|
||||
class="google_m_controls google_m_autocomplete <?php echo esc_attr( $is_hidden ); ?>" type="text"
|
||||
value="<?php echo esc_attr( trim( $full ) ); ?>"
|
||||
placeholder="<?php echo esc_attr( $this->field['placeholder'] ); ?>"/>
|
||||
|
||||
<div
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_type_selector"
|
||||
class="google_m_controls <?php echo esc_attr( $is_hidden ); ?>">
|
||||
|
||||
<input class="noUpdate" type="radio" name="type" id="changetype-all-<?php echo esc_attr( $this->field['id'] ); ?>" checked="checked"/>
|
||||
<label for="changetype-all-<?php echo esc_attr( $this->field['id'] ); ?>"><?php esc_html_e( 'All', 'redux-framework' ); ?></label>
|
||||
|
||||
<input class="noUpdate" type="radio" name="type" id="changetype-establishment-<?php echo esc_attr( $this->field['id'] ); ?>"/>
|
||||
<label for="changetype-establishment-<?php echo esc_attr( $this->field['id'] ); ?>"><?php esc_html_e( 'Place', 'redux-framework' ); ?></label>
|
||||
|
||||
<input class="noUpdate" type="radio" name="type" id="changetype-address-<?php echo esc_attr( $this->field['id'] ); ?>"/>
|
||||
<label for="changetype-address-<?php echo esc_attr( $this->field['id'] ); ?>"><?php esc_html_e( 'Address', 'redux-framework' ); ?></label>
|
||||
|
||||
<input class="noUpdate" type="radio" name="type" id="changetype-geocode-<?php echo esc_attr( $this->field['id'] ); ?>"/>
|
||||
<label for="changetype-geocode-<?php echo esc_attr( $this->field['id'] ); ?>"><?php esc_html_e( 'Geo', 'redux-framework' ); ?></label>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_map_canvas"
|
||||
class="google_m_canvas"
|
||||
data-default-long="<?php echo esc_attr( $the_long ); ?>"
|
||||
data-default-lat="<?php echo esc_attr( $the_lat ); ?>"
|
||||
data-default-zoom="<?php echo esc_attr( $the_zoom ); ?>"
|
||||
<?php echo $map_height; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
</div>
|
||||
|
||||
<div class="google_maps_address_results">
|
||||
<?php $is_hidden = $show_address ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper street-address" <?php echo $is_hidden; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_street_number"><?php esc_html_e( 'Address', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_street_number"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[street_number]"
|
||||
value="<?php echo esc_attr( $this->value['street_number'] ); ?>"
|
||||
class="slimField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['street_number'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div class="input_wrapper route" <?php echo $is_hidden; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_route"><?php esc_html_e( 'Street', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_route"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[route]"
|
||||
value="<?php echo esc_attr( $this->value['route'] ); ?>"
|
||||
class="wideField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['route'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_city ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper city" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_locality"><?php esc_html_e( 'City', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_locality"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[locality]"
|
||||
value="<?php echo esc_attr( $this->value['locality'] ); ?>"
|
||||
class="wideField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['locality'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_state ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper state" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_administrative_area_level_1"><?php esc_html_e( 'State', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_administrative_area_level_1"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[administrative_area_level_1]"
|
||||
value="<?php echo esc_attr( $this->value['administrative_area_level_1'] ); ?>"
|
||||
class="slimField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['administrative_area_level_1'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_postal ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper zip-code" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_postal_code"><?php esc_html_e( 'ZIP Code', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_postal_code"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[postal_code]"
|
||||
value="<?php echo esc_attr( $this->value['postal_code'] ); ?>"
|
||||
class="slimField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['postal_code'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_country ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper country" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_country"><?php esc_html_e( 'Country', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_country"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[country]"
|
||||
value="<?php echo esc_attr( $this->value['country'] ); ?>"
|
||||
class="wideField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['country'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_lat ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper latitude" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_latitude"><?php esc_html_e( 'Latitude', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_latitude"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[latitude]"
|
||||
value="<?php echo esc_attr( $this->value['latitude'] ); ?>"
|
||||
class="wideField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['latitude'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_long ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper longitude" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_longitude"><?php esc_html_e( 'Longitude', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_longitude"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[longitude]"
|
||||
value="<?php echo esc_attr( $this->value['longitude'] ); ?>"
|
||||
class="wideField field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['longitude'] ); ?>"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<?php $is_hidden = $show_marker_info ? '' : $hidden_style; ?>
|
||||
<div class="input_wrapper marker-info" <?php echo( $is_hidden ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
|
||||
<label for="<?php echo esc_attr( $this->field['id'] ); ?>_marker_info"><?php esc_html_e( 'Marker Info', 'redux-framework' ); ?></label>
|
||||
<textarea
|
||||
data-id="<?php echo esc_attr( $this->field['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $this->field['id'] ); ?>_marker_info"
|
||||
name="<?php echo esc_attr( $this->field['name'] . $this->field['name_suffix'] ); ?>[marker_info]"
|
||||
class="field"
|
||||
data-default-value="<?php echo esc_attr( $this->value['marker_info'] ); ?>"
|
||||
rows="3"
|
||||
type="text"><?php echo esc_textarea( $this->value['marker_info'] ); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ( $this->field['show_api_key'] ) { ?>
|
||||
<div class="input google_m_api_key">
|
||||
<a href="javascript:void(0);" class="button button-secondary google_m_api_key_button"><?php esc_html_e( 'Google Map API Key', 'redux-framework' ); ?></a>
|
||||
<div class="google_m_api_key_wrapper">
|
||||
<p class="description" id="google_m_api_key_description">
|
||||
<?php
|
||||
$api_key_site = ' ' . sprintf( '<a href="https://console.developers.google.com/flows/enableapi?apiid=maps_backend&keyType=CLIENT_SIDE&reusekey=true" target="_blank">%s</a>', esc_html__( 'Get an API Key', 'redux-framework' ) ) . ' ';
|
||||
$usage_limit_site = ' ' . sprintf( '<a href="https://developers.google.com/maps/documentation/javascript/usage" target="_blank">%s</a>', esc_html__( 'Google Map Usage Limits', 'redux-framework' ) ) . ' ';
|
||||
|
||||
// translators: %1$s: Google Maps API Key url. %2$s: Google Maps Usage URL.
|
||||
echo sprintf( esc_html__( 'Google Maps supports 25,000 free map loads per 24 hours for 90 consecutive days. In the events you run a high volume site, you may need to obtain an API Key to continue using Google Map output beyond the free quota. To sign up for an API Key, please visit the %1$s site. For more information about Google Map usage limits, please visit the %2$s guide.', 'redux-framework' ), $api_key_site, $usage_limit_site ) . '<br><br>' . esc_html__( 'Once you have obtained an API Key, please enter it in the text box below and save the options panel.', 'redux-framework' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
?>
|
||||
</p>
|
||||
<label for="google_m_api_key_input"><?php esc_html_e( 'API Key', 'redux-framework' ); ?></label>
|
||||
<input
|
||||
type="text"
|
||||
value="<?php echo esc_attr( $this->api_key ); ?>"
|
||||
name="<?php echo esc_attr( $this->parent->args['opt_name'] ); ?>[google_map_api_key]"
|
||||
id="google_m_api_key_input" class="large-text"/>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Function.
|
||||
* If this field requires any scripts, or css define this function and register/enqueue the scripts/css
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
$min = Redux_Functions::isMin();
|
||||
|
||||
$api_key = '';
|
||||
if ( ! empty( $this->api_key ) ) {
|
||||
$api_key = $this->api_key;
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
'redux-field-google-maps',
|
||||
$this->url . 'redux-google-maps' . $min . '.js',
|
||||
array( 'jquery', 'redux-js' ),
|
||||
Redux_Extension_Google_Maps::$version,
|
||||
true
|
||||
);
|
||||
|
||||
if ( ! wp_script_is( 'redux-field-google-maps' ) ) {
|
||||
$script = '(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
|
||||
key:"' . $api_key . '",
|
||||
v:"' . $this->field['map_version'] . '",
|
||||
libraries:"places",
|
||||
callback:"initMap"
|
||||
});';
|
||||
|
||||
wp_add_inline_script( 'redux-field-google-maps', $script );
|
||||
}
|
||||
|
||||
wp_enqueue_script( 'redux-field-google-maps' );
|
||||
|
||||
if ( $this->parent->args['dev_mode'] ) {
|
||||
wp_enqueue_style(
|
||||
'redux-field-google-maps',
|
||||
$this->url . 'redux-google-maps.css',
|
||||
array(),
|
||||
Redux_Extension_Google_Maps::$version
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* Silence is golden.
|
||||
*
|
||||
* @package Redux Framework
|
||||
*/
|
||||
|
||||
echo null;
|
||||
@@ -0,0 +1,45 @@
|
||||
.redux-main .redux-container-google_maps .google_m_api_key { padding-bottom: 10px; clear: left; padding-top: 20px; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper { display: none; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper label { display: block !important; position: relative; font-size: 12px !important; text-align: left; color: #999999; margin: 4px 0 2px 0 !important; cursor: default; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper .description { margin-bottom: 7px; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results { padding-top: 20px; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results .latitude { clear: left; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results .marker-info { clear: left !important; height: 75px !important; width: 100% !important; margin-bottom: 10px !important; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results label { display: block !important; position: relative; font-size: 12px !important; text-align: left; color: #999999; margin: 4px 0 2px 0 !important; cursor: default; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper { display: block; position: relative; float: left; clear: none; margin: 0 10px 0 0; height: 57px; -webkit-box-sizing: border-box; box-sizing: border-box; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper:nth-child(odd) { margin-right: 10px !important; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper:nth-child(even) { margin-right: 10px !important; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_canvas { margin: 0; padding: 0; height: 250px; width: 100%; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_canvas img { max-width: initial !important; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_autocomplete { background-color: #fff; color: #B5B5B5; font-family: Roboto, system-ui; font-size: 15px; font-weight: 300; margin-left: 12px; padding: 0 11px 0 13px; text-overflow: ellipsis; width: 50%; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_m_autocomplete:focus { border-color: #4d90fe; }
|
||||
|
||||
.redux-main .redux-container-google_maps .google_map_address_results { border: 1px solid #CBCBCB; background-color: #EDEDED; width: 100%; padding: 5px; }
|
||||
|
||||
.redux-main .redux-container-google_maps .field { width: 99%; }
|
||||
|
||||
.redux-main .redux-container-google_maps .slimField { width: 80px; }
|
||||
|
||||
.redux-main .redux-container-google_maps .wideField { width: 150px !important; }
|
||||
|
||||
.redux-main .google_m_controls { color: #fff; background-color: #4d90fe; padding: 5px 11px 0 11px; width: 50%; margin-top: 7px; border: 1px solid transparent; border-radius: 2px 0 0 2px; -webkit-box-sizing: border-box; box-sizing: border-box; height: 32px; outline: none; -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
.redux-main .google_m_controls label { margin-left: -5px !important; margin-right: 7px !important; font-size: 12px; }
|
||||
|
||||
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtZ29vZ2xlLW1hcHMuY3NzIiwic291cmNlcyI6WyJyZWR1eC1nb29nbGUtbWFwcy5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLEFBRVEsV0FGRyxDQUNQLDRCQUE0QixDQUN4QixpQkFBaUIsQ0FBQyxFQUNkLGNBQWMsRUFBRSxJQUFJLEVBQ3BCLEtBQUssRUFBRSxJQUFJLEVBQ1gsV0FBVyxFQUFFLElBQUksR0FtQnBCOztBQXhCVCxBQU9ZLFdBUEQsQ0FDUCw0QkFBNEIsQ0FDeEIsaUJBQWlCLENBS2IseUJBQXlCLENBQUMsRUFDdEIsT0FBTyxFQUFFLElBQUksR0FlaEI7O0FBdkJiLEFBVWdCLFdBVkwsQ0FDUCw0QkFBNEIsQ0FDeEIsaUJBQWlCLENBS2IseUJBQXlCLENBR3JCLEtBQUssQ0FBQyxFQUNGLE9BQU8sRUFBRSxnQkFBZ0IsRUFDekIsUUFBUSxFQUFFLFFBQVEsRUFDbEIsU0FBUyxFQUFFLGVBQWUsRUFDMUIsVUFBVSxFQUFFLElBQUksRUFDaEIsS0FBSyxFQUFFLE9BQU8sRUFDZCxNQUFNLEVBQUUsc0JBQXNCLEVBQzlCLE1BQU0sRUFBRSxPQUFPLEdBQ2xCOztBQWxCakIsQUFvQmdCLFdBcEJMLENBQ1AsNEJBQTRCLENBQ3hCLGlCQUFpQixDQUtiLHlCQUF5QixDQWFyQixZQUFZLENBQUMsRUFDVCxhQUFhLEVBQUUsR0FBRyxHQUNyQjs7QUF0QmpCLEFBMEJRLFdBMUJHLENBQ1AsNEJBQTRCLENBeUJ4Qiw0QkFBNEIsQ0FBQyxFQUN6QixXQUFXLEVBQUUsSUFBSSxHQXdDcEI7O0FBbkVULEFBNkJZLFdBN0JELENBQ1AsNEJBQTRCLENBeUJ4Qiw0QkFBNEIsQ0FHeEIsU0FBUyxDQUFDLEVBQ04sS0FBSyxFQUFFLElBQUksR0FDZDs7QUEvQmIsQUFpQ1ksV0FqQ0QsQ0FDUCw0QkFBNEIsQ0F5QnhCLDRCQUE0QixDQU94QixZQUFZLENBQUMsRUFDVCxLQUFLLEVBQUUsSUFBSSxDQUFBLFVBQVUsRUFDckIsTUFBTSxFQUFF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 */
|
||||
|
||||
/*# sourceMappingURL=redux-google-maps.css.map */
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,550 @@
|
||||
/**
|
||||
* Field Google Map
|
||||
*/
|
||||
|
||||
/* global jQuery, document, redux_change, redux, google */
|
||||
|
||||
(function( $ ) {
|
||||
'use strict';
|
||||
|
||||
var g_map;
|
||||
var g_marker;
|
||||
var g_autoComplete;
|
||||
var g_LatLng;
|
||||
|
||||
redux.field_objects = redux.field_objects || {};
|
||||
redux.field_objects.google_maps = redux.field_objects.google_maps || {};
|
||||
|
||||
/* LIBRARY INIT */
|
||||
redux.field_objects.google_maps.init = function( selector ) {
|
||||
if ( ! selector ) {
|
||||
selector = $( document ).find( '.redux-group-tab:visible' ).find( '.redux-container-google_maps:visible' );
|
||||
}
|
||||
|
||||
$( selector ).each(
|
||||
function( i ) {
|
||||
var containerID;
|
||||
var delayRender;
|
||||
|
||||
var el = $( this );
|
||||
var parent = el;
|
||||
|
||||
if ( ! el.hasClass( 'redux-field-container' ) ) {
|
||||
parent = el.parents( '.redux-field-container:first' );
|
||||
}
|
||||
|
||||
if ( parent.is( ':hidden' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( parent.hasClass( 'redux-field-init' ) ) {
|
||||
parent.removeClass( 'redux-field-init' );
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get container ID.
|
||||
containerID = el.find( '.redux_framework_google_maps' ).attr( 'id' );
|
||||
|
||||
// Check for delay render, which is useful for calling a map
|
||||
// render after javascript load.
|
||||
delayRender = Boolean( el.find( '.redux_framework_google_maps' ).data( 'delay-render' ) );
|
||||
|
||||
// API Key button.
|
||||
redux.field_objects.google_maps.clickHandler( el );
|
||||
|
||||
// Init our maps.
|
||||
redux.field_objects.google_maps.initMap( el, i, containerID, delayRender );
|
||||
|
||||
// Fucking radio button won't check on its own, for some reason.
|
||||
setTimeout(
|
||||
function() {
|
||||
$( '#changetype-all' ).prop( 'checked', true );
|
||||
},
|
||||
1
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* API BUTTON CLICK HANDLER */
|
||||
redux.field_objects.google_maps.clickHandler = function( el ) {
|
||||
|
||||
// Find the API Key button and react on click.
|
||||
el.find( '.google_m_api_key_button' ).on(
|
||||
'click',
|
||||
function() {
|
||||
|
||||
// Find message wrapper.
|
||||
var wrapper = el.find( '.google_m_api_key_wrapper' );
|
||||
|
||||
if ( wrapper.is( ':visible' ) ) {
|
||||
|
||||
// If wrapper is visible, close it.
|
||||
wrapper.slideUp(
|
||||
'fast',
|
||||
function() {
|
||||
el.find( '#google_m_api_key_input' ).trigger( 'focus' );
|
||||
}
|
||||
);
|
||||
} else {
|
||||
|
||||
// If wrapper is visible, open it.
|
||||
wrapper.slideDown(
|
||||
'medium',
|
||||
function() {
|
||||
el.find( '#google_m_api_key_input' ).trigger( 'focus' );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Auto select autocomplete contents,
|
||||
// since Google doesn't do this inherently.
|
||||
el.find( '.google_m_autocomplete' ).on(
|
||||
'click',
|
||||
function( e ) {
|
||||
this.trigger( 'focus' );
|
||||
this.trigger( 'select' );
|
||||
e.preventDefault();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* MAP RENDER FUNCTION */
|
||||
redux.field_objects.google_maps.renderMap = async function( el, mapClass ) {
|
||||
var scrollWheel;
|
||||
var streetView;
|
||||
var mapType;
|
||||
var address;
|
||||
var defLat;
|
||||
var defLong;
|
||||
var defaultZoom;
|
||||
var mapOptions;
|
||||
var geocoder;
|
||||
|
||||
var noLatLng = false;
|
||||
var containerID = el.find( '.redux_framework_google_maps' ).attr( 'id' );
|
||||
|
||||
// Set IDs to variables.
|
||||
var autocomplete = containerID + '_autocomplete';
|
||||
var canvas = containerID + '_map_canvas';
|
||||
var canvasId = $( '#' + canvas );
|
||||
var ac;
|
||||
|
||||
// Create the autocomplete object, restricting the search
|
||||
// to geographical location types.
|
||||
g_autoComplete = await google.maps.importLibrary( 'places' );
|
||||
|
||||
ac = new g_autoComplete.Autocomplete(
|
||||
( document.getElementById( autocomplete ) ),
|
||||
{
|
||||
types: ['geocode']
|
||||
}
|
||||
);
|
||||
|
||||
// Data bindings.
|
||||
scrollWheel = Boolean( mapClass.data( 'scroll-wheel' ) );
|
||||
streetView = Boolean( mapClass.data( 'street-view' ) );
|
||||
mapType = Boolean( mapClass.data( 'map-type' ) );
|
||||
|
||||
address = mapClass.data( 'address' );
|
||||
address = decodeURIComponent( address );
|
||||
address = address.trim();
|
||||
|
||||
// Set default Lat/lng.
|
||||
defLat = canvasId.data( 'default-lat' );
|
||||
defLong = canvasId.data( 'default-long' );
|
||||
defaultZoom = canvasId.data( 'default-zoom' );
|
||||
|
||||
// Eval whether to set maps based on lat/lng or address.
|
||||
if ( '' !== address ) {
|
||||
if ( '' === defLat || '' === defLong ) {
|
||||
noLatLng = true;
|
||||
}
|
||||
} else {
|
||||
noLatLng = false;
|
||||
}
|
||||
|
||||
// Can't have empty values, or the map API will complain.
|
||||
// Set default for middle of the United States.
|
||||
defLat = defLat ? defLat : 39.11676722061108;
|
||||
defLong = defLong ? defLong : - 100.47761000000003;
|
||||
|
||||
if ( noLatLng ) {
|
||||
|
||||
// If displaying map based on an address.
|
||||
geocoder = new google.maps.Geocoder();
|
||||
|
||||
// Set up Geocode and pass address.
|
||||
geocoder.geocode(
|
||||
{ 'address': address },
|
||||
function( results, status ) {
|
||||
var latitude;
|
||||
var longitude;
|
||||
|
||||
// Function results.
|
||||
if ( status === google.maps.GeocoderStatus.OK ) {
|
||||
|
||||
// A good address was passed.
|
||||
g_LatLng = results[0].geometry.location;
|
||||
|
||||
// Set map options.
|
||||
mapOptions = {
|
||||
center: g_LatLng,
|
||||
zoom: defaultZoom,
|
||||
streetViewControl: streetView,
|
||||
mapTypeControl: mapType,
|
||||
scrollwheel: scrollWheel,
|
||||
mapTypeControlOptions: {
|
||||
style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
|
||||
position: google.maps.ControlPosition.LEFT_BOTTOM
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Create map.
|
||||
g_map = new google.maps.Map( document.getElementById( canvas ), mapOptions );
|
||||
|
||||
// Render map controls.
|
||||
redux.field_objects.google_maps.renderControls( el, autocomplete, mapClass );
|
||||
|
||||
// Get and set lat/long data.
|
||||
latitude = el.find( '#' + containerID + '_latitude' );
|
||||
latitude.val( results[0].geometry.location.lat() );
|
||||
|
||||
longitude = el.find( '#' + containerID + '_longitude' );
|
||||
longitude.val( results[0].geometry.location.lng() );
|
||||
} else {
|
||||
|
||||
// No data found, alert the user.
|
||||
alert( 'Geocode was not successful for the following reason: ' + status );
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
|
||||
// If displaying map based on an lat/lng.
|
||||
g_LatLng = new google.maps.LatLng( defLat, defLong );
|
||||
|
||||
// Set map options.
|
||||
mapOptions = {
|
||||
center: g_LatLng,
|
||||
zoom: defaultZoom, // Start off far unless an item is selected, set by php.
|
||||
streetViewControl: streetView,
|
||||
mapTypeControl: mapType,
|
||||
scrollwheel: scrollWheel,
|
||||
mapTypeControlOptions: {
|
||||
style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
|
||||
position: google.maps.ControlPosition.LEFT_BOTTOM
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Create the map.
|
||||
g_map = new google.maps.Map( document.getElementById( canvas ), mapOptions );
|
||||
|
||||
// Render map controls.
|
||||
redux.field_objects.google_maps.renderControls( el, autocomplete, mapClass );
|
||||
}
|
||||
};
|
||||
|
||||
/* INIT MAP FUNCTION */
|
||||
redux.field_objects.google_maps.initMap = function( el, idx, containerID, delayRender ) {
|
||||
var delayed;
|
||||
|
||||
// Pull the map class.
|
||||
var mapClass = el.find( '.redux_framework_google_maps' );
|
||||
|
||||
// Add map index to data attr. Why, say we want to use delay_render,
|
||||
// and want to init the map later on. You'd need the index number in the
|
||||
// event of multiple map instances. This allows one to retrieve it
|
||||
// later.
|
||||
$( mapClass ).attr( 'data-idx', idx );
|
||||
if ( true === delayRender ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map has been rendered, no need to process again.
|
||||
if ( $( '#' + containerID ).hasClass( 'rendered' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If map is set to delay render and has been initiated
|
||||
// from another scrip, add the 'render' class so rendering
|
||||
// does not occur. it messes things up.
|
||||
delayed = Boolean( mapClass.data( 'delay-render' ) );
|
||||
if ( true === delayed ) {
|
||||
mapClass.addClass( 'rendered' );
|
||||
}
|
||||
|
||||
// Render the map.
|
||||
redux.field_objects.google_maps.renderMap( el, mapClass );
|
||||
};
|
||||
|
||||
/* RENDER CONTROLS FUNCTION */
|
||||
redux.field_objects.google_maps.renderControls = function( el, autoComplete, mapClass ) {
|
||||
var markerTooltip;
|
||||
var infoWindow;
|
||||
|
||||
// Set variables.
|
||||
var containerID = el.find( '.redux_framework_google_maps' ).attr( 'id' );
|
||||
var controls = containerID + '_type_selector';
|
||||
|
||||
// Get HTML.
|
||||
var input = document.getElementById( autoComplete );
|
||||
var types = document.getElementById( controls );
|
||||
|
||||
// Set objects into the map.
|
||||
g_map.controls[google.maps.ControlPosition.TOP_LEFT].push( input );
|
||||
g_map.controls[google.maps.ControlPosition.TOP_LEFT].push( types );
|
||||
|
||||
// Bind objects to the map.
|
||||
g_autoComplete = new google.maps.places.Autocomplete( input );
|
||||
g_autoComplete.bindTo( 'bounds', g_map );
|
||||
|
||||
// Get the marker tooltip data.
|
||||
markerTooltip = mapClass.data( 'marker-tooltip' );
|
||||
markerTooltip = decodeURIComponent( markerTooltip );
|
||||
|
||||
// Create infoWindow.
|
||||
infoWindow = new google.maps.InfoWindow();
|
||||
|
||||
// Create marker.
|
||||
g_marker = new google.maps.Marker(
|
||||
{
|
||||
position: g_LatLng,
|
||||
map: g_map,
|
||||
anchorPoint: new google.maps.Point( 0, - 29 ),
|
||||
draggable: true,
|
||||
title: markerTooltip,
|
||||
animation: google.maps.Animation.DROP
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
// Add Event Listeners.
|
||||
redux.field_objects.google_maps.addListeners( el, mapClass, g_marker, infoWindow );
|
||||
};
|
||||
|
||||
/* ADD LISTENERS FUNCTION */
|
||||
redux.field_objects.google_maps.addListeners = function( el, mapClass, marker ) {
|
||||
var infoWindow;
|
||||
|
||||
// Set variables.
|
||||
var containerID = el.find( '.redux_framework_google_maps' ).attr( 'id' );
|
||||
var latitude = containerID + '_latitude';
|
||||
var longitude = containerID + '_longitude';
|
||||
var marker_info = containerID + '_marker_info';
|
||||
var geoAlert = mapClass.data( 'geo-alert' );
|
||||
geoAlert = decodeURIComponent( geoAlert );
|
||||
|
||||
// Place change.
|
||||
google.maps.event.addListener(
|
||||
g_autoComplete,
|
||||
'place_changed',
|
||||
function() {
|
||||
var place;
|
||||
var address;
|
||||
|
||||
infoWindow.close();
|
||||
marker.setVisible( false );
|
||||
|
||||
// Get place data.
|
||||
place = g_autoComplete.getPlace();
|
||||
|
||||
// Display alert if something went wrong.
|
||||
if ( ! place.geometry ) {
|
||||
window.alert( geoAlert );
|
||||
return;
|
||||
}
|
||||
|
||||
// If the place has a geometry, then present it on a map.
|
||||
if ( place.geometry.viewport ) {
|
||||
g_map.fitBounds( place.geometry.viewport );
|
||||
} else {
|
||||
g_map.setCenter( place.geometry.location );
|
||||
g_map.setZoom( 17 ); // Why 17? Because it looks good.
|
||||
}
|
||||
|
||||
// Set the marker icon.
|
||||
marker.setIcon(
|
||||
({
|
||||
url: place.icon,
|
||||
size: new google.maps.Size( 71, 71 ),
|
||||
origin: new google.maps.Point( 0, 0 ),
|
||||
anchor: new google.maps.Point( 17, 34 ),
|
||||
scaledSize: new google.maps.Size( 35, 35 )
|
||||
})
|
||||
);
|
||||
|
||||
// Set marker position and display.
|
||||
marker.setPosition( place.geometry.location );
|
||||
marker.setVisible( true );
|
||||
|
||||
// Form array of address components.
|
||||
address = '';
|
||||
if ( place.address_components ) {
|
||||
address = [( place.address_components[0] && place.address_components[0].short_name || '' ),
|
||||
( place.address_components[1] && place.address_components[1].short_name || '' ),
|
||||
( place.address_components[2] && place.address_components[2].short_name || '' )].join( ' ' );
|
||||
}
|
||||
|
||||
// Set the default marker info window with address data.
|
||||
infoWindow.setContent( '<div><strong>' + place.name + '</strong><br>' + address );
|
||||
infoWindow.open( g_map, marker );
|
||||
|
||||
// Run Geolocation.
|
||||
redux.field_objects.google_maps.geoLocate();
|
||||
|
||||
// Fill in address inputs.
|
||||
redux.field_objects.google_maps.fillInAddress( el, latitude, longitude );
|
||||
}
|
||||
);
|
||||
|
||||
// Search radio buttons.
|
||||
redux.field_objects.google_maps.setupClickListener( 'changetype-all-' + containerID, [] );
|
||||
redux.field_objects.google_maps.setupClickListener( 'changetype-address-' + containerID, ['address'] );
|
||||
redux.field_objects.google_maps.setupClickListener( 'changetype-establishment-' + containerID, ['establishment'] );
|
||||
redux.field_objects.google_maps.setupClickListener( 'changetype-geocode-' + containerID, ['geocode'] );
|
||||
|
||||
// Marker drag.
|
||||
google.maps.event.addListener(
|
||||
marker,
|
||||
'drag',
|
||||
function( event ) {
|
||||
document.getElementById( latitude ).value = event.latLng.lat();
|
||||
document.getElementById( longitude ).value = event.latLng.lng();
|
||||
}
|
||||
);
|
||||
|
||||
// End marker drag.
|
||||
google.maps.event.addListener(
|
||||
marker,
|
||||
'dragend',
|
||||
function() {
|
||||
redux_change( el.find( '.redux_framework_google_maps' ) );
|
||||
}
|
||||
);
|
||||
|
||||
// Zoom Changed.
|
||||
g_map.addListener(
|
||||
'zoom_changed',
|
||||
function() {
|
||||
el.find( '.google_m_zoom_input' ).val( g_map.getZoom() );
|
||||
}
|
||||
);
|
||||
|
||||
// Marker Info Window.
|
||||
infoWindow = new google.maps.InfoWindow();
|
||||
|
||||
google.maps.event.addListener(
|
||||
marker,
|
||||
'click',
|
||||
function() {
|
||||
var infoValue = document.getElementById( marker_info ).value;
|
||||
|
||||
if ( '' !== infoValue ) {
|
||||
infoWindow.setContent( infoValue );
|
||||
infoWindow.open( g_map, g_marker );
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* FILL IN ADDRESS FUNCTION */
|
||||
redux.field_objects.google_maps.fillInAddress = function( el, latitude, longitude ) {
|
||||
|
||||
// Set variables.
|
||||
var containerID = el.find( '.redux_framework_google_maps' ).attr( 'id' );
|
||||
|
||||
// What if someone only wants city, or state, ect...
|
||||
// gotta do it this way to check for the address!
|
||||
// need to check each of the returned components to see what is returned.
|
||||
var componentForm = {
|
||||
street_number: 'short_name',
|
||||
route: 'long_name',
|
||||
locality: 'long_name',
|
||||
administrative_area_level_1: 'short_name',
|
||||
country: 'long_name',
|
||||
postal_code: 'short_name'
|
||||
};
|
||||
|
||||
// Get the place details from the autocomplete object.
|
||||
var place = g_autoComplete.getPlace();
|
||||
|
||||
var component;
|
||||
var i;
|
||||
var addressType;
|
||||
var _d_addressType;
|
||||
var val;
|
||||
var len;
|
||||
|
||||
document.getElementById( latitude ).value = place.geometry.location.lat();
|
||||
document.getElementById( longitude ).value = place.geometry.location.lng();
|
||||
|
||||
for ( component in componentForm ) {
|
||||
if ( componentForm.hasOwnProperty( component ) ) {
|
||||
|
||||
// Push in the dynamic form element ID again.
|
||||
component = containerID + '_' + component;
|
||||
|
||||
// Assign to proper place.
|
||||
document.getElementById( component ).value = '';
|
||||
document.getElementById( component ).disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get each component of the address from the place details
|
||||
// and fill the corresponding field on the form.
|
||||
len = place.address_components.length;
|
||||
|
||||
for ( i = 0; i < len; i += 1 ) {
|
||||
addressType = place.address_components[i].types[0];
|
||||
|
||||
if ( componentForm[addressType] ) {
|
||||
|
||||
// Push in the dynamic form element ID again.
|
||||
_d_addressType = containerID + '_' + addressType;
|
||||
|
||||
// Get the original.
|
||||
val = place.address_components[i][componentForm[addressType]];
|
||||
|
||||
// Assign to proper place.
|
||||
document.getElementById( _d_addressType ).value = val;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
redux.field_objects.google_maps.setupClickListener = function( id, types ) {
|
||||
var radioButton = document.getElementById( id );
|
||||
|
||||
google.maps.event.addListener(
|
||||
radioButton,
|
||||
'click',
|
||||
function() {
|
||||
g_autoComplete.setTypes( types );
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
redux.field_objects.google_maps.geoLocate = function() {
|
||||
if ( navigator.geolocation ) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
function( position ) {
|
||||
var geolocation = new google.maps.LatLng( position.coords.latitude, position.coords.longitude );
|
||||
|
||||
var circle = new google.maps.Circle(
|
||||
{
|
||||
center: geolocation,
|
||||
radius: position.coords.accuracy
|
||||
}
|
||||
);
|
||||
|
||||
g_autoComplete.setBounds( circle.getBounds() );
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
} )( jQuery );
|
||||
@@ -0,0 +1 @@
|
||||
.redux-main .redux-container-google_maps .google_m_api_key{padding-bottom:10px;clear:left;padding-top:20px}.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper{display:none}.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper label{display:block !important;position:relative;font-size:12px !important;text-align:left;color:#999;margin:4px 0 2px 0 !important;cursor:default}.redux-main .redux-container-google_maps .google_m_api_key .google_m_api_key_wrapper .description{margin-bottom:7px}.redux-main .redux-container-google_maps .google_maps_address_results{padding-top:20px}.redux-main .redux-container-google_maps .google_maps_address_results .latitude{clear:left}.redux-main .redux-container-google_maps .google_maps_address_results .marker-info{clear:left !important;height:75px !important;width:100% !important;margin-bottom:10px !important}.redux-main .redux-container-google_maps .google_maps_address_results label{display:block !important;position:relative;font-size:12px !important;text-align:left;color:#999;margin:4px 0 2px 0 !important;cursor:default}.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper{display:block;position:relative;float:left;clear:none;margin:0 10px 0 0;height:57px;-webkit-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper:nth-child(odd){margin-right:10px !important}.redux-main .redux-container-google_maps .google_maps_address_results .input_wrapper:nth-child(even){margin-right:10px !important}.redux-main .redux-container-google_maps .google_m_canvas{margin:0;padding:0;height:250px;width:100%}.redux-main .redux-container-google_maps .google_m_canvas img{max-width:initial !important}.redux-main .redux-container-google_maps .google_m_autocomplete{background-color:#fff;color:#b5b5b5;font-family:Roboto;font-size:15px;font-weight:300;margin-left:12px;padding:0 11px 0 13px;text-overflow:ellipsis;width:50%}.redux-main .redux-container-google_maps .google_m_autocomplete:focus{border-color:#4d90fe}.redux-main .redux-container-google_maps .google_map_address_results{border:1px solid #cbcbcb;background-color:#ededed;width:100%;padding:5px}.redux-main .redux-container-google_maps .field{width:99%}.redux-main .redux-container-google_maps .slimField{width:80px}.redux-main .redux-container-google_maps .wideField{width:150px !important}.redux-main .google_m_controls{color:#fff;background-color:#4d90fe;padding:5px 11px 0 11px;width:50%;margin-top:7px;border:1px solid transparent;border-radius:2px 0 0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-box-sizing:border-box;height:32px;outline:0;-webkit-box-shadow:0 2px 6px rgba(0,0,0,0.3);box-shadow:0 2px 6px rgba(0,0,0,0.3)}.redux-main .google_m_controls label{margin-left:-5px !important;margin-right:7px !important;font-size:12px}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,136 @@
|
||||
.redux-main {
|
||||
.redux-container-google_maps {
|
||||
.google_m_api_key {
|
||||
padding-bottom: 10px;
|
||||
clear: left;
|
||||
padding-top: 20px;
|
||||
|
||||
.google_m_api_key_wrapper {
|
||||
display: none;
|
||||
|
||||
label {
|
||||
display: block !important;
|
||||
position: relative;
|
||||
font-size: 12px !important;
|
||||
text-align: left;
|
||||
color: #999999;
|
||||
margin: 4px 0 2px 0 !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.google_maps_address_results {
|
||||
padding-top: 20px;
|
||||
|
||||
.latitude {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.marker-info {
|
||||
clear: left!important;
|
||||
height: 75px!important;
|
||||
width: 100%!important;
|
||||
margin-bottom: 10px!important;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block!important;
|
||||
position: relative;
|
||||
font-size: 12px !important;
|
||||
text-align: left;
|
||||
color: #999999;
|
||||
margin: 4px 0 2px 0 !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.input_wrapper {
|
||||
display: block;
|
||||
position: relative;
|
||||
float: left;
|
||||
clear: none;
|
||||
margin: 0 10px 0 0;
|
||||
height: 57px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:nth-child(odd) {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.google_m_canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 250px;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
max-width: initial !important;
|
||||
}
|
||||
}
|
||||
|
||||
.google_m_autocomplete {
|
||||
background-color: #fff;
|
||||
color: #B5B5B5;
|
||||
font-family: Roboto, system-ui;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
margin-left: 12px;
|
||||
padding: 0 11px 0 13px;
|
||||
text-overflow: ellipsis;
|
||||
width: 50%;
|
||||
|
||||
&:focus {
|
||||
border-color: #4d90fe;
|
||||
}
|
||||
}
|
||||
|
||||
.google_map_address_results {
|
||||
border: 1px solid #CBCBCB;
|
||||
background-color: #EDEDED;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.field {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
.slimField {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.wideField {
|
||||
width: 150px!important;
|
||||
}
|
||||
}
|
||||
|
||||
.google_m_controls {
|
||||
color: #fff;
|
||||
background-color: #4d90fe;
|
||||
padding: 5px 11px 0 11px;
|
||||
width: 50%;
|
||||
margin-top: 7px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px 0 0 2px;
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
outline: none;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
|
||||
label {
|
||||
margin-left: -5px!important;
|
||||
margin-right: 7px!important;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* Silence is golden.
|
||||
*
|
||||
* @package Redux Framework
|
||||
*/
|
||||
|
||||
echo null;
|
||||
Reference in New Issue
Block a user