<?php
namespace DiviBooster\DiviBooster\Modules\Accordion\Subtitle;

const DIVI5_ACCORDION_SLUG      = 'divi/accordion';
const DIVI5_ACCORDION_ITEM_SLUG = 'divi/accordion-item';

const ATTR_PARENT_FONT = 'diviboosterSubtitleFont';
const ATTR_ITEM_TEXT   = 'diviboosterSubtitle';
const ATTR_ITEM_FONT   = 'diviboosterSubtitleFont';

const GROUP_PARENT_SUBTITLE = 'designDiviboosterSubtitleText';
const GROUP_ITEM_SUBTITLE   = 'designDiviboosterSubtitleText';

const SUBTITLE_CLASS         = 'divibooster_accordion_item_subtitle';
const SUBTITLE_DEFAULT_LEVEL = 'span';
const STYLE_HANDLE           = 'divibooster-accordion-subtitles';
const TEXT_DOMAIN            = 'divi-booster';

if (function_exists('add_filter')) {
    add_filter('block_type_metadata_settings', __NAMESPACE__ . '\\register_divi5_attrs');
    add_filter('render_block_' . DIVI5_ACCORDION_ITEM_SLUG, __NAMESPACE__ . '\\render_accordion_item_subtitle', 10, 3);
    add_filter('render_block_' . DIVI5_ACCORDION_SLUG, __NAMESPACE__ . '\\render_accordion_subtitle_styles', 10, 3);
}

if (function_exists('add_action')) {
    add_action('wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_subtitle_styles');
    add_action('divi_visual_builder_assets_before_enqueue_styles', __NAMESPACE__ . '\\enqueue_subtitle_styles');
    add_action('divi_visual_builder_assets_before_enqueue_scripts', __NAMESPACE__ . '\\enqueue_visual_builder_script');
}

function register_divi5_attrs($settings) {
    if (!is_array($settings)) {
        return $settings;
    }

    $module_name = isset($settings['name']) ? $settings['name'] : '';

    if ($module_name !== DIVI5_ACCORDION_SLUG && $module_name !== DIVI5_ACCORDION_ITEM_SLUG) {
        return $settings;
    }

    if (!isset($settings['attributes']) || !is_array($settings['attributes'])) {
        $settings['attributes'] = array();
    }

    if ($module_name === DIVI5_ACCORDION_SLUG) {
        $settings['attributes'][ATTR_PARENT_FONT] = get_parent_font_attribute_definition();
    }

    if ($module_name === DIVI5_ACCORDION_ITEM_SLUG) {
        $settings['attributes'] = insert_array_item_after_key($settings['attributes'], 'title', ATTR_ITEM_TEXT, get_item_subtitle_attribute_definition());
        $settings['attributes'][ATTR_ITEM_FONT] = get_item_font_attribute_definition();
    }

    return $settings;
}

function insert_array_item_after_key($array, $after_key, $key, $value) {
    $result   = array();
    $inserted = false;

    foreach ($array as $current_key => $current_value) {
        if ($current_key === $key) {
            continue;
        }

        $result[$current_key] = $current_value;

        if ($current_key === $after_key) {
            $result[$key] = $value;
            $inserted     = true;
        }
    }

    if (!$inserted) {
        $result[$key] = $value;
    }

    return $result;
}

function render_accordion_item_subtitle($block_content, $parsed_block, $block) {
    if (!is_string($block_content)) {
        return $block_content;
    }

    $attrs    = get_block_attrs($parsed_block);
    $subtitle = get_subtitle_text_from_attrs($attrs);

    if ($subtitle === '') {
        return $block_content;
    }

    $item_font         = get_module_attr($attrs, ATTR_ITEM_FONT, array());
    $subtitle_markup   = build_subtitle_markup($subtitle);
    $block_content     = inject_subtitle_after_title($block_content, $subtitle_markup);
    $css_declarations  = get_font_css_declarations(get_font_value_from_attr($item_font));

    if (!empty($css_declarations)) {
        $item_order_class = get_order_class_from_content($block_content, 'et_pb_accordion_item');

        if (!$item_order_class) {
            return $block_content;
        }

        $css = build_font_css_rule(
            get_item_subtitle_selector($item_order_class),
            $css_declarations
        );

        if ($css !== '') {
            $block_content .= build_style_tag($css, 'item-' . $item_order_class);
        }
    }

    return $block_content;
}

function render_accordion_subtitle_styles($block_content, $parsed_block, $block) {
    if (!is_string($block_content) || strpos($block_content, SUBTITLE_CLASS) === false) {
        return $block_content;
    }

    $attrs       = get_block_attrs($parsed_block);
    $parent_font = get_module_attr($attrs, ATTR_PARENT_FONT, array());
    $css_declarations = get_font_css_declarations(get_font_value_from_attr($parent_font));

    if (!empty($css_declarations)) {
        $accordion_order_class = get_order_class_from_content($block_content, 'et_pb_accordion');

        if (!$accordion_order_class) {
            return $block_content;
        }

        $css = build_font_css_rule(get_accordion_subtitle_selector($accordion_order_class), $css_declarations);

        if ($css !== '') {
            $block_content .= build_style_tag($css, 'accordion-' . $accordion_order_class);
        }
    }

    return $block_content;
}

function enqueue_visual_builder_script() {
    if (!function_exists('et_builder_d5_enabled') || !\et_builder_d5_enabled()) {
        return;
    }

    if (!function_exists('wp_register_script') || !function_exists('wp_enqueue_script') || !function_exists('wp_add_inline_script')) {
        return;
    }

    enqueue_subtitle_styles();

    $handle = 'divibooster-divi5-accordion-subtitle';

    wp_register_script($handle, '', get_visual_builder_script_dependencies(), null, true);
    wp_enqueue_script($handle);
    wp_add_inline_script($handle, get_visual_builder_js());
}

