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:
Hotel Raxa Dev
2025-07-11 07:43:22 +02:00
commit 5b1e2453c7
9816 changed files with 2784509 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
<?php
/**
* Redux Repeater Extension Class
*
* @package Redux
* @author Dovy Paukstys & Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Extension_Repeater
*
* @version 4.3.13
*/
defined( 'ABSPATH' ) || exit;
// Don't duplicate me!
if ( ! class_exists( 'Redux_Extension_Repeater' ) ) {
/**
* Class Redux_Extension_Repeater
*/
class Redux_Extension_Repeater extends Redux_Extension_Abstract {
/**
* Extension version.
*
* @var string
*/
public static $version = '4.3.13';
/**
* Extension friendly name.
*
* @var string
*/
public $extension_name = 'Repeater';
/**
* Class Constructor. Defines the args for the extensions class
*
* @since 1.0.0
* @access public
*
* @param object $redux Parent settings.
*
* @return void
*/
public function __construct( $redux ) {
parent::__construct( $redux, __FILE__ );
$this->add_field( 'repeater' );
}
}
}
class_alias( 'Redux_Extension_Repeater', 'ReduxFramework_Extension_repeater' );

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,451 @@
<?php
/**
* Redux Repeater Field Class
*
* @package Redux
* @author Dovy Paukstys & Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Repeater
*/
defined( 'ABSPATH' ) || exit;
// Don't duplicate me!
if ( ! class_exists( 'Redux_Repeater' ) ) {
/**
* Class Redux_Repeater
*/
class Redux_Repeater extends Redux_Field {
/**
* Repeater values.
*
* @var string
*/
private $repeater_values;
/**
* Set defaults.
*/
public function set_defaults() {
$this->repeater_values = '';
if ( ! isset( $this->field['bind_title'] ) && ! empty( $this->field['fields'] ) ) {
$this->field['bind_title'] = $this->field['fields'][0]['id'];
}
$default = array(
'group_values' => false,
'item_name' => '',
'bind_title' => true,
'sortable' => true,
'limit' => 10,
'init_empty' => false,
'panels_closed' => false,
);
$this->field = wp_parse_args( $this->field, $default );
}
/**
* 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() {
$unallowed = array( 'tabbed', 'social_profiles', 'color_scheme', 'repeater' );
if ( isset( $this->field['group_values'] ) && $this->field['group_values'] ) {
$this->repeater_values = '[' . $this->field['id'] . ']';
}
if ( false === $this->field['init_empty'] ) {
$title = '';
if ( empty( $this->value ) || ! is_array( $this->value ) ) {
$this->value = array(
'redux_repeater_data' => array(
array(
'title' => $title,
),
),
);
}
}
if ( isset( $this->field['subfields'] ) && empty( $this->field['fields'] ) ) {
$this->field['fields'] = $this->field['subfields'];
unset( $this->field['subfields'] );
}
echo '<div class="redux-repeater-accordion" data-id="' . esc_attr( $this->field['id'] ) . '" data-panels-closed="' . esc_attr( $this->field['panels_closed'] ) . '">';
$x = 0;
if ( isset( $this->value['redux_repeater_data'] ) && is_array( $this->value['redux_repeater_data'] ) && ! empty( $this->value['redux_repeater_data'] ) ) {
$repeaters = $this->value['redux_repeater_data'];
if ( '' === $this->field['bind_title'] ) {
$keys = array_keys( min( $repeaters ) );
$this->field['bind_title'] = $keys[0];
}
foreach ( $repeaters as $repeater ) {
if ( empty( $repeater ) ) {
continue;
}
echo '<div class="redux-repeater-accordion-repeater" data-sortid="' . esc_attr( $x ) . '">';
echo '<table style="margin-top: 0;" class="redux-repeater-accordion redux-repeater form-table no-border">';
echo '<fieldset class="redux-field " data-id="' . esc_attr( $this->field['id'] ) . '">';
echo '<input type="hidden" name="' . esc_attr( $this->parent->args['opt_name'] ) . '[' . esc_attr( $this->field['id'] ) . '][redux_repeater_data][' . esc_attr( $x ) . '][title]" value="' . esc_attr( $repeater['title'] ) . '" class="regular-text slide-title" data-key="' . esc_attr( $x ) . '" />';
if ( isset( $this->field['bind_title'] ) ) {
foreach ( $this->field['fields'] as $field ) {
if ( $field['id'] === $this->field['bind_title'] ) {
if ( isset( $field['options'] ) ) {
// Sorter data filter.
if ( 'sorter' === $field['type'] && ! empty( $field['data'] ) && is_array( $field['data'] ) ) {
if ( ! isset( $field['args'] ) ) {
$field['args'] = array();
}
foreach ( $field['data'] as $key => $data ) {
if ( ! isset( $field['args'][ $key ] ) ) {
$field['args'][ $key ] = array();
}
$field['options'][ $key ] = $this->parent->get_wordpress_data( $data, $field['args'][ $key ] );
}
}
}
$default = $field['default'] ?? '';
if ( ! empty( $this->repeater_values ) ) {
$repeater['title'] = ! isset( $this->parent->options[ $this->field['id'] ][ $field['id'] ][ $x ] ) ? $default : $this->parent->options[ $this->field['id'] ][ $field['id'] ][ $x ];
} else {
$repeater['title'] = ! isset( $this->parent->options[ $field['id'] ][ $x ] ) ? $default : $this->parent->options[ $field['id'] ][ $x ];
}
if ( isset( $field['options'] ) && is_array( $field['options'] ) ) {
if ( isset( $field['options'][ $repeater['title'] ] ) ) {
$repeater['title'] = $field['options'][ $repeater['title'] ];
}
}
}
}
}
if ( is_array( $repeater['title'] ) ) {
$repeater['title'] = esc_html__( 'Title', 'redux-framework' );
}
echo '<h3><span class="redux-repeater-header">' . esc_html( $repeater['title'] ) . ' </span></h3>';
echo '<div>';
foreach ( $this->field['fields'] as $field ) {
if ( ! isset( $field['class'] ) ) {
$field['class'] = '';
}
if ( isset( $this->field['bind_title'] ) && $field['id'] === $this->field['bind_title'] ) {
$field['class'] .= ' bind_title';
}
if ( in_array( $field['type'], $unallowed, true ) ) {
echo esc_html__( 'The', 'redux-framework' ) . ' <code>' . esc_html( $field['type'] ) . '</code> ' . esc_html__( 'field is not supported within the Repeater field.', 'redux-framework' );
} else {
$this->output_field( $field, $x );
}
}
if ( ! isset( $this->field['static'] ) && empty( $this->field['static'] ) ) {
echo '<a href="javascript:void(0);" class="button deletion redux-warning-primary redux-repeaters-remove">' . esc_html__( 'Delete', 'redux-framework' ) . ' ' . esc_html( $this->field['item_name'] ) . '</a>';
}
echo '</div>';
echo '</fieldset>';
echo '</table>';
echo '</div>';
++$x;
}
}
if ( 0 === $x || ( isset( $this->field['static'] ) && ( $x - 1 ) < $this->field['static'] ) ) {
if ( isset( $this->field['static'] ) && $x < $this->field['static'] ) {
$loop = $this->field['static'] - $x;
} else {
$loop = 1;
}
$class = '';
if ( 0 === $x && true === $this->field['init_empty'] ) {
$class = 'close-me';
}
while ( $loop > 0 ) {
echo '<div class="redux-repeater-accordion-repeater ' . esc_attr( $class ) . '">';
echo '<table style="margin-top: 0;" class="redux-repeater-accordion redux-repeater form-table no-border">';
echo '<fieldset class="redux-field" data-id="' . esc_attr( $this->field['id'] ) . '">';
if ( ! isset( $this->value['redux_repeater_data'][ $x ]['title'] ) && is_array( $this->value ) && isset( $this->value['redux_repeater_data'] ) && is_array( $this->value['redux_repeater_data'] ) ) {
$this->value['redux_repeater_data'][ $x ]['title'] = null;
}
echo '<input type="hidden" name="' . esc_attr( $this->parent->args['opt_name'] ) . '[' . esc_attr( $this->field['id'] ) . '][redux_repeater_data][' . intval( $x ) . '][title]" value="" class="regular-text slide-title" />';
echo '<h3><span class="redux-repeater-header"> </span></h3>';
echo '<div>';
foreach ( $this->field['fields'] as $field ) {
if ( isset( $this->field['bind_title'] ) && $field['id'] === $this->field['bind_title'] ) {
if ( ! isset( $field['class'] ) || ( isset( $field['title'] ) && empty( $field['title'] ) ) ) {
$field['class'] = 'bind_title';
} else {
$field['class'] .= ' bind_title';
}
}
$this->output_field( $field, $x );
}
if ( ! isset( $this->field['static'] ) && empty( $this->field['static'] ) ) {
echo '<a href="javascript:void(0);" class="button deletion redux-repeaters-remove">' . esc_html__( 'Delete', 'redux-framework' ) . ' ' . esc_html( $this->field['item_name'] ) . '</a>';
}
echo '</div>';
echo '</fieldset>';
echo '</table>';
echo '</div>';
++$x;
--$loop;
}
}
echo '</div>';
if ( ! isset( $this->field['static'] ) && empty( $this->field['static'] ) ) {
$disabled = '';
if ( isset( $this->field['limit'] ) && is_integer( $this->field['limit'] ) ) {
if ( $x >= $this->field['limit'] ) {
$disabled = ' button-disabled';
}
}
echo '<a href="javascript:void(0);" class="button redux-repeaters-add button-primary' . esc_attr( $disabled ) . '" data-name="' . esc_attr( $this->parent->args['opt_name'] . $this->repeater_values ) . '[title][]">' . esc_html__( 'Add', 'redux-framework' ) . ' ' . esc_html( $this->field['item_name'] ) . '</a><br/>';
}
}
/**
* 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() {
wp_print_styles( 'editor-buttons' );
// Set up min files for dev_mode = false.
$min = Redux_Functions::is_min();
wp_enqueue_script(
'redux-field-repeater',
// phpcs:ignore WordPress.NamingConventions.ValidHookName
$this->url . 'redux-repeater' . $min . '.js',
array( 'jquery', 'jquery-ui-core', 'jquery-ui-accordion', 'jquery-ui-sortable', 'wp-color-picker', 'redux-js' ),
Redux_Extension_Repeater::$version,
true
);
if ( $this->parent->args['dev_mode'] ) {
wp_enqueue_style(
'redux-field-repeater',
// phpcs:ignore WordPress.NamingConventions.ValidHookName
$this->url . 'redux-repeater.css',
array(),
Redux_Extension_Repeater::$version
);
}
}
/**
* Output field.
*
* @param array $field Field array.
* @param int $x Index.
*/
public function output_field( array $field, int $x ) {
$this->enqueue_dependencies( $field );
if ( ! isset( $field['class'] ) ) {
$field['class'] = '';
}
$field['class'] .= ' repeater';
if ( ! empty( $field['title'] ) ) {
echo '<h4>' . wp_kses_post( $field['title'] ) . '</h4>';
}
if ( ! empty( $field['subtitle'] ) ) {
echo '<span class="description">' . wp_kses_post( $field['subtitle'] ) . '</span>';
}
$orig_field_id = $field['id'];
$field['id'] = $field['id'] . '-' . $x;
$field['name'] = $this->parent->args['opt_name'] . $this->repeater_values . '[' . $orig_field_id . ']';
$field['name_suffix'] = '[' . $x . ']';
$field['class'] .= ' in-repeater';
$field['repeater_id'] = $orig_field_id;
if ( isset( $field['options'] ) ) {
// Sorter data filter.
if ( 'sorter' === $field['type'] && ! empty( $field['data'] ) && is_array( $field['data'] ) ) {
if ( ! isset( $field['args'] ) ) {
$field['args'] = array();
}
foreach ( $field['data'] as $key => $data ) {
if ( ! isset( $field['args'][ $key ] ) ) {
$field['args'][ $key ] = array();
}
$field['options'][ $key ] = $this->parent->get_wordpress_data( $data, $field['args'][ $key ] );
}
}
}
$default = $field['default'] ?? '';
if ( ! empty( $this->repeater_values ) ) {
$value = empty( $this->parent->options[ $this->field['id'] ][ $orig_field_id ][ $x ] ) ? $default : $this->parent->options[ $this->field['id'] ][ $orig_field_id ][ $x ];
} else {
$value = empty( $this->parent->options[ $orig_field_id ][ $x ] ) ? $default : $this->parent->options[ $orig_field_id ][ $x ];
}
ob_start();
$this->parent->render_class->field_input( $field, $value );
$content = ob_get_contents();
// phpcs:ignore WordPress.NamingConventions.ValidHookName
$_field = apply_filters( 'redux-support-repeater', $content, $field, 0 );
ob_end_clean();
echo $_field; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Localize.
*
* @param array $field Field array.
* @param string $value Value.
*
* @return array|string
*/
public function localize( array $field, string $value = '' ) {
if ( isset( $field['subfields'] ) && empty( $field['fields'] ) ) {
$field['fields'] = $field['subfields'];
unset( $field['subfields'] );
}
if ( isset( $field['group_values'] ) && $field['group_values'] ) {
$this->repeater_values = '[' . $field['id'] . ']';
}
$var = '';
if ( '' === $value ) {
$value = array();
}
if ( ! empty( $field['fields'] ) ) {
ob_start();
foreach ( $field['fields'] as $f ) {
if ( isset( $this->field['bind_title'] ) && $f['id'] === $this->field['bind_title'] ) {
if ( ! isset( $f['class'] ) || ( isset( $f['title'] ) && empty( $f['title'] ) ) ) {
$f['class'] = 'bind_title';
} else {
$f['class'] .= ' bind_title';
}
}
$this->output_field( $f, 99999 );
}
$var = ob_get_contents();
$var = array(
'html' => $var . '<a href="javascript:void(0);" class="button deletion redux-repeaters-remove">' . esc_html__( 'Delete', 'redux-framework' ) . '</a>',
'count' => count( $value ),
'sortable' => true,
'limit' => '',
'name' => $this->parent->args['opt_name'] . '[' . $field['id'] . '][0]',
);
if ( isset( $field['sortable'] ) && is_bool( $this->field['sortable'] ) ) {
$var['sortable'] = $field['sortable'];
}
if ( isset( $field['limit'] ) && is_integer( $field['limit'] ) ) {
$var['limit'] = $field['limit'];
}
ob_end_clean();
}
return $var;
}
/**
* Enqueue Deps.
*
* @param array $field Field.
*/
private function enqueue_dependencies( array $field ) {
$field_type = $field['type'];
$field_class = 'Redux_' . $field_type;
if ( ! class_exists( $field_class ) ) {
$field_type = str_replace( '_', '-', $field_type );
// phpcs:ignore WordPress.NamingConventions.ValidHookName
$class_file = apply_filters( 'redux-typeclass-load', ReduxFramework::$_dir . 'inc/fields/' . $field_type . '/class-redux-' . $field_type . '.php', $field_class );
if ( file_exists( $class_file ) ) {
require_once $class_file;
}
}
if ( class_exists( $field_class ) && method_exists( $field_class, 'enqueue' ) ) {
$enqueue = new $field_class( '', '', $this->parent );
$enqueue->enqueue();
}
if ( class_exists( $field_class ) && method_exists( $field_class, 'localize' ) ) {
$enqueue = new $field_class( '', '', $this->parent );
$data = $enqueue->localize( $field );
$this->parent->enqueue_class->localize_data[ $field['type'] ][ $field['id'] ] = $data;
}
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,43 @@
.redux-container-repeater { margin-bottom: 7px; padding: 0 20px 15px; }
.redux-container-repeater h4 { margin: 5px 0 0 0; }
.redux-container-repeater h4:first-child { margin-top: 0; }
.redux-container-repeater .description { margin: 5px 0 5px 0; }
.redux-container-repeater .redux-repeater-accordion { width: 100%; }
.redux-container-repeater .redux-repeater-accordion .ui-state-focus { outline: none; }
.redux-container-repeater .redux-repeater-accordion-repeater { margin-bottom: 10px; }
.redux-container-repeater .redux-repeater-accordion-repeater > div { border: 1px solid #dfdfdf !important; border-radius: 0 !important; margin-top: 0 !important; padding: 10px; }
.redux-container-repeater .redux-repeater-accordion-repeater h3.ui-accordion-header { border: 1px solid #dfdfdf; cursor: move; font-weight: bold; padding: 0 10px; height: 40px; line-height: 40px; background-color: #f1f1f1; background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec); background-image: linear-gradient(to bottom, #f9f9f9, #ececec); overflow: hidden; border-radius: 3px; -webkit-box-shadow: inset 0 1px 0 #fff; box-shadow: inset 0 1px 0 #fff; text-align: center; margin-bottom: 0; }
.redux-container-repeater .redux-repeaters-add { float: right; }
.redux-container-repeater .redux-repeaters-add:after { clear: both; }
.redux-container-repeater .redux-repeaters-remove { /* color: #ef521d !important; */ float: right; }
.redux-container-repeater .redux-repeaters-remove:after { clear: both; }
.redux-container-repeater .redux-repeater-header { font-weight: bold; }
.redux-container-repeater .redux_repeaters_add_remove { margin-bottom: 10px; }
.redux-container-repeater .redux-field-container { padding: 0 0 10px 0; }
.redux-container-repeater .redux-field-container:last-child { padding-bottom: 0; }
.redux-container-repeater .ui-accordion .ui-accordion-content { padding: 1em; }
.redux-container-repeater .redux-container-sorter { margin-right: 0 !important; }
#poststuff .redux-container-repeater h3 { padding: 0; cursor: move !important; line-height: 40px; }
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtcmVwZWF0ZXIuY3NzIiwic291cmNlcyI6WyJyZWR1eC1yZXBlYXRlci5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLEFBQUEseUJBQXlCLENBQUMsRUFDdEIsYUFBYSxFQUFFLEdBQUcsRUFDbEIsT0FBTyxFQUFFLFdBQVcsR0F1RnZCOztBQXpGRCxBQUlJLHlCQUpxQixDQUlyQixFQUFFLENBQUMsRUFDQyxNQUFNLEVBQUUsU0FBUyxHQUlwQjs7QUFUTCxBQU1RLHlCQU5pQixDQUlyQixFQUFFLEFBRUcsWUFBWSxDQUFDLEVBQ1YsVUFBVSxFQUFFLENBQUMsR0FDaEI7O0FBUlQsQUFXSSx5QkFYcUIsQ0FXckIsWUFBWSxDQUFDLEVBQ1QsTUFBTSxFQUFFLFdBQVcsR0FDdEI7O0FBYkwsQUFlSSx5QkFmcUIsQ0FlckIseUJBQXlCLENBQUMsRUFDdEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUFqQkwsQUFtQkkseUJBbkJxQixDQW1CckIseUJBQXlCLENBQUMsZUFBZSxDQUFBLEVBQ3JDLE9BQU8sRUFBRSxJQUFJLEdBQ2hCOztBQXJCTCxBQXVCSSx5QkF2QnFCLENBdUJyQixrQ0FBa0MsQ0FBQyxFQUMvQixhQUFhLEVBQUUsSUFBSSxHQUN0Qjs7QUF6QkwsQUEyQkkseUJBM0JxQixDQTJCckIsa0NBQWtDLEdBQUcsR0FBRyxDQUFDLEVBQ3JDLE1BQU0sRUFBRSw0QkFBNEIsRUFDcEMsYUFBYSxFQUFFLFlBQVksRUFDM0IsVUFBVSxFQUFFLFlBQVksRUFDeEIsT0FBTyxFQUFFLElBQUksR0FDaEI7O0FBaENMLEFBa0NJLHlCQWxDcUIsQ0FrQ3JCLGtDQUFrQyxDQUFDLEVBQUUsQUFBQSxvQkFBb0IsQ0FBQyxFQUN0RCxNQUFNLEVBQUUsaUJBQWlCLEVBQ3pCLE1BQU0sRUFBRSxJQUFJLEVBQ1osV0FBVyxFQUFFLElBQUksRUFDakIsT0FBTyxFQUFFLE1BQU0sRUFDZixNQUFNLEVBQUUsSUFBSSxFQUNaLFdBQVcsRUFBRSxJQUFJLEVBQ2pCLGdCQUFnQixFQUFFLE9BQU8sRUFDekIsZ0JBQWdCLEVBQUUsMENBQTBDLEVBQzVELGdCQUFnQixFQUFFLDJDQUEyQyxFQUM3RCxnQkFBZ0IsRUFBRSx5Q0FBeUMsRUFDM0QsZ0JBQWdCLEVBQUUsMkVBQTJFLEVBQzdGLGdCQUFnQixFQUFFLDhDQUE4QyxFQUNoRSxnQkFBZ0IsRUFBRSw0Q0FBNEMsRUFDOUQsUUFBUSxFQUFFLE1BQU0sRUFDaEIscUJBQXFCLEVBQUUsR0FBRyxFQUMxQixrQkFBa0IsRUFBRSxHQUFHLEVBQ3ZCLGFBQWEsRUFBRSxHQUFHLEVBQ2xCLGVBQWUsRUFBRSxrQkFBa0IsRUFDbkMsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQ3RDLFVBQVUsRUFBRSxrQkFBa0IsRUFDOUIsVUFBVSxFQUFFLE1BQU0sRUFDbEIsYUFBYSxFQUFDLENBQUMsR0FDbEI7O0FBekRMLEFBMERJLHlCQTFEcUIsQ0EwRHJCLG9CQUFvQixDQUFDLEVBQ2pCLEtBQUssRUFBRSxLQUFLLEdBSWY7O0FBL0RMLEFBNERRLHlCQTVEaUIsQ0EwRHJCLG9CQUFvQixBQUVmLE1BQU0sQ0FBQyxFQUNKLEtBQUssRUFBRSxJQUFJLEdBQ2Q7O0FBOURULEFBZ0VJLHlCQWhFcUIsQ0FnRXJCLHVCQUF1QixDQUFDLEVBQ3BCLGdDQUFnQyxDQUNoQyxLQUFLLEVBQUUsS0FBSyxHQUlmOztBQXRFTCxBQW1FUSx5QkFuRWlCLENBZ0VyQix1QkFBdUIsQUFHbEIsTUFBTSxDQUFDLEVBQ0osS0FBSyxFQUFFLElBQUksR0FDZDs7QUFyRVQsQUF1RUkseUJBdkVxQixDQXVFckIsc0JBQXNCLENBQUMsRUFDbkIsV0FBVyxFQUFFLElBQUksR0FDcEI7O0FBekVMLEFBMEVJLHlCQTFFcUIsQ0EwRXJCLDJCQUEyQixDQUFDLEVBQ3hCLGFBQWEsRUFBRSxJQUFJLEdBQ3RCOztBQTVFTCxBQTZFSSx5QkE3RXFCLENBNkVyQixzQkFBc0IsQ0FBQyxFQUNuQixPQUFPLEVBQUUsVUFBVSxHQUN0Qjs7QUEvRUwsQUFnRkkseUJBaEZxQixDQWdGckIsc0JBQXNCLEFBQUEsV0FBVyxDQUFDLEVBQzlCLGNBQWMsRUFBRSxDQUFDLEdBQ3BCOztBQWxGTCxBQW1GSSx5QkFuRnFCLENBbUZyQixhQUFhLENBQUMscUJBQXFCLENBQUMsRUFDaEMsT0FBTyxFQUFFLEdBQUcsR0FDZjs7QUFyRkwsQUFzRkkseUJBdEZxQixDQXNGckIsdUJBQXVCLENBQUMsRUFDcEIsWUFBWSxFQUFFLFlBQVksR0FDN0I7O0FBR0wsQUFBQSxVQUFVLENBQUMseUJBQXlCLENBQUMsRUFBRSxDQUFDLEVBQ3BDLE9BQU8sRUFBRSxDQUFDLEVBQ1YsTUFBTSxFQUFFLGVBQWUsRUFDdkIsV0FBVyxFQUFFLElBQUksR0FDcEIifQ== */
/*# sourceMappingURL=redux-repeater.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,576 @@
/* global redux_change, redux, reduxRepeaterAccordionActivate, reduxRepeaterAccordionBeforeActivate */
( function ( $ ) {
'use strict';
var reduxObject;
var panelsClosed;
redux.field_objects = redux.field_objects || {};
redux.field_objects.repeater = redux.field_objects.repeater || {};
redux.field_objects.repeater.getOptName = function ( el ) {
var optName;
optName = el.parents().find( '.redux-ajax-security' ).data( 'opt-name' );
if ( undefined === optName ) {
optName = el.parents( '.redux-container' ).data( 'opt-name' );
}
if ( undefined === optName ) {
return redux;
} else {
return redux.optName;
}
};
redux.field_objects.repeater.init = function ( selector ) {
if ( ! selector ) {
selector = $( document ).find( '.redux-group-tab:visible' ).find( '.redux-container-repeater:visible' );
}
$( selector ).each(
function () {
var gid;
var blank;
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;
}
if ( ! el.hasClass( 'redux-field-container' ) ) {
parent = el.parents( '.redux-field-container:first' );
}
reduxObject = redux.field_objects.repeater.getOptName( el );
gid = parent.attr( 'data-id' );
blank = el.find( '.redux-repeater-accordion-repeater:last-child' );
reduxObject.repeater[gid].blank = blank.clone().wrap( '<p>' ).parent().html();
if ( parent.hasClass( 'redux-container-repeater' ) ) {
parent.addClass( 'redux-field-init' );
}
if ( parent.hasClass( 'redux-field-init' ) ) {
parent.removeClass( 'redux-field-init' );
} else {
return;
}
redux.field_objects.repeater.setAccordion( el, gid );
redux.field_objects.repeater.bindTitle( el );
redux.field_objects.repeater.remove( el, gid );
redux.field_objects.repeater.add( el );
}
);
};
redux.field_objects.repeater.add = function ( el ) {
/* jshint -W121 */
String.prototype.reduxReplaceAll = function ( s1, s2 ) {
return this.replace( new RegExp( s1.replace( /[.^$*+?()[{|]/g, '\\$&' ), 'g' ), s2 );
};
el.find( '.redux-repeaters-add' ).on(
'click',
function () {
var parent;
var count;
var gid;
var id;
var newSlide;
var html;
redux_change( $( this ) );
if ( $( this ).hasClass( 'button-disabled' ) ) {
return;
}
parent = $( this ).parent().find( '.redux-repeater-accordion:first' );
count = parent.find( '.redux-repeater-accordion-repeater' ).length;
gid = parent.attr( 'data-id' ); // Group id.
if ( '' !== reduxObject.repeater[gid].limit ) {
if ( count >= reduxObject.repeater[gid].limit ) {
$( this ).addClass( 'button-disabled' );
return;
}
}
count += 1;
id = parent.find( '.redux-repeater-accordion-repeater' ).length; // Index number.
if ( parent.find( '.redux-repeater-accordion-repeater:last' ).find( '.ui-accordion-header' ).hasClass( 'ui-state-active' ) ) {
parent.find( '.redux-repeater-accordion-repeater:last' ).find( '.ui-accordion-header' ).trigger( 'click' );
}
newSlide = parent.find( '.redux-repeater-accordion-repeater:last' ).clone( true, true );
if ( 0 === newSlide.length ) {
newSlide = reduxObject.repeater[gid].blank;
}
if ( reduxObject.repeater[gid] ) {
reduxObject.repeater[gid].count = el.find( '.redux-repeater-header' ).length;
html = reduxObject.repeater[gid].html.reduxReplaceAll( '99999', id );
$( newSlide ).find( '.redux-repeater-header' ).text( '' );
}
newSlide.find( '.ui-accordion-content' ).html( html );
// Append to the accordion.
$( parent ).append( newSlide );
// Reorder.
redux.field_objects.repeater.sort_repeaters( newSlide );
// Refresh the JS object.
newSlide = $( this ).parent().find( '.redux-repeater-accordion:first' );
newSlide.find( '.redux-repeater-accordion-repeater:last .ui-accordion-header' ).trigger( 'click' );
newSlide.find( '.redux-repeater-accordion-repeater:last .bind_title' ).on(
'change keyup',
function ( event ) {
var value;
if ( $( event.target ).find( ':selected' ).text().length > 0 ) {
value = $( event.target ).find( ':selected' ).text();
} else {
value = $( event.target ).val();
}
$( this ).closest( '.redux-repeater-accordion-repeater' ).find( '.redux-repeater-header' ).text( value );
}
);
$.redux.checkRequired( el );
if ( reduxObject.repeater[gid].limit > 0 && count >= reduxObject.repeater[gid].limit ) {
$( this ).addClass( 'button-disabled' );
}
if ( true === panelsClosed ) {
if ( count >= 2 ) {
el.find( '.redux-repeater-accordion' ).accordion( 'option', { active: false } );
}
}
if ( count > 1 ) {
redux.field_objects.repeater.remove( newSlide );
}
}
);
};
redux.field_objects.repeater.remove = function ( el ) {
var x;
// Handler to remove the given repeater.
el.find( '.redux-repeaters-remove' ).on(
'click',
function () {
var parent;
var gid;
var count;
redux_change( $( this ) );
parent = $( this ).parents( '.redux-container-repeater:first' );
gid = parent.attr( 'data-id' );
reduxObject.repeater[gid].blank = $( this ).parents( '.redux-repeater-accordion-repeater:first' ).clone( true, true );
$( this ).parents( '.redux-repeater-accordion-repeater:first' ).slideUp(
'medium',
function () {
$( this ).remove();
redux.field_objects.repeater.sort_repeaters( el );
if ( '' !== reduxObject.repeater[gid].limit ) {
count = parent.find( '.redux-repeater-accordion-repeater' ).length;
if ( count < reduxObject.repeater[gid].limit ) {
parent.find( '.redux-repeaters-add' ).removeClass( 'button-disabled' );
}
}
parent.find( '.redux-repeater-accordion-repeater:last .ui-accordion-header' ).trigger( 'click' );
}
);
}
);
x = el.find( '.redux-repeater-accordion-repeater' );
if ( x.hasClass( 'close-me' ) ) {
el.find( '.redux-repeaters-remove' ).trigger( 'click' );
}
};
redux.field_objects.repeater.bindTitle = function ( el ) {
el.find( '.redux-repeater-accordion-repeater .bind_title' ).on(
'change keyup',
function ( event ) {
var value;
if ( $( event.target ).find( ':selected' ).text().length > 0 ) {
value = $( event.target ).find( ':selected' ).text();
} else {
value = $( event.target ).val();
}
$( this ).closest( '.redux-repeater-accordion-repeater' ).find( '.redux-repeater-header' ).text( value );
}
);
};
redux.field_objects.repeater.setAccordion = function ( el, gid ) {
var active;
var accordion;
var base = el.find( '.redux-repeater-accordion' );
panelsClosed = Boolean( base.data( 'panels-closed' ) );
if ( true === panelsClosed ) {
active = Boolean( false );
} else {
active = 0;
}
accordion = el.find( '.redux-repeater-accordion' ).accordion(
{
header: '> div > fieldset > h3',
collapsible: true,
active: active,
beforeActivate: function ( event ) {
var a;
var relName;
var optName;
var bracket;
a = $( this ).next( '.redux-repeaters-add' );
relName = a.attr( 'data-name' );
bracket = relName.indexOf( '[' );
optName = relName.substring( 0, bracket );
if ( 'function' === typeof reduxRepeaterAccordionBeforeActivate ) {
reduxRepeaterAccordionBeforeActivate( $( this ), el, event, optName );
}
},
activate: function ( event, ui ) {
var a;
var relName;
var optName;
var bracket;
$.redux.initFields();
if ( 'function' === typeof reduxRepeaterAccordionActivate ) {
a = $( this ).next( '.redux-repeaters-add' );
relName = a.attr( 'data-name' );
bracket = relName.indexOf( '[' );
optName = relName.substring( 0, bracket );
reduxRepeaterAccordionActivate( $( this ), el, event, ui, optName );
}
},
heightStyle: 'content', icons: {
'header': 'ui-icon-plus', 'activeHeader': 'ui-icon-minus'
}
}
);
if ( true === reduxObject.repeater[gid].sortable ) {
accordion.sortable(
{
axis: 'y',
handle: 'h3',
placeholder: 'ui-state-highlight',
start: function ( e, ui ) {
e = null;
ui.placeholder.height( ui.item.height() );
ui.placeholder.width( ui.item.width() );
},
stop: function ( event, ui ) {
event = null;
// IE doesn't register the blur when sorting
// so trigger focusout handlers to remove .ui-state-focus.
ui.item.children( 'h3' ).triggerHandler( 'focusout' );
redux.field_objects.repeater.sort_repeaters( $( this ) );
}
}
);
} else {
accordion.find( 'h3.ui-accordion-header' ).css( 'cursor', 'pointer' );
}
};
redux.field_objects.repeater.sort_repeaters = function ( selector ) {
if ( ! selector.hasClass( 'redux-container-repeater' ) ) {
selector = selector.parents( '.redux-container-repeater:first' );
}
selector.find( '.redux-repeater-accordion-repeater' ).each(
function ( idx ) {
var header;
var split;
var content;
var id = $( this ).attr( 'data-sortid' );
var input = $( this ).find( '.redux-field .repeater[name*=\'[' + id + ']\']' );
input.each(
function () {
$( this ).attr( 'name', $( this ).attr( 'name' ).replace( '[' + id + ']', '[' + idx + ']' ) );
}
);
input = $( this ).find( '.slide-title' );
input.attr( 'name', input.attr( 'name' ).replace( '[' + id + ']', '[' + idx + ']' ) );
input.attr( 'data-key', idx );
$( this ).attr( 'data-sortid', idx );
// Fix the accordion header.
header = $( this ).find( '.ui-accordion-header' );
split = header.attr( 'id' ).split( '-header-' );
header.attr( 'id', split[0] + '-header-' + idx );
split = header.attr( 'aria-controls' ).split( '-panel-' );
header.attr( 'aria-controls', split[0] + '-panel-' + idx );
// Fix the accordion content.
content = $( this ).find( '.ui-accordion-content' );
split = content.attr( 'id' ).split( '-panel-' );
content.attr( 'id', split[0] + '-panel-' + idx );
split = content.attr( 'aria-labelledby' ).split( '-header-' );
content.attr( 'aria-labelledby', split[0] + '-header-' + idx );
}
);
};
redux.field_objects.repeater.check_parents_dependencies = function ( id ) {
var show = '';
var current = id;
var dash = current.lastIndexOf( '-' );
var index = current.substring( dash + 1 );
var fixedId = current.replace( index, '99999' );
if ( reduxObject.required_child.hasOwnProperty( fixedId ) ) {
$.each(
reduxObject.required_child[fixedId],
function ( i, parentData ) {
var parentValue;
var value;
var idx;
var x;
i = null;
idx = $( '#' + reduxObject.args.opt_name + '-' + parentData.parent + '-' + index );
if ( idx.hasClass( 'hide' ) ) {
show = false;
return false;
} else {
if ( false !== show ) {
value = idx.serializeForm();
if ( null !== value && 'object' === typeof value && value.hasOwnProperty( reduxObject.args.opt_name ) ) {
if ( undefined === value[reduxObject.args.opt_name][parentData.parent] ) {
x = Object.values( value[reduxObject.args.opt_name] )[0][parentData.parent];
} else {
x = value[reduxObject.args.opt_name][parentData.parent];
}
value = x[index];
}
if ( $( '#' + reduxObject.args.opt_name + '-' + id ).hasClass( 'redux-container-media' ) ) {
value = value.url;
}
parentValue = value;
show = $.redux.check_dependencies_visibility( parentValue, parentData );
return false;
}
}
}
);
} else {
show = true;
}
return show;
};
/* jshint -W117, -W098 */
/* jscs:disable disallowUnusedParams */
redux_hook(
$.redux,
'required',
function ( returnValue, originalFunction ) {
var reduxObj;
reduxObj = redux.field_objects.repeater.getOptName( $( '.redux-container-repeater' ) );
$.each(
reduxObj.folds,
function ( i, v ) {
var fieldset;
var div;
var rawTable;
if ( i.indexOf( '-99999' ) !== - 1 ) {
i = i.replace( '-99999', '' );
}
fieldset = $( '[id^=' + reduxObj.args.opt_name + '-' + i + ']' );
if ( fieldset.find( '*' ).hasClass( 'in-repeater' ) ) {
fieldset.addClass( 'fold' );
if ( 'hide' === v ) {
fieldset.addClass( 'hide' );
fieldset.prevUntil( 'fieldset' ).addClass( 'hide' );
if ( fieldset.hasClass( 'redux-container-section' ) ) {
div = $( '#section-' + i );
if ( div.hasClass( 'redux-section-indent-start' ) ) {
$( '#section-table-' + i ).hide().addClass( 'hide' );
div.hide().addClass( 'hide' );
}
}
if ( fieldset.hasClass( 'redux-container-info' ) ) {
$( '#info-' + i ).hide().addClass( 'hide' );
}
if ( fieldset.hasClass( 'redux-container-divide' ) ) {
$( '#divide-' + i ).hide().addClass( 'hide' );
}
if ( fieldset.hasClass( 'redux-container-raw' ) ) {
rawTable = fieldset.parents().find( 'table#' + redux.args.opt_name + '-' + i );
rawTable.hide().addClass( 'hide' );
}
}
}
}
);
}
);
redux_hook(
$.redux,
'check_dependencies',
function ( returnValue, originalFunction, variable ) {
var current;
var id;
var container;
var is_hidden;
var dash;
var idNoIndex;
var index;
if ( $( variable ).hasClass( 'in-repeater' ) ) {
current = $( variable );
id = current.parents( '.redux-field:first' ).data( 'id' );
container = current.parents( '.redux-field-container:first' );
is_hidden = container.hasClass( 'hide' );
dash = id.lastIndexOf( '-' );
idNoIndex = id.substring( 0, dash );
index = id.substring( dash + 1 );
$.each(
reduxObject.required[idNoIndex],
function ( child, dependents ) {
var current;
var show;
var childFieldset;
if ( child.indexOf( '99999' ) !== - 1 ) {
child = child.replace( '99999', index );
}
current = $( this );
show = false;
childFieldset = $( '#' + reduxObject.args.opt_name + '-' + child );
if ( ! is_hidden ) {
show = redux.field_objects.repeater.check_parents_dependencies( child );
}
if ( true === show ) {
childFieldset.fadeIn(
300,
function () {
$( this ).removeClass( 'hide' );
$( this ).prevUntil( 'fieldset' ).removeClass( 'hide' );
if ( reduxObject.required.hasOwnProperty( child ) ) {
$.redux.check_dependencies( $( '#' + reduxObject.args.opt_name + '-' + child ).children().first() );
}
$.redux.initFields();
}
);
} else {
childFieldset.fadeOut(
100,
function () {
$( this ).addClass( 'hide' );
$( this ).prevUntil( 'fieldset' ).addClass( 'hide' );
if ( reduxObject.required.hasOwnProperty( child ) ) {
$.redux.required_recursive_hide( child );
}
}
);
}
current.find( 'select, radio, input[type=checkbox]' ).trigger( 'change' );
}
);
}
}
);
} )( jQuery );

View File

@@ -0,0 +1 @@
.redux-container-repeater{padding:15px 20px;margin-bottom:7px;padding-top:0}.redux-container-repeater h4{margin:5px 0 0 0}.redux-container-repeater h4:first-child{margin-top:0}.redux-container-repeater .description{margin:5px 0 5px 0}.redux-container-repeater .redux-repeater-accordion{width:100%}.redux-container-repeater .redux-repeater-accordion .ui-state-focus{outline:0}.redux-container-repeater .redux-repeater-accordion-repeater{margin-bottom:10px}.redux-container-repeater .redux-repeater-accordion-repeater>div{border:1px solid #dfdfdf !important;border-radius:0 !important;margin-top:0 !important;padding:10px}.redux-container-repeater .redux-repeater-accordion-repeater h3.ui-accordion-header{border:1px solid #dfdfdf;cursor:move;font-weight:bold;padding:0 10px;height:40px;line-height:40px;background-color:#f1f1f1;background-image:-webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#ececec));background-image:-webkit-linear-gradient(top,#f9f9f9,#ececec);background-image:linear-gradient(to bottom,#f9f9f9,#ececec);overflow:hidden;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff;text-align:center;margin-bottom:0}.redux-container-repeater .redux-repeaters-add{float:right}.redux-container-repeater .redux-repeaters-add:after{clear:both}.redux-container-repeater .redux-repeaters-remove{color:#ef521d !important;float:right}.redux-container-repeater .redux-repeaters-remove:after{clear:both}.redux-container-repeater .redux-repeater-header{font-weight:bold}.redux-container-repeater .redux_repeaters_add_remove{margin-bottom:10px}.redux-container-repeater .redux-field-container{padding:0 0 10px 0}.redux-container-repeater .redux-field-container:last-child{padding-bottom:0}.redux-container-repeater .ui-accordion .ui-accordion-content{padding:1em}.redux-container-repeater .redux-container-sorter{margin-right:0 !important}#poststuff .redux-container-repeater h3{padding:0;cursor:move !important;line-height:40px}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,96 @@
.redux-container-repeater {
margin-bottom: 7px;
padding: 0 20px 15px;
h4 {
margin: 5px 0 0 0;
&:first-child {
margin-top: 0;
}
}
.description {
margin: 5px 0 5px 0;
}
.redux-repeater-accordion {
width: 100%;
}
.redux-repeater-accordion .ui-state-focus{
outline: none;
}
.redux-repeater-accordion-repeater {
margin-bottom: 10px;
}
.redux-repeater-accordion-repeater > div {
border: 1px solid #dfdfdf !important;
border-radius: 0 !important;
margin-top: 0 !important;
padding: 10px;
}
.redux-repeater-accordion-repeater h3.ui-accordion-header {
border: 1px solid #dfdfdf;
cursor: move;
font-weight: bold;
padding: 0 10px;
height: 40px;
line-height: 40px;
background-color: #f1f1f1;
background-image: -ms-linear-gradient(top, #f9f9f9, #ececec);
background-image: -moz-linear-gradient(top, #f9f9f9, #ececec);
background-image: -o-linear-gradient(top, #f9f9f9, #ececec);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec));
background-image: -webkit-linear-gradient(top, #f9f9f9, #ececec);
background-image: linear-gradient(to bottom, #f9f9f9, #ececec);
overflow: hidden;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: inset 0 1px 0 #fff;
-webkit-box-shadow: inset 0 1px 0 #fff;
box-shadow: inset 0 1px 0 #fff;
text-align: center;
margin-bottom:0;
}
.redux-repeaters-add {
float: right;
&:after {
clear: both;
}
}
.redux-repeaters-remove {
/* color: #ef521d !important; */
float: right;
&:after {
clear: both;
}
}
.redux-repeater-header {
font-weight: bold;
}
.redux_repeaters_add_remove {
margin-bottom: 10px;
}
.redux-field-container {
padding: 0 0 10px 0;
}
.redux-field-container:last-child {
padding-bottom: 0;
}
.ui-accordion .ui-accordion-content {
padding: 1em;
}
.redux-container-sorter {
margin-right: 0 !important;
}
}
#poststuff .redux-container-repeater h3 {
padding: 0;
cursor: move !important;
line-height: 40px;
}