<?php

declare(strict_types=1);

namespace formgenerator;

use formgenerator\components\Attribute;
use formgenerator\components\Button;
use formgenerator\components\Buttongroup;
use formgenerator\components\Checkboxes;
use formgenerator\components\Dependent;
use formgenerator\components\Fileuploader;
use formgenerator\components\FormSection;
use formgenerator\components\Hcaptcha;
use formgenerator\components\Heading;
use formgenerator\components\Html;
use formgenerator\components\Input;
use formgenerator\components\Paragraph;
use formgenerator\components\Plugin;
use formgenerator\components\Radio;
use formgenerator\components\Recaptcha;
use formgenerator\components\Select;
use formgenerator\components\Textarea;
use formgenerator\components\UserForm;
use phpformbuilder\Form;

/**
 * Class FormGenerator
 *
 * @version 1.0
 * @author  Gilles Migliori - gilles.migliori@gmail.com
 */

class FormGenerator
{
    public UserForm $userForm;
    /**
     * @var FormSection[]
     */
    public array $formSections;
    public string $iconFontUrl = '';
    /**
     * @var array<string>
     */
    private array $autoloadedPlugins = array('bootstrap-select', 'bootstrap-input-spinner', 'colorpicker', 'intl-tel-input', 'ladda', 'select2');

    private string $currentDir;

    // special data
    /**
     * @var array<int, array<string, string>>
     */
    private array $dbFields = [];

    private bool $hasRecaptcha = false;
    private string $recaptchaPrivateKey = '';

    private bool $hasHcaptcha = false;
    private string $hcaptchaSecretKey = '';

    private bool $hasJqueryPlugins = false;
    /**
     * @var array<string>
     */
    private array $frameworksWithoutJquery = ['bs5', 'bulma', 'tailwind', 'uikit'];

    /**
     * @var array<int, array<string, int|string>> $fileuploaders An array of FileUploader objects data.
     */
    private array $fileuploaders = [];

    private bool $isModal = false;
    private bool $isPopover = false;
    private string $popoverDataAttributes = '';

    private string $currentCols       = '4-8';
    private bool $currentlyCentered = false;

    private bool $hasEmailFields = false;
    /**
     * @var array<string> $emailFieldNames An array to store the names of email fields.
     */
    private array $emailFieldNames = [];

    private bool $outputPreview;
    private Form $phpForm;
    /**
     * @var array<string, mixed> $phpFormCode The PHP form code generated by the form generator.
     */
    private array $phpFormCode = [
        'components'     => [],
        'globalPlugins' => '',
        'head'           => '',
        'ifPosted'      => '',
        'main'           => '',
        'message'        => '',
        'scripts'        => '',
        'startForm'     => '',
        'start'          => ''
    ];

    private string $errorMsg = '';

    /**
     * FormGenerator constructor.
     *
     * @param string $jsontring     The JSON string representing the form structure.
     * @param bool   $outputPreview (optional) Whether to output a preview of the form. Default is true.
     */
    public function __construct(string $jsontring, bool $outputPreview = true)
    {
        $this->currentDir = $this->getCurrentDir();
        $this->outputPreview = $outputPreview;

        $json = json_decode($jsontring);
        // echo '<pre>' . $json . '</pre>';
        $jsonError = $this->getLastJsonError();
        if (!empty($jsonError)) {
            $this->errorMsg = $jsonError;
            $this->buildErrorMsg($this->errorMsg);
        } else {
            $this->userForm = new UserForm($json->userForm);
            $this->formSections = array_map(
                function ($sectionData) {
                    return new FormSection($sectionData);
                },
                $json->formSections
            );
            $this->iconFontUrl = $this->getIconFont();
            $this->getSpecialData();

            $this->createForm($this->userForm);

            foreach ($this->userForm->plugins as $plugin) {
                $this->addPlugin($plugin, true);
            }

            // Group components depending on their width
            // and the width of the following component
            $this->getSectionGroups();

            foreach ($this->formSections as $section) {
                $this->buildSection($section);
            }

            $this->buildCodeParts();
        }
    }

    /**
     * Outputs the generated code for the form.
     *
     * This method is responsible for generating and outputting the code for the form
     * based on the configured form elements and settings.
     *
     * @return void
     */
    public function outputCode(): void
    {
        $output = '';
        $codePartIndex = 1;
        $numberedClass = 'd-inline-block px-3 py-1 rounded-circle bg-info text-info-100 me-3';
        if ($this->userForm->ajax !== 'true') {
            $output .= '<h5 class="fw-light"><span class="' . $numberedClass . '">' . $codePartIndex . '</span>Add the following code at the very beginning of your file</h5>';
        } else {
            $output .= '<h5 class="fw-light"><span class="' . $numberedClass . ' mb-3">' . $codePartIndex . '</span>Create a <span class="badge bg-secondary fw-normal px-2 py-1">/ajax-forms</span> directory at the root of your project and place a file named <span class="badge bg-secondary fw-normal px-2 py-1">' . $this->userForm->id . '.php</span> in it.<br>Then save the code of the form below in this file</h5>';
        }

        $output .= '<pre><code class="language-php">';
        $output .= $this->phpFormCode['main'];
        $output .= '?&gt;</code></pre>';

        $codePartIndex++;

        $output .= '<hr class="mt-4">';
        if ($this->userForm->ajax === 'true') {
            $output .= '<h5 class="fw-light"><span class="' . $numberedClass . '">' . $codePartIndex . '</span>Add the following code in your page to display the form</h5>';
            $output .= '<pre><code class="language-php">';
            $output .= '&lt;div id="' . $this->userForm->id . '-loader"&gt;&lt;/div&gt;';
            $output .= '</code></pre>';
        } else {
            $output .= '<h5 class="fw-light"><span class="' . $numberedClass . '">' . $codePartIndex . '</span>Add the following code between &lt;head&gt;&lt;/head&gt;</h5>';
            $output .= '<pre><code class="language-php">';
            $output .= $this->phpFormCode['head'];
            $output .= '</code></pre>';

            $codePartIndex++;

            $output .= '<hr class="mt-4">';
            $output .= '<h5 class="fw-light"><span class="' . $numberedClass . '">' . $codePartIndex . '</span>Add the following code in your page to display the form</h5>';
            $output .= '<pre><code class="language-php">';
            $output .= $this->phpFormCode['render'];
            $output .= '</code></pre>';
        }

        $codePartIndex++;

        $requiresjQuery = $this->hasJqueryPlugins;
        if (!in_array($this->userForm->framework, $this->frameworksWithoutJquery)) {
            $requiresjQuery = true;
        }

        $output .= '<hr class="mt-4">';
        $output .= '<h5 class="fw-light"><span class="' . $numberedClass . '">' . $codePartIndex . '</span>Add the following code just before &lt;/body&gt;';
        if ($requiresjQuery && $this->userForm->ajax === 'true') {
            $output .= '<sup class="text-danger ms-2">*</sup>';
        }
        $output .= '</h5>';
        $output .= '<pre><code class="language-php">';
        $output .= $this->phpFormCode['scripts'];
        $output .= '</code></pre>';
        if ($requiresjQuery && $this->userForm->ajax === 'true') {
            $output .= '<p><sup class="text-danger me-2">*</sup><span class="text-secondary">jQuery script must have already been added before.</span></p>';
        }

        echo $output;
    }

    /**
     * Outputs the generated page code.
     *
     * This method is responsible for generating and outputting the code for the form page.
     * It should be called after the form has been generated using the `generateForm()` method.
     *
     * @return void
     */
    public function outputPageCode(): void
    {
        $output = '';
        if ($this->userForm->ajax === 'true') {
            $output .= '<div class="alert alert-info"><p>Forms loaded with Ajax use 2 files - refer to the <em>Form code</em> tab</p><p class="mb-0">It is therefore not possible to display a complete one-page code here</p></div>';
        } else {
            $pageCode = '';
            if ($fc = file_get_contents('sample-pages/' . $this->userForm->framework . '.html')) {
                $pageCode = htmlspecialchars($fc);
            }

            $find = array("`\{form-php\}[\r\n]+`", "`\{form-head\}[\r\n]+`", "`\{form\}`", "`\{form-js\}`");

            $headCode = $this->reindentCode($this->phpFormCode['head'], 4);
            $renderCode = $this->reindentCode($this->phpFormCode['render'], 16);
            $scriptsCode = $this->reindentCode($this->phpFormCode['scripts'], 4);

            $replace = array($this->phpFormCode['main'] . "?&gt;\n", $headCode, $renderCode, $scriptsCode);
            $pageCode = preg_replace($find, $replace, $pageCode);
            $output .= '<pre><code class="language-php">' . $pageCode . '</code></pre>';
        }

        echo $output;
    }

    /**
     * Outputs a preview of the generated form.
     *
     * This method is responsible for generating and displaying a preview of the form
     * that has been generated using the form generator. The preview can be used to
     * visually inspect the form layout and make any necessary adjustments before
     * finalizing the form.
     *
     * @return void
     */
    public function outputPreview(): void
    {
        if ($this->isModal) {
            $btnClass = $this->getTriggerButtonClass();
            echo '<button data-micromodal-trigger="modal-' . $this->userForm->id . '" class="' . $btnClass . '">Open the modal form</button>';
        } elseif ($this->isPopover) {
            $btnClass = $this->getTriggerButtonClass();
            echo '<button data-popover-trigger="' . $this->userForm->id . '"' . $this->popoverDataAttributes . ' class="' . $btnClass . '">Open the popover form</button>';
        }
        $this->phpForm->render();
    }

