(() => {

    const { __ } = wp.i18n;

    /*----------------------------------------
    フォームビルダー取得
    ----------------------------------------*/
    const builder = document.querySelector(
        '[data-contact-form-builder]'
    );

    if (!builder) {
        return;
    }


    /*----------------------------------------
    フォーム項目の追加先
    ----------------------------------------*/
    const fieldsContainer = builder.querySelector(
        '[data-fields-container]'
    );

    if (!fieldsContainer) {
        return;
    }

    const previewActions = fieldsContainer.querySelector(
        '[data-field-preview-actions]'
    );


    /*----------------------------------------
    必須項目の表記
    ----------------------------------------*/
    const requiredLabel =
        builder.dataset.requiredLabel || '*';


    /*----------------------------------------
    許可するファイル形式
    ----------------------------------------*/
    const FILE_TYPES = [
        {
            value: 'jpeg',
            label: 'JPG / JPEG',
        },
        {
            value: 'png',
            label: 'PNG',
        },
        {
            value: 'webp',
            label: 'WebP',
        },
        {
            value: 'pdf',
            label: 'PDF',
        },
    ];

    const strings = {
        reorderField: __(
            'Reorder field',
            'im-mail-form'
        ),
        dragToReorder: __(
            'Drag to reorder',
            'im-mail-form'
        ),
        untitledField: __(
            'Untitled Field',
            'im-mail-form'
        ),
        htmlInsert: __(
            'HTML Insert',
            'im-mail-form'
        ),
        hiddenField: __(
            'Hidden Field',
            'im-mail-form'
        ),
        required: __(
            'Required',
            'im-mail-form'
        ),
        deleteField: __(
            'Delete field',
            'im-mail-form'
        ),
        openFieldSettings: __(
            'Open field settings',
            'im-mail-form'
        ),
        closeFieldSettings: __(
            'Close field settings',
            'im-mail-form'
        ),
        fieldLabel: __(
            'Field Label',
            'im-mail-form'
        ),
        fieldLabelExample: __(
            'e.g. Name',
            'im-mail-form'
        ),
        description: __(
            'Description',
            'im-mail-form'
        ),
        optionalInstructions: __(
            'Optional instructions or notes',
            'im-mail-form'
        ),
        fieldName: __(
            'Field Name',
            'im-mail-form'
        ),
        fieldNameExample: __(
            'e.g. name',
            'im-mail-form'
        ),
        fieldNameHelp: __(
            'Use lowercase letters, numbers, hyphens, and underscores.',
            'im-mail-form'
        ),
        maximumFileSize: __(
            'Maximum File Size',
            'im-mail-form'
        ),
        allowedFileTypes: __(
            'Allowed File Types',
            'im-mail-form'
        ),
        fieldType: __(
            'Field Type',
            'im-mail-form'
        ),
        emptyOptionLabel: __(
            'Empty Option Label',
            'im-mail-form'
        ),
        placeholder: __(
            'Placeholder',
            'im-mail-form'
        ),
        hiddenValue: __(
            'Hidden Value',
            'im-mail-form'
        ),
        fixedValue: __(
            'Enter the fixed value to submit',
            'im-mail-form'
        ),
        htmlContent: __(
            'HTML',
            'im-mail-form'
        ),
        htmlContentHelp: __(
            'Enter the HTML to display at this position in the form. Scripts and other unsafe HTML are not allowed.',
            'im-mail-form'
        ),
        validation: __(
            'Validation',
            'im-mail-form'
        ),
        requiredSetting: __(
            'Required Setting',
            'im-mail-form'
        ),
        makeRequired: __(
            'Make this field required',
            'im-mail-form'
        ),
        characterLimits: __(
            'Character Limits',
            'im-mail-form'
        ),
        minimum: __(
            'Minimum',
            'im-mail-form'
        ),
        maximum: __(
            'Maximum',
            'im-mail-form'
        ),
        options: __(
            'Options',
            'im-mail-form'
        ),
        oneOptionPerLine: __(
            'Enter one option per line',
            'im-mail-form'
        ),
        oneOptionPerLineHelp: __(
            'Enter one option per line.',
            'im-mail-form'
        ),
        optionFormatHelp: __(
            'To use separate submitted values and labels, use this format:',
            'im-mail-form'
        ),
        optionFormat: __(
            'value | label',
            'im-mail-form'
        ),
        selectAnOption: __(
            'Select an option',
            'im-mail-form'
        ),
        saveError: __(
            'The settings could not be saved because there are errors in the input.',
            'im-mail-form'
        ),
    };


    /*----------------------------------------
    フォーム項目プリセット
    ----------------------------------------*/
    const presets = {
        name: {
            label: __(
                'Name',
                'im-mail-form'
            ),
            description: '',
            name: 'name',
            type: 'text',
            validation: '',
            required: true,
            minLength: '',
            maxLength: '100',
            options: '',
        },

        gender: {
            label: __(
                'Gender',
                'im-mail-form'
            ),
            description: '',
            name: 'gender',
            type: 'radio',
            validation: '',
            required: false,
            options: __(
                'Male\nFemale\nPrefer not to say',
                'im-mail-form'
            ),
        },

        email: {
            label: __(
                'Email Address',
                'im-mail-form'
            ),
            description: '',
            name: 'email',
            type: 'email',
            validation: '',
            required: true,
            options: '',
        },

        postalCode: {
            label: __(
                'Postal Code',
                'im-mail-form'
            ),
            description: __(
                'e.g. 1234567',
                'im-mail-form'
            ),
            name: 'postal_code',
            type: 'text',
            validation: 'number',
            required: false,
            minLength: '7',
            maxLength: '7',
            options: '',
        },

        address: {
            label: __(
                'Address',
                'im-mail-form'
            ),
            description: '',
            name: 'address',
            type: 'text',
            validation: '',
            required: false,
            minLength: '',
            maxLength: '200',
            options: '',
        },

        tel: {
            label: __(
                'Phone Number',
                'im-mail-form'
            ),
            description: '',
            name: 'tel',
            type: 'tel',
            validation: '',
            required: false,
            placeholder: __(
                'e.g. 1234567890 (without hyphens)',
                'im-mail-form'
            ),
            options: '',
        },

        date: {
            label: __(
                'Preferred Date',
                'im-mail-form'
            ),
            description: '',
            name: 'preferred_date',
            type: 'date',
            validation: '',
            required: false,
            options: '',
        },

        message: {
            label: __(
                'Message',
                'im-mail-form'
            ),
            description: '',
            name: 'message',
            type: 'textarea',
            validation: '',
            required: true,
            minLength: '',
            maxLength: '2000',
            options: '',
        },

        file: {
            label: __(
                'Attachment',
                'im-mail-form'
            ),
            description: '',
            name: 'attachment',
            type: 'file',
            validation: '',
            required: false,
            options: '',
            allowedFileTypes: [
                'jpeg',
                'png',
                'webp',
                'pdf',
            ],
            maxFileSize: '10',
        },

        custom: {
            label: '',
            description: '',
            name: '',
            type: 'text',
            validation: '',
            required: false,
            minLength: '',
            maxLength: '',
            hiddenValue: '',
            options: '',
            htmlContent: '',
        },

    };


    /*----------------------------------------
    HTMLエスケープ
    ----------------------------------------*/
    function escapeHtml(value) {
        return String(value ?? '')
            .replaceAll('&', '&amp;')
            .replaceAll('<', '&lt;')
            .replaceAll('>', '&gt;')
            .replaceAll('"', '&quot;')
            .replaceAll("'", '&#039;');
    }


    /*----------------------------------------
    フォーム種類に応じた見出しを取得
    ----------------------------------------*/
    function getFieldTitleLabel(type, label = '') {

        if (type === 'html') {
            return strings.htmlInsert;
        }

        if (type === 'hidden') {
            return strings.hiddenField;
        }

        return label || strings.untitledField;
    }


    /*----------------------------------------
    見出しに入力プレビューを表示する項目タイプ
    ----------------------------------------*/
    const FIELD_PREVIEW_TYPES = [
        'text',
        'email',
        'tel',
        'textarea',
        'select',
        'radio',
        'checkbox',
        'file',
        'date',
    ];


    /*----------------------------------------
    プレビュー用の選択肢を取得
    ----------------------------------------*/
    function getFieldPreviewOptions(options) {
        return String(options || '')
            .split(/\r\n|\r|\n/)
            .map((option) => option.trim())
            .filter((option) => option !== '')
            .map((option) => {
                const separatorIndex = option.indexOf('|');

                return separatorIndex === -1
                    ? option
                    : option
                        .slice(separatorIndex + 1)
                        .trim();
            });
    }


    /*----------------------------------------
    見出しの入力プレビューHTMLを生成
    ----------------------------------------*/
    function createFieldPreviewMarkup(field) {

        if (!FIELD_PREVIEW_TYPES.includes(field.type)) {
            return '';
        }

        const optionLabels =
            getFieldPreviewOptions(field.options);

        let previewControl = '';

        if (field.type === 'textarea') {
            previewControl = `
                <textarea
                    class="im-mail-form-admin__preview-input"
                    placeholder="${escapeHtml(field.placeholder || '')}"
                    rows="1"
                    tabindex="-1"
                    aria-hidden="true"
                ></textarea>
            `;
        } else if (field.type === 'select') {
            previewControl = `
                <select
                    class="im-mail-form-admin__preview-input"
                    tabindex="-1"
                    aria-hidden="true"
                >
                    <option>
                        ${escapeHtml(
                            field.placeholder ||
                            strings.selectAnOption
                        )}
                    </option>
                    ${optionLabels
                        .map((optionLabel) => `
                            <option>
                                ${escapeHtml(optionLabel)}
                            </option>
                        `)
                        .join('')}
                </select>
            `;
        } else if (field.type === 'radio') {
            previewControl = `
                <span
                    class="im-mail-form-admin__preview-choices"
                    aria-hidden="true"
                >
                    ${optionLabels
                        .map((optionLabel) => `
                            <label
                                class="im-mail-form-admin__preview-choice"
                            >
                                <input
                                    type="radio"
                                    tabindex="-1"
                                >
                                <span>
                                    ${escapeHtml(optionLabel)}
                                </span>
                            </label>
                        `)
                        .join('')}
                </span>
            `;
        } else if (field.type === 'checkbox') {
            previewControl = `
                <span
                    class="im-mail-form-admin__preview-choices"
                    aria-hidden="true"
                >
                    ${optionLabels
                        .map((optionLabel) => `
                            <label
                                class="im-mail-form-admin__preview-choice"
                            >
                                <input
                                    type="checkbox"
                                    tabindex="-1"
                                >
                                <span>
                                    ${escapeHtml(optionLabel)}
                                </span>
                            </label>
                        `)
                        .join('')}
                </span>
            `;
        } else if (field.type === 'file') {
            previewControl = `
                <input
                    type="file"
                    class="im-mail-form-admin__preview-file"
                    tabindex="-1"
                    aria-hidden="true"
                >
            `;
        } else {
            previewControl = `
                <input
                    type="${escapeHtml(field.type)}"
                    class="im-mail-form-admin__preview-input"
                    placeholder="${escapeHtml(field.placeholder || '')}"
                    tabindex="-1"
                    aria-hidden="true"
                >
            `;
        }

        return `
            <span
                class="im-mail-form-admin__field-preview"
                data-field-preview
            >
                ${previewControl}
            </span>
        `;
    }


    /*----------------------------------------
    見出しの説明文プレビューHTMLを生成
    ----------------------------------------*/
    function createFieldDescriptionMarkup(description) {

        const descriptionText = String(description || '');

        if (descriptionText === '') {
            return '';
        }

        return `
            <p
                class="im-mail-form-admin__preview-description"
                data-field-preview-description
            >${escapeHtml(descriptionText).replace(
                /\r\n|\r|\n/g,
                '<br>'
            )}</p>
        `;
    }


    /*----------------------------------------
    見出しの説明文プレビューを更新
    ----------------------------------------*/
    function updateFieldDescriptionPreview(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const descriptionInput = fieldItem.querySelector(
            'textarea[name$="[description]"]'
        );

        const fieldTitle = fieldItem.querySelector(
            '[data-field-title]'
        );

        const currentDescription = fieldItem.querySelector(
            '[data-field-preview-description]'
        );

        if (
            !typeSelect ||
            !fieldTitle ||
            !FIELD_PREVIEW_TYPES.includes(typeSelect.value) ||
            !descriptionInput?.value
        ) {
            currentDescription?.remove();
            return;
        }

        const descriptionTemplate =
            document.createElement('template');

        descriptionTemplate.innerHTML =
            createFieldDescriptionMarkup(
                descriptionInput.value
            ).trim();

        const updatedDescription =
            descriptionTemplate.content.firstElementChild;

        if (!updatedDescription) {
            currentDescription?.remove();
            return;
        }

        if (currentDescription) {
            currentDescription.replaceWith(updatedDescription);
        } else {
            fieldTitle.append(updatedDescription);
        }
    }


    /*----------------------------------------
    見出しの入力プレビューを更新
    ----------------------------------------*/
    function updateFieldPreview(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const placeholderInput = fieldItem.querySelector(
            'input[name$="[placeholder]"]'
        );

        const optionsInput = fieldItem.querySelector(
            'textarea[name$="[options]"]'
        );

        const fieldTitle = fieldItem.querySelector(
            '[data-field-title]'
        );

        let preview = fieldItem.querySelector(
            '[data-field-preview]'
        );

        if (!typeSelect || !fieldTitle) {
            return;
        }

        if (!FIELD_PREVIEW_TYPES.includes(typeSelect.value)) {
            preview?.remove();
            return;
        }

        const previewMarkup = createFieldPreviewMarkup({
            type: typeSelect.value,
            placeholder: placeholderInput?.value || '',
            options: optionsInput?.value || '',
        });

        const previewTemplate =
            document.createElement('template');
        previewTemplate.innerHTML = previewMarkup.trim();

        const updatedPreview =
            previewTemplate.content.firstElementChild;

        if (!updatedPreview) {
            return;
        }

        if (preview) {
            preview.replaceWith(updatedPreview);
        } else {
            fieldTitle.append(updatedPreview);
        }
    }


    /*----------------------------------------
    selectのoption生成
    ----------------------------------------*/
    function createOptions(options, selectedValue) {
        return Object.entries(options)
            .map(([value, label]) => {

                const selected =
                    value === selectedValue
                        ? 'selected'
                        : '';

                return `
                    <option
                        value="${escapeHtml(value)}"
                        ${selected}
                    >
                        ${escapeHtml(label)}
                    </option>
                `;
            })
            .join('');
    }


    /*----------------------------------------
    選択肢入力欄の表示切り替え
    ----------------------------------------*/
    function updateOptionsVisibility(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const optionsField = fieldItem.querySelector(
            '[data-options-field]'
        );

        if (!typeSelect || !optionsField) {
            return;
        }

        // 選択肢を使用するフォーム種類
        const optionTypes = [
            'select',
            'radio',
            'checkbox',
        ];

        // 選択式の場合のみ表示する
        optionsField.hidden = !optionTypes.includes(
            typeSelect.value
        );

    }


    /*----------------------------------------
    プレースホルダー設定の表示切り替え
    ----------------------------------------*/
    function updatePlaceholderSettings(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const placeholderField = fieldItem.querySelector(
            '[data-placeholder-field]'
        );

        const placeholderLabel = fieldItem.querySelector(
            '[data-placeholder-label]'
        );

        if (
            !typeSelect ||
            !placeholderField ||
            !placeholderLabel
        ) {
            return;
        }

        const placeholderTypes = [
            'text',
            'email',
            'tel',
            'textarea',
            'select',
        ];

        placeholderField.hidden =
            !placeholderTypes.includes(typeSelect.value);

        placeholderLabel.textContent =
            typeSelect.value === 'select'
                ? __(
                    'Empty Option Label',
                    'im-mail-form'
                )
                : __(
                    'Placeholder',
                    'im-mail-form'
                );

    }


    /*----------------------------------------
    フォーム種類に応じたバリデーション設定
    ----------------------------------------*/
    function updateValidationSettings(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const validationSelect = fieldItem.querySelector(
            'select[name$="[validation]"]'
        );

        const validationField = fieldItem.querySelector(
            '[data-validation-field]'
        );

        if (
            !typeSelect ||
            !validationSelect ||
            !validationField
        ) {
            return;
        }

        const validationSettings = {
            text: {
                visible: true,
                value: null,
                options: [
                    '',
                    'number',
                    'alphanumeric',
                ],
            },
            textarea: {
                visible: false,
                value: '',
                options: [],
            },
            email: {
                visible: false,
                value: 'email',
                options: [],
            },
            tel: {
                visible: false,
                value: 'tel',
                options: [],
            },
            date: {
                visible: false,
                value: 'date',
                options: [],
            },
            file: {
                visible: false,
                value: 'file',
                options: [],
            },
            select: {
                visible: false,
                value: '',
                options: [],
            },
            radio: {
                visible: false,
                value: '',
                options: [],
            },
            checkbox: {
                visible: false,
                value: '',
                options: [],
            },
            hidden: {
                visible: false,
                value: '',
                options: [],
            },
            html: {
                visible: false,
                value: '',
                options: [],
            },
        };

        const setting =
            validationSettings[typeSelect.value] ||
            validationSettings.text;

        validationField.hidden = !setting.visible;

        if (setting.value !== null) {
            validationSelect.value = setting.value;
        }

        [...validationSelect.options].forEach((option) => {

            option.hidden =
                setting.visible &&
                !setting.options.includes(option.value);

            option.disabled = option.hidden;

        });

        if (
            setting.visible &&
            !setting.options.includes(validationSelect.value)
        ) {
            validationSelect.value = '';
        }

    }


    /*----------------------------------------
    プリセットの項目名とフィールド名を重複させない
    ----------------------------------------*/
    function createUniquePresetField(preset) {

        const fieldNames = [
            ...fieldsContainer.querySelectorAll(
                'input[name$="[name]"]'
            ),
        ].map((input) => input.value);

        if (!fieldNames.includes(preset.name)) {
            return { ...preset };
        }

        const escapedName = preset.name.replace(
            /[.*+?^${}()|[\]\\]/g,
            '\\$&'
        );

        const pattern = new RegExp(
            `^${escapedName}_(\\d+)$`
        );

        let maxNumber = 1;

        fieldNames.forEach((fieldName) => {

            const match = fieldName.match(pattern);

            if (!match) {
                return;
            }

            maxNumber = Math.max(
                maxNumber,
                Number(match[1])
            );

        });

        const nextNumber = maxNumber + 1;

        return {
            ...preset,
            label: `${preset.label} ${nextNumber}`,
            name: `${preset.name}_${nextNumber}`,
        };

    }

    /*----------------------------------------
    フォーム項目を追加
    ----------------------------------------*/
    function createField(field, isOpen = false) {

        const index = fieldsContainer.querySelectorAll(
            '[data-field-item]'
        ).length;

        //ファイル形式チェックボックス
        const fileTypeCheckboxes = FILE_TYPES
            .map((fileType) => `
                <label>

                    <input
                        type="checkbox"
                        name="im_mail_form_fields[${index}][allowed_file_types][]"
                        value="${escapeHtml(fileType.value)}"
                        ${(field.allowedFileTypes || []).includes(fileType.value)
                            ? 'checked'
                            : ''}
                    >

                    ${escapeHtml(fileType.label)}

                </label>
            `)
            .join('');

        const wrapper = document.createElement('div');

        wrapper.className =
            'im-mail-form-admin__field';

        wrapper.dataset.fieldItem = '';

        wrapper.draggable = true;

        wrapper.innerHTML = `
        <div class="im-mail-form-admin__field-header">

            <div
                class="im-mail-form-admin__field-title"
                data-field-title
            >
                <button
                    type="button"
                    class="im-mail-form-admin__toggle"
                    aria-label="${escapeHtml(
                        isOpen
                            ? strings.closeFieldSettings
                            : strings.openFieldSettings
                    )}"
                    title="${escapeHtml(
                        isOpen
                            ? strings.closeFieldSettings
                            : strings.openFieldSettings
                    )}"
                    aria-expanded="${isOpen}"
                    data-toggle-field
                >
                    <span class="im-mail-form-admin__field-title-label">
                        <span data-field-title-label>
                            ${escapeHtml(
                                getFieldTitleLabel(
                                    field.type,
                                    field.label
                                )
                            )}
                        </span>

                        <span
                            class="im-mail-form-admin__required"
                            aria-label="${escapeHtml(strings.required)}"
                            ${field.required ? '' : 'hidden'}
                            data-field-required-mark
                        >${escapeHtml(requiredLabel)}</span>
                    </span>
                </button>

                ${createFieldPreviewMarkup(field)}
                ${
                    FIELD_PREVIEW_TYPES.includes(field.type)
                        ? createFieldDescriptionMarkup(
                            field.description
                        )
                        : ''
                }
            </div>

            <div class="im-mail-form-admin__field-actions">

                <button
                    type="button"
                    class="im-mail-form-admin__drag-handle"
                    aria-label="${escapeHtml(strings.reorderField)}"
                    title="${escapeHtml(strings.dragToReorder)}"
                    data-drag-handle
                ></button>

                <button
                    type="button"
                    class="im-mail-form-admin__edit"
                    aria-label="${escapeHtml(
                        isOpen
                            ? strings.closeFieldSettings
                            : strings.openFieldSettings
                    )}"
                    title="${escapeHtml(
                        isOpen
                            ? strings.closeFieldSettings
                            : strings.openFieldSettings
                    )}"
                    aria-expanded="${isOpen}"
                    data-toggle-field
                >
                    <span
                        class="dashicons dashicons-edit"
                        aria-hidden="true"
                    ></span>
                </button>

                <button
                    type="button"
                    class="im-mail-form-admin__delete"
                    aria-label="${escapeHtml(strings.deleteField)}"
                    title="${escapeHtml(strings.deleteField)}"
                    data-remove-field
                >
                    <span
                        class="dashicons dashicons-trash"
                        aria-hidden="true"
                    ></span>
                </button>

            </div>

        </div>

            <div
                class="im-mail-form-admin__field-body"
                data-field-body
                ${isOpen ? '' : 'hidden'}
            >

                <div
                    class="im-mail-form-admin__row"
                    data-label-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.fieldLabel)}
                        <span
                            class="im-mail-form-admin__required"
                            aria-label="${escapeHtml(strings.required)}"
                        >*</span>
                    </label>

                    <input
                        type="text"
                        name="im_mail_form_fields[${index}][label]"
                        value="${escapeHtml(field.label)}"
                        class="widefat"
                        maxlength="50"
                        placeholder="${escapeHtml(
                            strings.fieldLabelExample
                        )}"
                        required
                        data-field-label
                    >

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-description-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.description)}
                    </label>

                    <div>

                        <textarea
                            name="im_mail_form_fields[${index}][description]"
                            rows="3"
                            class="widefat"
                            maxlength="200"
                            placeholder="${escapeHtml(
                                strings.optionalInstructions
                            )}"
                        >${escapeHtml(field.description || '')}</textarea>

                    </div>

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-name-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.fieldName)}
                        <span
                            class="im-mail-form-admin__required"
                            aria-label="${escapeHtml(strings.required)}"
                        >*</span>
                    </label>

                    <input
                        type="text"
                        name="im_mail_form_fields[${index}][name]"
                        value="${escapeHtml(field.name)}"
                        class="widefat"
                        maxlength="20"
                        pattern="[a-z0-9_\\-]+"
                        placeholder="${escapeHtml(
                            strings.fieldNameExample
                        )}"
                        title="${escapeHtml(
                            strings.fieldNameHelp
                        )}"
                        required
                    >

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-max-file-size-field
                    ${field.type === 'file' ? '' : 'hidden'}
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.maximumFileSize)}
                    </label>

                    <div>

                        <input
                            type="number"
                            name="im_mail_form_fields[${index}][max_file_size]"
                            value="${escapeHtml(field.maxFileSize || '')}"
                            class="small-text"
                            min="1"
                            step="1"
                        >

                        MB

                    </div>

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-file-types-field
                    ${field.type === 'file' ? '' : 'hidden'}
                >

                    <span class="im-mail-form-admin__label">
                        ${escapeHtml(strings.allowedFileTypes)}
                    </span>

                    <div class="im-mail-form-admin__file-types">

                        ${fileTypeCheckboxes}

                    </div>

                </div>

                <div class="im-mail-form-admin__row">

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.fieldType)}
                    </label>

                    <select
                        name="im_mail_form_fields[${index}][type]"
                        class="widefat"
                    >
                        ${createOptions(
                {
                    text: __(
                        'Text',
                        'im-mail-form'
                    ),
                    email: __(
                        'Email Address',
                        'im-mail-form'
                    ),
                    tel: __(
                        'Phone Number',
                        'im-mail-form'
                    ),
                    textarea: __(
                        'Textarea',
                        'im-mail-form'
                    ),
                    select: __(
                        'Select Box',
                        'im-mail-form'
                    ),
                    radio: __(
                        'Radio Button',
                        'im-mail-form'
                    ),
                    checkbox: __(
                        'Checkbox',
                        'im-mail-form'
                    ),
                    date: __(
                        'Date',
                        'im-mail-form'
                    ),
                    file: __(
                        'File Upload',
                        'im-mail-form'
                    ),
                    hidden: __(
                        'Hidden Field',
                        'im-mail-form'
                    ),
                    html: __(
                        'HTML',
                        'im-mail-form'
                    ),
                },
                field.type
            )}
                    </select>

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-html-content-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.htmlContent)}
                        <span
                            class="im-mail-form-admin__required"
                            aria-label="${escapeHtml(strings.required)}"
                        >*</span>
                    </label>

                    <div>

                        <textarea
                            name="im_mail_form_fields[${index}][html_content]"
                            rows="6"
                            class="widefat code"
                            spellcheck="false"
                        >${escapeHtml(field.htmlContent || '')}</textarea>

                        <p class="description">
                            ${escapeHtml(strings.htmlContentHelp)}
                        </p>

                    </div>

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-placeholder-field
                >

                    <label
                        class="im-mail-form-admin__label"
                        data-placeholder-label
                    >
                        ${escapeHtml(
                            field.type === 'select'
                                ? strings.emptyOptionLabel
                                : strings.placeholder
                        )}
                    </label>

                    <input
                        type="text"
                        name="im_mail_form_fields[${index}][placeholder]"
                        value="${escapeHtml(field.placeholder || '')}"
                        class="widefat"
                        maxlength="200"
                    >

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-hidden-value-field
                >
                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.hiddenValue)}
                    </label>

                    <input
                        type="text"
                        name="im_mail_form_fields[${index}][hidden_value]"
                        value="${escapeHtml(field.hiddenValue || '')}"
                        class="widefat"
                        placeholder="${escapeHtml(
                            strings.fixedValue
                        )}"
                    >
                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-validation-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.validation)}
                    </label>

                    <select
                        name="im_mail_form_fields[${index}][validation]"
                        class="widefat"
                    >
                        ${createOptions(
                {
                    '': __(
                        'None',
                        'im-mail-form'
                    ),
                    text: __(
                        'Text',
                        'im-mail-form'
                    ),
                    number: __(
                        'Numbers only',
                        'im-mail-form'
                    ),
                    alphanumeric: __(
                        'Letters, numbers, and symbols (- _ . @)',
                        'im-mail-form'
                    ),
                },
                field.validation
            )}
                    </select>

                </div>

                <div
                    class="im-mail-form-admin__row"
                    data-required-field
                >

                    <span class="im-mail-form-admin__label">
                        ${escapeHtml(strings.requiredSetting)}
                    </span>

                    <label>

                        <input
                            type="checkbox"
                            name="im_mail_form_fields[${index}][required]"
                            value="1"
                            ${field.required ? 'checked' : ''}
                        >

                        ${escapeHtml(strings.makeRequired)}

                    </label>

                </div>


                <div
                    class="im-mail-form-admin__row"
                    data-length-field
                >

                    <span class="im-mail-form-admin__label">
                        ${escapeHtml(strings.characterLimits)}
                    </span>

                    <div class="im-mail-form-admin__length-fields">

                        <label>
                            ${escapeHtml(strings.minimum)}
                            <input
                                type="number"
                                name="im_mail_form_fields[${index}][min_length]"
                                value="${escapeHtml(field.minLength || '')}"
                                class="small-text"
                                min="1"
                                step="1"
                            >
                        </label>

                        <label>
                            ${escapeHtml(strings.maximum)}
                            <input
                                type="number"
                                name="im_mail_form_fields[${index}][max_length]"
                                value="${escapeHtml(field.maxLength || '')}"
                                class="small-text"
                                min="1"
                                step="1"
                            >
                        </label>

                    </div>

                </div>


                <div
                    class="im-mail-form-admin__row"
                    data-options-field
                >

                    <label class="im-mail-form-admin__label">
                        ${escapeHtml(strings.options)}
                        <span
                            class="im-mail-form-admin__required"
                            aria-label="${escapeHtml(strings.required)}"
                        >*</span>
                    </label>

                    <div>

                        <textarea
                            name="im_mail_form_fields[${index}][options]"
                            rows="4"
                            class="widefat"
                            placeholder="${escapeHtml(
                                strings.oneOptionPerLine
                            )}"
                        >${escapeHtml(field.options)}</textarea>

                        <p class="description">
                            ${escapeHtml(strings.oneOptionPerLineHelp)}
                            ${escapeHtml(strings.optionFormatHelp)}
                            <code class="im-mail-form-admin__option-format">${escapeHtml(strings.optionFormat)}</code>
                        </p>

                    </div>

                </div>

            </div>
        `;

        fieldsContainer.insertBefore(
            wrapper,
            previewActions
        );

        /*----------------------------------------
        追加した項目の表示状態を初期化
        ----------------------------------------*/
        updateFieldSettingsVisibility(wrapper);
        updateOptionsVisibility(wrapper);
        updatePlaceholderSettings(wrapper);
        updateValidationSettings(wrapper);
        updateLengthVisibility(wrapper);
        updateHiddenValueVisibility(wrapper);
        updateFileSettingsVisibility(wrapper);

        // 自動返信先の選択肢に新しい項目を反映する
        syncReplyToFieldOptions();

        updatePreviewSubmitButton();
    }


    /*----------------------------------------
    送信ボタンプレビューを更新
    ----------------------------------------*/
    function updatePreviewSubmitButton() {

        if (!previewActions) {
            return;
        }

        const fieldCount = fieldsContainer.querySelectorAll(
            '[data-field-item]'
        ).length;

        previewActions.hidden = fieldCount === 0;

        if (fieldCount === 0) {
            return;
        }

        const previewSubmitButton = previewActions.querySelector(
            '.im-mail-form-admin__preview-submit'
        );

        const useConfirm = document.querySelector(
            '[name="im_mail_form_use_confirm"]'
        );

        const buttonLabelField = document.querySelector(
            useConfirm?.checked
                ? '[name="im_mail_form_confirm_button_label"]'
                : '[name="im_mail_form_submit_button_label"]'
        );

        if (previewSubmitButton && buttonLabelField) {
            previewSubmitButton.textContent =
                buttonLabelField.value;
        }
    }


    /*----------------------------------------
    フォーム項目のindexを振り直す
    ----------------------------------------*/
    function reindexFields() {

        const fields = fieldsContainer.querySelectorAll(
            '[data-field-item]'
        );

        fields.forEach((field, index) => {

            const controls = field.querySelectorAll(
                '[name^="im_mail_form_fields["]'
            );

            controls.forEach((control) => {

                control.name = control.name.replace(
                    /im_mail_form_fields\[\d+\]/,
                    `im_mail_form_fields[${index}]`
                );

            });

        });
    }


    /*----------------------------------------
    フォーム項目の設定エリアをアニメーション
    ----------------------------------------*/
    const FIELD_BODY_ANIMATION_DURATION = 200;
    const fieldBodyAnimations = new WeakMap();

    function animateFieldBody(fieldBody, shouldOpen) {

        const activeAnimation =
            fieldBodyAnimations.get(fieldBody);

        if (activeAnimation) {
            fieldBody.removeEventListener(
                'transitionend',
                activeAnimation.onTransitionEnd
            );
            window.clearTimeout(
                activeAnimation.fallbackTimer
            );
            window.cancelAnimationFrame(
                activeAnimation.animationFrame
            );
        }

        if (
            window.matchMedia(
                '(prefers-reduced-motion: reduce)'
            ).matches
        ) {
            fieldBody.hidden = !shouldOpen;
            fieldBody.style.removeProperty('transition');
            fieldBody.style.removeProperty('box-sizing');
            fieldBody.style.removeProperty('overflow');
            fieldBody.style.removeProperty('height');
            fieldBody.style.removeProperty('margin-top');
            fieldBody.style.removeProperty('will-change');
            fieldBodyAnimations.delete(fieldBody);
            return;
        }

        const computedExpandedMarginTop =
            parseFloat(
                window.getComputedStyle(fieldBody).marginTop
            );

        const expandedMarginTop =
            activeAnimation?.expandedMarginTop ??
            (
                Number.isFinite(computedExpandedMarginTop)
                    ? computedExpandedMarginTop
                    : 0
            );

        if (fieldBody.hidden) {
            fieldBody.hidden = false;
        }

        const currentStyles =
            window.getComputedStyle(fieldBody);

        const currentHeight =
            activeAnimation
                ? fieldBody.getBoundingClientRect().height
                : shouldOpen
                    ? 0
                    : fieldBody.getBoundingClientRect().height;

        const currentMarginTop =
            activeAnimation
                ? parseFloat(currentStyles.marginTop)
                : shouldOpen
                    ? 0
                    : expandedMarginTop;

        fieldBody.style.transition = 'none';
        fieldBody.style.boxSizing = 'border-box';
        fieldBody.style.overflow = 'hidden';
        fieldBody.style.height = `${currentHeight}px`;
        fieldBody.style.marginTop =
            `${currentMarginTop}px`;
        fieldBody.style.willChange =
            'height, margin-top';

        // 現在値を確定してから目標値へのtransitionを開始する。
        void fieldBody.offsetHeight;

        const targetHeight =
            shouldOpen
                ? fieldBody.scrollHeight
                : 0;

        let hasFinished = false;

        const finishAnimation = () => {
            if (hasFinished) {
                return;
            }

            hasFinished = true;
            fieldBody.removeEventListener(
                'transitionend',
                onTransitionEnd
            );
            fieldBody.hidden = !shouldOpen;
            fieldBody.style.removeProperty('transition');
            fieldBody.style.removeProperty('box-sizing');
            fieldBody.style.removeProperty('overflow');
            fieldBody.style.removeProperty('height');
            fieldBody.style.removeProperty('margin-top');
            fieldBody.style.removeProperty('will-change');
            fieldBodyAnimations.delete(fieldBody);
        };

        const onTransitionEnd = (transitionEvent) => {
            if (
                transitionEvent.target !== fieldBody ||
                transitionEvent.propertyName !== 'height'
            ) {
                return;
            }

            fieldBody.removeEventListener(
                'transitionend',
                onTransitionEnd
            );
            window.clearTimeout(fallbackTimer);
            finishAnimation();
        };

        const fallbackTimer = window.setTimeout(
            finishAnimation,
            FIELD_BODY_ANIMATION_DURATION + 50
        );

        fieldBody.style.transition =
            `height ${FIELD_BODY_ANIMATION_DURATION}ms ease, ` +
            `margin-top ${FIELD_BODY_ANIMATION_DURATION}ms ease`;

        fieldBody.addEventListener(
            'transitionend',
            onTransitionEnd
        );

        const animationFrame =
            window.requestAnimationFrame(() => {
                fieldBody.style.height = `${targetHeight}px`;
                fieldBody.style.marginTop =
                    shouldOpen
                        ? `${expandedMarginTop}px`
                        : '0px';
            });

        fieldBodyAnimations.set(
            fieldBody,
            {
                animationFrame,
                expandedMarginTop,
                fallbackTimer,
                onTransitionEnd,
            }
        );
    }


    /*----------------------------------------
    クリック処理
    ----------------------------------------*/
    builder.addEventListener('click', (event) => {

        /*----------------------------------------
        プリセット項目を追加
        ----------------------------------------*/
        const presetButton = event.target.closest(
            '[data-add-preset]'
        );

        if (presetButton) {

            const presetName =
                presetButton.dataset.addPreset;

            const preset = presets[presetName];

            if (preset) {
                createField(
                    createUniquePresetField(preset)
                );
            }

            return;
        }


        /*----------------------------------------
        自由項目を追加
        ----------------------------------------*/
        const customFieldButton = event.target.closest(
            '[data-add-custom-field]'
        );

        if (customFieldButton) {

            const fieldType =
                customFieldButton.dataset.addCustomField ||
                'text';

            const field =
                fieldType === 'file'
                    ? {
                        ...presets.file,
                        label: '',
                        name: '',
                    }
                    : {
                        ...presets.custom,
                        type: fieldType,
                        placeholder:
                            fieldType === 'select'
                                ? strings.selectAnOption
                                : '',
                    };

            createField(
                field,
                true
            );

            return;
        }


        /*----------------------------------------
        フォーム項目の開閉
        ----------------------------------------*/
        const clickedFieldHeader = event.target.closest(
            '[data-field-item] > .im-mail-form-admin__field-header'
        );

        const clickedFieldAction = event.target.closest(
            '[data-remove-field]'
        );

        const toggleButton =
            event.target.closest('[data-toggle-field]') ||
            (
                clickedFieldHeader &&
                !clickedFieldAction
                    ? clickedFieldHeader.querySelector(
                        '[data-toggle-field]'
                    )
                    : null
            );

        if (toggleButton) {
            if (
                Date.now() - lastFieldDragEndTime <
                FIELD_DRAG_CLICK_DELAY
            ) {
                return;
            }

            const fieldItem = toggleButton.closest(
                '[data-field-item]'
            );

            const fieldBody = fieldItem?.querySelector(
                '[data-field-body]'
            );

            if (!fieldBody) {
                return;
            }

            const isOpen =
                toggleButton.getAttribute(
                    'aria-expanded'
                ) === 'true';

            animateFieldBody(fieldBody, !isOpen);

            fieldItem
                .querySelectorAll('[data-toggle-field]')
                .forEach((fieldToggleButton) => {
                    fieldToggleButton.setAttribute(
                        'aria-expanded',
                        String(!isOpen)
                    );

                    fieldToggleButton.setAttribute(
                        'aria-label',
                        isOpen
                            ? strings.openFieldSettings
                            : strings.closeFieldSettings
                    );

                    fieldToggleButton.setAttribute(
                        'title',
                        isOpen
                            ? strings.openFieldSettings
                            : strings.closeFieldSettings
                    );
                });

            return;
        }


        /*----------------------------------------
        フォーム項目を削除
        ----------------------------------------*/
        const removeButton = event.target.closest(
            '[data-remove-field]'
        );

        if (removeButton) {

            removeButton
                .closest('[data-field-item]')
                ?.remove();

            reindexFields();
            syncReplyToFieldOptions();
            updatePreviewSubmitButton();
        }

    });


    /*----------------------------------------
    項目名変更時に見出しへ反映
    ----------------------------------------*/
    builder.addEventListener('input', (event) => {

        if (!event.target.matches('[data-field-label]')) {
            return;
        }

        const field = event.target.closest(
            '[data-field-item]'
        );

        const titleLabel = field?.querySelector(
            '[data-field-title-label]'
        );

        if (titleLabel) {
            const typeSelect = field?.querySelector(
                'select[name$="[type]"]'
            );

            titleLabel.textContent = getFieldTitleLabel(
                typeSelect?.value || 'text',
                event.target.value
            );
        }

    });


    /*----------------------------------------
    説明文・プレースホルダー・選択肢変更時に見出しへ反映
    ----------------------------------------*/
    builder.addEventListener('input', (event) => {

        if (
            !event.target.matches(
                'textarea[name$="[description]"], ' +
                'input[name$="[placeholder]"], ' +
                'textarea[name$="[options]"]'
            )
        ) {
            return;
        }

        const fieldItem = event.target.closest(
            '[data-field-item]'
        );

        if (fieldItem) {
            if (
                event.target.matches(
                    'textarea[name$="[description]"]'
                )
            ) {
                updateFieldDescriptionPreview(fieldItem);
            } else {
                updateFieldPreview(fieldItem);
            }
        }

    });


    /*----------------------------------------
    項目名・フィールド名変更時に自動返信先の選択肢へ反映
    ----------------------------------------*/
    builder.addEventListener('input', (event) => {

        if (
            !event.target.matches(
                '[data-field-label], input[name$="[name]"]'
            )
        ) {
            return;
        }

        syncReplyToFieldOptions();

    });


    /*----------------------------------------
    必須設定を見出しへ反映
    ----------------------------------------*/
    builder.addEventListener('change', (event) => {

        if (
            !event.target.matches(
                'input[name$="[required]"]'
            )
        ) {
            return;
        }

        const field = event.target.closest(
            '[data-field-item]'
        );

        const requiredMark = field?.querySelector(
            '[data-field-required-mark]'
        );

        if (requiredMark) {
            requiredMark.hidden = !event.target.checked;
        }

    });

    /*----------------------------------------
    フォーム種類変更時の表示切り替え
    ----------------------------------------*/
    builder.addEventListener('change', (event) => {

        const typeSelect = event.target.closest(
            'select[name$="[type]"]'
        );

        if (!typeSelect) {
            return;
        }

        const fieldItem = typeSelect.closest(
            '[data-field-item]'
        );

        if (!fieldItem) {
            return;
        }

        const titleLabel = fieldItem.querySelector(
            '[data-field-title-label]'
        );

        const labelInput = fieldItem.querySelector(
            '[data-field-label]'
        );

        if (titleLabel) {
            titleLabel.textContent = getFieldTitleLabel(
                typeSelect.value,
                labelInput?.value || ''
            );
        }

        // hidden選択時に不要な設定欄をまとめて切り替える
        updateFieldSettingsVisibility(fieldItem);

        // 選択肢欄の表示を切り替える
        updateOptionsVisibility(fieldItem);

        // プレースホルダー設定を切り替える
        updatePlaceholderSettings(fieldItem);

        // バリデーション設定を切り替える
        updateValidationSettings(fieldItem);

        // 文字数制限欄の表示を切り替える
        updateLengthVisibility(fieldItem);

        // hidden値欄の表示を切り替える
        updateHiddenValueVisibility(fieldItem);

        // ファイル設定欄の表示を切り替える
        updateFileSettingsVisibility(fieldItem);

        // 見出しの入力プレビューを切り替える
        updateFieldPreview(fieldItem);

        // 見出しの説明文プレビューを切り替える
        updateFieldDescriptionPreview(fieldItem);

        // 自動返信先の選択肢へ種類変更を反映する
        syncReplyToFieldOptions();
    });


    /*----------------------------------------
    バリデーション変更時に自動返信先の選択肢へ反映
    ----------------------------------------*/
    builder.addEventListener('change', (event) => {

        if (
            !event.target.matches(
                'select[name$="[validation]"]'
            )
        ) {
            return;
        }

        syncReplyToFieldOptions();

    });


    /*----------------------------------------
    フォーム項目：ドラッグ＆ドロップ並び替え
    ----------------------------------------*/
    let draggedItem = null;
    let lastFieldDragEndTime = 0;
    const FIELD_DRAG_CLICK_DELAY = 250;

    // ヘッダー上で押した場合のみ、移動後のネイティブドラッグを有効化
    fieldsContainer.addEventListener('mousedown', (event) => {

        const fieldItem = event.target.closest(
            '[data-field-item]'
        );

        if (!fieldItem) {
            return;
        }

        const fieldHeader = event.target.closest(
            '[data-field-item] > .im-mail-form-admin__field-header'
        );

        const removeButton = event.target.closest(
            '[data-remove-field]'
        );

        fieldItem.draggable =
            event.button === 0 &&
            fieldHeader !== null &&
            removeButton === null;

    });

    fieldsContainer.addEventListener('dragstart', (event) => {

        const fieldItem = event.target.closest(
            '[data-field-item]'
        );

        if (!fieldItem) {
            return;
        }

        draggedItem = fieldItem;

        fieldItem.classList.add('is-dragging');

        event.dataTransfer.effectAllowed = 'move';
    });

    fieldsContainer.addEventListener('dragend', () => {

        if (draggedItem) {
            draggedItem.classList.remove('is-dragging');
            draggedItem.draggable = false;
        }

        draggedItem = null;
        lastFieldDragEndTime = Date.now();

        reindexFields();
    });

    fieldsContainer.addEventListener('dragover', (event) => {

        event.preventDefault();

        if (!draggedItem) {
            return;
        }

        const afterElement = getDragAfterElement(
            fieldsContainer,
            event.clientY
        );

        if (!afterElement) {
            fieldsContainer.insertBefore(
                draggedItem,
                previewActions
            );
            return;
        }

        fieldsContainer.insertBefore(
            draggedItem,
            afterElement
        );
    });


    /*----------------------------------------
    ドラッグ位置から挿入先を取得
    ----------------------------------------*/
    function getDragAfterElement(container, mouseY) {

        const items = [
            ...container.querySelectorAll(
                '[data-field-item]:not(.is-dragging)'
            ),
        ];

        return items.reduce(
            (closest, item) => {

                const box = item.getBoundingClientRect();
                const offset =
                    mouseY - box.top - box.height / 2;

                if (
                    offset < 0 &&
                    offset > closest.offset
                ) {
                    return {
                        offset,
                        element: item,
                    };
                }

                return closest;
            },
            {
                offset: Number.NEGATIVE_INFINITY,
                element: null,
            }
        ).element;
    }


    /*----------------------------------------
    保存済みフォーム項目の初期表示
    ----------------------------------------*/
    fieldsContainer
        .querySelectorAll('[data-field-item]')
        .forEach((fieldItem) => {

            // hidden選択時の大まかな表示切り替え
            updateFieldSettingsVisibility(fieldItem);

            // 選択肢欄の表示切り替え
            updateOptionsVisibility(fieldItem);

            // プレースホルダー設定を切り替える
            updatePlaceholderSettings(fieldItem);

            // バリデーション設定を切り替える
            updateValidationSettings(fieldItem);

            // 文字数制限欄の表示切り替え
            updateLengthVisibility(fieldItem);

            // hidden値欄の表示切り替え
            updateHiddenValueVisibility(fieldItem);

            // ファイル設定欄の表示を切り替える
            updateFileSettingsVisibility(fieldItem);

        });


    /*----------------------------------------
    メール設定の入力エラー表示
    ----------------------------------------*/
    document
        .querySelectorAll(
            '[data-invalid-mail-fields]'
        )
        .forEach((container) => {

            const invalidFieldNames =
                container.dataset.invalidMailFields
                    .split(' ')
                    .filter(Boolean);

            container
                .querySelectorAll('[name]')
                .forEach((field) => {

                    if (
                        !invalidFieldNames.includes(
                            field.name
                        )
                    ) {
                        return;
                    }

                    field.classList.add(
                        'im-mail-form-admin__input--error'
                    );

                    field.setAttribute(
                        'aria-invalid',
                        'true'
                    );

                });

        });


    /*----------------------------------------
    文字数制限欄の表示切り替え
    ----------------------------------------*/
    function updateLengthVisibility(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const lengthField = fieldItem.querySelector(
            '[data-length-field]'
        );

        if (!typeSelect || !lengthField) {
            return;
        }

        const typesWithLength = [
            'text',
            'textarea',
        ];

        lengthField.hidden = !typesWithLength.includes(
            typeSelect.value
        );
    }


    /*----------------------------------------
    hidden値入力欄の表示切り替え
    ----------------------------------------*/
    function updateHiddenValueVisibility(fieldItem) {
        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const hiddenValueField = fieldItem.querySelector(
            '[data-hidden-value-field]'
        );

        if (!typeSelect || !hiddenValueField) {
            return;
        }

        hiddenValueField.hidden =
            typeSelect.value !== 'hidden';
    }

    /*----------------------------------------
    ファイル設定欄の表示切り替え
    ----------------------------------------*/
    function updateFileSettingsVisibility(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        const maxFileSizeField = fieldItem.querySelector(
            '[data-max-file-size-field]'
        );

        const fileTypesField = fieldItem.querySelector(
            '[data-file-types-field]'
        );

        if (!typeSelect) {
            return;
        }

        const isFile =
            typeSelect.value === 'file';

        if (maxFileSizeField) {
            maxFileSizeField.hidden = !isFile;
        }

        if (fileTypesField) {
            fileTypesField.hidden = !isFile;
        }
    }

    /*----------------------------------------
    メール設定：使用時のみ必須項目にする
    ----------------------------------------*/
    function updateMailRequiredFields(
        checkboxName,
        requiredFieldNames,
        requiredMarkSelector = '',
        disabledFieldSelector = ''
    ) {
        const checkbox = document.querySelector(
            `[name="${checkboxName}"]`
        );

        if (!checkbox) {
            return;
        }

        function updateRequired() {
            requiredFieldNames.forEach((fieldName) => {
                const field = document.querySelector(
                    `[name="${fieldName}"]`
                );

                if (!field) {
                    return;
                }

                field.required = checkbox.checked;
            });

            if (requiredMarkSelector) {

                document
                    .querySelectorAll(requiredMarkSelector)
                    .forEach((requiredMark) => {
                        requiredMark.hidden = !checkbox.checked;
                    });

            }

            if (disabledFieldSelector) {

                document
                    .querySelectorAll(
                        disabledFieldSelector
                    )
                    .forEach((field) => {
                        field.disabled = !checkbox.checked;
                    });

            }
        }

        checkbox.addEventListener(
            'change',
            updateRequired
        );

        updateRequired();
    }


    /*----------------------------------------
    自動返信メール：必須項目
    ----------------------------------------*/
    updateMailRequiredFields(
        'im_mail_form_use_reply',
        [
            'im_mail_form_reply_to_field',
            'im_mail_form_reply_from_name',
            'im_mail_form_reply_from',
            'im_mail_form_reply_subject',
            'im_mail_form_reply_body',
        ],
        '[data-reply-required-mark]',
        '#im_mail_form_reply_mail '
            + '[name^="im_mail_form_reply_"]'
            + ':not([data-reply-to-field-select]), '
            + '#im_mail_form_reply_mail '
            + '.im-mail-form-merge-tags button'
    );


    /*----------------------------------------
    自動返信メール：自動返信先フィールドの選択肢を同期
    ----------------------------------------*/
    const replyToFieldSelect = document.querySelector(
        '[data-reply-to-field-select]'
    );

    const replyToggleCheckbox = document.querySelector(
        '[name="im_mail_form_use_reply"]'
    );

    /*
    * 自動返信先として選択中のフォーム項目（DOM要素）。
    * フィールド名は変更され得るため、名前ではなくこのDOM要素の
    * 同一性で選択状態を追跡する。
    */
    let selectedFieldItem = null;

    function syncReplyToFieldOptions() {

        if (!replyToFieldSelect) {
            return;
        }

        const fieldInfos = [
            ...fieldsContainer.querySelectorAll(
                '[data-field-item]'
            ),
        ].map((fieldItem) => {

            const nameInput = fieldItem.querySelector(
                'input[name$="[name]"]'
            );

            const labelInput = fieldItem.querySelector(
                'input[name$="[label]"]'
            );

            const typeSelect = fieldItem.querySelector(
                'select[name$="[type]"]'
            );

            const validationSelect = fieldItem.querySelector(
                'select[name$="[validation]"]'
            );

            return {
                fieldItem,
                name: nameInput?.value || '',
                label: labelInput?.value || '',
                isEmailType:
                    (typeSelect?.value || '') === 'email'
                    || (validationSelect?.value || '') === 'email',
            };

        });

        /*
        * 選択中のDOM要素が未確定の場合（初回同期時）は、
        * プルダウンの現在値から対象の項目を特定する。
        */
        if (!selectedFieldItem) {

            const initialValue = replyToFieldSelect.value;

            const initialMatch = fieldInfos.find(
                (info) =>
                    info.isEmailType
                    && info.name !== ''
                    && info.name === initialValue
            );

            if (initialMatch) {
                selectedFieldItem = initialMatch.fieldItem;
            }
        }

        /*
        * 選択中の項目がメール以外へ変更された、または
        * 削除された場合だけ選択状態を手放す
        * （フィールド名の変更・一時的な空欄では手放さない）。
        */
        const trackedStillEmail = selectedFieldItem
            ? fieldInfos.some(
                (info) =>
                    info.fieldItem === selectedFieldItem
                    && info.isEmailType
            )
            : false;

        if (!trackedStillEmail) {
            selectedFieldItem = null;
        }

        const optionInfos = fieldInfos.filter(
            (info) => info.isEmailType && info.name !== ''
        );

        if (!selectedFieldItem && optionInfos.length > 0) {
            selectedFieldItem = optionInfos[0].fieldItem;
        }

        replyToFieldSelect.innerHTML = optionInfos
            .map((info) => `
                <option value="${escapeHtml(info.name)}">
                    ${escapeHtml(
                        `${info.label || info.name} (${info.name})`
                    )}
                </option>
            `)
            .join('');

        const selectedOption = optionInfos.find(
            (info) => info.fieldItem === selectedFieldItem
        );

        if (selectedOption) {
            replyToFieldSelect.value = selectedOption.name;
        }

        replyToFieldSelect.disabled =
            optionInfos.length === 0
            || (
                replyToggleCheckbox
                && !replyToggleCheckbox.checked
            );

    }

    if (replyToggleCheckbox) {
        replyToggleCheckbox.addEventListener(
            'change',
            syncReplyToFieldOptions
        );
    }

    /*
    * ユーザーがプルダウンを手動で変更した場合、その選択を
    * selectedFieldItemへ反映し、以後の同期処理でも維持する。
    */
    if (replyToFieldSelect) {
        replyToFieldSelect.addEventListener('change', () => {

            const matchedFieldItem = [
                ...fieldsContainer.querySelectorAll(
                    '[data-field-item]'
                ),
            ].find((fieldItem) => {

                const nameInput = fieldItem.querySelector(
                    'input[name$="[name]"]'
                );

                return (
                    (nameInput?.value || '')
                    === replyToFieldSelect.value
                );

            });

            selectedFieldItem = matchedFieldItem || null;

        });
    }

    syncReplyToFieldOptions();
    updatePreviewSubmitButton();


    /*----------------------------------------
    確認画面設定：ボタン文言の有効切り替え
    ----------------------------------------*/
    const useConfirmCheckbox = document.querySelector(
        '[name="im_mail_form_use_confirm"]'
    );

    const confirmButtonLabelField = document.querySelector(
        '[name="im_mail_form_confirm_button_label"]'
    );

    const backButtonLabelField = document.querySelector(
        '[name="im_mail_form_back_button_label"]'
    );

    const submitButtonLabelField = document.querySelector(
        '[name="im_mail_form_submit_button_label"]'
    );

    if (
        useConfirmCheckbox &&
        confirmButtonLabelField &&
        backButtonLabelField
    ) {

        function updateConfirmButtonLabelFields() {

            const disabled =
                !useConfirmCheckbox.checked;

            confirmButtonLabelField.disabled =
                disabled;

            backButtonLabelField.disabled =
                disabled;
        }

        useConfirmCheckbox.addEventListener(
            'change',
            () => {
                updateConfirmButtonLabelFields();
                updatePreviewSubmitButton();
            }
        );

        updateConfirmButtonLabelFields();
    }

    [
        confirmButtonLabelField,
        submitButtonLabelField,
    ].forEach((buttonLabelField) => {

        buttonLabelField?.addEventListener(
            'input',
            updatePreviewSubmitButton
        );

    });


    /*----------------------------------------
    完了画面設定：リダイレクトURLの有効切り替え
    ----------------------------------------*/
    const redirectCheckbox = document.querySelector(
        '[name="im_mail_form_redirect_enabled"]'
    );

    const redirectUrlField = document.querySelector(
        '[name="im_mail_form_redirect_url"]'
    );

    const redirectRequiredMark = document.querySelector(
        '[data-redirect-required-mark]'
    );

    const completeMessageField = document.querySelector(
        '[name="im_mail_form_complete_message"]'
    );

    const completeMessageTagButtons =
    document.querySelectorAll(
        '#im_mail_form_complete '
            + '.im-mail-form-merge-tags button'
    );

    const completeMessageRequiredMark = document.querySelector(
        '[data-complete-message-required-mark]'
    );

    if (redirectCheckbox && redirectUrlField) {

        function updateRedirectUrlField() {

            redirectUrlField.disabled =
                !redirectCheckbox.checked;

            redirectUrlField.required =
                redirectCheckbox.checked;

            if (redirectRequiredMark) {
                redirectRequiredMark.hidden =
                    !redirectCheckbox.checked;
            }

            if (completeMessageField) {

                completeMessageField.disabled =
                    redirectCheckbox.checked;

                completeMessageField.required =
                    !redirectCheckbox.checked;

            }

            completeMessageTagButtons.forEach(
                (button) => {

                    button.disabled =
                        redirectCheckbox.checked;

                }
            );

            if (completeMessageRequiredMark) {
                completeMessageRequiredMark.hidden =
                    redirectCheckbox.checked;
            }

        }

        redirectCheckbox.addEventListener(
            'change',
            updateRedirectUrlField
        );

        updateRedirectUrlField();

    }

    /*----------------------------------------
    入力エラーがある項目を自動で開く
    ----------------------------------------*/
    builder.addEventListener(
        'invalid',
        (event) => {

            const invalidField = event.target;

            const fieldItem = invalidField.closest(
                '[data-field-item]'
            );

            if (!fieldItem) {
                return;
            }

            const fieldBody = fieldItem.querySelector(
                '[data-field-body]'
            );

            const toggleButton = fieldItem.querySelector(
                '[data-toggle-field]'
            );

            if (!fieldBody || !toggleButton) {
                return;
            }

            // アコーディオンを開く
            fieldBody.hidden = false;

            toggleButton.setAttribute(
                'aria-expanded',
                'true'
            );

            toggleButton.setAttribute(
                'aria-label',
                strings.closeFieldSettings
            );

            toggleButton.setAttribute(
                'title',
                strings.closeFieldSettings
            );

        },
        true
    );

    /*----------------------------------------
    フォーム種類に応じた設定欄の表示切り替え
    ----------------------------------------*/
    function updateFieldSettingsVisibility(fieldItem) {

        const typeSelect = fieldItem.querySelector(
            'select[name$="[type]"]'
        );

        if (!typeSelect) {
            return;
        }

        const isHidden = typeSelect.value === 'hidden';
        const isHtml = typeSelect.value === 'html';

        const hiddenTargets = [
            '[data-description-field]',
            '[data-validation-field]',
            '[data-required-field]',
            '[data-length-field]',
            '[data-options-field]',
        ];

        hiddenTargets.forEach((selector) => {
            const target = fieldItem.querySelector(selector);

            if (target) {
                target.hidden = isHidden || isHtml;
            }
        });

        const hiddenValueField = fieldItem.querySelector(
            '[data-hidden-value-field]'
        );

        if (hiddenValueField) {
            hiddenValueField.hidden = !isHidden;
        }

        // HTMLブロックはラベル・フィールド名が不要
        const labelField = fieldItem.querySelector(
            '[data-label-field]'
        );

        if (labelField) {
            labelField.hidden = isHtml;
        }

        const nameField = fieldItem.querySelector(
            '[data-name-field]'
        );

        if (nameField) {
            nameField.hidden = isHtml;
        }

        // HTML入力欄はHTMLブロックの場合のみ表示
        const htmlContentField = fieldItem.querySelector(
            '[data-html-content-field]'
        );

        if (htmlContentField) {
            htmlContentField.hidden = !isHtml;
        }
    }


    /*----------------------------------------
    保存時にフォーム項目を検証
    ----------------------------------------*/
    const postForm = document.querySelector('#post');

    if (postForm) {
        postForm.noValidate = true;
    }

    /*----------------------------------------
    保存ボタン下に保存エラーを表示
    ----------------------------------------*/
    const saveErrorNotice = document.querySelector(
        '[data-contact-form-save-error]'
    );

    const publishingActions = document.querySelector(
        '#submitdiv .inside'
    );

    if (
        saveErrorNotice &&
        publishingActions
    ) {

        const saveErrorMessage =
            document.createElement('p');

        saveErrorMessage.className =
            'im-mail-form-admin__save-error';

        saveErrorMessage.setAttribute(
            'role',
            'alert'
        );

        saveErrorMessage.textContent =
            strings.saveError;

        publishingActions.append(
            saveErrorMessage
        );
    }

    /*----------------------------------------
    差し込みタグをカーソル位置へ挿入
    ----------------------------------------*/
    document.addEventListener('click', (event) => {

        const button = event.target.closest(
            '[data-merge-tag]'
        );

        if (!button) {
            return;
        }

        const tag = button.dataset.mergeTag;
        const targetId = button.dataset.mergeTagTarget;
        const target = document.getElementById(targetId);

        if (!tag || !target) {
            return;
        }

        const start = target.selectionStart;
        const end = target.selectionEnd;

        target.setRangeText(
            tag,
            start,
            end,
            'end'
        );

        target.dispatchEvent(
            new Event('input', {
                bubbles: true,
            })
        );

        target.focus();

    });

    /*----------------------------------------
    ショートコードをクリップボードへコピー
    ----------------------------------------*/
    const shortcodeCopy = document.querySelector(
        '[data-shortcode-copy]'
    );

    const shortcodeCopyMessage = document.querySelector(
        '[data-shortcode-copy-message]'
    );

    if (
        shortcodeCopy &&
        shortcodeCopyMessage
    ) {

        let copyMessageTimer;

        shortcodeCopy.addEventListener(
            'click',
            async () => {

                shortcodeCopy.select();

                let copied = false;

                try {

                    if (
                        navigator.clipboard &&
                        window.isSecureContext
                    ) {

                        await navigator.clipboard.writeText(
                            shortcodeCopy.value
                        );

                        copied = true;

                    } else {

                        copied = document.execCommand(
                            'copy'
                        );

                    }

                } catch (error) {

                    copied = document.execCommand(
                        'copy'
                    );

                }

                if (!copied) {
                    return;
                }

                shortcodeCopyMessage.hidden = false;

                window.clearTimeout(
                    copyMessageTimer
                );

                copyMessageTimer = window.setTimeout(
                    () => {
                        shortcodeCopyMessage.hidden = true;
                    },
                    2000
                );

            }
        );
    }

    /*----------------------------------------
    管理画面タブ切り替え
    ----------------------------------------*/
    const adminTabs = document.querySelector(
        '[data-contact-form-tabs]'
    );

    if (adminTabs) {

        const tabButtons = [
            ...adminTabs.querySelectorAll(
                '[data-contact-form-tab]'
            ),
        ];

        const tabPanels = {
            fields: [
                document.getElementById(
                    'im_mail_form_fields'
                ),
            ],
            mail: [
                document.getElementById(
                    'im_mail_form_reply_mail'
                ),
                document.getElementById(
                    'im_mail_form_admin_mail'
                ),
            ],
            complete: [
                document.getElementById(
                    'im_mail_form_complete'
                ),
            ],
        };

        const postId =
            document.getElementById('post_ID')?.value ||
            'new';

        const tabStorageKey =
            `imMailFormActiveTab:${postId}`;

        function saveActiveAdminTab(activeTab) {

            try {

                window.localStorage.setItem(
                    tabStorageKey,
                    activeTab
                );

            } catch (error) {

                return;

            }

        }

        function getActiveAdminTab() {

            try {

                const savedTab =
                    window.localStorage.getItem(
                        tabStorageKey
                    );

                if (
                    savedTab &&
                    Object.prototype.hasOwnProperty.call(
                        tabPanels,
                        savedTab
                    )
                ) {
                    return savedTab;
                }

            } catch (error) {

                return 'fields';

            }

            return 'fields';

        }

        function activateAdminTab(activeTab) {

            /*
            * Mail Settingsタブを表示する直前に、保存前の
            * フォーム項目DOMを基に自動返信先の選択肢を再構築する。
            */
            if (activeTab === 'mail') {
                syncReplyToFieldOptions();
            }

            Object.entries(tabPanels)
                .forEach(([tabName, panels]) => {

                    panels.forEach((panel) => {

                        if (!panel) {
                            return;
                        }

                        panel.classList.toggle(
                            'im-mail-form-tab-panel--hidden',
                            tabName !== activeTab
                        );

                    });

                });

            tabButtons.forEach((button) => {

                const isActive =
                    button.dataset.contactFormTab ===
                    activeTab;

                button.classList.toggle(
                    'is-active',
                    isActive
                );

                if (isActive) {
                    button.setAttribute(
                        'aria-current',
                        'page'
                    );
                } else {
                    button.removeAttribute(
                        'aria-current'
                    );
                }

            });

        }

        let invalidTabActivated = false;

        const postForm = document.getElementById('post');

        if (postForm) {

            postForm.addEventListener(
                'invalid',
                (event) => {

                    if (invalidTabActivated) {
                        return;
                    }

                    const invalidTab =
                        Object.entries(tabPanels)
                            .find(
                                ([tabName, panels]) =>
                                    panels.some(
                                        (panel) =>
                                            panel &&
                                            panel.contains(
                                                event.target
                                            )
                                    )
                            );

                    if (!invalidTab) {
                        return;
                    }

                    invalidTabActivated = true;

                    activateAdminTab(
                        invalidTab[0]
                    );

                    saveActiveAdminTab(
                        invalidTab[0]
                    );

                    window.setTimeout(
                        () => {
                            invalidTabActivated = false;
                        },
                        0
                    );

                },
                true
            );
        }

        adminTabs.addEventListener(
            'click',
            (event) => {

                const button = event.target.closest(
                    '[data-contact-form-tab]'
                );

                if (!button) {
                    return;
                }

                activateAdminTab(
                    button.dataset.contactFormTab
                );

                saveActiveAdminTab(
                    button.dataset.contactFormTab
                );

            }
        );

        const errorNotices = [
            ...document.querySelectorAll(
                '[data-contact-form-error-tabs], ' +
                '[data-contact-form-error-tab]'
            ),
        ];

        const errorTabs = [
            ...new Set(
                errorNotices
                    .flatMap((errorNotice) => [
                        ...(
                            errorNotice.dataset.contactFormErrorTabs ||
                            ''
                        ).split(' '),
                        errorNotice.dataset.contactFormErrorTab || '',
                    ])
                    .filter(
                        (tabName) =>
                            Object.prototype.hasOwnProperty.call(
                                tabPanels,
                                tabName
                            )
                    )
            ),
        ];

        errorTabs.forEach((errorTab) => {

            const errorTabButton = tabButtons.find(
                (button) =>
                    button.dataset.contactFormTab ===
                    errorTab
            );

            if (errorTabButton) {
                errorTabButton.classList.add(
                    'im-mail-form-tabs__button--error'
                );
            }

        });

        const initialTab =
            errorTabs[0] ||
            getActiveAdminTab();

        activateAdminTab(
            initialTab
        );

        document.body.classList.add(
            'im-mail-form-tabs-ready'
        );

        if (errorTabs.length > 0) {
            saveActiveAdminTab(
                initialTab
            );
        }

    }


    /*----------------------------------------
    WordPress標準メタボックス操作を無効化
    ----------------------------------------*/
    window.jQuery(() => {

        if (
            !document.body.classList.contains(
                'im-mail-form-editor'
            )
        ) {
            return;
        }

        const metaBoxSortables = window.jQuery(
            '.im-mail-form-editor .meta-box-sortables'
        );

        metaBoxSortables.each((index, sortableElement) => {

            const sortable = window.jQuery(sortableElement);

            if (sortable.hasClass('ui-sortable')) {
                sortable.sortable('disable');
            }

        });

        const metaBoxes = window.jQuery(
            '.im-mail-form-editor .postbox'
        );

        // 保存済みの折りたたみ状態にかかわらず常に展開する。
        metaBoxes.removeClass('closed');
        metaBoxes
            .find('.handlediv')
            .attr('aria-expanded', 'true');

        metaBoxes
            .find(
                '.hndle, ' +
                '.handlediv, ' +
                '.handle-order-higher, ' +
                '.handle-order-lower'
            )
            .off('click.postboxes');

    });

/*----------------------------------------*/
})();
