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,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Redux Multi Media Extension Class
|
||||
*
|
||||
* @package Redux
|
||||
* @author Kevin Provance <kevin.provance@gmail.com>
|
||||
* @class Redux_Extension_Multi_Media
|
||||
*
|
||||
* @version 4.4.1
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
// Don't duplicate me!
|
||||
if ( ! class_exists( 'Redux_Extension_Multi_Media' ) ) {
|
||||
|
||||
/**
|
||||
* Main Redux_Extension_multi_media extension class
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Redux_Extension_Multi_Media extends Redux_Extension_Abstract {
|
||||
|
||||
/**
|
||||
* Extension version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $version = '4.4.1';
|
||||
|
||||
/**
|
||||
* Extension name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $extension_name = 'Multi Media';
|
||||
|
||||
/**
|
||||
* Class Constructor. Defines the args for the extensions class
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*
|
||||
* @param ReduxFramework $redux Parent settings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $redux ) {
|
||||
parent::__construct( $redux, __FILE__ );
|
||||
|
||||
$this->add_field( 'multi_media' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extended image data.
|
||||
*
|
||||
* @param int|string $id image ID.
|
||||
*
|
||||
* @return array
|
||||
* @depreacted Remove camelCase function name.
|
||||
*/
|
||||
public static function getExtendedData( $id ) {
|
||||
_deprecated_function( 'getExtendedData', '4.3.15', 'Redux_Extension_Multi_Media::get_extended_data( $id )' );
|
||||
|
||||
return self::get_extended_data( $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extended image data.
|
||||
*
|
||||
* @param int|string $id image ID.
|
||||
*
|
||||
* @return array|void
|
||||
*/
|
||||
public static function get_extended_data( $id ) {
|
||||
if ( '' !== $id && is_numeric( $id ) ) {
|
||||
return wp_prepare_attachment_for_js( $id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* Silence is golden.
|
||||
*
|
||||
* @package Redux Framework
|
||||
*/
|
||||
|
||||
echo null;
|
||||
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/**
|
||||
* Redux Multi Media Field Class
|
||||
*
|
||||
* @package Redux
|
||||
* @author Kevin Provance <kevin.provance@gmail.com>
|
||||
* @class Redux_Multi_Media
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! class_exists( 'Redux_Multi_Media' ) ) {
|
||||
|
||||
/**
|
||||
* Main ReduxFramework_multi_media class
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Redux_Multi_Media extends Redux_Field {
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
$field_id = $this->field['id'];
|
||||
|
||||
$button_text = $this->field['labels']['button'] ?? esc_html__( 'Add or Upload File(s)', 'redux-framework' );
|
||||
$max_file_count = $this->field['max_file_upload'] ?? 0;
|
||||
|
||||
// Set library filter data, if it's set.
|
||||
if ( ! isset( $this->field['library_filter'] ) ) {
|
||||
$lib_filter = '';
|
||||
} else {
|
||||
if ( ! is_array( $this->field['library_filter'] ) ) {
|
||||
$this->field['library_filter'] = array( $this->field['library_filter'] );
|
||||
}
|
||||
|
||||
$mime_types = get_allowed_mime_types();
|
||||
|
||||
$lib_array = $this->field['library_filter'];
|
||||
|
||||
$json_arr = array();
|
||||
|
||||
// Enum mime types.
|
||||
foreach ( $mime_types as $ext => $type ) {
|
||||
if ( strpos( $ext, '|' ) ) {
|
||||
$exp_arr = explode( '|', $ext );
|
||||
|
||||
foreach ( $exp_arr as $ext ) {
|
||||
if ( in_array( $ext, $lib_array, true ) ) {
|
||||
$json_arr[ $ext ] = $type;
|
||||
}
|
||||
}
|
||||
} elseif ( in_array( $ext, $lib_array, true ) ) {
|
||||
$json_arr[ $ext ] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode for transit to JS.
|
||||
$lib_filter = rawurlencode( wp_json_encode( $json_arr ) );
|
||||
}
|
||||
|
||||
// primary container.
|
||||
echo '<div
|
||||
class="redux-multi-media-container' . esc_attr( $this->field['class'] ) . '"
|
||||
id="' . esc_attr( $field_id ) . '"
|
||||
data-max-file-upload="' . intval( $max_file_count ) . '"
|
||||
data-id="' . esc_attr( $field_id ) . '">';
|
||||
|
||||
// Library filter.
|
||||
echo '<input type="hidden" class="library-filter" data-lib-filter="' . $lib_filter . '" />'; // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
|
||||
// Hidden inout for file(s).
|
||||
echo '<input
|
||||
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '"
|
||||
id="' . esc_attr( $field_id ) . '-multi-media"
|
||||
class="redux_upload_file redux_upload_list"
|
||||
type="hidden"
|
||||
value=""
|
||||
size="45" />';
|
||||
|
||||
// Upload button.
|
||||
echo '<input
|
||||
type="button"
|
||||
class="redux_upload_button button redux_upload_list"
|
||||
name=""
|
||||
id="' . esc_attr( $field_id ) . '-multi-media-upload"
|
||||
value="' . esc_attr( $button_text ) . '" />';
|
||||
|
||||
// list container.
|
||||
echo '<ul id="' . esc_attr( $this->parent->args['opt_name'] ) . '_' . esc_attr( $field_id ) . '_status" class="redux_media_status attach_list">';
|
||||
|
||||
$file_arr = array();
|
||||
$img_arr = array();
|
||||
$all_arr = array();
|
||||
|
||||
// Check for file entries in array format.
|
||||
if ( $this->value && is_array( $this->value ) ) {
|
||||
|
||||
// Enum existing file entries.
|
||||
foreach ( $this->value as $id => $url ) {
|
||||
|
||||
// hidden ID input.
|
||||
$id_input = '<input
|
||||
type="hidden"
|
||||
value="' . $url . '"
|
||||
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '[' . intval( $id ) . ']"
|
||||
id="filelist-' . $id . '"
|
||||
class="" />';
|
||||
|
||||
// Check for a valid image extension.
|
||||
if ( $this->is_valid_img_ext( $url ) ) {
|
||||
|
||||
// Add image to array.
|
||||
$html = '<li class="img_status">';
|
||||
$html .= wp_get_attachment_image( $id, array( 50, 50 ) );
|
||||
$html .= '<p class="redux_remove_wrapper">';
|
||||
$html .= '<a href="#" class="redux_remove_file_button">' . esc_html__( 'Remove Image', 'redux-framework' ) . '</a>';
|
||||
$html .= '</p>';
|
||||
$html .= $id_input;
|
||||
$html .= '</li>';
|
||||
|
||||
$img_arr[] = $html;
|
||||
|
||||
// No image? Output standard file info.
|
||||
} else {
|
||||
|
||||
// Get parts of URL.
|
||||
$parts = explode( '/', $url );
|
||||
|
||||
// Get the filename.
|
||||
$title = '';
|
||||
$part_count = count( $parts );
|
||||
|
||||
for ( $i = 0; $i < $part_count; ++$i ) {
|
||||
$title = $parts[ $i ];
|
||||
}
|
||||
|
||||
// Add file to array.
|
||||
$html = '<li>';
|
||||
$html .= esc_html__( 'File: ', 'redux-framework' );
|
||||
$html .= '<strong>' . $title . '</strong> ';
|
||||
$html .= '(<a href="' . $url . '" target="_blank" rel="external">' . esc_html__( 'Download', 'redux-framework' ) . '</a> / <a href="#" class="redux_remove_file_button">' . __( 'Remove', 'redux-framework' ) . '</a>)';
|
||||
$html .= $id_input;
|
||||
$html .= '</li>';
|
||||
|
||||
$file_arr[] = $html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push images onto array stack.
|
||||
if ( ! empty( $img_arr ) ) {
|
||||
foreach ( $img_arr as $html ) {
|
||||
$all_arr[] = $html;
|
||||
}
|
||||
}
|
||||
|
||||
// Push files onto array stack.
|
||||
if ( ! empty( $file_arr ) ) {
|
||||
foreach ( $file_arr as $html ) {
|
||||
$all_arr[] = $html;
|
||||
}
|
||||
}
|
||||
|
||||
// Output array to page.
|
||||
if ( ! empty( $all_arr ) ) {
|
||||
foreach ( $all_arr as $html ) {
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
}
|
||||
|
||||
// Close the list.
|
||||
echo '</ul>';
|
||||
|
||||
// Close container.
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a file's extension
|
||||
*
|
||||
* @param string $file File url.
|
||||
*
|
||||
* @return string|false File extension or false
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function get_file_ext( string $file ) {
|
||||
$parsed = wp_parse_url( $file, PHP_URL_PATH );
|
||||
|
||||
return $parsed ? strtolower( pathinfo( $parsed, PATHINFO_EXTENSION ) ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a file has a valid image extension
|
||||
*
|
||||
* @param string $file File url.
|
||||
*
|
||||
* @return bool Whether the file has a valid image extension
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function is_valid_img_ext( string $file ): bool {
|
||||
$file_ext = $this->get_file_ext( $file );
|
||||
|
||||
$ext_arr = array( 'jpg', 'jpeg', 'png', 'gif', 'ico', 'icon' );
|
||||
|
||||
$valid = empty( $valid ) ? (array) apply_filters( 'redux_valid_img_types', $ext_arr ) : $valid;
|
||||
|
||||
return ( $file_ext && in_array( $file_ext, $valid, true ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
// Get labels for localization.
|
||||
$upload_file = $this->field['labels']['upload_file'] ?? esc_html__( 'Select File(s)', 'redux-framework' );
|
||||
$remove_image = $this->field['labels']['remove_image'] ?? esc_html__( 'Remove Image', 'redux-framework' );
|
||||
$remove_file = $this->field['labels']['remove_file'] ?? esc_html__( 'Remove', 'redux-framework' );
|
||||
$file_label = $this->field['labels']['file'] ?? esc_html__( 'File: ', 'redux-framework' );
|
||||
$download_label = $this->field['labels']['download'] ?? esc_html__( 'Download', 'redux-framework' );
|
||||
$media_title = $this->field['labels']['title'] ?? 'Title';
|
||||
|
||||
// translators: %s: Filename.
|
||||
$dup_warn = $this->field['labels']['duplicate'] ?? esc_html__( '%s already exists in your file queue.', 'redux-framework' );
|
||||
|
||||
// translators: %s: Upload limit.
|
||||
$max_warn = $this->field['labels']['max_limit'] ?? esc_html__( 'Maximum upload limit of %s reached/exceeded.', 'redux-framework' );
|
||||
|
||||
// Set up min files for dev_mode = false.
|
||||
$min = Redux_Functions::isMin();
|
||||
|
||||
if ( function_exists( 'wp_enqueue_media' ) ) {
|
||||
wp_enqueue_media();
|
||||
} else {
|
||||
wp_enqueue_script( 'media-upload' );
|
||||
}
|
||||
|
||||
// Field dependent JS.
|
||||
wp_enqueue_script(
|
||||
'redux-field-multi-media',
|
||||
$this->url . 'redux-multi-media' . $min . '.js',
|
||||
array( 'jquery', 'redux-js' ),
|
||||
Redux_Extension_Multi_Media::$version,
|
||||
true
|
||||
);
|
||||
|
||||
if ( $this->parent->args['dev_mode'] ) {
|
||||
wp_enqueue_style(
|
||||
'redux-field-multi-media',
|
||||
$this->url . 'redux-multi-media.css',
|
||||
array(),
|
||||
Redux_Extension_Multi_Media::$version
|
||||
);
|
||||
}
|
||||
|
||||
// Localization.
|
||||
$data_arr = array(
|
||||
'upload_file' => $upload_file,
|
||||
'remove_image' => $remove_image,
|
||||
'remove_file' => $remove_file,
|
||||
'file' => $file_label,
|
||||
'download' => $download_label,
|
||||
'title' => $media_title,
|
||||
'dup_warn' => $dup_warn,
|
||||
'max_warn' => $max_warn,
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'redux-field-multi-media',
|
||||
'redux_multi_media_l10',
|
||||
apply_filters( 'redux_multi_media_localized_data', $data_arr )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* Silence is golden.
|
||||
*
|
||||
* @package Redux Framework
|
||||
*/
|
||||
|
||||
echo null;
|
||||
@@ -0,0 +1,23 @@
|
||||
.redux-container-multi_media .redux_media_status { margin: 10px 0 0 0; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .redux-file-exists, .redux-container-multi_media .redux_media_status .redux-max-limit { color: red; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .img_status img { width: 50px !important; height: 50px !important; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .img_status img, .redux-container-multi_media .redux_media_status .embed_status { border: 1px solid #DFDFDF; background: #FAFAFA; max-width: 350px; padding: 5px; border-radius: 2px; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .img_status { clear: none; float: left; display: inline-block; margin-right: 10px; width: auto; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .embed_status { float: left; max-width: 800px; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .img_status, .redux-container-multi_media .redux_media_status .embed_status { position: relative; }
|
||||
|
||||
.redux-container-multi_media .redux_media_status .img_status .redux_remove_file_button, .redux-container-multi_media .redux_media_status .embed_status .redux_remove_file_button { text-indent: -9999px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==); width: 16px; height: 16px; position: absolute; top: -5px; left: -5px; }
|
||||
|
||||
.redux-container-multi_media .attach_list li { clear: both; display: inline-block; margin-bottom: 25px; width: 100%; }
|
||||
|
||||
.redux-container-multi_media .attach_list li img { float: left; margin-right: 10px; }
|
||||
|
||||
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtbXVsdGktbWVkaWEuY3NzIiwic291cmNlcyI6WyJyZWR1eC1tdWx0aS1tZWRpYS5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLEFBQ0ksNEJBRHdCLENBQ3hCLG1CQUFtQixDQUFDLEVBQ2hCLE1BQU0sRUFBRSxVQUFVLEdBZ0RyQjs7QUFsREwsQUFJUSw0QkFKb0IsQ0FDeEIsbUJBQW1CLENBR2Ysa0JBQWtCLEVBSjFCLDRCQUE0QixDQUN4QixtQkFBbUIsQ0FJZixnQkFBZ0IsQ0FBQyxFQUNiLEtBQUssRUFBQyxHQUFHLEdBQ1o7O0FBUFQsQUFTUSw0QkFUb0IsQ0FDeEIsbUJBQW1CLENBUWYsV0FBVyxDQUFDLEdBQUcsQ0FBQyxFQUNaLEtBQUssRUFBRSxJQUFJLENBQUEsVUFBVSxFQUNyQixNQUFNLEVBQUUsSUFBSSxDQUFBLFVBQVUsR0FDekI7O0FBWlQsQUFjUSw0QkFkb0IsQ0FDeEIsbUJBQW1CLENBYWYsV0FBVyxDQUFDLEdBQUcsRUFkdkIsNEJBQTRCLENBQ3hCLG1CQUFtQixDQWFFLGFBQWEsQ0FBQyxFQUMzQixNQUFNLEVBQUMsaUJBQWlCLEVBQ3hCLFVBQVUsRUFBRSxPQUFPLEVBQ25CLFNBQVMsRUFBQyxLQUFLLEVBQ2YsT0FBTyxFQUFFLEdBQUcsRUFDWixrQkFBa0IsRUFBRSxHQUFHLEVBQ3ZCLGFBQWEsRUFBRSxHQUFHLEdBQ3JCOztBQXJCVCxBQXVCUSw0QkF2Qm9CLENBQ3hCLG1CQUFtQixDQXNCZixXQUFXLENBQUMsRUFDUixLQUFLLEVBQUUsSUFBSSxFQUNYLEtBQUssRUFBRSxJQUFJLEVBQ1gsT0FBTyxFQUFFLFlBQVksRUFDckIsWUFBWSxFQUFFLElBQUksRUFDbEIsS0FBSyxFQUFFLElBQUksR0FDZDs7QUE3QlQsQUErQlEsNEJBL0JvQixDQUN4QixtQkFBbUIsQ0E4QmYsYUFBYSxDQUFDLEVBQ1YsS0FBSyxFQUFFLElBQUksRUFDWCxTQUFTLEVBQUMsS0FBSyxHQUNsQjs7QUFsQ1QsQUFvQ1EsNEJBcENvQixDQUN4QixtQkFBbUIsQ0FtQ2YsV0FBVyxFQXBDbkIsNEJBQTRCLENBQ3hCLG1CQUFtQixDQW9DZixhQUFhLENBQUMsRUFDVixRQUFRLEVBQUUsUUFBUSxHQVdyQjs7QUFqRFQsQUF3Q1ksNEJBeENnQixDQUN4QixtQkFBbUIsQ0FtQ2YsV0FBVyxDQUlQLHlCQUF5QixFQXhDckMsNEJBQTRCLENBQ3hCLG1CQUFtQixDQW9DZixhQUFhLENBR1QseUJBQXlCLENBQUMsRUFDdEIsV0FBVyxFQUFFLE9BQU8sRUFDcEIsVUFBVSxFQUFFLHU5QkFBdTlCLEVBQ24rQixLQUFLLEVBQUUsSUFBSSxFQUNYLE1BQU0sRUFBRSxJQUFJLEVBQ1osUUFBUSxFQUFFLFFBQVEsRUFDbEIsR0FBRyxFQUFFLElBQUksRUFDVCxJQUFJLEVBQUUsSUFBSSxHQUNiOztBQWhEYixBQW9ESSw0QkFwRHdCLENBb0R4QixZQUFZLENBQUMsRUFBRSxDQUFDLEVBQ1osS0FBSyxFQUFFLElBQUksRUFDWCxPQUFPLEVBQUUsWUFBWSxFQUNyQixhQUFhLEVBQUUsSUFBSSxFQUNuQixLQUFLLEVBQUUsSUFBSSxHQUNkOztBQXpETCxBQTJESSw0QkEzRHdCLENBMkR4QixZQUFZLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUNoQixLQUFLLEVBQUUsSUFBSSxFQUNYLFlBQVksRUFBRSxJQUFJLEdBQ3JCIn0= */
|
||||
|
||||
/*# sourceMappingURL=redux-multi-media.css.map */
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,412 @@
|
||||
/* global redux, redux_multi_media_l10, wp, redux_change */
|
||||
|
||||
/**
|
||||
* Multi Media Selector library
|
||||
*
|
||||
* @author Kevin Provance (kprovance)
|
||||
*/
|
||||
|
||||
( function( $ ) {
|
||||
'use strict';
|
||||
|
||||
var l10n;
|
||||
|
||||
redux.field_objects = redux.field_objects || {};
|
||||
redux.field_objects.multi_media = redux.field_objects.multi_media || {};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: init
|
||||
*
|
||||
* Runs when the library is loaded.
|
||||
******************************************************************************/
|
||||
redux.field_objects.multi_media.init = function( selector ) {
|
||||
|
||||
// If no selector is passed, grab one from the HTML.
|
||||
if ( ! selector ) {
|
||||
selector = $( document ).find( '.redux-group-tab:visible' ).find( '.redux-container-multi_media:visible' );
|
||||
}
|
||||
|
||||
// Enum instances of our object.
|
||||
$( selector ).each(
|
||||
function() {
|
||||
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;
|
||||
}
|
||||
|
||||
// Handle clicking of the delete button.
|
||||
redux.field_objects.multi_media.bindDelete( el );
|
||||
|
||||
// Handle clicking of the upload icon.
|
||||
el.find( '.redux_upload_button' ).off().on(
|
||||
'click',
|
||||
function( event ) {
|
||||
redux.field_objects.multi_media.addFile( event, $( this ).parents( 'fieldset.redux-field:first' ), $( this ) );
|
||||
}
|
||||
);
|
||||
|
||||
// Init module level code.
|
||||
redux.field_objects.multi_media.modInit( el );
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: bindDelete
|
||||
*
|
||||
* Force DOM to recognize new delete button instances.
|
||||
******************************************************************************/
|
||||
redux.field_objects.multi_media.bindDelete = function( el ) {
|
||||
el.find( '.redux_remove_file_button' ).off( 'click' ).on(
|
||||
'click',
|
||||
function( event ) {
|
||||
redux.field_objects.multi_media.removeFile( event, $( this ).parents( 'fieldset.redux-field:first' ), $( this ) );
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: modInit
|
||||
*
|
||||
* Module level init
|
||||
******************************************************************************/
|
||||
redux.field_objects.multi_media.modInit = function() {
|
||||
|
||||
// Localization variable.
|
||||
l10n = redux_multi_media_l10;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: removeErrMsgs
|
||||
*
|
||||
* Removes all error messages after clicking upload
|
||||
* button.
|
||||
******************************************************************************/
|
||||
|
||||
// Removes error message(s) when clicking the Upload button.
|
||||
redux.field_objects.multi_media.removeErrMsgs = function( mainID ) {
|
||||
|
||||
// Enumerate and remove existing 'file exists' messages.
|
||||
$( '#' + mainID + ' .attach_list li.redux-file-exists' ).each(
|
||||
function( idx, li ) {
|
||||
idx = null;
|
||||
|
||||
$( li ).remove();
|
||||
}
|
||||
);
|
||||
|
||||
// Enumerate and remove existing 'max upload' messages.
|
||||
$( '#' + mainID + ' .attach_list li.redux-max-limit' ).each(
|
||||
function( idx, li ) {
|
||||
idx = null;
|
||||
|
||||
$( li ).remove();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Checks for duplicate after file selection.
|
||||
redux.field_objects.multi_media.selExists = function( mainID, item ) {
|
||||
var len;
|
||||
|
||||
var val = false;
|
||||
|
||||
// Enumerate existing files.
|
||||
$( '#' + mainID + ' .attach_list li' ).each(
|
||||
function( idx, li ) {
|
||||
idx = null;
|
||||
|
||||
// Check for duplicate based on ID.
|
||||
len = $( li ).find( 'input#filelist-' + item );
|
||||
|
||||
// If it exists, exit .each.
|
||||
if ( 0 !== len.length ) {
|
||||
val = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Return value.
|
||||
return val;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: addFile
|
||||
*
|
||||
* Runs when upload button is clicked.
|
||||
******************************************************************************/
|
||||
redux.field_objects.multi_media.addFile = function( event, selector, self ) {
|
||||
|
||||
// Variables.
|
||||
var frame;
|
||||
var libFilter;
|
||||
var filter;
|
||||
var maxFileUpload;
|
||||
|
||||
var isList = true;
|
||||
var uploadStatus = true;
|
||||
|
||||
// Get input ID.
|
||||
var inputID = self.prev( 'input' ).attr( 'id' );
|
||||
|
||||
// Make form field ID.
|
||||
var $formfield = $( '#' + inputID );
|
||||
|
||||
// Get form name.
|
||||
var formName = $formfield.attr( 'name' );
|
||||
|
||||
var mainID = selector.attr( 'data-id' );
|
||||
|
||||
// Prevent default action.
|
||||
event.preventDefault();
|
||||
|
||||
// If the media frame already exists, reopen it.
|
||||
if ( frame ) {
|
||||
frame.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existing error messages.
|
||||
redux.field_objects.multi_media.removeErrMsgs( mainID );
|
||||
|
||||
// Get library filter data.
|
||||
filter = $( selector ).find( '.library-filter' ).data( 'lib-filter' );
|
||||
|
||||
// Get max file upload number.
|
||||
maxFileUpload = $( selector ).find( '.redux-multi-media-container' ).data( 'max-file-upload' );
|
||||
|
||||
// Library filter MUST exist to do decoding.
|
||||
if ( undefined !== filter ) {
|
||||
if ( '' !== filter ) {
|
||||
libFilter = [];
|
||||
filter = decodeURIComponent( filter );
|
||||
filter = JSON.parse( filter );
|
||||
|
||||
// Enum file extensions.
|
||||
$.each(
|
||||
filter,
|
||||
function( index, value ) {
|
||||
index = null;
|
||||
|
||||
libFilter.push( value );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the media frame.
|
||||
frame = wp.media(
|
||||
{
|
||||
multiple: isList,
|
||||
title: l10n.title,
|
||||
library: {
|
||||
type: libFilter
|
||||
},
|
||||
button: {
|
||||
text: l10n.upload_file
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// When an image is selected, run a callback.
|
||||
frame.on(
|
||||
'select',
|
||||
function() {
|
||||
var addCount = 0;
|
||||
var doChange;
|
||||
|
||||
// Set up our fileGroup array.
|
||||
var fileGroup = [];
|
||||
var fileArr = [];
|
||||
var imgArr = [];
|
||||
var msgArr = [];
|
||||
|
||||
// Grab the selected attachment.
|
||||
var selection = frame.state().get( 'selection' );
|
||||
|
||||
// Get all of our selected files.
|
||||
var attachment = selection.toJSON();
|
||||
|
||||
// Get existing file count.
|
||||
var childCount = $( '#' + mainID + ' .attach_list' ).children().length;
|
||||
|
||||
$formfield.val( attachment.url );
|
||||
$( '#' + inputID + '_id' ).val( attachment.id );
|
||||
|
||||
// Enum through each attachment.
|
||||
$( attachment ).each(
|
||||
function() {
|
||||
var dupMsg;
|
||||
var maxMsg;
|
||||
|
||||
// Respect max upload limit.
|
||||
if ( maxFileUpload <= 0 || ( addCount + childCount ) < maxFileUpload ) {
|
||||
|
||||
// Check for duplicates and format duplicate message.
|
||||
if ( redux.field_objects.multi_media.selExists( mainID, this.id ) ) {
|
||||
dupMsg = l10n.dup_warn;
|
||||
dupMsg = dupMsg.replace( '%s', '<strong>' + this.filename + '</strong>' );
|
||||
uploadStatus = '<li class="redux-file-exists">' + dupMsg + '</li>';
|
||||
|
||||
msgArr.push( uploadStatus );
|
||||
|
||||
// If only file, then don't ask to save changes.
|
||||
doChange = false;
|
||||
|
||||
// Continue equivalent.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle images.
|
||||
if ( this.type && 'image' === this.type ) {
|
||||
|
||||
// Image preview.
|
||||
/* jscs:disable maximumLineLength */
|
||||
uploadStatus = '<li class="img_status"><img width="50" height="50" src="' + this.url + '" class="attachment-50x50" alt="' + this.filename + '"><p><a href="#" class="redux_remove_file_button" rel="' + inputID + '[' + this.id + ']">' + l10n.remove_image + '</a></p><input type="hidden" id="filelist-' + this.id + '" name="' + formName + '[' + this.id + ']" value="' + this.url + '"></li>';
|
||||
|
||||
// Add our file to our fileGroup array.
|
||||
imgArr.push( uploadStatus );
|
||||
|
||||
// Set change flag.
|
||||
doChange = true;
|
||||
|
||||
// Handle everything else.
|
||||
} else {
|
||||
|
||||
// Standard generic output if it's not an image.
|
||||
uploadStatus = '<li>' + l10n.file + ' <strong>' + this.filename + '</strong> (<a href="' + this.url + '" target="_blank" rel="external">' + l10n.download + '</a> / <a href="#" class="redux_remove_file_button" rel="' + inputID + '[' + this.id + ']">' + l10n.remove_file + '</a>)<input type="hidden" id="filelist-' + this.id + '" name="' + formName + '[' + this.id + ']" value="' + this.url + '"></li>';
|
||||
|
||||
fileArr.push( uploadStatus );
|
||||
}
|
||||
|
||||
// Increment count of added files.
|
||||
addCount++; // += 1;
|
||||
|
||||
// If max file upload reached, generate error message.
|
||||
} else {
|
||||
maxMsg = l10n.max_warn;
|
||||
maxMsg = maxMsg.replace( '%s', '<strong>' + maxFileUpload + '</strong>' );
|
||||
uploadStatus = '<li class="redux-max-limit">' + maxMsg + '</li>';
|
||||
|
||||
msgArr.push( uploadStatus );
|
||||
|
||||
// Bail out of .each for good!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Push images files onto end of stack.
|
||||
if ( ! $.isEmptyObject( imgArr ) ) {
|
||||
$( imgArr ).each(
|
||||
function( idx, val ) {
|
||||
idx = null;
|
||||
|
||||
fileGroup.push( val );
|
||||
doChange = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Push none image files onto end of stack.
|
||||
if ( ! $.isEmptyObject( fileArr ) ) {
|
||||
$( fileArr ).each(
|
||||
function( idx, val ) {
|
||||
idx = null;
|
||||
|
||||
fileGroup.push( val );
|
||||
doChange = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Push errors onto end of stack.
|
||||
if ( ! $.isEmptyObject( msgArr ) ) {
|
||||
$( msgArr ).each(
|
||||
function( idx, val ) {
|
||||
idx = null;
|
||||
|
||||
fileGroup.push( val );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Append each item from our fileGroup array to .redux_media_status.
|
||||
$( fileGroup ).each(
|
||||
function() {
|
||||
$formfield.siblings( '.redux_media_status' ).slideDown().append( this );
|
||||
}
|
||||
);
|
||||
|
||||
// Close media frame.
|
||||
frame.close();
|
||||
|
||||
// Prompt for save changes, if necessary.
|
||||
if ( true === doChange ) {
|
||||
redux.field_objects.multi_media.bindDelete( selector );
|
||||
|
||||
redux_change( $( selector ).find( '.redux_media_status' ) );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Finally, open the modal.
|
||||
frame.open();
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Function: removeFile Function
|
||||
*
|
||||
* Runs when the delete icon or remove link is clicked.
|
||||
******************************************************************************/
|
||||
redux.field_objects.multi_media.removeFile = function( event, selector, self ) {
|
||||
var inputID;
|
||||
var $container;
|
||||
|
||||
var $self = self;
|
||||
|
||||
// Prevent default action.
|
||||
event.preventDefault();
|
||||
|
||||
// If delete icon is clicked.
|
||||
if ( $self.is( '.attach_list .redux_remove_file_button' ) ) {
|
||||
|
||||
// Remove image from page.
|
||||
$self.parents( 'li' ).remove();
|
||||
|
||||
// Prompt for save changes.
|
||||
redux_change( $( selector ).find( '.redux_media_status' ) );
|
||||
|
||||
// Bail out.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove file link from page.
|
||||
inputID = $self.attr( 'rel' );
|
||||
$container = $self.parents( '.img_status' );
|
||||
|
||||
selector.find( 'input#' + inputID ).val( '' );
|
||||
selector.find( 'input#' + inputID + '_id' ).val( '' );
|
||||
|
||||
if ( ! $container.length ) {
|
||||
$self.parents( '.redux_media_status' ).html( '' );
|
||||
} else {
|
||||
$container.html( '' );
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
})( jQuery );
|
||||
@@ -0,0 +1 @@
|
||||
.redux-container-multi_media .redux_media_status{margin:10px 0 0 0}.redux-container-multi_media .redux_media_status .redux-file-exists,.redux-container-multi_media .redux_media_status .redux-max-limit{color:red}.redux-container-multi_media .redux_media_status .img_status img{width:50px !important;height:50px !important}.redux-container-multi_media .redux_media_status .img_status img,.redux-container-multi_media .redux_media_status .embed_status{border:1px solid #dfdfdf;background:#fafafa;max-width:350px;padding:5px;border-radius:2px}.redux-container-multi_media .redux_media_status .img_status{clear:none;float:left;display:inline-block;margin-right:10px;width:auto}.redux-container-multi_media .redux_media_status .embed_status{float:left;max-width:800px}.redux-container-multi_media .redux_media_status .img_status,.redux-container-multi_media .redux_media_status .embed_status{position:relative}.redux-container-multi_media .redux_media_status .img_status .redux_remove_file_button,.redux-container-multi_media .redux_media_status .embed_status .redux_remove_file_button{text-indent:-9999px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==);width:16px;height:16px;position:absolute;top:-5px;left:-5px}.redux-container-multi_media .attach_list li{clear:both;display:inline-block;margin-bottom:25px;width:100%}.redux-container-multi_media .attach_list li img{float:left;margin-right:10px}
|
||||
@@ -0,0 +1 @@
|
||||
!function(x){"use strict";var p;redux.field_objects=redux.field_objects||{},redux.field_objects.multi_media=redux.field_objects.multi_media||{},redux.field_objects.multi_media.init=function(e){e=e||x(document).find(".redux-group-tab:visible").find(".redux-container-multi_media:visible"),x(e).each(function(){var e=x(this),i=e;(i=e.hasClass("redux-field-container")?i:e.parents(".redux-field-container:first")).is(":hidden")||i.hasClass("redux-field-init")&&(i.removeClass("redux-field-init"),redux.field_objects.multi_media.bindDelete(e),e.find(".redux_upload_button").off().on("click",function(e){redux.field_objects.multi_media.addFile(e,x(this).parents("fieldset.redux-field:first"),x(this))}),redux.field_objects.multi_media.modInit(e))})},redux.field_objects.multi_media.bindDelete=function(e){e.find(".redux_remove_file_button").off("click").on("click",function(e){redux.field_objects.multi_media.removeFile(e,x(this).parents("fieldset.redux-field:first"),x(this))})},redux.field_objects.multi_media.modInit=function(){p=redux_multi_media_l10},redux.field_objects.multi_media.removeErrMsgs=function(e){x("#"+e+" .attach_list li.redux-file-exists").each(function(e,i){x(i).remove()}),x("#"+e+" .attach_list li.redux-max-limit").each(function(e,i){x(i).remove()})},redux.field_objects.multi_media.selExists=function(e,t){var l=!1;return x("#"+e+" .attach_list li").each(function(e,i){if(0!==x(i).find("input#filelist-"+t).length)return!(l=!0)}),l},redux.field_objects.multi_media.addFile=function(e,r,i){var u,t,o,f=!0,c=i.prev("input").attr("id"),m=x("#"+c),_=m.attr("name"),h=r.attr("data-id");e.preventDefault(),redux.field_objects.multi_media.removeErrMsgs(h),i=x(r).find(".library-filter").data("lib-filter"),o=x(r).find(".redux-multi-media-container").data("max-file-upload"),void 0!==i&&""!==i&&(t=[],i=decodeURIComponent(i),i=JSON.parse(i),x.each(i,function(e,i){t.push(i)})),(u=wp.media({multiple:!0,title:p.title,library:{type:t},button:{text:p.upload_file}})).on("select",function(){var t,i=0,l=[],d=[],s=[],a=[],e=u.state().get("selection").toJSON(),n=x("#"+h+" .attach_list").children().length;m.val(e.url),x("#"+c+"_id").val(e.id),x(e).each(function(){var e;return o<=0||i+n<o?redux.field_objects.multi_media.selExists(h,this.id)?(e=(e=p.dup_warn).replace("%s","<strong>"+this.filename+"</strong>"),f='<li class="redux-file-exists">'+e+"</li>",a.push(f),!(t=!1)):(this.type&&"image"===this.type?(f='<li class="img_status"><img width="50" height="50" src="'+this.url+'" class="attachment-50x50" alt="'+this.filename+'"><p><a href="#" class="redux_remove_file_button" rel="'+c+"["+this.id+']">'+p.remove_image+'</a></p><input type="hidden" id="filelist-'+this.id+'" name="'+_+"["+this.id+']" value="'+this.url+'"></li>',s.push(f),t=!0):(f="<li>"+p.file+" <strong>"+this.filename+'</strong> (<a href="'+this.url+'" target="_blank" rel="external">'+p.download+'</a> / <a href="#" class="redux_remove_file_button" rel="'+c+"["+this.id+']">'+p.remove_file+'</a>)<input type="hidden" id="filelist-'+this.id+'" name="'+_+"["+this.id+']" value="'+this.url+'"></li>',d.push(f)),void i++):(e=(e=p.max_warn).replace("%s","<strong>"+o+"</strong>"),f='<li class="redux-max-limit">'+e+"</li>",a.push(f),!1)}),x.isEmptyObject(s)||x(s).each(function(e,i){l.push(i),t=!0}),x.isEmptyObject(d)||x(d).each(function(e,i){l.push(i),t=!0}),x.isEmptyObject(a)||x(a).each(function(e,i){l.push(i)}),x(l).each(function(){m.siblings(".redux_media_status").slideDown().append(this)}),u.close(),!0===t&&(redux.field_objects.multi_media.bindDelete(r),redux_change(x(r).find(".redux_media_status")))}),u.open()},redux.field_objects.multi_media.removeFile=function(e,i,t){var l;return e.preventDefault(),t.is(".attach_list .redux_remove_file_button")?(t.parents("li").remove(),redux_change(x(i).find(".redux_media_status"))):(e=t.attr("rel"),l=t.parents(".img_status"),i.find("input#"+e).val(""),i.find("input#"+e+"_id").val(""),(l.length?l:t.parents(".redux_media_status")).html("")),!1}}(jQuery);
|
||||
@@ -0,0 +1,64 @@
|
||||
.redux-container-multi_media {
|
||||
.redux_media_status {
|
||||
margin: 10px 0 0 0;
|
||||
|
||||
.redux-file-exists,
|
||||
.redux-max-limit {
|
||||
color:red;
|
||||
}
|
||||
|
||||
.img_status img {
|
||||
width: 50px!important;
|
||||
height: 50px!important;
|
||||
}
|
||||
|
||||
.img_status img, .embed_status {
|
||||
border:1px solid #DFDFDF;
|
||||
background: #FAFAFA;
|
||||
max-width:350px;
|
||||
padding: 5px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.img_status {
|
||||
clear: none;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.embed_status {
|
||||
float: left;
|
||||
max-width:800px;
|
||||
}
|
||||
|
||||
.img_status,
|
||||
.embed_status {
|
||||
position: relative;
|
||||
|
||||
.redux_remove_file_button {
|
||||
text-indent: -9999px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attach_list li {
|
||||
clear: both;
|
||||
display: inline-block;
|
||||
margin-bottom: 25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attach_list li img {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user