    /**
     * Prints the JavaScript code for the form generator.
     *
     * @return void
     */
    public function printJsCode(): void
    {
        $jsCode = '';
        if ($this->hasJqueryPlugins && in_array($this->userForm->framework, $this->frameworksWithoutJquery)) {
            $jsCode .= '<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>';
        }
        $jsCode .= $this->phpForm->getJsCode(false);
        // base_url for tinymce in preview
        echo str_replace(array('location.protocol', 'location.host'), array('window.parent.location.protocol', 'window.parent.location.host'), $jsCode);
    }

    /**
     * Adds a new attribute to the component attribute array.
     *
     * @param  string           $newAttrName  The name of the new attribute.
     * @param  mixed            $newAttrValue The value of the new attribute.
     * @param  array<Attribute> $compAttr     The component attribute array.
     * @return array<Attribute> The updated component attribute array.
     */
    private function addAttribute(string $newAttrName, $newAttrValue, array $compAttr): array
    {
        // create new attr object then push it to the component attributes
        $newAttr = new \stdClass();
        $newAttr->name = $newAttrName;
        $newAttr->value = $newAttrValue;
        $compAttr[] = new Attribute($newAttr);

        return $compAttr;
    }

    /**
     * @param  \formgenerator\components\Button $comp
     * @return void
     */
    private function addBtn(Button $comp): void
    {
        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);
        $label = $this->addBtnIcon($comp->icon, $comp->iconPosition, $comp->label);
        if ($this->outputPreview) {
            if ($comp->center === 'true') {
                $this->phpForm->centerContent();
            }
            $this->phpForm->addBtn($comp->type, $comp->name, $comp->value, $label, $attr);
            if ($comp->center === 'true') {
                $this->phpForm->centerContent(false);
            }
        }
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent();\n";
        }
        $this->phpFormCode['components'][] = "\$form->addBtn('$comp->type', '$comp->name', '$comp->value', '" . $this->sanitize($label) . "', '" . $this->sanitize($attr) . "');\n";
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent(false);\n";
        }
    }

    /**
     * @param  \formgenerator\components\Buttongroup $comp
     * @return void
     */
    private function addBtngroup(ButtonGroup $comp): void
    {
        foreach ($comp->plugins as $plugin) {
            if ($plugin->pluginName == 'ladda') {
                foreach ($comp->buttons as $btn) {
                    foreach ($plugin->dataAttributes as $attr) {
                        $btn->attr = $this->addAttribute('data-' . $attr->name, $attr->value, $btn->attr);
                    }
                }
            }
        }
        foreach ($comp->buttons as $btn) {
            $attrArray = $this->mergeAttributes($btn);
            $attr = $this->getAttributes($attrArray);
            $label = $this->addBtnIcon($btn->icon, $btn->iconPosition, $btn->label);
            if ($this->outputPreview) {
                $this->phpForm->addBtn($btn->type, $btn->name, $btn->value, $label, $attr, $comp->name);
            }
            $this->phpFormCode['components'][] = "\$form->addBtn('$btn->type', '$btn->name', '$btn->value', '" . $this->sanitize($label) . "', '" . $this->sanitize($attr) . "', '$comp->name');\n";
        }
        if ($this->outputPreview) {
            if ($comp->center === 'true') {
                $this->phpForm->centerContent();
            }
            $this->phpForm->printBtnGroup($comp->name);
            if ($comp->center === 'true') {
                $this->phpForm->centerContent(false);
            }
        }
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent();\n";
        }
        $this->phpFormCode['components'][] = "\$form->printBtnGroup('$comp->name');\n";
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent(false);\n";
        }
    }

    /**
     * @param  string $icon
     * @param  string $iconPosition
     * @param  string $label
     * @return string
     */
    private function addBtnIcon(string $icon, string $iconPosition, string $label): string
    {
        if (!empty($icon)) {
            $iconHtml = '<i class="' . $icon . '" aria-hidden="true"></i>';
            if ($iconPosition === 'before') {
                $label = $iconHtml . ' ' . $label;
            } else {
                $label .= ' ' . $iconHtml;
            }
        }

        return $label;
    }

    /**
     * @param  \formgenerator\components\Checkboxes $comp
     * @return void
     */
    private function addCheckboxGroup(Checkboxes $comp): void
    {
        $phpCode = '';

        $inline = false;
        if (filter_var($comp->inline, FILTER_VALIDATE_BOOLEAN)) {
            $inline = true;
        }

        // helper
        $this->addHelper($comp->helper, $comp->name);

        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);

        foreach ($comp->checkboxes as $chk) {
            $chkAttr = [];

            if ($comp->value === $chk->value) {
                $chkAttr[] = 'checked';
            }
            if (isset($chk->disabled) && $chk->disabled) {
                $chkAttr[] = 'disabled';
            }
            foreach ($chk->attr as $attrib) {
                if (!empty($attrib->value)) {
                    $chkAttr[] = $attrib->name . '=' . $attrib->value;
                }
            }
            $chkAttr = implode(', ', $chkAttr);
            if ($this->outputPreview) {
                $this->phpForm->addCheckbox($comp->name, $chk->text, $chk->value, $chkAttr);
            }
            $phpCode .= "\$form->addCheckbox('$comp->name', '" . $this->sanitize($chk->text) . "', '$chk->value', '" . $this->sanitize($chkAttr) . "');\n";
        }
        if ($this->outputPreview) {
            if ($comp->center === 'true') {
                $this->phpForm->centerContent();
            }
            $this->phpForm->printCheckboxGroup($comp->name, $comp->label, $inline, $attr);
            if ($comp->center === 'true') {
                $this->phpForm->centerContent(false);
            }
        }
        if ($comp->center === 'true') {
            $phpCode .= "\$form->centerContent();\n";
        }
        $phpCode .= "\$form->printCheckboxGroup('$comp->name', '" . $this->sanitize($comp->label) . "', $comp->inline, '" . $this->sanitize($attr) . "');\n";
        if ($comp->center === 'true') {
            $phpCode .= "\$form->centerContent(false);\n";
        }
        $this->phpFormCode['components'][] = $phpCode;
        foreach ($comp->plugins as $plugin) {
            if (!in_array($plugin->pluginName, $this->autoloadedPlugins)) {
                $this->addPlugin($plugin);
            }
        }
    }

    /**
     * addGroupInputs
     *
     * @param  array{string, string, string|null} $group
     * @return void
     */
    private function addGroupInputs(array $group): void
    {
        if ($this->outputPreview) {
            if (isset($group[2])) {
                $this->phpForm->groupElements($group[0], $group[1], $group[2]);
            } else {
                $this->phpForm->groupElements($group[0], $group[1]);
            }
        }
        if (isset($group[2])) {
            $this->phpFormCode['components'][] = "\$form->groupElements('$group[0]', '$group[1]', '$group[2]');\n";
        } else {
            $this->phpFormCode['components'][] = "\$form->groupElements('$group[0]', '$group[1]');\n";
        }
    }

    /**
     * @param  \formgenerator\components\Fileuploader $comp
     * @return void
     */
    private function addFileuploader(Fileuploader $comp): void
    {
        $this->hasJqueryPlugins = true;
        $this->fileuploaders[] = [
            'index'      => $comp->index,
            'fieldname'  => $comp->name,
            'upload_dir' => '../../../../..' . $comp->uploadDir
        ];
        $this->addHelper($comp->helper, $comp->name);
        if (empty($comp->extensions)) {
            $extensions = null;
        } else {
            $extensions = '[\'' . preg_replace('`([\s]?),([\s]?)`', "', '", $comp->extensions) . '\']';
        }
        if ($this->outputPreview) {
            $fileUploadConfig = [
                'upload_dir'    => '../../../../..' . $comp->uploadDir,
                'limit'         => $comp->limit,
                'file_max_size' => $comp->fileMaxSize,
                'debug'         => true
            ];
            if ($extensions !== null) {
                $fileUploadConfig['extensions']  = $extensions;
            }
            if ($comp->xml === 'image-upload') {
                $fileUploadConfig['thumbnails']  = $comp->thumbnails;
                $fileUploadConfig['editor']      = $comp->editor;
                $fileUploadConfig['width']       = $comp->widthImg;
                $fileUploadConfig['height']      = $comp->heightImg;
                $fileUploadConfig['crop']        = $comp->crop;
            }
            $alert = Form::buildAlert('Information: The file upload is just simulated in the preview. No file is really uploaded.', $this->userForm->framework, 'info');
            $this->phpForm->addHtml($alert);
            $this->phpForm->addFileUpload($comp->name, '', $comp->label, '', $fileUploadConfig);
        }

        $phpfcode = [];

        $phpfcode[] = "
    // Prefill upload with existing file
    \$currentFile = []; // default empty

    \$currentFilePath = '../../../../..$comp->uploadDir';

    /* INSTRUCTIONS:
        If you get a filename from your database or anywhere
        and want to prefill the uploader with this file,
        replace \"filename.ext\" with your filename variable in the line below.
    */
    \$currentFileName = 'filename.ext';

    if (file_exists(\$currentFilePath . \$currentFileName)) {
        \$currentFileSize = filesize(\$currentFilePath . \$currentFileName);
        \$currentFileType = mime_content_type(\$currentFilePath . \$currentFileName);
        \$currentFile = [
            'name' => \$currentFileName,
            'size' => \$currentFileSize,
            'type' => \$currentFileType,
            'file' => \$currentFilePath . \$currentFileName, // url of the file
            'data' => [
                'listProps' => [
                    'file' => \$currentFileName
                ]
            ]
        ];
    }\n\n";
        $phpfcode[] = "\$fileUploadConfig = [
    'upload_dir'    => '../../../../..$comp->uploadDir',
    'limit'         => $comp->limit,
    'file_max_size' => $comp->fileMaxSize,";
        if ($extensions !== null) {
            $phpfcode[] = "'extensions'    => $extensions,";
        }
        if ($comp->xml === 'image-upload') {
            $phpfcode[] = "
    'thumbnails'    => $comp->thumbnails,
    'editor'        => $comp->editor,
    'width'         => $comp->widthImg,
    'height'        => $comp->heightImg,
    'crop'          => $comp->crop,";
        }
        $phpfcode[] = "\n    'debug'         => true\n];\n";

        $phpfcode[] = "\$form->addFileUpload('$comp->name', '', '" . $this->sanitize($comp->label) . "', '', \$fileUploadConfig, \$currentFile);\n";

        $this->phpFormCode['components'][] = implode('', $phpfcode);
    }

    /**
     * @param  string $helper
     * @param  string $name
     * @return void
     */
    private function addHelper(string $helper, string $name): void
    {
        if (!empty($helper)) {
            if ($this->outputPreview) {
                $this->phpForm->addHelper($helper, $name);
            }
            $this->phpFormCode['components'][] = "\$form->addHelper('" . $this->sanitize($helper) . "', '$name');\n";
        }
    }

    /**
     * @param  string $icon
     * @param  string $iconPosition
     * @param  string $name
     * @return void
     */
    private function addIcon(string $icon, string $iconPosition, string $name): void
    {
        if (!empty($icon)) {
            if ($this->userForm->framework === 'foundation') {
                $icon = 'input-group-label ' . $icon;
            } elseif ($this->userForm->framework === 'uikit') {
                $icon = 'uk-form-icon ' . $icon;
            }
            $iconHtml = '<i class="' . $icon . '" aria-label="hidden"></i>';
            if (strpos($icon, 'material') > -1) {
                $ic = explode(' ', $icon);
                if (isset($ic[2])) {
                    $iconClass = $ic[0];
                    $iconText = $ic[1];
                } else {
                    $iconClass = $ic[0] . ' ' . $ic[1];
                    $iconText = $ic[2];
                }
                $iconHtml = '<i class="' . $iconClass . '" aria-label="hidden">' . $iconText . '</i>';
            }
            if ($this->outputPreview) {
                $this->phpForm->addIcon($name, $iconHtml, $iconPosition);
            }
            $this->phpFormCode['components'][] = "\$form->addIcon( '$name', '" . $this->sanitize($iconHtml) . "', '$iconPosition');\n";
        }
    }

    /**
     * @param  Html $comp
     * @return void
     */
    private function addHtml(Html $comp): void
    {
        if ($this->outputPreview) {
            $this->phpForm->addHtml($comp->value);
        }
        $this->phpFormCode['components'][] = "\$form->addHtml('" . $this->sanitize($comp->value) . "');\n";
    }

    /**
     * @param  \formgenerator\components\Input $comp
     * @return void
     */
    private function addInput(Input $comp): void
    {
        foreach ($comp->plugins as $plugin) {
            if ($plugin->pluginName == 'colorpicker') {
                $comp->attr = $this->addAttribute('data-colorpicker', 'true', $comp->attr);
            }
        }
        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);
        $this->addIcon($comp->icon, $comp->iconPosition, $comp->name);
        $this->addHelper($comp->helper, $comp->name);
        if ($this->outputPreview) {
            $this->phpForm->addInput($comp->type, $comp->name, $comp->value, $comp->label, $attr);
        }
        $this->phpFormCode['components'][] = "\$form->addInput('$comp->type', '$comp->name', '$comp->value', '" . $this->sanitize($comp->label) . "', '" . $this->sanitize($attr) . "');\n";
        foreach ($comp->plugins as $plugin) {
            if (!in_array($plugin->pluginName, $this->autoloadedPlugins)) {
                $this->addPlugin($plugin);
            }
        }
    }

    /**
     * @param  \formgenerator\components\Paragraph $comp
     * @return void
     */
    private function addParagraph(Paragraph $comp): void
    {
        $class = '';
        if (!empty($comp->clazz)) {
            $class = ' class="' . $comp->clazz . '"';
        }
        if ($this->outputPreview) {
            $this->phpForm->addHtml('<p' . $class . '>' . $comp->value . '</p>');
        }
        $this->phpFormCode['components'][] = "\$form->addHtml('" . $this->sanitize('<p' . $class . '>' . $comp->value . '</p>') . "');\n";
    }

    /**
     * @param  \formgenerator\components\Plugin $plugin
     * @param  bool                             $global
     * @return void
     */
    private function addPlugin(Plugin $plugin, bool $global = false): void
    {
        $replacements = [];
        $replacementsCodeArray = [];
        foreach ($plugin->replacements as $key => $repl) {
            $replacements["$key"] = $repl;
            $replacementsCodeArray[] = "'$key' => '$repl'";
        }
        // array('availableTags' => '"value 1","value 2","add other values ..."')

        if (empty($replacements)) {
            if ($this->outputPreview) {
                if ($plugin->pluginName === 'modal') {
                    $modalOptions = [
                        'title'       => $plugin->title,
                        'title-class' => $plugin->titleClass,
                        'title-tag'   => $plugin->titleTag,
                        'animation'   => $plugin->animation,
                        'blur'        => $plugin->blur
                    ];
                    $this->phpForm->modal($modalOptions);
                } elseif ($plugin->pluginName === 'pretty-checkbox') {
                    $plain = '';
                    if ($plugin->plain === 'true') {
                        $plain = 'plain';
                    }
                    $prettyCheckboxOptions = [
                        'checkboxStyle'  => $plugin->checkboxStyle,
                        'radioStyle'     => $plugin->radioStyle,
                        'fill'           => $plugin->fill,
                        'plain'          => $plain,
                        'size'           => $plugin->size,
                        'animations'     => $plugin->animations
                    ];
                    $this->phpForm->addPlugin($plugin->pluginName, '#' . $this->userForm->id, $plugin->jsConfig, $prettyCheckboxOptions);
                } elseif ($plugin->pluginName === 'popover') {
                    $this->phpForm->popover();
                } else {
                    $this->phpForm->addPlugin($plugin->pluginName, $plugin->selector, $plugin->jsConfig);
                }
            }

            if ($plugin->pluginName === 'formvalidation') {
                $formCode = "\$form->addPlugin('$plugin->pluginName', '$plugin->selector');\n";
            } elseif ($plugin->pluginName === 'modal') {
                $this->isModal = true;
                $fcode = [
                    "\$modalOptions = [",
                    "    'title'       => '$plugin->title',",
                    "    'title-class' => '$plugin->titleClass',",
                    "    'title-tag'   => '$plugin->titleTag',",
                    "    'animation'   => '$plugin->animation',",
                    "    'blur'        => '$plugin->blur'",
                    "];",
                    "\$form->modal(\$modalOptions);"
                ];
                $formCode = implode("\n", $fcode);
            } elseif ($plugin->pluginName === 'pretty-checkbox') {
                $plain = '';
                if ($plugin->plain === 'true') {
                    $plain = 'plain';
                }
                $fcode = [
                    "\$prettyCheckboxOptions = [",
                    "    'checkboxStyle'  => '$plugin->checkboxStyle',",
                    "    'radioStyle'     => '$plugin->radioStyle',",
                    "    'fill'           => '$plugin->fill',",
                    "    'plain'          => '$plain',",
                    "    'size'           => '$plugin->size',",
                    "    'animations'     => '$plugin->animations'",
                    "];",
                    "\$form->addPlugin('pretty-checkbox', '#" . $this->userForm->id . "', '" . $plugin->jsConfig . "', \$prettyCheckboxOptions);"
                ];
                $formCode = implode("\n", $fcode);
            } elseif ($plugin->pluginName === 'popover') {
                $this->isPopover = true;
                $this->popoverDataAttributes = '';
                foreach ($plugin->dataAttributes as $attr) {
                    if (!empty($attr->value)) {
                        $this->popoverDataAttributes .= ' data-' . $attr->name . '="' . $attr->value . '"';
                    }
                }
                $formCode = "\$form->popover();\n";
            } else {
                $formCode = "\$form->addPlugin('$plugin->pluginName', '$plugin->selector', '$plugin->jsConfig');\n";
            }
        } else {
            if ($this->outputPreview) {
                $this->phpForm->addPlugin($plugin->pluginName, $plugin->selector, $plugin->jsConfig, $replacements);
            }

            $replacementsCode = implode(', ', $replacementsCodeArray);
            $formCode = "\$form->addPlugin('$plugin->pluginName', '$plugin->selector', '$plugin->jsConfig', array($replacementsCode));\n";
        }

        if ($global) {
            $this->phpFormCode['globalPlugins'] .= $formCode;
        } else {
            $this->phpFormCode['components'][] = $formCode;
        }
    }

    /**
     * @param  \formgenerator\components\Radio $comp
     * @return void
     */
    private function addRadioGroup(Radio $comp): void
    {
        $phpCode = '';

        $inline = false;
        if (filter_var($comp->inline, FILTER_VALIDATE_BOOLEAN)) {
            $inline = true;
        }

        // helper
        $this->addHelper($comp->helper, $comp->name);

        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);

        foreach ($comp->radioButtons as $rad) {
            $radAttr = [];

            if ($comp->value === $rad->value) {
                $radAttr[] = 'checked';
            }
            if ($rad->disabled) {
                $radAttr[] = 'disabled';
            }
            foreach ($rad->attr as $attrib) {
                if (!empty($attrib->value)) {
                    $radAttr[] = $attrib->name . '=' . $attrib->value;
                }
            }
            $radAttr = implode(', ', $radAttr);
            if ($this->outputPreview) {
                $this->phpForm->addRadio($comp->name, $rad->text, $rad->value, $radAttr);
            }
            $phpCode .= "\$form->addRadio('$comp->name', '" . $this->sanitize($rad->text) . "', '$rad->value', '" . $this->sanitize($radAttr) . "');\n";
        }
        if ($this->outputPreview) {
            if ($comp->center === 'true') {
                $this->phpForm->centerContent();
            }
            $this->phpForm->printRadioGroup($comp->name, $comp->label, $inline, $attr);
            if ($comp->center === 'true') {
                $this->phpForm->centerContent(false);
            }
        }
        if ($comp->center === 'true') {
            $phpCode .= "\$form->centerContent();\n";
        }
        $phpCode .= "\$form->printRadioGroup('$comp->name', '" . $this->sanitize($comp->label) . "', $comp->inline, '" . $this->sanitize($attr) . "');\n";
        if ($comp->center === 'true') {
            $phpCode .= "\$form->centerContent(false);\n";
        }
        $this->phpFormCode['components'][] = $phpCode;
        foreach ($comp->plugins as $plugin) {
            if (!in_array($plugin->pluginName, $this->autoloadedPlugins, true)) {
                $this->addPlugin($plugin);
            }
        }
    }

    /**
     * @param  \formgenerator\components\Recaptcha $comp
     * @return void
     */
    private function addRecaptcha(Recaptcha $comp): void
    {
        if ($this->outputPreview) {
            $this->phpForm->addRecaptchaV3($comp->publickey);
        }
        if (empty($comp->publickey)) {
            $comp->publickey = 'RECAPTCHA_PUBLIC_KEY_HERE';
        }
        $this->phpFormCode['components'][] = "\$form->addRecaptchaV3('$comp->publickey');\n";
    }

    /**
     * @param  \formgenerator\components\Select $comp
     * @return void
     */
    private function addSelect(Select $comp): void
    {
        $phpCode = '';
        $hasBootstrapSelect = false;
        $hasSelect2 = false;
        $hasSlimselect = false;
        foreach ($comp->plugins as $plugin) {
            if ($plugin->pluginName === 'bootstrap-select') {
                $comp->attr = $this->addAttribute('class', 'selectpicker', $comp->attr);
                $hasBootstrapSelect = true;
            } elseif ($plugin->pluginName === 'select2') {
                $comp->attr = $this->addAttribute('class', 'select2', $comp->attr);
                $hasSelect2 = true;
            } elseif ($plugin->pluginName === 'slimselect') {
                $hasSlimselect = true;
            }
        }

        // helper
        $this->addHelper($comp->helper, $comp->name);

        // placeholder
        if (!empty($comp->placeholder)) {
            if ($hasBootstrapSelect) {
                $comp->attr = $this->addAttribute('title', $comp->placeholder, $comp->attr);
            } elseif ($hasSelect2) {
                $comp->attr = $this->addAttribute('data-placeholder', $comp->placeholder, $comp->attr);
            } elseif ($hasSlimselect) {
                $phpCode .= "\$form->addOption('$comp->name', '', '$comp->placeholder', '', 'disabled, data-placeholder=true');\n";
            } else {
                $phpCode .= "\$form->addOption('$comp->name', '', '$comp->placeholder', '', 'disabled, selected');\n";
            }
            foreach ($comp->attr as $index => $attr) {
                if ($attr->name === 'placeholder') {
                    // placeholder has been added to the form, we delete the attribute & reindex
                    unset($comp->attr[$index]);
                    $comp->attr = array_values($comp->attr);
                }
            }
        }
        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);
        $isMultiple = false;
        if (!empty($attr) && strpos('multiple=true', $attr) !== false) {
            $isMultiple = true;
        }
        if (!empty($comp->placeholder) && $hasBootstrapSelect !== true) {
            if ($this->outputPreview) {
                $opAttr = 'disabled, selected';
                if ($hasSlimselect) {
                    $opAttr = 'data-placeholder=true';
                }
                $this->phpForm->addOption($comp->name, '', $comp->placeholder, '', $opAttr);
            }
            $phpCode .= "\$form->addOption('$comp->name', '', '$comp->placeholder', '', 'disabled, selected');\n";
        }
        if ($isMultiple !== true) {
            foreach ($comp->selectOptions as $option) {
                $optAttr = [];

                if ($comp->value === $option->value) {
                    $optAttr[] = 'selected';
                }
                $optAttr = implode(', ', $optAttr);
                if ($this->outputPreview) {
                    $this->phpForm->addOption($comp->name, $option->value, $option->text, $option->groupname, $optAttr);
                }
                $phpCode .= "\$form->addOption('$comp->name', '$option->value', '" . $this->sanitize($option->text) . "', '$option->groupname', '" . $this->sanitize($optAttr) . "');\n";
            }
        } else {
            $selectArrayValues = array_map('trim', explode(',', $comp->value));
            foreach ($comp->selectOptions as $option) {
                $optAttr = [];

                if (in_array($option->value, $selectArrayValues)) {
                    $optAttr[] = 'selected';
                }
                $optAttr = implode(', ', $optAttr);
                if ($this->outputPreview) {
                    $this->phpForm->addOption($comp->name, $option->value, $option->text, $option->groupname, $optAttr);
                }
                $phpCode .= "\$form->addOption('$comp->name', '$option->value', '" . $this->sanitize($option->text) . "', '$option->groupname', '" . $this->sanitize($optAttr) . "');\n";
            }
        }
        if ($this->outputPreview) {
            $this->phpForm->addSelect($comp->name, $comp->label, $attr);
        }
        $phpCode .= "\$form->addSelect('$comp->name', '" . $this->sanitize($comp->label) . "', '" . $this->sanitize($attr) . "');\n";
        $this->phpFormCode['components'][] = $phpCode;
        foreach ($comp->plugins as $plugin) {
            if (!in_array($plugin->pluginName, $this->autoloadedPlugins, true)) {
                $this->addPlugin($plugin);
            }
        }
    }

    /**
     * @param  \formgenerator\components\FormSection $section
     * @return void
     */
    private function addSetCols(FormSection $section): void
    {
        $comp = $section->getComponent();
        if (isset($comp->label) && isset($comp->width)) {
            $cw = $comp->width;
            if (empty($comp->label)) {
                $cols = [
                    '100%' => '0-12',
                    '66%'  => '0-8',
                    '50%'  => '0-6',
                    '33%'  => '0-4'
                ];
            } else {
                $cols = [
                    '100%' => '4-8',
                    '66%'  => '3-5',
                    '50%'  => '2-4',
                    '33%'  => '2-2'
                ];
            }
            $formCols = $cols[$cw];
            if ($formCols !== $this->currentCols) {
                $newCols = explode('-', $formCols);
                if ($this->outputPreview) {
                    $this->phpForm->setCols((int)$newCols[0], (int)$newCols[1]);
                }
                $this->phpFormCode['components'][] = "\$form->setCols(" . (int)$newCols[0] . ", " . (int)$newCols[1] . ");\n";

                $this->currentCols = $formCols;
            }
            $this->currentlyCentered = false;
        } elseif (($section->componentType == 'buttongroup' || $section->componentType == 'button') && $comp !== null && property_exists($comp, 'center') && $comp->center === 'true' && !$this->currentlyCentered) {
            if ($this->outputPreview) {
                $this->phpForm->setCols(0, 12);
                $this->phpForm->centerContent();
            }
            $this->phpFormCode['components'][] = "\$form->setCols(0, 12);\n";
            $this->phpFormCode['components'][] = "\$form->centerContent();\n";

            $this->currentCols = '0-12';
            $this->currentlyCentered = true;
        }
    }

    /**
     * @param  \formgenerator\components\Textarea $comp
     * @return void
     */
    private function addTextarea(Textarea $comp): void
    {
        foreach ($comp->plugins as $plugin) {
            if ($plugin->pluginName == 'tinymce') {
                $comp->attr = $this->addAttribute('class', 'tinymce', $comp->attr);
            }
        }
        $attrArray = $this->mergeAttributes($comp);
        $attr = $this->getAttributes($attrArray);
        $this->addHelper($comp->helper, $comp->name);
        if ($this->outputPreview) {
            $this->phpForm->addTextarea($comp->name, $comp->value, $comp->label, $attr);
        }
        $this->phpFormCode['components'][] = "\$form->addTextarea('$comp->name', '$comp->value', '$comp->label', '$attr');\n";
        foreach ($comp->plugins as $plugin) {
            if (!in_array($plugin->pluginName, $this->autoloadedPlugins, true)) {
                $this->addPlugin($plugin);
            }
        }
    }

    /**
     * @param  \formgenerator\components\Hcaptcha $comp
     * @return void
     */
    private function addHcaptcha(Hcaptcha $comp): void
    {
        if ($this->outputPreview) {
            if ($comp->center === 'true') {
                $this->phpForm->centerContent();
            }
            $this->phpForm->addHcaptcha($comp->sitekey, 'data-theme=' . $comp->theme . ', data-size=' . $comp->size);
            if ($comp->center === 'true') {
                $this->phpForm->centerContent(false);
            }
        }
        if (empty($comp->sitekey)) {
            $comp->sitekey = 'HCAPTCHA_SITE_KEY_HERE';
        }
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent();\n";
        }
        $this->phpFormCode['components'][] = "\$form->addHcaptcha('$comp->sitekey', 'data-size=$comp->size, data-theme=$comp->theme');\n";
        if ($comp->center === 'true') {
            $this->phpFormCode['components'][] = "\$form->centerContent(false);\n";
        }
    }

    /**
     * @param  \formgenerator\components\Heading $comp
     * @return void
     */
    private function addHeading(Heading $comp): void
    {
        $attr = '';
        if (!empty($comp->clazz)) {
            $attr = 'class=' . $comp->clazz;
        }
        if ($this->outputPreview) {
            $this->phpForm->addHeading($comp->value, $comp->type, $attr);
        }
        $this->phpFormCode['components'][] = "\$form->addHeading('" . $this->sanitize($comp->value) . "', '" . $comp->type . "', '" . $attr . "');\n";
    }

    /**
     * @param  array<mixed> $array
     * @return mixed|null
     */
    private function arrayKeyLast(array $array)
    {
        if (!is_array($array) || empty($array)) {
            return null;
        }

        return array_keys($array)[count($array) - 1];
    }

    /**
     * @return void
     */
    private function buildCodeParts()
    {
        /* Start
        -------------------------------------------------- */

        $start1 = array(
            '&lt;?php',
            'use phpformbuilder\Form;',
            'use phpformbuilder\Validator\Validator;'
        );
        if (!empty($this->fileuploaders)) {
            $start1[] = 'use fileuploader\server\FileUploader;';
        }
        $start2 = [];
        if ($this->userForm->aftervalidation === 'db-insert' || $this->userForm->aftervalidation === 'db-update' || $this->userForm->aftervalidation === 'db-delete') {
            $start2 = array('use Migliori\PowerLitePdo\Db;');
        }
        $start3 = array(
            '',
            '/* =============================================',
            '    start session and include the autoloader',
            '============================================= */',
            '',
            'session_start();',
            'include_once rtrim($_SERVER[\'DOCUMENT_ROOT\'], DIRECTORY_SEPARATOR) . \'' . $this->currentDir . 'autoload.php\';',
            ''
        );
        if (!empty($this->fileuploaders)) {
            $start3[] = '// include the fileuploader';
            $start3[] = '';
            $start3[] = 'include_once rtrim($_SERVER[\'DOCUMENT_ROOT\'], DIRECTORY_SEPARATOR) . \'' . $this->currentDir . 'plugins/fileuploader/server/class.fileuploader.php\';';
            $start3[] = '';
        }

        $start = array_merge($start1, $start2, $start3);
        $this->phpFormCode['start'] = implode("\n", $start);

        /* ifPosted
        -------------------------------------------------- */

        $ifPosted1 = array(
            '',
            '/* =============================================',
            '    validation if posted',
            '============================================= */',
            '',
            'if ($_SERVER["REQUEST_METHOD"] == "POST" && Form::testToken(\'' . $this->userForm->id . '\')) {',
            '    // create validator & auto-validate required fields',
            '    $validator = Form::validate(\'' . $this->userForm->id . '\');'
        );
        $ifPosted2 = [];
        if ($this->hasEmailFields) {
            $ifPosted2 = array(
                '',
                '    // additional validation'
            );
            foreach ($this->emailFieldNames as $fieldName) {
                $ifPosted2[] = '    $validator->email()->validate(\'' . $fieldName . '\');';
            }
        }
        $ifPosted3 = [];
        if ($this->hasRecaptcha) {
            if (empty($this->recaptchaPrivateKey)) {
                $this->recaptchaPrivateKey = 'RECAPTCHA_PRIVATE_KEY_HERE';
            }
            $ifPosted3 = array(
                '',
                '    // recaptcha validation',
                '    $validator->recaptcha(\'' . $this->recaptchaPrivateKey . '\', \'Recaptcha Error\')->validate(\'g-recaptcha-response\');'
            );
        } elseif ($this->hasHcaptcha) {
            if (empty($this->hcaptchaSecretKey)) {
                $this->hcaptchaSecretKey = 'HCAPTCHA_SECRET_KEY_HERE';
            }
            $ifPosted3 = array(
                '',
                '    // hcaptcha validation',
                '    $validator->hcaptcha(\'' . $this->hcaptchaSecretKey . '\', \'Captcha Error\')->validate(\'h-captcha-response\');'
            );
        }
        $ifPosted4 = array(
            '',
            '    // check for errors',
            '    if ($validator->hasErrors()) {',
            '        $_SESSION[\'errors\'][\'' . $this->userForm->id . '\'] = $validator->getAllErrors();',
            '    } else {'
        );

        $ifPosted5 = [];
        if (!empty($this->fileuploaders)) {
            $ifPosted5[] = "        \$uploadedFiles = [];";
            foreach ($this->fileuploaders as $fuploader) {
                $ifPosted5[] = "        if (isset(\$_POST['" . $fuploader['fieldname'] . "']) && !empty(\$_POST['" . $fuploader['fieldname'] . "'])) {";
                $ifPosted5[] = "            \$postedFile = FileUploader::getPostedFiles(\$_POST['" . $fuploader['fieldname'] . "']);";
                $ifPosted5[] = "            \$uploadedFiles['" . $fuploader['fieldname'] . "'] = [";
                $ifPosted5[] = "                'upload_dir' => '" . $fuploader['upload_dir'] . "',";
                $ifPosted5[] = "                'filename' => \$postedFile[0]['file']";
                $ifPosted5[] = "            ];";
                $ifPosted5[] = "        }";
            }
        }

        $ifPosted6 = [];
        if ($this->userForm->aftervalidation === 'send-email') {
            // Email sending
            if (!empty($this->fileuploaders)) {
                $ifPosted6[] = '        /* Send email with attached file(s) */';
                $ifPosted6[] = '        $attachments = [];';
                $ifPosted6[] = '        foreach ($uploadedFiles as $f) {';
                $ifPosted6[] = "            \$attachments[] = \$f['upload_dir'] . \$f['filename'];";
                $ifPosted6[] = '        }';
                $ifPosted6[] = '        $attachments = implode(\', \', $attachments);';
            } else {
                $ifPosted6[] = '        // send email';
            }
            $ifPosted6[] = '        $emailConfig = array(';
            $ifPosted6[] = '            \'sender_email\'    => \'' . $this->userForm->senderEmail . '\',';
            $ifPosted6[] = '            \'recipient_email\' => \'' . $this->userForm->recipientEmail . '\',';
            $ifPosted6[] = '            \'subject\'         => \'' . $this->userForm->subject . '\',';
            if (!empty($this->userForm->senderName)) {
                $ifPosted6[] = '            \'sender_name\'     => \'' . $this->userForm->senderName . '\',';
            }
            if (!empty($this->userForm->replyToEmail)) {
                $ifPosted6[] = '            \'reply_to_email\'  => \'' . $this->userForm->replyToEmail . '\',';
            }
            if (!empty($this->userForm->sentMessage)) {
                $ifPosted6[] = '            \'sent_message\'    => \'' . $this->sanitize($this->userForm->sentMessage) . '\',';
            }
            if (!empty($this->fileuploaders)) {
                $ifPosted6[] = '            \'attachments\'    =>  $attachments,';
            }
            $filterValues = [$this->userForm->id];
            if (!empty($this->fileuploaders)) {
                foreach ($this->fileuploaders as $fuploader) {
                    $filterValues[] = $fuploader['fieldname'];
                    $filterValues[] = 'uploader-' . $fuploader['fieldname'];
                }
            }
            $filterValues = implode(', ', $filterValues);
            $ifPosted6[] = '            \'filter_values\'   => \'' . $filterValues . '\'';
            $ifPosted6[] = '        );';
            $ifPosted6[] = '        $sentMessage = Form::sendMail($emailConfig);';

            $message = array(
                'if (isset($sentMessage)) {',
                '    echo $sentMessage;',
                '}'
            );
            $this->phpFormCode['message'] = implode("\n", $message);
        } elseif ($this->userForm->aftervalidation === 'db-insert' || $this->userForm->aftervalidation === 'db-update' || $this->userForm->aftervalidation === 'db-delete') {
            $message = array(
                'if (isset($msg)) {',
                '    echo $msg;',
                '}'
            );
            $this->phpFormCode['message'] = implode("\n", $message);
            // DB insert
            $ifPosted6 = array(
                '        // Database connection',
                '        // Setup your connection in phpformbuilder/vendor/migliori/power-lite-pdo/src/connection.php',
                '        $container = include_once rtrim($_SERVER[\'DOCUMENT_ROOT\'], DIRECTORY_SEPARATOR) . \'' . $this->currentDir . 'vendor/migliori/power-lite-pdo/src/bootstrap.php\';',
                '        $db = $container->get(Db::class);'
            );

            if ($this->userForm->aftervalidation === 'db-insert') {
                $ifPosted6[] = '        // Insert a new record';
                $ifPosted6[] = '        $values = array(';
                foreach ($this->dbFields as $key => $dbField) {
                    if ($dbField['component_name'] === $this->userForm->dbPrimary) {
                        continue;
                    } elseif ($dbField['multiple']) {
                        $temp = '            \'' . $dbField['component_name'] . '\' => json_encode($_POST[\'' . $dbField['component_name'] . '\']),';
                    } else {
                        if ($dbField['component_type'] !== 'fileuploader') {
                            $temp = '            \'' . $dbField['component_name'] . '\' => $_POST[\'' . $dbField['component_name'] . '\'],';
                        } else {
                            $temp = '            \'' . $dbField['component_name'] . '\' => $uploadedFiles[\'' . $dbField['component_name'] . '\'][\'filename\'],';
                        }
                    }
                    if ($key === $this->arrayKeyLast($this->dbFields)) {
                        $temp = \rtrim($temp, ',');
                    }
                    $ifPosted6[] = $temp;
                }
                $ifPosted6[] = '        );';
                $ifPosted6[] = '        if (!$db->insert(\'' . $this->userForm->dbTable . '\', $values)) {';
                $ifPosted6[] = '            $msg = Form::buildAlert($db->error(), \'' . $this->userForm->framework . '\', \'danger\');';
                $ifPosted6[] = '        } else {';
                $ifPosted6[] = '            $id  = $db->getLastInsertId();';
                $ifPosted6[] = '            $msg = Form::buildAlert(\'1 Record inserted ; id = #\' . $id, \'' . $this->userForm->framework . '\', \'success\');';
                $ifPosted6[] = '        }';
            } elseif ($this->userForm->aftervalidation === 'db-update') {
                $ifPosted6[] = '        // Update an existing record';
                $filterFound = false;
                foreach ($this->dbFields as $dbField) {
                    if ($dbField['component_name'] === $this->userForm->dbFilter) {
                        $ifPosted6[] = '        $where  = array(\'' . $dbField['component_name'] . '\' => $_POST[\'' . $dbField['component_name'] . '\']);';
                        $filterFound = true;
                    }
                }
                if (!$filterFound) {
                    echo Form::buildAlert('<p><strong>Your form will not work because you didn\'t set any value to filter the Database update records.</strong><br>Open the <em>Main Settings</em>, go to <em>Form Action</em> and enter a value in <em>Filter field name</em>.<br>The value must be the field name that will be used in the SQL WHERE clause to update your records and your form must have a field with the same name.</p>', $this->phpForm->framework, 'warning');
                }
                                $ifPosted6[] = '        $update = array(';
                foreach ($this->dbFields as $key => $dbField) {
                    if ($dbField['component_name'] !== $this->userForm->dbFilter) {
                        if ($dbField['multiple']) {
                            $temp = '        \'' . $dbField['component_name'] . '\'] => json_encode($_POST[\'' . $dbField['component_name'] . '\']),';
                        } else {
                            $temp = '        \'' . $dbField['component_name'] . '\' => $_POST[\'' . $dbField['component_name'] . '\'],';
                        }
                        if ($key === $this->arrayKeyLast($this->dbFields)) {
                            $temp = \rtrim($temp, ',');
                        }
                        $ifPosted6[] = $temp;
                    }
                }
                                $ifPosted6[] = '        );';
                                $ifPosted6[] = '        if (!$db->update(\'' . $this->userForm->dbTable . '\', $update, $where)) {';
                                $ifPosted6[] = '            $msg = Form::buildAlert($db->error(), \'' . $this->userForm->framework . '\', \'danger\');';
                                $ifPosted6[] = '        } else {';
                                $ifPosted6[] = '            $msg = Form::buildAlert(\'Database updated successfully\', \'' . $this->userForm->framework . '\', \'success\');';
                                $ifPosted6[] = '        }';
            } elseif ($this->userForm->aftervalidation === 'db-delete') {
                foreach ($this->dbFields as $dbField) {
                    if ($dbField['component_name'] === $this->userForm->dbFilter) {
                        $ifPosted6[] = '        $where = array(\'' . $dbField['component_name'] . '\' => $_POST[\'' . $dbField['component_name'] . '\']);';
                    }
                }
                $ifPosted6[] = '        if (!$db->delete(\'' . $this->userForm->dbTable . '\', $where)) {';
                $ifPosted6[] = '            $msg = Form::buildAlert($db->error(), \'' . $this->userForm->framework . '\', \'danger\');';
                $ifPosted6[] = '        } else {';
                $ifPosted6[] = '            $msg = Form::buildAlert(\'1 record deleted\', \'' . $this->userForm->framework . '\', \'success\');';
                $ifPosted6[] = '        }';
            }
        }

                        $ifPosted6[] = '        // clear the form';
                        $ifPosted6[] = '        Form::clear(\'' . $this->userForm->id . '\');';
        if (!empty($this->userForm->redirectUrl)) {
            $ifPosted6[] = '        // redirect after success';
            $ifPosted6[] = '        header(\'Location:' . $this->userForm->redirectUrl . '\');';
            $ifPosted6[] = '        exit;';
        }
                        $ifPosted6[] = '    }';
                        $ifPosted6[] = '}';
                        $ifPosted6[] = '';

                        $ifPosted = array_merge($ifPosted1, $ifPosted2, $ifPosted3, $ifPosted4, $ifPosted5, $ifPosted6);
                        $this->phpFormCode['ifPosted'] = implode("\n", $ifPosted);

                        /* head
                        -------------------------------------------------- */

        if ($this->userForm->ajax !== 'true') {
            $this->phpFormCode['head'] = '';
            if (!empty($this->iconFontUrl)) {
                $iconFont = $this->userForm->iconFont;
                $this->phpFormCode['head'] .= "&lt;!-- $iconFont --&gt;\n\n&lt;link rel=\"stylesheet\" href=\"$this->iconFontUrl\"&gt;\n\n";
            }
            $this->phpFormCode['head'] .= "&lt;?php \$form->printIncludes('css'); ?&gt;\n";
        }

                        /* render
                        -------------------------------------------------- */

                        $render = [];
        if ($this->isModal) {
            $btnClass = $this->getTriggerButtonClass();
            $render[] = '&lt;button data-micromodal-trigger="modal-' . $this->userForm->id . '" class="' . $btnClass . '"&gt;Open the modal form&lt;/button>';
        } elseif ($this->isPopover) {
            $btnClass = $this->getTriggerButtonClass();
            $render[] = '&lt;button data-popover-trigger="' . $this->userForm->id . '"' . $this->popoverDataAttributes . ' class="' . $btnClass . '"&gt;Open the popover form&lt;/button&gt;';
        }
        if ($this->userForm->ajax !== 'true') {
            $render[] = '&lt;?php';
        } else {
            $render[] = '';
        }
        if (!empty($this->phpFormCode['message'])) {
            $render[] = $this->phpFormCode['message'];
        }
        if ($this->userForm->ajax === 'true') {
            $render[] = '';
        }
                        $render[] = '$form->render();';
        if ($this->userForm->ajax !== 'true') {
            $render[] = '?&gt;';
        } else {
            $render[] = '';
        }

                        $this->phpFormCode['render'] = implode("\n", $render);

                        /* scripts
                        -------------------------------------------------- */

        if ($this->userForm->ajax !== 'true') {
            $scripts = [];
            if ($this->hasJqueryPlugins && in_array($this->userForm->framework, $this->frameworksWithoutJquery)) {
                $scripts[] = '&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js&quot; integrity=&quot;sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/script&gt;';
            }
            $scripts[] = '&lt;?php';
            $scripts[] = '$form->printIncludes(\'js\');';
            $scripts[] = '$form->printJsCode();';
            $scripts[] = '?&gt;';
            if ($this->userForm->framework === 'material' || $this->userForm->framework === 'bs4-material') {
                $scripts[] = '&lt;script&gt;';
                $scripts[] = '$(document).ready(function() {';
                $scripts[] = '    $(\'select:not(.selectpicker):not(.select2)\').formSelect();';
                $scripts[] = '});';
                $scripts[] = '&lt;/script&gt;';
            }
            $this->phpFormCode['scripts'] = implode("\n", $scripts);
        } else {
            if (!$this->outputPreview) {
                $this->phpForm = new Form($this->userForm->id);
                $this->phpForm->setPluginsUrl();
            }
            $scripts = array(
                '&lt;!-- Ajax form loader --&gt;',
                '',
                '&lt;!-- Change the src url below if necessary --&gt;',
                '&lt;script src=&quot;' . $this->phpForm->pluginsUrl . 'ajax-data-loader/ajax-data-loader.min.js&quot;&gt;&lt;/script&gt;',
                '',
                '&lt;script&gt;',
                '    // --- SETUP YOUR FORM(S) BELOW IN THE &quot;ajaxForms&quot; VARIABLE ---',
                '    var ajaxForms = [',
                '        {',
                '            formId: \'' . $this->userForm->id . '\',',
                '            container: \'#' . $this->userForm->id . '-loader\',',
                '            url: \'/ajax-forms/' . $this->userForm->id . '.php\'',
                '        }',
                '    ];',
                '',
                '    // --- NO NEED TO CHANGE ANYTHING AFTER THIS LINE ---',
                '    // --- COPY/PASTE THE FOLLOWING CODE IN THE HTML FILE THAT WILL LOAD THE FORM ---',
                '',
                '    document.addEventListener(&apos;DOMContentLoaded&apos;, function() {',
                '        ajaxForms.forEach(function(currentForm) {',
                '            const $formContainer = document.querySelector(currentForm.container);',
                '            if (typeof($formContainer.dataset.ajaxForm) === &apos;undefined&apos;) {',
                '                fetch(currentForm.url)',
                '                .then((response) =&gt; {',
                '                    return response.text()',
                '                })',
                '                .then((data) =&gt; {',
                '                    $formContainer.innerHTML = &apos;&apos;;',
                '                    $formContainer.dataset.ajaxForm = currentForm;',
                '                    $formContainer.dataset.ajaxFormId = currentForm.formId;',
                '                    loadData(data, currentForm.container);',
                '                }).catch((error) =&gt; {',
                '                    console.log(error);',
                '                });',
                '            }',
                '        });',
                '    });',
                '&lt;/script&gt;',
            );
            $this->phpFormCode['scripts'] = implode("\n", $scripts);
        }

                        /* Main form code
                        -------------------------------------------------- */

                        $mainCode = '';
                        $mainCode .= $this->phpFormCode['start'];
                        $mainCode .= $this->phpFormCode['ifPosted'];
                        $mainCode .= $this->phpFormCode['startForm'];
        foreach ($this->phpFormCode['components'] as $comp) {
            $mainCode .= $comp;
        }
                        $mainCode .= $this->phpFormCode['globalPlugins'];
        if ($this->userForm->ajax === 'true') {
            $mainCode .= $this->phpFormCode['render'];
        }
                        $this->phpFormCode['main'] = $mainCode;
    }

    /**
     * display error message if
     *     - iCheck used with material
     *
     * @param string $msg
     */
    /**
     * @param  string $msg
     * @return void
     */
    private function buildErrorMsg(string $msg): void
    {
        $this->errorMsg .= '<div style="line-height:30px;border-radius:5px;border-bottom:1px solid #ac2925;background-color: #c9302c;margin:10px auto;"><p style="color:#fff;text-align:center;font-size:16px;margin:0">' . $msg . '</p></div>';
    }

    /**
     * @param  \formgenerator\components\FormSection $section
     * @return void
     */
    private function buildSection(FormSection $section): void
    {
        if ($this->userForm->layout === 'horizontal') {
            $this->addSetCols($section);

            if ($section->groupInputs !== false) {
                $this->addGroupInputs($section->groupInputs);
            }
        }
        switch ($section->componentType) {
            case 'button':
                $this->addBtn($section->button);
                break;

            case 'buttongroup':
                $this->addBtngroup($section->buttongroup);
                break;

            case 'checkbox':
                $this->addCheckboxGroup($section->checkboxes);
                break;

            case 'dependent':
                $this->startDependent($section->dependent);
                break;

            case 'dependentend':
                $this->endDependent();
                break;

            case 'fileuploader':
                $this->addFileuploader($section->fileuploader);
                break;

            case 'hcaptcha':
                $this->addHcaptcha($section->hcaptcha);
                break;

            case 'heading':
                $this->addHeading($section->heading);
                break;

            case 'html':
                $this->addHtml($section->html);
                break;

            case 'input':
                $this->addInput($section->input);
                break;

            case 'paragraph':
                $this->addParagraph($section->paragraph);
                break;

            case 'radio':
                $this->addRadioGroup($section->radio);
                break;

            case 'recaptcha':
                $this->addRecaptcha($section->recaptcha);
                break;

            case 'select':
                $this->addSelect($section->select);
                break;

            case 'textarea':
                $this->addTextarea($section->textarea);
                break;

            default:
                break;
        }
    }

    /**
     * @param  \formgenerator\components\UserForm $jsonForm
     * @return void
     */
    private function createForm(UserForm $jsonForm): void
    {
        $formFramework = $jsonForm->framework;
        $isBs4Material = false;
        if ($formFramework === 'bs4-material') {
            $formFramework = 'material';
            $isBs4Material = true;
        }
        foreach ($jsonForm->plugins as $pl) {
            if ($pl->pluginName === 'formvalidation') {
                if (empty($jsonForm->attr)) {
                    $jsonForm->attr = 'data-fv-no-icon=true';
                } else {
                    $jsonForm->attr .= ', data-fv-no-icon=true';
                }
            }
            if ($pl->isjQuery) {
                $this->hasJqueryPlugins = true;
            }
        }
        if ($this->outputPreview) {
            $this->phpForm = new Form($jsonForm->id, $jsonForm->layout, $jsonForm->attr, $formFramework);
            if ($isBs4Material) {
                $this->phpForm->addPlugin('materialize', '#' . $jsonForm->id);
            }
            $this->phpForm->setAction('#', false);
            $this->phpForm->useLoadJs();
            $this->phpForm->setMode('development');
        }
        $this->phpFormCode['startForm'] = "\n/* ==================================================\n    The Form\n ================================================== */\n\n";
        $this->phpFormCode['startForm'] .= "\$form = new Form('$jsonForm->id', '$jsonForm->layout', '$jsonForm->attr', '$formFramework');\n";
        if ($this->userForm->ajax === 'true') {
            $this->phpFormCode['startForm'] .= "// enable Ajax loading\n\$form->setOptions(['ajax' => true]);\n\n";
        }
        if ($isBs4Material) {
            $this->phpFormCode['startForm'] .= "\$form->addPlugin('materialize', '#$jsonForm->id');\n";
        }
        $this->phpFormCode['startForm'] .= "// \$form->setMode('development');\n";
    }

    /**
     * @return void
     */
    private function endDependent(): void
    {
        if ($this->outputPreview) {
            $this->phpForm->endDependentFields();
        }
        $this->phpFormCode['components'][] = "\$form->endDependentFields();\n";
    }

    /**
     * @param  array<Attribute> $attrArray
     * @return string
     */
    private function getAttributes(array $attrArray): string
    {
        $tempArray = [];
        foreach ($attrArray as $attr) {
            if (isset($attr->name) && !empty($attr->value)) {
                $tempArray[] = $attr->name . '=' . str_replace(',', '\,', $attr->value);
            } else {
                $tempArray[] = $attr->name;
            }
        }
        return implode(',', $tempArray);
    }

    /**
     * getCurrentDir
     *
     * @return string root-relative dir to phpformbuilder with starting & ending DIRECTORY_SEPARATOR
     */
    private function getCurrentDir(): string
    {
        $phpformbuilderPath = (string) realpath('../../phpformbuilder');

        $documentRoot = strtr($_SERVER['DOCUMENT_ROOT'], ['/' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR]);
        $phpformbuilderPath = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $phpformbuilderPath);

        $curDir = DIRECTORY_SEPARATOR . ltrim(str_replace($documentRoot, '', $phpformbuilderPath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

        // var_dump($curDir);

        return str_replace(DIRECTORY_SEPARATOR, '/', $curDir);
    }

    /**
     * getIconFont
     * The fonts URLs are listed in src/ts/defaultConfig.ts
     *
     * @return string The link to the font stylesheet || ''
     */
    private function getIconFont(): string
    {
        $uipCssUrl = str_replace('ajax/preview.php', 'lib/universal-icon-picker/assets/stylesheets/', $_SERVER['SCRIPT_NAME']);
        $iconFonts = array(
            'font-awesome'           => '//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css',
            'font-awesome-solid'           => '//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css',
            'font-awesome-regular'           => '//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css',
            'font-awesome-brands'           => '//cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css',
            'material-icons-filled'  => '//fonts.googleapis.com/css2?family=Material+Icons',
            'material-icons-outlined'  => '//fonts.googleapis.com/css2?family=Material+Icons+Outlined',
            'material-icons-round'  => '//fonts.googleapis.com/css2?family=Material+Icons+Round',
            'material-icons-sharp'  => '//fonts.googleapis.com/css2?family=Material+Icons+Sharp',
            'material-icons-two-tone'  => '//fonts.googleapis.com/css2?family=Material+Icons+Two+Tone',
            'bootstrap-icons'        => '//cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css',
            'elegant-icons'          => $uipCssUrl . 'elegant-icons.min.css',
            'feather-icons'          => $uipCssUrl . 'feather-icons.min.css',
            'foundation-icons'       => $uipCssUrl . 'foundation-icons.min.css',
            'happy-icons'            => $uipCssUrl . 'happy-icons.min.css',
            'icomoon'                => $uipCssUrl . 'icomoon.min.css',
            'open-iconic'            => $uipCssUrl . 'open-iconic.min.css',
            'tabler-icons'           => $uipCssUrl . 'tabler-icons.min.css',
            'weather-icons'          => $uipCssUrl . 'weather-icons.min.css',
            'zondicons'              => $uipCssUrl . 'zondicons.min.css'
        );
        if (array_key_exists($this->userForm->iconFont, $iconFonts)) {
            $icf = $this->userForm->iconFont;
            return $iconFonts[$icf];
        }
        return '';
    }

    /**
     * @return string
     */
    private function getLastJsonError(): string
    {
        switch (json_last_error()) {
            case JSON_ERROR_NONE:
                return '';
            case JSON_ERROR_DEPTH:
                $errMsg = 'JSON Error - Maximum stack depth exceeded';
                break;
            case JSON_ERROR_STATE_MISMATCH:
                $errMsg = 'JSON Error - Underflow or the modes mismatch';
                break;
            case JSON_ERROR_CTRL_CHAR:
                $errMsg = 'JSON Error - Unexpected control character found';
                break;
            case JSON_ERROR_SYNTAX:
                $errMsg = 'JSON Error - Syntax error, malformed JSON';
                break;
            case JSON_ERROR_UTF8:
                $errMsg = 'JSON Error - Malformed UTF-8 characters, possibly incorrectly encoded';
                break;
            default:
                $errMsg = 'JSON Error - Unknown error';
                break;
        }

        return $errMsg;
    }

    /**
     * @return void
     */
    private function getSectionGroups(): void
    {
        $userFormSectionsCount = count($this->formSections);
        $grouppableComponentTypes = ['checkbox', 'input', 'radio', 'select', 'textarea'];
        $groupStarted = false;
        for ($i = 0; $i < $userFormSectionsCount; $i++) {
            $currentSection = $this->formSections[$i];
            $currentSection->groupInputs = false;
            if (isset($this->formSections[$i + 1]) && $groupStarted === false) {
                $nextSection = $this->formSections[$i + 1];
                if (in_array($currentSection->componentType, $grouppableComponentTypes, true) && in_array($nextSection->componentType, $grouppableComponentTypes, true)) {
                    $currentComponent = $currentSection->getComponent();
                    $currentComponentWidth = 0;
                    if ($currentComponent !== null) {
                        $currentComponentWidth = isset($currentComponent->width) ? intval(str_replace('%', '', $currentComponent->width)) : 0;
                    }
                    $nextComponent = $nextSection->getComponent();
                    $nextComponentWidth = ($nextComponent !== null && isset($nextComponent->width)) ? intval(str_replace('%', '', $nextComponent->width)) : 0;
                    if ($currentComponentWidth + $nextComponentWidth <= 100) {
                        $currentComponent = $currentSection->getComponent();
                        $nextComponent = $nextSection->getComponent();
                        if ($currentComponent !== null && $nextComponent !== null && isset($currentComponent->name) && isset($nextComponent->name)) {
                            $currentSection->groupInputs = [(string)$currentComponent->name, (string)$nextComponent->name, null];
                        }
                        $groupStarted = true;
                        // group 3rd component
                        if (isset($this->formSections[$i + 2])) {
                            $thirdSection = $this->formSections[$i + 2];
                            if (in_array($thirdSection->componentType, $grouppableComponentTypes, true)) {
                                $thirdComponent = $thirdSection->getComponent();
                                if ($thirdComponent !== null && isset($thirdComponent->width)) {
                                    $thirdComponentWidth = intval(str_replace('%', '', $thirdComponent->width));
                                    if ($currentComponentWidth + $nextComponentWidth + $thirdComponentWidth <= 100 && isset($thirdComponent->name) && is_array($currentSection->groupInputs)) {
                                        $currentSection->groupInputs[] = $thirdComponent->name;
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                $groupStarted = false;
            }
        }
    }

    /**
     * @return void
     */
    private function getSpecialData(): void
    {
        $addToDbFields = ['checkboxes', 'input', 'radio', 'select', 'textarea', 'fileuploader'];

        foreach ($this->formSections as $section) {
            if (in_array($section->componentType, $addToDbFields, true)) {
                $multiple = false;
                $component = $section->getComponent();
                if ($section->componentType === 'checkboxes' || ($section->componentType === 'select' && $component !== null && property_exists($component, 'attr') && in_array('multiple', $component->attr, true))) {
                    $multiple = true;
                }
                $dbField = [
                    'component_type' => $section->componentType,
                    'component_name' => $section->getComponent() && property_exists($section->getComponent(), 'name') ? $section->getComponent()->name : '',
                    'multiple'       => $multiple
                ];
                $this->dbFields[] = $dbField;
            }
            if ($section->componentType === 'recaptcha') {
                $this->hasRecaptcha = true;
                $component = $section->getComponent();
                if ($component !== null && property_exists($component, 'privatekey')) {
                    $this->recaptchaPrivateKey = $component->privatekey;
                }
            } elseif ($section->componentType === 'hcaptcha') {
                $this->hasHcaptcha = true;
                $component = $section->getComponent();
                if ($component !== null && property_exists($component, 'secretkey')) {
                    $this->hcaptchaSecretKey = $component->secretkey;
                }
            } elseif ($section->componentType === 'input') {
                $component = $section->getComponent();
                if ($component !== null && property_exists($component, 'type') && $component->type === 'email') {
                    $this->hasEmailFields = true;
                    if (property_exists($component, 'name')) {
                        $this->emailFieldNames[] = $component->name;
                    }
                }
            }
            if (isset($section->getComponent()->plugins)) {
                foreach ($section->getComponent()->plugins as $plugin) {
                    if ($plugin->isjQuery) {
                        $this->hasJqueryPlugins = true;
                    }
                }
            }
        }
    }

    /**
     * @return string
     */
    private function getTriggerButtonClass(): string
    {
        $btnClass = [
            'bs4'         => 'btn btn-primary',
            'bs5'         => 'btn btn-primary',
            'bulma'       => 'button is-primary',
            'foundation'  => 'button primary',
            'material'    => 'btn waves-effect waves-light',
            'tailwind'    => 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-r-lg text-sm px-5 py-2.5 text-center mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800',
            'uikit'       => 'uk-button uk-button-primary'
        ];
        $framework = $this->userForm->framework;

        return $btnClass[$framework];
    }

    /**
     * @param  mixed $component
     * @return array<Attribute>
     */
    private function mergeAttributes($component): array
    {
        if (property_exists($component, 'attr')) {
            if (!empty($component->plugins)) {
                foreach ($component->plugins as $plugin) {
                    if ($plugin->dataAttributes !== null) {
                        foreach ($plugin->dataAttributes as $attr) {
                            if ($attr !== null && isset($attr->name) && isset($attr->value) && $attr->value !== null) {
                                // create new attr object then push it to the component attributes
                                $pluginAttr = [
                                    'name' => '',
                                    'value' => ''
                                ];
                                if ($plugin->pluginName !== 'bootstrap-input-spinner') {
                                    $pluginAttr['name'] = 'data-' . $attr->name;
                                } else {
                                    if ($attr->name === 'min' || $attr->name === 'max' || $attr->name === 'step') {
                                        $pluginAttr['name'] = $attr->name;
                                    } else {
                                        $pluginAttr['name'] = 'data-' . $attr->name;
                                    }
                                }
                                $pluginAttr['value'] = $attr->value;

                                // if $component is one of Button|Buttongroup|Checkboxes|Input|Radio|Select|Textarea
                                try {
                                    if (!property_exists($component, 'attr')) {
                                        throw new \Exception('The component has no attribute property');
                                    }
                                    if (!isset($component->attr)) {
                                        $component->attr = [];
                                    }
                                    $component->attr[] = new Attribute((object) $pluginAttr);
                                } catch (\Exception $e) {
                                    echo $e->getMessage();
                                }
                            }
                        }
                    }
                }
            }
        } else {
            $component->attr = [];
        }

        return $component->attr;
    }

    /**
     * @param  string $codepart
     * @param  int    $spaces
     * @return string
     */
    private function reindentCode(string $codepart, int $spaces): string
    {
        $replacement = '';
        for ($i = 0; $i < $spaces; $i++) {
            $replacement .= ' ';
        }
        return rtrim(str_replace("\n", "\n" . $replacement, $codepart)) . "\n";
    }

    /**
     * @param  string $html
     * @return string
     */
    private function sanitize(string $html): string
    {
        return htmlspecialchars(str_replace(array("\\", "'"), array("\\\\", "\'"), $html));
    }

    /**
     * @param  \formgenerator\components\Dependent $component
     * @return void
     */
    private function startDependent(Dependent $component): void
    {
        if ($this->outputPreview) {
            $this->phpForm->startDependentFields($component->name, $component->value);
        }
        $this->phpFormCode['components'][] = "\$form->startDependentFields('$component->name', '" . $this->sanitize($component->value) . "');\n";
    }
}