function enqueue_subtitle_styles() {
    static $inline_style_added = false;

    if (!function_exists('wp_style_is') || !function_exists('wp_register_style') || !function_exists('wp_enqueue_style') || !function_exists('wp_add_inline_style')) {
        return;
    }

    if (!wp_style_is(STYLE_HANDLE, 'registered')) {
        wp_register_style(STYLE_HANDLE, false, array(), null);
    }

    wp_enqueue_style(STYLE_HANDLE);

    if (!$inline_style_added) {
        wp_add_inline_style(STYLE_HANDLE, get_subtitle_base_css());
        $inline_style_added = true;
    }
}

function get_subtitle_base_css() {
    return '.' . SUBTITLE_CLASS . '{display:block;margin:4px 0 0;padding:0 30px 0 0;font-size:.9em;font-weight:400;line-height:1.4em;color:inherit;}';
}

function get_visual_builder_script_dependencies() {
    if (!function_exists('wp_script_is')) {
        return array('lodash', 'wp-hooks', 'wp-element', 'wp-i18n');
    }

    $hooks_handle = wp_script_is('divi-vendor-wp-hooks', 'registered') ? 'divi-vendor-wp-hooks' : 'wp-hooks';
    $element_handle = wp_script_is('divi-vendor-react', 'registered') ? 'divi-vendor-react' : 'wp-element';
    $i18n_handle = wp_script_is('divi-vendor-wp-i18n', 'registered') ? 'divi-vendor-wp-i18n' : 'wp-i18n';

    return array('lodash', $hooks_handle, $element_handle, $i18n_handle);
}

function translate_html($text) {
    if (function_exists('esc_html__')) {
        return esc_html__($text, TEXT_DOMAIN);
    }

    return escape_html($text);
}

function encode_json($value) {
    $encoded = function_exists('wp_json_encode') ? wp_json_encode($value) : json_encode($value);

    return is_string($encoded) ? $encoded : 'null';
}

