<?php

use DiviBooster\DiviBooster\Color;

include plugin_dir_path(__FILE__) . 'admin/settings/field_types/color.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/color-with-alpha.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/image.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/number.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/padding-top-bottom.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/select.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/text.php';
include plugin_dir_path(__FILE__) . 'admin/settings/field_types/textbox.php';


if (!class_exists('wtfplugin_1_0')) {

    class wtfplugin_1_0 {

        var $inlinecss = false;
        var $minifiedcss = true;
        var $inlinejs = false;
        var $minifiedjs = true;

        var $config;
        var $slug;
        var $error_handler;

        private $cacheurl;
        private $cachedir;

        function __construct($config) {

            $this->config = $config;
            $this->slug = $config['plugin']['slug'];

            // Enable minification
            add_filter('dbdb_cache_file_content_wp_head.css', 'booster_minify_css');
            add_filter('dbdb_cache_file_content_wp_footer.js', 'booster_minify_js');

            // Set up the cache
            $uploads = wp_upload_dir();
            $this->cacheurl = set_url_scheme($uploads['baseurl'] . '/' . $this->slug . '/');
            $this->cachedir = $uploads['basedir'] . '/' . $this->slug . '/';
            wp_mkdir_p($this->cachedir);

            /* Check for plugin update */
            add_action('init', array($this, 'update_cache'));
            add_action('db-divi-booster-updated', array($this, 'compile_patch_files'));

            // Customizer
            // Regenerate cache files when customizer saved
            add_action('customize_save_after', array($this, 'compile_patch_files'), 99);

            // === Load the function files (needs to happen before file compilation) ===

            // Get the enabled options
            $options = get_option($this->slug);

            // Ensure the retrieved option is an array
            if (!is_array($options)) {
                update_option($this->slug, array()); // Repair corrupted option value in the database
                $options = array();
            }

            $fixes = (isset($options['fixes']) && is_array($options['fixes'])) ? $options['fixes'] : array();

            // Enable any "Enabled by Default" features
            $enabled_by_default = apply_filters('dbdb_enabled_by_default', array());
            foreach ($enabled_by_default as $fix_slug) {
                if (!isset($fixes[$fix_slug]['enabled'])) {
                    $fixes[$fix_slug]['enabled'] = true;
                }
            }

            $fixes = apply_filters('divibooster_fixes', $fixes);

            foreach ($fixes as $fix => $data) {

                $fix_dir = BOOSTER_DIR_FIXES . "$fix/";

                // Load fix functions.php files, if enabled
                if (isset($data['enabled']) && $data['enabled']) {
                    $fix_fn_file = $fix_dir . "functions.php";
                    if (file_exists($fix_fn_file)) {
                        include_once($fix_fn_file);
                    }
                }
            }

            if (is_admin()) {

                // Set up settings plugin settings page
                add_action('admin_menu', array($this, 'create_settings_page'), 11); // register the settings page
                add_action('divibooster_settings_page_init', array($this, 'settings_page_init')); // register the settings
                add_action('admin_init', array($this, 'register_settings')); // register the settings

            } else {

                // === Site is being displayed, so output the various theme fix components === //

                // javascript
                if ($this->inlinejs) {
                    add_action('wp_footer', array($this, 'output_user_js_inline'));
                } else {
                    add_action('wp_enqueue_scripts', array($this, 'enqueue_user_js'), 9999); // load late to ensure dependencies available
                }

                // css
                if ($this->inlinecss) {
                    add_action('wp_head', array($this, 'output_user_css_inline'));
                } else {
                    add_action('wp_enqueue_scripts', array($this, 'enqueue_user_css'));
                }

                // footer html
                add_action('wp_footer', array($this, 'output_user_footer_html_inline'));
            }
        }

        function cacheurl() {
            return apply_filters('dbdb_cacheurl', $this->cacheurl);
        }

        function cachedir() {
            return apply_filters('dbdb_cachedir', $this->cachedir);
        }

        function settings_page_init() {
            // Re-compile the css / js / html files if the settings have been saved
            if (isset($_GET['settings-updated']) and $_GET['settings-updated'] == true) {
                $this->compile_patch_files();
            }

            // Load the settings page CSS and JS
            $this->enqueue_settings_files();
        }

        /* Rebuild the cache if needed */
        function update_cache() {
            if (!file_exists($this->cachedir) or !file_exists($this->cachedir . 'wp_head.css')) {
                $this->compile_patch_files(); // rebuild the cached files
            }
        }

        // === Handle JS and CSS files

        function enqueue_user_js() {
            clearstatcache();
            if (!file_exists($this->cachedir() . 'wp_footer.js') || !filesize($this->cachedir() . 'wp_footer.js')) {
                return;
            }
            $dependencies = array('jquery');
            if (wp_script_is('divi-custom-script', 'enqueued')) {
                $dependencies[] = 'divi-custom-script';
            }
            wp_enqueue_script(
                $this->slug . '-user-js',
                $this->cacheurl() . 'wp_footer.js',
                apply_filters($this->slug . '-js-dependencies', $dependencies),
                $this->last_save(),
                true
            );
        }

        function enqueue_user_css() {
            clearstatcache();
            if (!filesize($this->cachedir() . 'wp_head.css')) {
                return;
            }
            wp_enqueue_style($this->slug . '-user-css', $this->cacheurl() . 'wp_head.css', array(), $this->last_save());
        }

        function last_save() {
            $options = get_option($this->slug);
            $timestamp = isset($options['lastsave']) ? $options['lastsave'] : 0;
            return $timestamp;
        }

        function output_user_js_inline() {
            echo '<script>' . @file_get_contents($this->cachedir() . 'wp_footer.js') . '</script>';
        }
        function output_user_css_inline() {
            echo '<style>' . @file_get_contents($this->cachedir() . 'wp_head.css') . '</style>';
        }

        function output_user_footer_html_inline() {
            echo @file_get_contents($this->cachedir() . 'wp_footer.txt');
        }

        function enqueue_settings_files() {

            // plugin styles
            wp_enqueue_style($this->slug . '_admin_css', plugin_dir_url(__FILE__) . 'admin/settings.css', array(), BOOSTER_VERSION);

            // Color picker assets used by setting field controls.
            wp_enqueue_style('wp-color-picker');
            wp_enqueue_script('wp-color-picker');
            wp_enqueue_script('wp-color-picker-alpha', plugins_url('libs/wp-color-picker-alpha.min.js', __FILE__), array('wp-color-picker'), time(), true);

            // Media library APIs used by image / icon picker controls.
            wp_enqueue_media();

            // Plugin admin JS depends on color picker + media APIs.
            wp_enqueue_script(
                $this->slug . '_admin_js',
                plugin_dir_url(__FILE__) . 'admin/admin.js',
                array('jquery', 'wp-color-picker', 'wp-color-picker-alpha', 'media-editor'),
                BOOSTER_VERSION,
                true
            );
        }

        function compile_patch_files() {

            $files = array(
                'wp_head_style.php' => 'wp_head.css',
                'wp_footer_script.php' => 'wp_footer.js',
                'wp_footer.php' => 'wp_footer.txt',
                'wp_htaccess.php' => 'htaccess.txt'
            );

            foreach ($files as $in => $out) {
                $content = $this->patch_file_content($out, $in);
                file_put_contents($this->cachedir . $out, $content);
            }

            do_action('dbdb_compile_patch_files_after', $this, $files);

            //dbdb_save_mod_rewrite_rules();

            // Append our htaccess rules to the wordpress htaccess file
            if (!function_exists('get_home_path')) {
                require_once(ABSPATH . '/wp-admin/includes/file.php');
            }
            $wp_htaccess_file = get_home_path() . '/.htaccess';
            if (@is_readable($wp_htaccess_file) && @is_writeable($wp_htaccess_file)) {
                $htaccess = @file_get_contents($wp_htaccess_file);
                if (!empty($htaccess)) {
                    $rules = file_get_contents($this->cachedir . 'htaccess.txt');
                    if (strpos($htaccess, '# BEGIN ' . $this->slug) !== false) {
                        $htaccess = preg_replace(
                            '/# BEGIN ' . preg_quote($this->slug, '/') . '.*# END ' . preg_quote($this->slug, '/') . '/is',
                            "# BEGIN " . $this->slug . "\n$rules\n# END " . $this->slug,
                            $htaccess
                        );
                    } else {
                        $htaccess .= "\n# BEGIN " . $this->slug . "\n$rules\n# END " . $this->slug . "\n";
                    }
                    @file_put_contents($wp_htaccess_file, $htaccess);
                }
            }

            // Try to clear website caches, using Divi's own cache clearing code
            if (function_exists('et_core_clear_wp_cache') && function_exists('et_core_security_check_passed')) {
                et_core_clear_wp_cache();
            }
        }

        function patch_file_content($out, $in) {
            $result = $this->load_fixes_to_string($in); // Old way - load from files
            $result .= "\n";
            $result .= $this->do_action_to_string($out); // New way - use hooks 
            return apply_filters("dbdb_cache_file_content_{$out}", $result);
        }

        function load_fixes_to_string($in) {
            $options = get_option($this->slug);
            $content = '';
            if (isset($options['fixes'])) {
                foreach (@$options['fixes'] as $fix => $data) {
                    if (@$data['enabled']) {
                        ob_start();
                        $fixfile = BOOSTER_DIR_FIXES . "$fix/$in";
                        if (file_exists($fixfile)) {
                            $content .= "\n";
                            include($fixfile);
                        }
                        $content .= trim(ob_get_contents());
                        ob_end_clean();
                    }
                }
            }
            return $content;
        }

        function do_action_to_string($hook_name) {
            $result = '';
            ob_start();
            do_action($hook_name, $this);
            $result .= trim(ob_get_contents());
            ob_end_clean();
            return $result;
        }

        function register_settings() {
            register_setting(
                $this->slug . '-group',
                $this->slug,
                array(
                    'sanitize_callback' => array($this, 'sanitize_wtfdivi_option'),
                )
            );
        }

        /**
         * Sanitize the Divi Booster main option (wtfdivi).
         *
         * This is intentionally conservative:
         * - Force the top-level value to be an array.
         * - Recursively allow only arrays and scalar values.
         * - Drop objects/resources to reduce risk of PHP object injection / unexpected types.
         * - Keep strings/numbers/booleans as-is (WordPress will handle serialization).
         *
         * @param mixed $input
         * @return array
         */
        function sanitize_wtfdivi_option($input) {
            if (!is_array($input)) {
                return array();
            }

            return $this->sanitize_wtfdivi_option_value($input, 0);
        }

        /**
         * Recursive sanitizer helper.
         *
         * @param mixed $value
         * @param int $depth
         * @return mixed
         */
        private function sanitize_wtfdivi_option_value($value, $depth) {
            // Avoid pathological recursion. The option structure is expected to be shallow.
            if ($depth > 50) {
                return null;
            }

            if (is_array($value)) {
                $out = array();
                foreach ($value as $k => $v) {
                    // Only allow scalar keys (string/int). Skip anything else.
                    if (!is_string($k) && !is_int($k)) {
                        continue;
                    }

                    $sanitized = $this->sanitize_wtfdivi_option_value($v, $depth + 1);
                    // We use null as a sentinel for "drop this value".
                    if ($sanitized === null) {
                        continue;
                    }
                    $out[$k] = $sanitized;
                }
                return $out;
            }

            // Allow scalar values through unchanged.
            if (is_scalar($value)) {
                return $value;
            }

            // Disallow objects/resources/etc.
            return null;
        }

        function create_settings_page() {
            $page = add_submenu_page(dbdb_admin_menu_slug(), $this->config['plugin']['name'], $this->config['plugin']['shortname'], 'manage_options', BOOSTER_SETTINGS_PAGE_SLUG, array($this, 'settings_page'));
        }

        // create the options page
        function settings_page() {
            if (!current_user_can('manage_options')) {
                wp_die(__('You do not have sufficient permissions to access this page.'));
            }

            add_filter("db-settings-divi-booster-active-tab", function () {
                return 'general';
            });

            // Shorthand
            $slug = $this->slug;

            // Hook prior to settings page execution
            do_action("$slug-before-settings-page", $this);

            // Get last error, if any
            $last_error = get_option(BOOSTER_OPTION_LAST_ERROR);
            $last_error_details = get_option(BOOSTER_OPTION_LAST_ERROR_DESC);
            $has_error = !empty($last_error);
            $has_error_details = !empty($last_error_details);
            update_option(BOOSTER_OPTION_LAST_ERROR, ''); // clear last error

?>

            <div class="db-settings-wrap">

                <form enctype="multipart/form-data" method="post" action="options.php">

                    <?php do_action('db-settings-settings-box-before'); ?>
                    <div id="db-settings-box">
                        <h1 id="db-settings-box-title"><?php esc_html_e($this->config['plugin']['name']); ?> Settings</h1>

                        <?php
                        // Hide the shared page-level nav tabs (General / Updates) on the main settings page.
                        // This avoids a 3-deep navigation once we add the in-page 2-level tabs.
                        // (The updates / licensing page will continue to show the nav tabs.)
                        ?>

                        <div class="db-settings-box-tab-content">

                            <input type="hidden" name="<?php esc_attr_e($slug); ?>[lastsave]" value="<?php esc_attr_e(intval(time())); ?>" />

                            <?php
                            $options = get_option($slug);
                            $plugin_dir_url = plugin_dir_url(__FILE__);
                            $image_dir_url = $plugin_dir_url . 'img/';

                            // === New Divi-5 style tabs ===
                            // Convert config sections into a 2-level structure:
                            // - top headings: entries without a dash in the slug
                            // - sub headings: entries with a dash, grouped by the prefix before the first dash
                            $dbdb_section_groups = array();
                            $dbdb_topheadings = array();
                            $dbdb_general_subtab_slugs = array(); // top_slug => generated subtab slug
                            foreach ($this->config['sections'] as $sectionslug => $sectionheading) {
                                $is_subheading = (strpos($sectionslug, '-') !== false);
                                if (!$is_subheading) {
                                    $dbdb_topheadings[$sectionslug] = $sectionheading;
                                    if (!isset($dbdb_section_groups[$sectionslug])) {
                                        $dbdb_section_groups[$sectionslug] = array();
                                    }
                                    continue;
                                }

                                $parent = substr($sectionslug, 0, strpos($sectionslug, '-'));
                                if (!isset($dbdb_section_groups[$parent])) {
                                    $dbdb_section_groups[$parent] = array();
                                }
                                $dbdb_section_groups[$parent][$sectionslug] = $sectionheading;
                            }

                            // Any topheading with no subheadings gets an auto "General" subtab.
                            // This supports sections where settings are registered directly under the topheading hook.
                            foreach ($dbdb_topheadings as $top_slug => $top_label) {
                                if (!empty($dbdb_section_groups[$top_slug])) {
                                    continue;
                                }
                                $generated_slug = $top_slug . '-general';
                                $dbdb_general_subtab_slugs[$top_slug] = $generated_slug;
                                $dbdb_section_groups[$top_slug][$generated_slug] = __('General', 'divi-booster');
                            }

                            // Pick defaults (first top heading with at least one subheading, else first top heading)
                            $dbdb_default_top = '';
                            foreach ($dbdb_topheadings as $top_slug => $top_label) {
                                if (!empty($dbdb_section_groups[$top_slug])) {
                                    $dbdb_default_top = $top_slug;
                                    break;
                                }
                            }
                            if (empty($dbdb_default_top)) {
                                $dbdb_default_top = key($dbdb_topheadings);
                            }

                            $dbdb_default_sub = '';
                            if (!empty($dbdb_default_top) && !empty($dbdb_section_groups[$dbdb_default_top])) {
                                $dbdb_default_sub = key($dbdb_section_groups[$dbdb_default_top]);
                            }


                            // Tabs row (top headings) - lives *inside* the General tab content so we keep 2 rows total.
                            ?>
                            <div class="dbdb-settings-tabs" data-default-top="<?php echo esc_attr($dbdb_default_top); ?>" data-default-sub="<?php echo esc_attr($dbdb_default_sub); ?>">
                                <ul class="dbdb-settings-top-tabs" role="tablist">
                                    <?php foreach ($dbdb_topheadings as $top_slug => $top_label) {
                                        $has_sub = !empty($dbdb_section_groups[$top_slug]);
                                    ?>
                                        <li class="db-settings-box-tab dbdb-settings-top-tab" role="presentation">
                                            <a href="#" class="dbdb-settings-tab-link" role="tab" aria-selected="false" data-top="<?php echo esc_attr($top_slug); ?>" data-has-sub="<?php echo esc_attr($has_sub ? '1' : '0'); ?>">
                                                <?php echo esc_html($top_label); ?>
                                            </a>
                                        </li>
                                    <?php } ?>
                                </ul>

                                <div class="dbdb-settings-subtabs-wrapper">
                                    <?php foreach ($dbdb_topheadings as $top_slug => $top_label) {
                                        $subtabs = isset($dbdb_section_groups[$top_slug]) ? $dbdb_section_groups[$top_slug] : array();
                                    ?>
                                        <ul class="dbdb-settings-sub-tabs" role="tablist" data-top="<?php echo esc_attr($top_slug); ?>" style="display:none;">
                                            <?php foreach ($subtabs as $sectionslug => $sectionheading) { ?>
                                                <li class="db-settings-box-tab dbdb-settings-sub-tab" role="presentation">
                                                    <a href="#" class="dbdb-settings-subtab-link" role="tab" aria-selected="false" data-top="<?php echo esc_attr($top_slug); ?>" data-sub="<?php echo esc_attr($sectionslug); ?>">
                                                        <?php echo esc_html($sectionheading); ?>
                                                    </a>
                                                </li>
                                            <?php } ?>
                                        </ul>
                                    <?php } ?>
                                </div>

                                <div class="dbdb-settings-panels">
                                    <?php
                                    // Output panels:
                                    // - real subheading groups (existing behavior)
                                    // - auto "General" panels for topheadings that lacked subheadings
                                    foreach ($this->config['sections'] as $sectionslug => $sectionheading) {
                                        $is_subheading = (strpos($sectionslug, '-') !== false);
                                        if (!$is_subheading) {
                                            continue;
                                        }
                                        $parent = substr($sectionslug, 0, strpos($sectionslug, '-'));
                                    ?>
                                        <div class="<?php esc_attr_e(dbdb_setting_group_classes(true)); ?> dbdb-settings-panel" data-top="<?php echo esc_attr($parent); ?>" data-sub="<?php echo esc_attr($sectionslug); ?>" style="display:none;">
                                            <?php if (has_action("$slug-$sectionslug")) { ?>
                                                <?php do_action("$slug-$sectionslug", $this); // output settings ?>
                                            <?php } ?>
                                        </div>
                                    <?php } ?>

                                    <?php foreach ($dbdb_general_subtab_slugs as $top_slug => $generated_slug) { ?>
                                        <div class="<?php esc_attr_e(dbdb_setting_group_classes(true)); ?> dbdb-settings-panel" data-top="<?php echo esc_attr($top_slug); ?>" data-sub="<?php echo esc_attr($generated_slug); ?>" style="display:none;">
                                            <?php if (has_action("$slug-$top_slug")) { ?>
                                                <?php do_action("$slug-$top_slug", $this); // output settings registered directly under the topheading ?>
                                            <?php } ?>
                                        </div>
                                    <?php } ?>
                                </div>
                            </div>

                            <?php

                            settings_fields("$slug-group");
                            do_settings_sections("$slug-group");
                            ?>
                        </div>
                    </div>
                    <?php
                    submit_button();
                    ?>
                    <hr />
                </form>
                <div style="clear:both"></div>
                <?php do_action("$slug-plugin-footer"); ?>
            </div>

            <?php
        }

        function add_setting($hook, $callback) {
            add_action($this->slug . '-' . $hook, $callback);
        }

        function setting_start($classes = '') {
            echo '<div class="wtf-setting ' . esc_attr($classes) . '">';
        }

        function setting_end() {
            echo '</div>';
        }

        // return the base name and option var for a given settings file
        function get_setting_bases($file) {
            $fixslug = $this->feature_slug($file); // use the fix's directory name as its slug
            $options = get_option($this->slug, array());
            if (!is_array($options)) {
                $options = array();
            }
            $namebase = $this->slug . '[fixes][' . $fixslug . ']';
            $optionbase = @$options['fixes'][$fixslug];
            return array($namebase, $optionbase);
        }

        // Return the slug for a fix from the __FILE__ value
        function feature_slug($file) {
            return basename(dirname($file));
        }

        function get_option_name($group, $feature, $setting) {
            $feature = basename(dirname($feature)); // allow __FILE__ or direct setting name
            return $this->slug . "[$group][$feature][$setting]";
        }

        // === Settings UI Components === //

        function techlink($url = false) {
            if ($url) {
            ?>
                <a href="<?php esc_attr_e($url); ?>" class="techlink dashicons dashicons-info dbdb_no_active_outline" title="Read my post on this fix" target="_blank"></a>
            <?php
            }
        }

        function hiddenfield($file, $field = '') {
            list($name, $option) = $this->get_setting_bases($file);
            $value = '';
            if (is_array($option) && isset($option[$field]) && is_scalar($option[$field])) {
                $value = (string) $option[$field];
            }
            ?>
            <input type="hidden" name="<?php echo $name; ?><?php echo empty($field) ? '' : esc_attr("[$field]"); ?>" value="<?php echo esc_attr($value); ?>" />
        <?php
        }

        function hiddencheckbox($file, $field = 'enabled') {
            list($name, $option) = $this->get_setting_bases($file); ?>
            <input type="checkbox" style="visibility:hidden" name="<?php esc_attr_e($name); ?>[<?php esc_attr_e($field); ?>]" value="1" checked="checked" />
        <?php
        }

        function checkbox($file, $field = 'enabled') {
            list($name, $option) = $this->get_setting_bases($file);

            $feature_slug = $this->feature_slug($file);

            // Get current checkbox status
            $is_checked = empty($option[$field]) ? false : $option[$field];
            $is_checked = apply_filters("divibooster_checkbox_{$feature_slug}_{$field}", $is_checked);

            $field_name = "{$name}[{$field}]";
        ?>
            <?php
            $input_id = 'dbdb-' . $feature_slug . '-' . $field;
            $toggle_state_class = $is_checked ? 'dbdb-on-state' : 'dbdb-off-state';
            ?>

            <span class="dbdb-toggle et-box-content">
                <input
                    id="<?php esc_attr_e($input_id); ?>"
                    type="checkbox"
                    class="dbdb-toggle-checkbox et-checkbox yes_no_button"
                    name="<?php esc_attr_e($field_name); ?>"
                    value="1"
                    <?php checked($is_checked, 1); ?>
                />
                <label class="dbdb-toggle-track et_pb_yes_no_button <?php echo esc_attr($toggle_state_class); ?>" for="<?php esc_attr_e($input_id); ?>">
                    <span class="dbdb-toggle-thumb et_pb_button_slider"></span>
                </label>
            </span>
<?php
        }

        function selectpicker($file, $field_id, $choices, $selected_choice) {
            $feature_slug = $this->feature_slug($file);
            list($setting_id, $option) = $this->get_setting_bases($file);
            $field = new DBDBSelectField($feature_slug, $setting_id, $field_id, $choices, $selected_choice);
            $field->render();
        }

        function numberpicker($file, $field_id, $default_value = 1, $minimum_value = 0) {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $current_value = empty($option[$field_id]) ? '' : $option[$field_id];
            $field = new DBDBNumberField($setting_id, $field_id, $current_value, $default_value, $minimum_value);
            $field->render();
        }

        function paddingpicker($file, $padding_defaults = array()) {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $field = new DBDBPaddingTopBottomField($setting_id, $option, $padding_defaults);
            $field->render();
        }

        function textpicker($file, $field_id, $default = '') {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $value = empty($option[$field_id]) ? '' : $option[$field_id];
            $field = new DBDBTextField($setting_id, $field_id, $value, $default);
            $field->render();
        }

        function textboxpicker($file, $field_id, $default_text = '') {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $current_text = empty($option[$field_id]) ? '' : $option[$field_id];
            $field = new DBDBTextboxField($setting_id, $field_id, $current_text, $default_text);
            $field->render();
        }

        function imagepicker($file, $field_id) {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $value = empty($option[$field_id]) ? '' : $option[$field_id];
            $field = new DBDBImageField($setting_id, $field_id, $value);
            $field->render();
        }

        function colorpicker($file, $field_id, $default_color = "#ffffff", $has_opacity_control = false) {
            list($setting_id, $option) = $this->get_setting_bases($file);
            $current_color = empty($option[$field_id]) ? $default_color : $option[$field_id];
            if ($has_opacity_control) {
                $field = new DBDBColorWithAlphaField(
                    $setting_id,
                    $field_id,
                    Color::fromText($current_color),
                    Color::fromText($default_color)
                );
            } else {;
                $field = new DBDBColorField(
                    $setting_id,
                    $field_id,
                    Color::fromText($current_color),
                    Color::fromText($default_color)
                );
            }
            $field->render();
        }

        // create html input 
        function input($attribs = array()) {
            $html = "";
            foreach ($attribs as $k => $v) {
                $html .= " " . esc_html($k) . '="' . esc_attr($v) . '"';
            }
            return "<input $html/>";
        }
    }
}