function escape_html($value) {
    if (function_exists('esc_html')) {
        return esc_html($value);
    }

    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

function escape_attr($value) {
    if (function_exists('esc_attr')) {
        return esc_attr($value);
    }

    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

function strip_all_tags($value) {
    if (function_exists('wp_strip_all_tags')) {
        return wp_strip_all_tags($value);
    }

    return strip_tags((string) $value);
}

function get_parent_font_attribute_definition() {
    return get_font_attribute_definition(
        GROUP_PARENT_SUBTITLE,
        ATTR_PARENT_FONT . '.decoration.font',
        '{{selector}} .' . SUBTITLE_CLASS
    );
}

function get_item_font_attribute_definition() {
    return get_font_attribute_definition(
        GROUP_ITEM_SUBTITLE,
        ATTR_ITEM_FONT . '.decoration.font',
        '{{selector}} .' . SUBTITLE_CLASS
    );
}

function get_item_subtitle_attribute_definition() {
    return array(
        'type'       => 'object',
        'selector'   => '{{selector}} .' . SUBTITLE_CLASS,
        'attributes' => array(
            'class' => SUBTITLE_CLASS,
        ),
        'settings'   => array(
            'innerContent' => array(
                'groupType' => 'group-item',
                'item'      => array(
                    'groupSlug'   => 'contentText',
                    'attrName'    => ATTR_ITEM_TEXT . '.innerContent',
                    'label'       => translate_html('Subtitle'),
                    'description' => translate_html('Enter an optional subtitle to display below the accordion item title.'),
                    'category'    => 'basic_option',
                    'priority'    => 10,
                    'render'      => true,
                    'features'    => array(
                        'dynamicContent' => array(
                            'type' => 'text',
                        ),
                        'sticky' => false,
                        'preset' => 'content',
                    ),
                    'component'   => array(
                        'type' => 'field',
                        'name' => 'divi/text',
                    ),
                    'defaultAttr' => array(
                        'desktop' => array(
                            'value' => '',
                        ),
                    ),
                ),
            ),
        ),
    );
}

function get_font_attribute_definition($group_slug, $attr_name, $selector) {
    return array(
        'type'       => 'object',
        'selector'   => $selector,
        'styleProps' => array(
            'font' => array(
                'selector'  => $selector,
                'important' => array(
                    'font' => array(
                        'desktop' => array(
                            'value' => array(
                                'color' => true,
                            ),
                        ),
                    ),
                ),
            ),
        ),
        'settings'   => array(
            'decoration' => array(
                'font' => array(
                    'groupType' => 'group-item',
                    'item'      => array(
                        'groupSlug' => $group_slug,
                        'attrName'  => $attr_name,
                        'priority'  => 10,
                        'render'    => true,
                        'component' => array(
                            'type'  => 'group',
                            'name'  => 'divi/font',
                            'props' => array(
                                'grouped'          => false,
                                'groupLabel'       => translate_html('Subtitle Text'),
                                'fieldLabel'       => translate_html('Subtitle'),
                                'defaultGroupAttr' => array(
                                    'font' => array(
                                        'desktop' => array(
                                            'value' => array(
                                                'size'         => '14px',
                                                'lineHeight'   => '1.4em',
                                            ),
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ),
    );
}

function get_visual_builder_js() {
    $accordion_slug      = encode_json(DIVI5_ACCORDION_SLUG);
    $accordion_item_slug = encode_json(DIVI5_ACCORDION_ITEM_SLUG);
    $parent_font_attr    = encode_json(ATTR_PARENT_FONT);
    $item_text_attr      = encode_json(ATTR_ITEM_TEXT);
    $item_font_attr      = encode_json(ATTR_ITEM_FONT);
    $parent_group        = encode_json(GROUP_PARENT_SUBTITLE);
    $item_group          = encode_json(GROUP_ITEM_SUBTITLE);
    $subtitle_class      = encode_json(SUBTITLE_CLASS);
    $text_domain         = encode_json(TEXT_DOMAIN);
    $default_level       = encode_json(SUBTITLE_DEFAULT_LEVEL);
    $parent_attr_def     = encode_json(get_parent_font_attribute_definition());
    $item_text_def       = encode_json(get_item_subtitle_attribute_definition());
    $item_font_def       = encode_json(get_item_font_attribute_definition());

    return <<<JS
(function(){
    var hooks = window.vendor && window.vendor.wp && window.vendor.wp.hooks ? window.vendor.wp.hooks : (window.wp && window.wp.hooks ? window.wp.hooks : null);
    var lodash = window.lodash || {};
    var wpElement = window.wp && window.wp.element ? window.wp.element : (window.vendor && window.vendor.React ? window.vendor.React : null);
    var i18n = window.wp && window.wp.i18n ? window.wp.i18n : (window.vendor && window.vendor.wp && window.vendor.wp.i18n ? window.vendor.wp.i18n : null);
    var __ = i18n && i18n.__ ? i18n.__ : function(text){ return text; };

    if (!hooks || typeof hooks.addFilter !== 'function') {
        return;
    }

    var ACCORDION = {$accordion_slug};
    var ITEM = {$accordion_item_slug};
    var PARENT_FONT_ATTR = {$parent_font_attr};
    var ITEM_TEXT_ATTR = {$item_text_attr};
    var ITEM_FONT_ATTR = {$item_font_attr};
    var PARENT_GROUP = {$parent_group};
    var ITEM_GROUP = {$item_group};
    var SUBTITLE_CLASS = {$subtitle_class};
    var TEXT_DOMAIN = {$text_domain};
    var DEFAULT_LEVEL = {$default_level};
    var PARENT_ATTR_DEF = {$parent_attr_def};
    var ITEM_TEXT_DEF = {$item_text_def};
    var ITEM_FONT_DEF = {$item_font_def};

    hooks.addFilter('divi.moduleLibrary.moduleMapping', 'divibooster/accordion-subtitle', function(modules){
        if (!modules || typeof modules !== 'object') {
            return modules;
        }

        setModuleAttribute(modules, ACCORDION, PARENT_FONT_ATTR, PARENT_ATTR_DEF);
        setModuleAttribute(modules, ITEM, ITEM_TEXT_ATTR, ITEM_TEXT_DEF, 'title');
        setModuleAttribute(modules, ITEM, ITEM_FONT_ATTR, ITEM_FONT_DEF);
        setModuleGroup(modules, ACCORDION, PARENT_GROUP, getDesignGroup(PARENT_GROUP, 25));
        setModuleGroup(modules, ITEM, ITEM_GROUP, getDesignGroup(ITEM_GROUP, 25));
        return modules;
    });

    hooks.addFilter('divi.moduleLibrary.moduleSettings.groups', 'divibooster/accordion-subtitle', function(groups, metadata){
        if (!groups || typeof groups !== 'object') {
            return groups;
        }

        var name = metadata && metadata.name ? metadata.name : '';
        if (name === ACCORDION && !groups[PARENT_GROUP]) {
            groups[PARENT_GROUP] = getDesignGroup(PARENT_GROUP, 25);
        }
        if (name === ITEM && !groups[ITEM_GROUP]) {
            groups[ITEM_GROUP] = getDesignGroup(ITEM_GROUP, 25);
        }
        return groups;
    });

    hooks.addFilter('divi.moduleLibrary.moduleSettings.groups.' + ACCORDION.replace('/', '.'), 'divibooster/accordion-subtitle', function(groups){
        if (!groups || typeof groups !== 'object') {
            return groups;
        }

        if (!groups[PARENT_GROUP]) {
            groups[PARENT_GROUP] = getDesignGroup(PARENT_GROUP, 25);
        }
        return groups;
    });

    hooks.addFilter('divi.moduleLibrary.moduleSettings.groups.' + ITEM.replace('/', '.'), 'divibooster/accordion-subtitle', function(groups){
        if (!groups || typeof groups !== 'object') {
            return groups;
        }

        if (!groups[ITEM_GROUP]) {
            groups[ITEM_GROUP] = getDesignGroup(ITEM_GROUP, 25);
        }
        return groups;
    });

    hooks.addFilter('divi.moduleLibrary.moduleAttributes.' + ACCORDION.replace('/', '.'), 'divibooster/accordion-subtitle', function(attributes){
        if (!attributes || typeof attributes !== 'object') {
            return attributes;
        }

        attributes[PARENT_FONT_ATTR] = PARENT_ATTR_DEF;
        return attributes;
    });

    hooks.addFilter('divi.moduleLibrary.moduleAttributes.' + ITEM.replace('/', '.'), 'divibooster/accordion-subtitle', function(attributes){
        if (!attributes || typeof attributes !== 'object') {
            return attributes;
        }

        setAttributeAfter(attributes, 'title', ITEM_TEXT_ATTR, ITEM_TEXT_DEF);
        attributes[ITEM_FONT_ATTR] = ITEM_FONT_DEF;
        return attributes;
    });

    hooks.addFilter('divi.module.wrapper.render', 'divibooster/accordion-subtitle', function(rendered, details){
        var moduleName = getModuleName(details);

        if (moduleName === ITEM) {
            var attrs = getModuleAttrs(details);
            rendered = addSubtitleToRenderedItem(rendered, attrs);
            rendered = appendBuilderFontStyleToRendered(rendered, details, attrs, ITEM_FONT_ATTR, true);
        } else if (moduleName === ACCORDION) {
            var attrs = getModuleAttrs(details);
            rendered = appendBuilderFontStyleToRendered(rendered, details, attrs, PARENT_FONT_ATTR, false);
        }

        return rendered;
    });

    hooks.addFilter('divi.module.elements.afterRender', 'divibooster/accordion-subtitle', function(afterElement, param, moduleElements){
        var attrName = getObjectValue(param, 'attrName');

        if (!isTitleElementAttrName(attrName)) {
            return afterElement;
        }

        var moduleName = getModuleElementsModuleName(moduleElements, param);

        if (moduleName !== ITEM) {
            return afterElement;
        }

        var attrs = getModuleElementsAttrs(moduleElements, param);
        var subtitle = getSubtitleTextFromAttrs(attrs);
        subtitle = typeof subtitle === 'string' ? subtitle.trim() : '';

        if (!subtitle || !wpElement || !wpElement.createElement) {
            return afterElement;
        }

        return appendSubtitleAfterElement(afterElement, attrs, subtitle);
    });

    function setModuleAttribute(modules, moduleName, attrName, attrDef, afterAttrName) {
        var target = getOrCreatePath(modules, [moduleName, 'metadata', 'attributes']);

        if (!target || typeof target !== 'object') {
            return;
        }

        if (afterAttrName) {
            setAttributeAfter(target, afterAttrName, attrName, attrDef);
        } else {
            target[attrName] = attrDef;
        }
    }

    function setAttributeAfter(target, afterAttrName, attrName, attrDef) {
        var ordered = {};
        var inserted = false;
        var key;

        if (!target || typeof target !== 'object') {
            return;
        }

        for (key in target) {
            if (!Object.prototype.hasOwnProperty.call(target, key) || key === attrName) {
                continue;
            }

            ordered[key] = target[key];

            if (key === afterAttrName) {
                ordered[attrName] = attrDef;
                inserted = true;
            }
        }

        if (!inserted) {
            ordered[attrName] = attrDef;
        }

        for (key in target) {
            if (Object.prototype.hasOwnProperty.call(target, key)) {
                delete target[key];
            }
        }

        for (key in ordered) {
            if (Object.prototype.hasOwnProperty.call(ordered, key)) {
                target[key] = ordered[key];
            }
        }
    }

    function setModuleGroup(modules, moduleName, groupName, groupDef) {
        var target = getOrCreatePath(modules, [moduleName, 'metadata', 'settings', 'groups']);

        if (!target || typeof target !== 'object') {
            return;
        }

        target[groupName] = groupDef;
    }

    function getOrCreatePath(object, path) {
        if (!object || typeof object !== 'object') {
            return null;
        }

        var target = lodash.get ? lodash.get(object, path) : getPath(object, path);

        if (target && typeof target === 'object') {
            return target;
        }

        if (typeof lodash.set === 'function') {
            try {
                lodash.set(object, path, {});
                target = lodash.get ? lodash.get(object, path) : getPath(object, path);
                return target && typeof target === 'object' ? target : null;
            } catch (e) {
                return null;
            }
        }

        return setPath(object, path, {});
    }

    function getDesignGroup(groupName, priority) {
        return {
            groupName: groupName,
            panel: 'design',
            priority: priority,
            multiElements: true,
            component: {
                name: 'divi/composite',
                props: {
                    groupLabel: __('Subtitle Text', TEXT_DOMAIN),
                    presetGroup: 'divi/font'
                }
            }
        };
    }

    function addSubtitleToRenderedItem(rendered, attrs) {
        var subtitle = getSubtitleTextFromAttrs(attrs);
        subtitle = typeof subtitle === 'string' ? subtitle.trim() : '';

        if (!subtitle) {
            return rendered;
        }

        if (typeof rendered === 'string') {
            return injectSubtitleIntoString(rendered, subtitle);
        }

        if (!wpElement || !wpElement.createElement || !wpElement.cloneElement || !wpElement.isValidElement || !wpElement.Children) {
            return rendered;
        }

        if (containsSubtitleElement(rendered)) {
            return rendered;
        }

        return injectSubtitleIntoElement(rendered, subtitle);
    }

    function appendSubtitleAfterElement(afterElement, attrs, subtitle) {
        var subtitleProps = {
            className: SUBTITLE_CLASS,
            key: 'divibooster-accordion-subtitle-after-title'
        };

        var subtitleEl = wpElement.createElement(
            DEFAULT_LEVEL,
            subtitleProps,
            subtitle
        );

        if (!afterElement) {
            return subtitleEl;
        }

        if (wpElement.isValidElement && containsSubtitleElement(afterElement)) {
            return afterElement;
        }

        return wpElement.createElement(wpElement.Fragment, null, afterElement, subtitleEl);
    }

    function appendBuilderFontStyleToRendered(rendered, details, attrs, fontAttrName, isItem) {
        var moduleElements = getObjectValue(details, 'elements') || getObjectValue(details, 'moduleElements');
        var diviModule = window.divi && window.divi.module ? window.divi.module : null;
        var StyleContainer = diviModule && diviModule.StyleContainer ? diviModule.StyleContainer : null;

        if (isItem && !hasExplicitBuilderFontAttr(moduleElements, attrs, fontAttrName)) {
            return rendered;
        }

        if (!StyleContainer || !moduleElements || typeof moduleElements.style !== 'function' || !wpElement || !wpElement.createElement || !wpElement.cloneElement || !wpElement.isValidElement || !wpElement.Children || !wpElement.isValidElement(rendered)) {
            return rendered;
        }

        var fontStyleEl = getBuilderFontStyleElement(moduleElements, fontAttrName, isItem);
        if (!fontStyleEl) {
            return rendered;
        }

        var renderedProps = rendered.props || {};
        var moduleState = getObjectValue(details, 'state') || '';
        var styleContainerEl = wpElement.createElement(
            StyleContainer,
            {
                key: 'divibooster-accordion-subtitle-style-container',
                mode: 'builder',
                state: renderedProps.isEdited ? moduleState : '',
                noStyleTag: false
            },
            fontStyleEl
        );
        var children = renderedProps.children ? wpElement.Children.toArray(renderedProps.children) : [];
        children.push(styleContainerEl);

        return wpElement.cloneElement(rendered, renderedProps, children);
    }

    function hasExplicitBuilderFontAttr(moduleElements, attrs, fontAttrName) {
        var moduleAttrs = normalizeBuilderValue(getObjectValue(moduleElements, 'attrs'));
        var fontAttr = moduleAttrs && getObjectValue(moduleAttrs, fontAttrName);

        if (fontAttr === undefined || fontAttr === null) {
            fontAttr = getObjectValue(attrs, fontAttrName);
        }

        fontAttr = normalizeBuilderValue(fontAttr);

        return !!fontAttr && typeof fontAttr === 'object' && Object.keys(fontAttr).length > 0;
    }

    function getBuilderFontStyleElement(moduleElements, fontAttrName, isItem) {
        if (!moduleElements || typeof moduleElements.style !== 'function') {
            return null;
        }

        var orderClass = normalizeOrderClassSelector(getObjectValue(moduleElements, 'orderClass'));
        var styleOptions = { attrName: fontAttrName };

        if (orderClass) {
            var selector = getBuilderSubtitleSelector(orderClass, isItem);
            styleOptions.styleProps = {
                selector: selector,
                font: {
                    selector: selector
                }
            };
            styleOptions.isMergeRecursiveProps = true;
        }

        try {
            var styleEl = moduleElements.style(styleOptions);

            return wpElement.isValidElement && wpElement.isValidElement(styleEl)
                ? wpElement.cloneElement(styleEl, { key: 'divibooster-accordion-subtitle-font-style' })
                : styleEl;
        } catch (e) {
            return null;
        }
    }

    function normalizeOrderClassSelector(orderClass) {
        orderClass = String(orderClass || '').trim();

        if (orderClass && orderClass.charAt(0) !== '.' && orderClass.charAt(0) !== '#' && orderClass.charAt(0) !== '[') {
            return '.' + orderClass;
        }

        return orderClass;
    }

    function getBuilderSubtitleSelector(orderClass, isItem) {
        return isItem
            ? '.et_pb_accordion .et_pb_accordion_item' + orderClass + ' .' + SUBTITLE_CLASS
            : '.et_pb_accordion' + orderClass + ' .' + SUBTITLE_CLASS;
    }

    function injectSubtitleIntoString(html, subtitle) {
        if (html.indexOf(SUBTITLE_CLASS) !== -1) {
            return html;
        }

       var escaped = escapeHtml(subtitle);
       var markup = '<' + DEFAULT_LEVEL + ' class="' + SUBTITLE_CLASS + '">' + escaped + '</' + DEFAULT_LEVEL + '>';
       var updated = html.replace(/(<([a-z0-9]+)\\b[^>]*class=(['\"])[^'\"]*\\bet_pb_toggle_title\\b[^'\"]*\\3[^>]*>[\\s\\S]*?<\/\\2>)/i, '$1' + markup);

       if (updated !== html || typeof window.DOMParser !== 'function') {
           return updated;
       }

       var doc = new window.DOMParser().parseFromString('<div>' + html + '</div>', 'text/html');
       var title = doc.querySelector('.et_pb_toggle_title');
       if (!title || !title.insertAdjacentHTML) {
           return html;
       }

       title.insertAdjacentHTML('afterend', markup);
       return doc.body && doc.body.firstElementChild ? doc.body.firstElementChild.innerHTML : html;
   }

    function injectSubtitleIntoElement(element, subtitle) {
        if (!wpElement.isValidElement(element)) {
            return element;
        }

        var props = element.props || {};
        var className = getClassName(props.className);

        if (hasClass(className, SUBTITLE_CLASS)) {
            return element;
        }

        if (hasClass(className, 'et_pb_toggle_title')) {
            var subtitleEl = wpElement.createElement(
                DEFAULT_LEVEL,
                {
                    className: SUBTITLE_CLASS,
                    key: 'divibooster-accordion-subtitle'
                },
                subtitle
            );
            return wpElement.createElement(wpElement.Fragment, null, element, subtitleEl);
        }

        var children = props.children;
        if (!children) {
            return element;
        }

        var changed = false;
        var newChildren = wpElement.Children.map(children, function(child){
            var newChild = injectSubtitleIntoElement(child, subtitle);
            if (newChild !== child) {
                changed = true;
            }
            return newChild;
        });

        return changed ? wpElement.cloneElement(element, props, newChildren) : element;
    }

    function containsSubtitleElement(element) {
        if (!wpElement || !wpElement.isValidElement || !wpElement.Children || !wpElement.isValidElement(element)) {
            return false;
        }

        var props = element.props || {};
        var className = getClassName(props.className);

        if (hasClass(className, SUBTITLE_CLASS)) {
            return true;
        }

        var children = props.children ? wpElement.Children.toArray(props.children) : [];
        for (var i = 0; i < children.length; i++) {
            if (containsSubtitleElement(children[i])) {
                return true;
            }
        }

        return false;
    }

    function getFontValue(fontAttr) {
        if (!fontAttr) {
            return null;
        }
        fontAttr = normalizeBuilderValue(fontAttr);

        var candidates = [
            getObjectValue(getObjectValue(getObjectValue(fontAttr, 'decoration'), 'font'), 'font'),
            getObjectValue(getObjectValue(fontAttr, 'decoration'), 'font'),
            getObjectValue(fontAttr, 'font'),
            fontAttr
        ];

        for (var i = 0; i < candidates.length; i++) {
            var candidate = candidates[i];
            var value = getAttrValue(candidate, null);
            if (value && typeof value === 'object') {
                return value;
            }
        }

        return null;
    }

   function getAttrValue(attr, fallback) {
       if (!attr) {
           return fallback;
       }
       attr = normalizeBuilderValue(attr);
       if (typeof attr !== 'object') {
           return attr;
       }
       if (attr.desktop && attr.desktop.value !== undefined) {
           return attr.desktop.value;
       }
       if (attr.value !== undefined) {
           return attr.value;
        }
       return fallback;
   }

   function getSubtitleTextFromAttrs(attrs) {
       var attr = getObjectValue(attrs, ITEM_TEXT_ATTR);

       return getFirstAttrValue([
           getObjectValue(attr, 'innerContent'),
           attr,
           getObjectValue(getObjectValue(getObjectValue(attr, 'desktop'), 'value'), 'innerContent'),
           getObjectValue(attrs, ITEM_TEXT_ATTR + '.innerContent')
       ], '');
   }

   function getFirstAttrValue(candidates, fallback) {
       for (var i = 0; i < candidates.length; i++) {
           var value = getAttrValue(candidates[i], null);
           if (typeof value === 'string' && value.trim() !== '') {
               return value;
           }
           if (value !== null && value !== undefined && typeof value !== 'object') {
               return value;
           }
       }

       return fallback;
   }

    function getModuleName(details) {
        return details && (
            getObjectValue(details, 'name') ||
            getObjectValue(details, 'moduleName') ||
            getObjectValue(getObjectValue(details, 'metadata'), 'name') ||
            getObjectValue(getObjectValue(details, 'module'), 'name')
        ) || '';
    }

   function getModuleAttrs(details) {
       var props = getObjectValue(details, 'props');
       var module = getObjectValue(details, 'module');
       var moduleProps = getObjectValue(module, 'props');
       var block = getObjectValue(details, 'block');

       return normalizeBuilderValue(details && (
           getObjectValue(details, 'attrs') ||
           getObjectValue(details, 'attributes') ||
           getObjectValue(details, 'moduleAttrs') ||
           getObjectValue(details, 'moduleAttributes') ||
           getObjectValue(props, 'attrs') ||
           getObjectValue(props, 'attributes') ||
           getObjectValue(props, 'moduleAttrs') ||
           getObjectValue(props, 'moduleAttributes') ||
           getObjectValue(module, 'attrs') ||
           getObjectValue(module, 'attributes') ||
           getObjectValue(moduleProps, 'attrs') ||
           getObjectValue(moduleProps, 'attributes') ||
           getObjectValue(block, 'attrs') ||
           getObjectValue(block, 'attributes')
       )) || {};
   }

   function getModuleElementsModuleName(moduleElements, param) {
       return (
           getObjectValue(getObjectValue(moduleElements, 'moduleMetadata'), 'name') ||
           getObjectValue(getObjectValue(param, 'moduleMetadata'), 'name') ||
           getObjectValue(moduleElements, 'name') ||
           getObjectValue(param, 'moduleName') ||
           getObjectValue(param, 'name') ||
           getModuleName(param)
       ) || '';
   }

   function getModuleElementsAttrs(moduleElements, param) {
       var metadata = getObjectValue(moduleElements, 'moduleMetadata');
       var props = getObjectValue(moduleElements, 'props');
       var module = getObjectValue(moduleElements, 'module');
       var moduleProps = getObjectValue(module, 'props');
       var candidates = [
           getModuleAttrs(param),
           getModuleAttrs(moduleElements),
           getObjectValue(moduleElements, 'attrs'),
           getObjectValue(moduleElements, 'attributes'),
           getObjectValue(moduleElements, 'moduleAttrs'),
           getObjectValue(moduleElements, 'moduleAttributes'),
           getObjectValue(metadata, 'attrs'),
           getObjectValue(metadata, 'attributes'),
           getObjectValue(props, 'attrs'),
           getObjectValue(props, 'attributes'),
           getObjectValue(module, 'attrs'),
           getObjectValue(module, 'attributes'),
           getObjectValue(moduleProps, 'attrs'),
           getObjectValue(moduleProps, 'attributes')
       ];

       for (var i = 0; i < candidates.length; i++) {
           var candidate = normalizeBuilderValue(candidates[i]);
           if (candidate && typeof candidate === 'object' && Object.keys(candidate).length > 0) {
               return candidate;
           }
       }

       return {};
   }

   function isTitleElementAttrName(attrName) {
       attrName = String(attrName || '');

       return attrName === 'title' || attrName === 'title.innerContent';
   }

   function normalizeBuilderValue(value) {
       if (!value || typeof value !== 'object') {
           return value;
       }
       if (typeof value.asMutable === 'function') {
           try {
               return value.asMutable({ deep: true });
           } catch (e) {}
       }
       if (typeof value.toJS === 'function') {
           try {
               return value.toJS();
           } catch (e2) {}
       }
       return value;
   }

   function getObjectValue(object, key) {
       var value;

       if (!object || !key) {
           return undefined;
       }
       if (Object.prototype.hasOwnProperty.call(Object(object), key)) {
           return object[key];
       }
       if (typeof object.get === 'function') {
           try {
               value = object.get(key);
               if (value !== undefined) {
                   return value;
               }
           } catch (e) {}
       }
       if (typeof object.getIn === 'function') {
           try {
               value = object.getIn(String(key).split('.'));
               if (value !== undefined) {
                   return value;
               }
           } catch (e2) {}
       }
       return undefined;
   }

    function getClassName(className) {
        if (typeof className === 'string') {
            return className;
        }
        if (Array.isArray(className)) {
            return className.join(' ');
        }
        return '';
    }

    function hasClass(className, classToFind) {
        return (' ' + String(className || '') + ' ').indexOf(' ' + classToFind + ' ') !== -1;
    }

    function escapeHtml(text) {
        return String(text).replace(/[&<>"']/g, function(character){
            return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[character];
        });
    }

    function getPath(object, path) {
        var target = object;
        if (!target || typeof target !== 'object') {
            return undefined;
        }

        for (var i = 0; i < path.length; i++) {
            if (!target || typeof target !== 'object' || target[path[i]] === undefined) {
                return undefined;
            }
            target = target[path[i]];
        }
        return target;
    }

    function setPath(object, path, value) {
        var target = object;
        if (!target || typeof target !== 'object') {
            return null;
        }

        for (var i = 0; i < path.length; i++) {
            if (i === path.length - 1) {
                target[path[i]] = value;
            } else {
                if (!target[path[i]] || typeof target[path[i]] !== 'object') {
                    target[path[i]] = {};
                }
                target = target[path[i]];
            }
        }
        return value;
    }
})();
JS;
}

function get_subtitle_text_from_attrs($attrs) {
    if (!is_array($attrs)) {
        return '';
    }

	$subtitle_attr = isset($attrs[ATTR_ITEM_TEXT]) ? $attrs[ATTR_ITEM_TEXT] : null;

	return trim((string) get_first_responsive_attr_value(array(
		is_array($subtitle_attr) && isset($subtitle_attr['innerContent']) ? $subtitle_attr['innerContent'] : null,
		$subtitle_attr,
		is_array($subtitle_attr) && isset($subtitle_attr['desktop']['value']['innerContent']) ? $subtitle_attr['desktop']['value']['innerContent'] : null,
		isset($attrs[ATTR_ITEM_TEXT . '.innerContent']) ? $attrs[ATTR_ITEM_TEXT . '.innerContent'] : null,
	), ''));
}

function get_first_responsive_attr_value($candidates, $fallback = '') {
    if (!is_array($candidates)) {
        return $fallback;
    }

    foreach ($candidates as $candidate) {
        if ($candidate === null) {
            continue;
        }

        $value = get_responsive_attr_value($candidate, null);

        if (is_string($value) && trim($value) !== '') {
            return $value;
        }

        if (!is_array($value) && $value !== null && $value !== '') {
            return $value;
        }
    }

    return $fallback;
}

function get_block_attrs($parsed_block) {
    return is_array($parsed_block) && isset($parsed_block['attrs']) && is_array($parsed_block['attrs']) ? $parsed_block['attrs'] : array();
}

function get_module_attr($attrs, $attr_name, $fallback) {
    return is_array($attrs) && isset($attrs[$attr_name]) ? $attrs[$attr_name] : $fallback;
}

function get_item_subtitle_selector($item_order_class) {
    return '.et_pb_accordion .et_pb_accordion_item.' . $item_order_class . ' .' . SUBTITLE_CLASS;
}

function get_accordion_subtitle_selector($accordion_order_class) {
    return '.' . $accordion_order_class . ' .' . SUBTITLE_CLASS;
}

function build_subtitle_markup($subtitle) {
    return sprintf(
        '<%1$s class="%2$s">%3$s</%1$s>',
        escape_html(SUBTITLE_DEFAULT_LEVEL),
        escape_attr(SUBTITLE_CLASS),
        escape_html($subtitle)
    );
}

function inject_subtitle_after_title($content, $subtitle_markup) {
    if (strpos($content, SUBTITLE_CLASS) !== false) {
        return $content;
    }

    $pattern = '/(<h[1-6]\b(?=[^>]*class=(["\'])[^"\']*\bet_pb_toggle_title\b[^"\']*\2)[^>]*>[\s\S]*?<\/h[1-6]>)/i';

    $updated = preg_replace($pattern, '$1' . $subtitle_markup, $content, 1);

    if (is_string($updated) && $updated !== $content) {
        return $updated;
    }

    $pattern = '/(<([a-z0-9]+)\b(?=[^>]*class=(["\'])[^"\']*\bet_pb_toggle_title\b[^"\']*\3)[^>]*>[\s\S]*?<\/\2>)/i';
    $updated = preg_replace($pattern, '$1' . $subtitle_markup, $content, 1);

    return is_string($updated) && $updated !== $content ? $updated : inject_subtitle_after_title_with_dom($content, $subtitle_markup);
}

function inject_subtitle_after_title_with_dom($content, $subtitle_markup) {
    if (!class_exists('DOMDocument') || !class_exists('DOMXPath')) {
        return $content;
    }

    $previous = function_exists('libxml_use_internal_errors') ? libxml_use_internal_errors(true) : null;
    $document = new \DOMDocument();
    $options  = 0;

    if (defined('LIBXML_HTML_NOIMPLIED')) {
        $options |= LIBXML_HTML_NOIMPLIED;
    }

    if (defined('LIBXML_HTML_NODEFDTD')) {
        $options |= LIBXML_HTML_NODEFDTD;
    }

    $loaded = $document->loadHTML('<?xml encoding="utf-8" ?><div id="divibooster-accordion-subtitle-root">' . $content . '</div>', $options);

    if (function_exists('libxml_clear_errors')) {
        libxml_clear_errors();
    }

    if ($previous !== null && function_exists('libxml_use_internal_errors')) {
        libxml_use_internal_errors($previous);
    }

    if (!$loaded) {
        return $content;
    }

    $xpath = new \DOMXPath($document);
    $titles = $xpath->query('//*[contains(concat(" ", normalize-space(@class), " "), " et_pb_toggle_title ")]');

    if (!$titles || $titles->length === 0) {
        return $content;
    }

    $fragment = $document->createDocumentFragment();
    if (!$fragment->appendXML($subtitle_markup)) {
        return $content;
    }

    $title = $titles->item(0);
    if (!$title || !$title->parentNode) {
        return $content;
    }

    if ($title->nextSibling) {
        $title->parentNode->insertBefore($fragment, $title->nextSibling);
    } else {
        $title->parentNode->appendChild($fragment);
    }

    $root = $document->getElementById('divibooster-accordion-subtitle-root');
    if (!$root) {
        return $content;
    }

    $updated = '';
    foreach ($root->childNodes as $child) {
        $updated .= $document->saveHTML($child);
    }

    return $updated !== '' ? $updated : $content;
}

function build_font_css_rule($selector, $declarations) {
    if (!is_array($declarations) || empty($declarations)) {
        return '';
    }

    return sanitize_css_selector($selector) . '{' . implode(';', $declarations) . '}';
}

function get_font_css_declarations($font_value) {
    if (!is_array($font_value)) {
        return array();
    }

    $declarations = array();
    $font_properties = array(
        'color'           => isset($font_value['color']) ? $font_value['color'] : null,
        'font-size'       => isset($font_value['size']) ? $font_value['size'] : (isset($font_value['fontSize']) ? $font_value['fontSize'] : null),
        'line-height'     => isset($font_value['lineHeight']) ? $font_value['lineHeight'] : null,
        'letter-spacing'  => isset($font_value['letterSpacing']) ? $font_value['letterSpacing'] : null,
        'text-align'      => isset($font_value['textAlign']) ? $font_value['textAlign'] : null,
        'font-family'     => isset($font_value['fontFamily']) ? $font_value['fontFamily'] : (isset($font_value['family']) ? $font_value['family'] : null),
        'font-weight'     => isset($font_value['fontWeight']) ? $font_value['fontWeight'] : (isset($font_value['weight']) ? $font_value['weight'] : null),
        'font-style'      => isset($font_value['fontStyle']) ? $font_value['fontStyle'] : null,
        'text-transform'  => isset($font_value['textTransform']) ? $font_value['textTransform'] : null,
        'text-decoration' => isset($font_value['textDecoration']) ? $font_value['textDecoration'] : null,
    );

    foreach ($font_properties as $property => $value) {
        add_css_declaration($declarations, $property, $value);
    }

    if (isset($font_value['styles']) && is_array($font_value['styles'])) {
        $style_declarations = array(
            'italic'    => 'font-style:italic !important',
            'uppercase' => 'text-transform:uppercase !important',
            'underline' => 'text-decoration:underline !important',
            'bold'      => 'font-weight:700 !important',
        );

        foreach ($style_declarations as $style => $declaration) {
            if (in_array($style, $font_value['styles'], true)) {
                $declarations[] = $declaration;
            }
        }
    }

    return $declarations;
}

function get_font_value_from_attr($font_attr) {
    if (!is_array($font_attr)) {
        return array();
    }

    $candidates = array(
        isset($font_attr['decoration']['font']['font']) ? $font_attr['decoration']['font']['font'] : null,
        isset($font_attr['decoration']['font']) ? $font_attr['decoration']['font'] : null,
        isset($font_attr['font']) ? $font_attr['font'] : null,
        $font_attr,
    );

    foreach ($candidates as $candidate) {
        $value = get_responsive_attr_value($candidate, null);
        if (is_array($value)) {
            return $value;
        }
    }

    return array();
}

function get_responsive_attr_value($attr, $fallback = '') {
    if (!is_array($attr)) {
        return $attr !== null ? $attr : $fallback;
    }

    if (isset($attr['desktop']) && is_array($attr['desktop']) && array_key_exists('value', $attr['desktop'])) {
        return $attr['desktop']['value'];
    }

    if (array_key_exists('value', $attr)) {
        return $attr['value'];
    }

    return $fallback;
}

function add_css_declaration(&$declarations, $property, $value) {
    if (!is_array($declarations)) {
        $declarations = array();
    }

    if (!is_scalar($value)) {
        return;
    }

    $property = sanitize_css_property($property);
    $value    = sanitize_css_value((string) $value, $property);

    if ($property === '' || $value === '') {
        return;
    }

    if (function_exists('safecss_filter_attr')) {
        $filtered = safecss_filter_attr($property . ':' . $value);

        if (!is_string($filtered) || stripos(trim($filtered), $property . ':') !== 0) {
            return;
        }
    }

    $declarations[] = $property . ':' . $value . ' !important';
}

function build_style_tag($css, $name) {
    $css = trim($css);

    if ($css === '') {
        return '';
    }

    return '<style data-divibooster-accordion-subtitle="' . escape_attr($name) . '">' . strip_all_tags($css) . '</style>';
}

function get_order_class_from_content($content, $module_class) {
    if (!is_string($content) || !is_string($module_class) || $module_class === '') {
        return false;
    }

    $classes = get_classes_from_content($content, $module_class);
    $pattern = '/^' . preg_quote($module_class, '/') . '_\d+(_tb_header|_tb_footer)?$/';

    foreach ($classes as $class) {
        if (preg_match($pattern, $class)) {
            return $class;
        }
    }

    return false;
}

function get_classes_from_content($content, $required_class) {
    if (!is_string($content) || !is_string($required_class) || $required_class === '') {
        return array();
    }

    if (!preg_match_all('/<[^>]+\sclass=(["\"])\s*([^"\"]*)\1/i', $content, $matches)) {
        return array();
    }

    foreach ($matches[2] as $class_attr) {
        $classes = preg_split('/\s+/', trim($class_attr));
        if (is_array($classes) && in_array($required_class, $classes, true)) {
            return $classes;
        }
    }

    return array();
}

function sanitize_css_selector($selector) {
    $sanitized = preg_replace('/[^A-Za-z0-9_\-\.\#\s>:,*\[\]=\"\'\(\)]/', '', (string) $selector);

    return is_string($sanitized) ? $sanitized : '';
}

function sanitize_css_property($property) {
    $property = strtolower(trim((string) $property));

    return preg_match('/^[a-z-]+$/', $property) ? $property : '';
}

function sanitize_css_value($value, $property = '') {
    $value = trim($value);

    if ($value === '' || preg_match('/[{};<>]/', $value) || preg_match('/[\x00-\x1F\x7F]/', $value)) {
        return '';
    }

    if (preg_match('/\/\*|\*\/|@import|expression\s*\(|url\s*\(|(?:java|vb)script\s*:|data\s*:|-moz-binding/i', $value)) {
        return '';
    }

    if (is_numeric_css_property($property) && !is_safe_numeric_css_value($value)) {
        return '';
    }

    if ($property === 'text-align' && !preg_match('/^(left|right|center|justify|start|end|inherit|initial|unset)$/i', $value)) {
        return '';
    }

    if ($property === 'font-weight' && !preg_match('/^(normal|bold|bolder|lighter|[1-9]00|inherit|initial|unset)$/i', $value)) {
        return '';
    }

    if ($property === 'font-style' && !preg_match('/^(normal|italic|oblique(?:\s+-?\d+(?:\.\d+)?deg)?|inherit|initial|unset)$/i', $value)) {
        return '';
    }

    if ($property === 'text-transform' && !preg_match('/^(none|capitalize|uppercase|lowercase|full-width|full-size-kana|inherit|initial|unset)$/i', $value)) {
        return '';
    }

    if ($property === 'font-family' && !preg_match('/^[A-Za-z0-9\s,\-_"\']+$/', $value)) {
        return '';
    }

    if ($property === 'text-decoration' && !preg_match('/^[A-Za-z\s\-]+$/', $value)) {
        return '';
    }

    return $value;
}

function is_numeric_css_property($property) {
    return in_array($property, array('font-size', 'line-height', 'letter-spacing'), true);
}

function is_safe_numeric_css_value($value) {
    if (preg_match('/^-?(?:\d+|\d*\.\d+)(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex|cm|mm|in|pt|pc)?$/i', $value)) {
        return true;
    }

    return preg_match('/^(?:calc|min|max|clamp|var)\([A-Za-z0-9\s,\-_.%+*\/()]+\)$/i', $value) === 1;
}
