<?php

namespace DiviBooster\DiviBooster\Modules\Timeline\HorizontalCarousel;

if (!defined('ABSPATH')) {
    exit();
}

const TIMELINE_SLUG = 'divi/timeline';
const ATTR_NAME = 'dbTimelineHorizontalCarousel';
const SCROLL_MODE_ATTR_NAME = 'dbTimelineHorizontalCarouselScrollMode';
const ARROW_SIZE_ATTR_NAME = 'dbTimelineHorizontalCarouselArrowSize';
const ARROW_GROUP_SLUG = 'designCarouselArrow';
const DEFAULT_SCROLL_MODE = 'view';
const DEFAULT_ARROW_COLOR = '#2b2f36';
const DEFAULT_ARROW_BACKGROUND_COLOR = 'rgba(255,255,255,0.92)';
const DEFAULT_ARROW_SIZE = '22px';

if (function_exists('add_action')) {
    add_action('wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_builder_fields');
    add_action('wp_footer', __NAMESPACE__ . '\\output_runtime_assets', 100);
}

if (function_exists('add_filter')) {
    add_filter('divi_module_library_register_module_attrs', __NAMESPACE__ . '\\register_module_attrs', 10, 2);
    add_filter('block_type_metadata_settings', __NAMESPACE__ . '\\register_block_attrs');
    add_filter('render_block_' . TIMELINE_SLUG, __NAMESPACE__ . '\\filter_timeline_output', 30, 3);
}

function is_divi5_active()
{
    return function_exists('et_builder_d5_enabled') && \et_builder_d5_enabled();
}

function is_divi5_visual_builder()
{
    return is_divi5_active() && function_exists('et_core_is_fb_enabled') && \et_core_is_fb_enabled();
}

function enqueue_builder_fields()
{
    if (!is_divi5_visual_builder()) {
        return;
    }

    $handle = sanitize_title('divi-booster-' . TIMELINE_SLUG . '-horizontal-carousel');
    \dbdb_enqueue_inline_script_if_dependencies_available($handle, get_builder_inline_js(), array('lodash', array('divi-vendor-wp-hooks', 'wp-hooks')));
}

function get_builder_inline_js()
{
    $timeline_slug = wp_json_encode(TIMELINE_SLUG);
    $attr_name = wp_json_encode(ATTR_NAME);
    $scroll_mode_attr_name = wp_json_encode(SCROLL_MODE_ATTR_NAME);
    $arrow_size_attr_name = wp_json_encode(ARROW_SIZE_ATTR_NAME);
    $arrow_group_slug = wp_json_encode(ARROW_GROUP_SLUG);
    $attr_definitions = wp_json_encode(get_attribute_definitions());
    $breakpoint_settings = wp_json_encode(get_carousel_breakpoint_settings());

    return <<<JS
(function() {
    if (!window.vendor || !window.vendor.wp || !window.vendor.wp.hooks || !window.lodash) {
        return;
    }

    var hooks = window.vendor.wp.hooks;
    var get = window.lodash.get;
    var set = window.lodash.set;
    var timelineSlug = {$timeline_slug};
    var attrName = {$attr_name};
    var scrollModeAttrName = {$scroll_mode_attr_name};
    var arrowSizeAttrName = {$arrow_size_attr_name};
    var arrowGroupSlug = {$arrow_group_slug};
    var attrDefinitions = {$attr_definitions};
    var breakpointSettings = {$breakpoint_settings};

    function addCarouselArrowGroup(groups, metadata) {
        var moduleName = get(metadata || {}, ['name'], '');
        if (moduleName && moduleName !== timelineSlug) {
            return groups;
        }

        groups = groups || {};
        groups[arrowGroupSlug] = Object.assign({}, groups[arrowGroupSlug] || {}, {
            groupName: arrowGroupSlug,
            panel: 'design',
            priority: 47,
            multiElements: true,
            component: {
                name: 'divi/composite',
                props: Object.assign({}, get(groups, [arrowGroupSlug, 'component', 'props'], {}), {
                    groupLabel: 'Carousel Arrow',
                    visible: function(ctx) {
                        return isCarouselActiveForContext(ctx);
                    }
                })
            }
        });

        return groups;
    }

    hooks.addFilter('divi.moduleLibrary.moduleMapping', 'divibooster.timelineHorizontalCarousel', function(modules) {
        var attrsPath = [timelineSlug, 'metadata', 'attributes'];
        var attrs = get(modules, attrsPath);
        if (!attrs) {
            set(modules, attrsPath, (attrs = {}));
        }
        Object.keys(attrDefinitions).forEach(function(name) {
            attrs[name] = attrDefinitions[name];
        });
        applyVisibilityCallbacks(attrs);
        applyGroupVisibilityCallbacks(get(modules, [timelineSlug], {}));
        return modules;
    });

    hooks.addFilter('divi.moduleLibrary.moduleSettings.groups', 'divibooster.timelineHorizontalCarousel', addCarouselArrowGroup);
    hooks.addFilter('divi.moduleLibrary.moduleSettings.groups.divi.timeline', 'divibooster.timelineHorizontalCarousel', function(groups, metadata) {
        return addCarouselArrowGroup(groups, Object.assign({}, metadata || {}, { name: timelineSlug }));
    });

    function nestedValue(object, path) {
        var value = object;
        for (var i = 0; i < path.length; i++) {
            if (!value || typeof value !== 'object' || !Object.prototype.hasOwnProperty.call(value, path[i])) {
                return null;
            }
            value = value[path[i]];
        }
        return value;
    }

    function hasResponsiveValue(value) {
        if (value === null || value === undefined) {
            return false;
        }
        return !(typeof value === 'string' && value.trim() === '');
    }

    function getResponsiveAttrValue(attr, breakpoint, paths) {
        var orderedPaths = [];
        var i;

        if (breakpoint && breakpoint !== 'desktop') {
            for (i = 0; i < paths.length; i++) {
                orderedPaths.push(paths[i](breakpoint));
            }
        }
        for (i = 0; i < paths.length; i++) {
            orderedPaths.push(paths[i]('desktop'));
        }

        for (i = 0; i < orderedPaths.length; i++) {
            var value = nestedValue(attr, orderedPaths[i]);
            if (hasResponsiveValue(value)) {
                return value;
            }
        }

        return '';
    }

    function attrSubValue(attrs, subName, breakpoint) {
        var attr = attrs && attrs[attrName];
        var value = getResponsiveAttrValue(attr, breakpoint || 'desktop', [
            function(bp) { return [bp, 'value', subName]; },
            function(bp) { return [subName, bp, 'value']; }
        ]);

        if (hasResponsiveValue(value)) {
            return value;
        }

        var legacyPaths = [
            ['value', subName],
            [subName, 'value'],
            [subName]
        ];

        for (var i = 0; i < legacyPaths.length; i++) {
            value = nestedValue(attr, legacyPaths[i]);
            if (hasResponsiveValue(value)) {
                return value;
            }
        }

        return '';
    }

    function contextAttrs(ctx) {
        return (ctx && ctx.attrs) || ctx || {};
    }

    function contextBreakpoint(ctx) {
        var candidates = [
            ctx && ctx.breakpoint,
            ctx && ctx.breakpointName,
            ctx && ctx.activeBreakpoint,
            ctx && ctx.currentBreakpoint,
            ctx && ctx.responsiveState,
            ctx && ctx.device,
            ctx && ctx.state && ctx.state.breakpoint,
            ctx && ctx.state && ctx.state.activeBreakpoint,
            ctx && ctx.context && ctx.context.breakpoint,
            ctx && ctx.context && ctx.context.activeBreakpoint
        ];

        for (var i = 0; i < candidates.length; i++) {
            if (typeof candidates[i] === 'string' && candidates[i]) {
                return candidates[i];
            }
        }

        return getCurrentBreakpointName();
    }

    function getCurrentBreakpointName() {
        var names = Object.keys(breakpointSettings || {});
        var width = window.innerWidth || document.documentElement.clientWidth || 0;
        var fallback = breakpointSettings && breakpointSettings.desktop ? 'desktop' : (names[0] || 'desktop');

        names.sort(function(a, b) {
            return ((breakpointSettings[a] && breakpointSettings[a].order) || 0) - ((breakpointSettings[b] && breakpointSettings[b].order) || 0);
        });

        for (var i = 0; i < names.length; i++) {
            if (breakpointMatchesWidth(breakpointSettings[names[i]], width)) {
                return names[i];
            }
        }

        return fallback;
    }

    function breakpointMatchesWidth(settings, width) {
        if (!settings) {
            return false;
        }

        var min = parsePixelValue(settings.minWidth && settings.minWidth.value);
        var max = parsePixelValue(settings.maxWidth && settings.maxWidth.value);

        if (min !== null && width < min) {
            return false;
        }
        if (max !== null && width > max) {
            return false;
        }

        return true;
    }

    function parsePixelValue(value) {
        value = typeof value === 'number' ? String(value) : (typeof value === 'string' ? value.trim() : '');
        if (!value) {
            return null;
        }
        var match = value.match(/^(-?\d+(?:\.\d+)?)px$/i) || value.match(/^(-?\d+(?:\.\d+)?)$/);
        return match ? parseFloat(match[1]) : null;
    }

    function isCarouselActiveForContext(ctx) {
        var attrs = contextAttrs(ctx);
        var breakpoint = contextBreakpoint(ctx);
        return directionIsHorizontal(attrs, breakpoint) && isEnabled(attrSubValue(attrs, 'enabled', breakpoint));
    }

    function applyVisibilityCallbacks(attributes) {
        var carouselItems = get(attributes, [attrName, 'settings', 'innerContent', 'items'], {});
        var scrollModeItems = get(attributes, [scrollModeAttrName, 'settings', 'innerContent', 'items'], {});
        var arrowSizeItems = get(attributes, [arrowSizeAttrName, 'settings', 'innerContent', 'items'], {});

        if (carouselItems.dbTimelineCarouselEnabled) {
            carouselItems.dbTimelineCarouselEnabled.visible = function(ctx) {
                return directionIsHorizontal(contextAttrs(ctx), contextBreakpoint(ctx));
            };
        }

        if (scrollModeItems.dbTimelineCarouselScrollMode) {
            scrollModeItems.dbTimelineCarouselScrollMode.visible = function(ctx) {
                return isCarouselActiveForContext(ctx);
            };
        }

        [
            carouselItems.dbTimelineCarouselPrevIcon,
            carouselItems.dbTimelineCarouselNextIcon,
            carouselItems.dbTimelineCarouselArrowColor,
            carouselItems.dbTimelineCarouselArrowBackgroundColor,
            arrowSizeItems.dbTimelineCarouselArrowSize
        ].forEach(function(item) {
            if (!item) {
                return;
            }
            item.visible = function(ctx) {
                return isCarouselActiveForContext(ctx);
            };
        });
    }

    function applyGroupVisibilityCallbacks(moduleDefinition) {
        if (!moduleDefinition || typeof moduleDefinition !== 'object') {
            return;
        }

        set(moduleDefinition, ['metadata', 'callbacks', 'design', arrowGroupSlug, 'visible'], function(ctx) {
            return isCarouselActiveForContext(ctx);
        });
        set(moduleDefinition, ['callbacks', 'design', arrowGroupSlug, 'visible'], function(ctx) {
            return isCarouselActiveForContext(ctx);
        });
    }

    function isEnabled(value) {
        return value === true || value === 1 || value === '1' || value === 'on' || value === 'yes' || value === 'true';
    }

    function directionIsHorizontal(attrs, breakpoint) {
        var timelineAttr = nestedValue(attrs || {}, ['module', 'advanced', 'timeline']);
        var direction = getResponsiveAttrValue(timelineAttr, breakpoint || 'desktop', [
            function(bp) { return [bp, 'value', 'direction']; }
        ]);

        return (direction || 'vertical') === 'horizontal';
    }
})();
JS;
}

function register_module_attrs($module_attrs, $filter_args)
{
    $name = isset($filter_args['name']) ? $filter_args['name'] : '';

    if ($name === TIMELINE_SLUG) {
        foreach (get_attribute_definitions() as $attr_name => $definition) {
            $module_attrs[$attr_name] = $definition;
        }
    }

    return $module_attrs;
}

function register_block_attrs($settings)
{
    if (isset($settings['name']) && $settings['name'] === TIMELINE_SLUG && isset($settings['attributes']) && is_array($settings['attributes'])) {
        foreach (get_attribute_definitions() as $attr_name => $definition) {
            $settings['attributes'][$attr_name] = $definition;
        }
    }

    return $settings;
}

function get_attribute_definitions()
{
    return array(
        ATTR_NAME => get_attribute_definition(),
        SCROLL_MODE_ATTR_NAME => get_scroll_mode_attribute_definition(),
        ARROW_SIZE_ATTR_NAME => get_arrow_size_attribute_definition(),
    );
}

function get_attribute_definition()
{
    return array(
        'type' => 'object',
        'defaultAttr' => array(
            'desktop' => array(
                'value' => array(
                    'scrollMode' => DEFAULT_SCROLL_MODE,
                    'arrowColor' => DEFAULT_ARROW_COLOR,
                    'arrowBackgroundColor' => DEFAULT_ARROW_BACKGROUND_COLOR,
                    'arrowSize' => DEFAULT_ARROW_SIZE,
                ),
            ),
        ),
        'settings' => array(
            'innerContent' => array(
                'groupType' => 'group-items',
                'items' => array(
                    'dbTimelineCarouselEnabled' => array(
                        'groupSlug' => 'designTimeline',
                        'attrName' => ATTR_NAME,
                        'subName' => 'enabled',
                        'label' => 'Display as Carousel',
                        'description' => 'Display horizontal timeline items in a carousel with navigation arrows.',
                        'category' => 'layout',
                        'priority' => 45,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/toggle',
                        ),
                    ),
                    'dbTimelineCarouselPrevIcon' => array(
                        'groupSlug' => ARROW_GROUP_SLUG,
                        'attrName' => ATTR_NAME,
                        'subName' => 'prevIcon',
                        'label' => 'Previous Arrow Icon',
                        'description' => 'Choose the icon used for the previous carousel arrow.',
                        'category' => 'layout',
                        'priority' => 10,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/icon-picker',
                        ),
                    ),
                    'dbTimelineCarouselNextIcon' => array(
                        'groupSlug' => ARROW_GROUP_SLUG,
                        'attrName' => ATTR_NAME,
                        'subName' => 'nextIcon',
                        'label' => 'Next Arrow Icon',
                        'description' => 'Choose the icon used for the next carousel arrow.',
                        'category' => 'layout',
                        'priority' => 20,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/icon-picker',
                        ),
                    ),
                    'dbTimelineCarouselArrowColor' => array(
                        'groupSlug' => ARROW_GROUP_SLUG,
                        'attrName' => ATTR_NAME,
                        'subName' => 'arrowColor',
                        'label' => 'Arrow Icon Color',
                        'description' => 'Choose the carousel arrow icon color.',
                        'category' => 'layout',
                        'priority' => 30,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'dynamicContent' => array(
                                'type' => 'color',
                            ),
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/color-picker',
                        ),
                        'defaultAttr' => array(
                            'desktop' => array(
                                'value' => DEFAULT_ARROW_COLOR,
                            ),
                        ),
                    ),
                    'dbTimelineCarouselArrowBackgroundColor' => array(
                        'groupSlug' => ARROW_GROUP_SLUG,
                        'attrName' => ATTR_NAME,
                        'subName' => 'arrowBackgroundColor',
                        'label' => 'Arrow Background Color',
                        'description' => 'Choose the carousel arrow circle background color.',
                        'category' => 'layout',
                        'priority' => 40,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'dynamicContent' => array(
                                'type' => 'color',
                            ),
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/color-picker',
                        ),
                        'defaultAttr' => array(
                            'desktop' => array(
                                'value' => DEFAULT_ARROW_BACKGROUND_COLOR,
                            ),
                        ),
                    ),
                ),
            ),
        ),
    );
}

function get_scroll_mode_attribute_definition()
{
    return array(
        'type' => 'object',
        'settings' => array(
            'innerContent' => array(
                'groupType' => 'group-items',
                'items' => array(
                    'dbTimelineCarouselScrollMode' => array(
                        'groupSlug' => 'designTimeline',
                        'attrName' => SCROLL_MODE_ATTR_NAME,
                        'label' => 'Carousel Movement',
                        'description' => 'Choose whether each arrow click moves the carousel by the visible area or advances to the next timeline item.',
                        'category' => 'layout',
                        'priority' => 46,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/select',
                            'props' => array(
                                'options' => array(
                                    'view' => array(
                                        'label' => 'Slide',
                                    ),
                                    'item' => array(
                                        'label' => 'Step Item by Item',
                                    ),
                                ),
                            ),
                        ),
                        'defaultAttr' => array(
                            'desktop' => array(
                                'value' => DEFAULT_SCROLL_MODE,
                            ),
                        ),
                    ),
                ),
            ),
        ),
    );
}

function get_arrow_size_attribute_definition()
{
    return array(
        'type' => 'object',
        'settings' => array(
            'innerContent' => array(
                'groupType' => 'group-items',
                'items' => array(
                    'dbTimelineCarouselArrowSize' => array(
                        'groupSlug' => ARROW_GROUP_SLUG,
                        'attrName' => ARROW_SIZE_ATTR_NAME,
                        'label' => 'Carousel Arrow Size',
                        'description' => 'Choose the carousel arrow icon size.',
                        'category' => 'layout',
                        'priority' => 50,
                        'render' => true,
                        'features' => array(
                            'responsive' => true,
                            'hover' => false,
                            'sticky' => false,
                            'preset' => 'advanced',
                        ),
                        'component' => array(
                            'type' => 'field',
                            'name' => 'divi/range',
                            'props' => array(
                                'defaultUnit' => 'px',
                                'min' => 12,
                                'minLimit' => 1,
                                'max' => 96,
                            ),
                        ),
                        'defaultAttr' => array(
                            'desktop' => array(
                                'value' => DEFAULT_ARROW_SIZE,
                            ),
                        ),
                    ),
                ),
            ),
        ),
    );
}

function filter_timeline_output($block_content, $parsed_block, $block)
{
    if (!is_divi5_active() || !is_string($block_content)) {
        return $block_content;
    }

    $attrs = isset($parsed_block['attrs']) && is_array($parsed_block['attrs']) ? $parsed_block['attrs'] : array();
    $configs = get_carousel_breakpoint_configs($attrs);
    if (!has_active_carousel_config($configs)) {
        return $block_content;
    }

    return add_carousel_attrs_to_timeline($block_content, $configs);
}

function get_timeline_direction($attrs, $breakpoint = 'desktop')
{
    $direction = get_responsive_attr_sub_value(
        get_nested_array_value($attrs, array('module', 'advanced', 'timeline')),
        'direction',
        $breakpoint,
        'vertical'
    );
    return is_string($direction) && $direction !== '' ? $direction : 'vertical';
}

function is_carousel_enabled($attrs, $breakpoint = 'desktop')
{
    return is_truthy(get_attr_sub_value($attrs, ATTR_NAME, 'enabled', $breakpoint));
}

function get_carousel_config($attrs, $breakpoint = 'desktop')
{
    $color = sanitize_css_color(get_attr_sub_value($attrs, ATTR_NAME, 'arrowColor', $breakpoint));
    $background_color = sanitize_css_color(get_attr_sub_value($attrs, ATTR_NAME, 'arrowBackgroundColor', $breakpoint));
    $size = sanitize_css_length(get_attr_value($attrs, ARROW_SIZE_ATTR_NAME, get_attr_sub_value($attrs, ATTR_NAME, 'arrowSize', $breakpoint), $breakpoint), DEFAULT_ARROW_SIZE);
    $scroll_mode = sanitize_scroll_mode(get_attr_value($attrs, SCROLL_MODE_ATTR_NAME, get_attr_sub_value($attrs, ATTR_NAME, 'scrollMode', $breakpoint), $breakpoint));
    $legacy_icon = get_arrow_icon_config(get_attr_sub_value($attrs, ATTR_NAME, 'icon', $breakpoint));
    $prev_icon = get_arrow_icon_config(get_attr_sub_value($attrs, ATTR_NAME, 'prevIcon', $breakpoint));
    $next_icon = get_arrow_icon_config(get_attr_sub_value($attrs, ATTR_NAME, 'nextIcon', $breakpoint));

    if ($prev_icon['icon'] === '') {
        $prev_icon = $legacy_icon;
    }
    if ($next_icon['icon'] === '') {
        $next_icon = $legacy_icon;
    }

    return array(
        'color' => $color !== '' ? $color : DEFAULT_ARROW_COLOR,
        'background_color' => $background_color !== '' ? $background_color : DEFAULT_ARROW_BACKGROUND_COLOR,
        'size' => $size,
        'scroll_mode' => $scroll_mode,
        'prev_icon' => $prev_icon['icon'],
        'prev_icon_family' => $prev_icon['family'],
        'prev_icon_weight' => $prev_icon['weight'],
        'next_icon' => $next_icon['icon'],
        'next_icon_family' => $next_icon['family'],
        'next_icon_weight' => $next_icon['weight'],
    );
}

function get_carousel_breakpoint_configs($attrs)
{
    $configs = array();
    foreach (get_carousel_breakpoint_names($attrs) as $breakpoint) {
        $active = is_carousel_enabled($attrs, $breakpoint) && get_timeline_direction($attrs, $breakpoint) === 'horizontal';
        $configs[$breakpoint] = array_merge(
            array('active' => $active),
            $active ? get_carousel_config($attrs, $breakpoint) : array()
        );
    }

    return $configs;
}

function get_carousel_breakpoint_names($attrs)
{
    $names = array_keys(get_carousel_breakpoint_settings());
    $responsive_attrs = array(
        get_nested_array_value($attrs, array('module', 'advanced', 'timeline')),
        isset($attrs[ATTR_NAME]) ? $attrs[ATTR_NAME] : array(),
        isset($attrs[SCROLL_MODE_ATTR_NAME]) ? $attrs[SCROLL_MODE_ATTR_NAME] : array(),
        isset($attrs[ARROW_SIZE_ATTR_NAME]) ? $attrs[ARROW_SIZE_ATTR_NAME] : array(),
    );

    foreach ($responsive_attrs as $attr) {
        if (!is_array($attr)) {
            continue;
        }
        foreach ($attr as $key => $value) {
            if (is_array($value) && isset($value['value']) && !in_array($key, $names, true)) {
                $names[] = $key;
            }
        }
    }

    return in_array('desktop', $names, true) ? $names : array_merge(array('desktop'), $names);
}

function has_active_carousel_config($configs)
{
    foreach ($configs as $config) {
        if (!empty($config['active'])) {
            return true;
        }
    }

    return false;
}

function get_carousel_breakpoint_settings()
{
    $class = '\\ET\\Builder\\Framework\\Breakpoint\\Breakpoint';
    if (class_exists($class) && is_callable(array($class, 'get_style_breakpoint_settings'))) {
        $settings = call_user_func(array($class, 'get_style_breakpoint_settings'));
        if (is_array($settings) && !empty($settings)) {
            return $settings;
        }
    }

    return array(
        'phone' => array(
            'order' => 10,
            'maxWidth' => array('value' => '767px'),
        ),
        'tablet' => array(
            'order' => 30,
            'minWidth' => array('value' => '768px'),
            'maxWidth' => array('value' => '980px'),
        ),
        'desktop' => array(
            'order' => 50,
            'baseDevice' => true,
            'minWidth' => array('value' => '981px'),
        ),
    );
}

function get_nested_array_value($array, $path)
{
    $value = $array;

    foreach ($path as $key) {
        if (!is_array($value) || !array_key_exists($key, $value)) {
            return null;
        }
        $value = $value[$key];
    }

    return $value;
}

function get_attr_sub_value($attrs, $attr_name, $sub_name, $breakpoint = 'desktop')
{
    if (!isset($attrs[$attr_name]) || !is_array($attrs[$attr_name])) {
        return '';
    }

    return get_responsive_attr_sub_value($attrs[$attr_name], $sub_name, $breakpoint);
}

function get_responsive_attr_sub_value($attr, $sub_name, $breakpoint = 'desktop', $fallback = '')
{
    if (!is_array($attr)) {
        return $fallback;
    }

    $paths = array(
        array($breakpoint, 'value', $sub_name),
        array('desktop', 'value', $sub_name),
        array('value', $sub_name),
        array($sub_name, $breakpoint, 'value'),
        array($sub_name, 'desktop', 'value'),
        array($sub_name, 'value'),
        array($sub_name),
    );

    foreach ($paths as $path) {
        $value = get_nested_array_value($attr, $path);
        if (is_scalar($value) && trim((string) $value) !== '') {
            return (string) $value;
        }
        if (is_array($value) && !empty($value)) {
            return $value;
        }
    }

    return $fallback;
}

function get_attr_value($attrs, $attr_name, $fallback = '', $breakpoint = 'desktop')
{
    if (!isset($attrs[$attr_name]) || !is_array($attrs[$attr_name])) {
        return $fallback;
    }

    $paths = array(
        array($breakpoint, 'value'),
        array('desktop', 'value'),
        array('value'),
    );

    foreach ($paths as $path) {
        $value = get_nested_array_value($attrs[$attr_name], $path);
        if (is_scalar($value) && trim((string) $value) !== '') {
            return (string) $value;
        }
    }

    return $fallback;
}

function is_truthy($value)
{
    return in_array($value, array(true, 1, '1', 'on', 'yes', 'true'), true);
}

function sanitize_css_color($value)
{
    $value = is_string($value) ? trim($value) : '';
    if ($value === '') {
        return '';
    }

    if (preg_match('/^(#[0-9a-f]{3,8}|rgba?\\([0-9.,%\\s]+\\)|hsla?\\([0-9.,%\\s]+\\)|[a-z]+)$/i', $value)) {
        return $value;
    }

    if (function_exists('safecss_filter_attr')) {
        $filtered = \safecss_filter_attr('color:' . $value);
        if (preg_match('/(?:^|;)\\s*color\\s*:\\s*([^;]+)\\s*;?$/i', $filtered, $matches)) {
            return trim($matches[1]);
        }
        return '';
    }

    return '';
}

function sanitize_css_length($value, $fallback)
{
    $value = is_string($value) ? trim($value) : '';
    if ($value === '') {
        return $fallback;
    }

    if (is_numeric($value)) {
        return $value . 'px';
    }

    return preg_match('/^-?(?:\\d+|\\d*\\.\\d+)(?:px|em|rem|vw|vh|%)$/i', $value) ? $value : $fallback;
}

function sanitize_scroll_mode($value)
{
    $value = is_string($value) ? trim($value) : '';
    return in_array($value, array('item', 'view'), true) ? $value : DEFAULT_SCROLL_MODE;
}

function sanitize_arrow_icon($value)
{
    if (is_array($value)) {
        $preferred_keys = array('unicode', 'icon', 'char', 'code', 'value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker');
        foreach ($preferred_keys as $key) {
            if (array_key_exists($key, $value)) {
                $icon = sanitize_arrow_icon($value[$key]);
                if ($icon !== '') {
                    return $icon;
                }
            }
        }

        foreach ($value as $nested_value) {
            $icon = sanitize_arrow_icon($nested_value);
            if ($icon !== '') {
                return $icon;
            }
        }

        return '';
    }

    if (is_numeric($value)) {
        $icon = html_entity_decode('&#x' . dechex((int) $value) . ';', ENT_QUOTES, 'UTF-8');
        return is_string($icon) ? $icon : '';
    }

    $value = is_string($value) ? trim($value) : '';
    if ($value === '' || strpos($value, '$variable(') === 0) {
        return '';
    }

    $value = trim(explode('||', $value, 2)[0]);

    if (preg_match('/^&#x([0-9a-f]+);?$/i', $value, $matches)) {
        return html_entity_decode('&#x' . $matches[1] . ';', ENT_QUOTES, 'UTF-8');
    }

    if (preg_match('/^&#([0-9]+);?$/', $value, $matches)) {
        return html_entity_decode('&#' . $matches[1] . ';', ENT_QUOTES, 'UTF-8');
    }

    $decoded = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
    $value = is_string($decoded) ? trim($decoded) : $value;

    if (preg_match('/^\\\\u([0-9a-f]{4,6})$/i', $value, $matches) ||
        preg_match('/^U\+([0-9a-f]{4,6})$/i', $value, $matches) ||
        preg_match('/^0x([0-9a-f]{4,6})$/i', $value, $matches) ||
        preg_match('/^([0-9a-f]{4,6})$/i', $value, $matches)) {
        $icon = html_entity_decode('&#x' . $matches[1] . ';', ENT_QUOTES, 'UTF-8');
        return is_string($icon) ? $icon : '';
    }

    return function_exists('mb_substr') ? mb_substr($value, 0, 4) : substr($value, 0, 4);
}

function get_arrow_icon_config($value)
{
    $icon = sanitize_arrow_icon($value);
    $family = get_arrow_icon_family($value, $icon);

    return array(
        'icon' => $icon,
        'family' => $family,
        'weight' => $family === 'FontAwesome' ? normalize_fontawesome_weight(get_icon_meta_prop($value, 'weight'), get_icon_meta_prop($value, 'type')) : '',
    );
}

function get_icon_meta_prop($value, $prop_name)
{
    if (!is_string($prop_name) || $prop_name === '') {
        return '';
    }

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

    if (array_key_exists($prop_name, $value) && is_scalar($value[$prop_name]) && trim((string) $value[$prop_name]) !== '') {
        return trim((string) $value[$prop_name]);
    }

    $preferred_keys = array('value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker');
    foreach ($preferred_keys as $key) {
        if (array_key_exists($key, $value)) {
            $prop = get_icon_meta_prop($value[$key], $prop_name);
            if ($prop !== '') {
                return $prop;
            }
        }
    }

    foreach ($value as $nested_value) {
        $prop = get_icon_meta_prop($nested_value, $prop_name);
        if ($prop !== '') {
            return $prop;
        }
    }

    return '';
}

function get_arrow_icon_family($value, $icon)
{
    $type = get_icon_meta_prop($value, 'type');
    $family = get_icon_meta_prop($value, 'fontFamily');

    if ($family === 'FontAwesome' || is_fontawesome_icon_type($type) || is_fontawesome_icon_char($icon)) {
        return 'FontAwesome';
    }

    if (is_string($value) && function_exists('et_pb_get_icon_font_family')) {
        try {
            return et_pb_get_icon_font_family($value) === 'FontAwesome' ? 'FontAwesome' : '';
        } catch (\Throwable $e) {
            return '';
        }
    }

    return '';
}

function is_fontawesome_icon_type($icon_type)
{
    if (!is_string($icon_type) || $icon_type === '') {
        return false;
    }

    $type = strtolower(trim($icon_type));
    return strpos($type, 'fa') === 0 || in_array($type, array('brands', 'regular', 'solid', 'fontawesome'), true);
}

function is_fontawesome_icon_char($icon)
{
    if (!is_string($icon) || $icon === '' || !function_exists('mb_ord')) {
        return false;
    }

    $codepoint = mb_ord($icon, 'UTF-8');
    return is_int($codepoint) && $codepoint >= 0xf000 && $codepoint <= 0xf8ff;
}

function normalize_fontawesome_weight($weight, $type = '')
{
    $weight = is_scalar($weight) ? strtolower(trim((string) $weight)) : '';

    if (in_array($weight, array('100', '200', '300', '400', '500', '600', '700', '800', '900'), true)) {
        return (int) $weight >= 600 ? '900' : '400';
    }

    if (in_array($weight, array('regular', 'normal', 'book', 'light', 'thin', 'hairline', 'medium'), true)) {
        return '400';
    }

    if (in_array($weight, array('solid', 'bold', 'semibold', 'semi-bold', 'black', 'heavy'), true)) {
        return '900';
    }

    $type = is_string($type) ? strtolower(trim($type)) : '';
    if (in_array($type, array('fab', 'fa-brands', 'brands', 'far', 'fa-regular', 'regular'), true)) {
        return '400';
    }

    return '900';
}

function add_carousel_attrs_to_timeline($html, $configs)
{
    $config = isset($configs['desktop']) && is_array($configs['desktop']) ? $configs['desktop'] : reset($configs);
    $config = is_array($config) ? $config : array();
    if (empty($config['active'])) {
        foreach ($configs as $candidate) {
            if (!empty($candidate['active'])) {
                $config = $candidate;
                break;
            }
        }
    }

    $style = '--dbtl-carousel-arrow-color:' . $config['color'] . ';--dbtl-carousel-arrow-background-color:' . $config['background_color'] . ';--dbtl-carousel-arrow-size:' . $config['size'] . ';';
    $encoded_configs = wp_json_encode($configs);

    if (class_exists('\\WP_HTML_Tag_Processor')) {
        $processor = new \WP_HTML_Tag_Processor($html);
        if ($processor->next_tag(array('class_name' => 'et_pb_timeline'))) {
            $processor->add_class('dbtl-horizontal-carousel');
            $existing_style = (string) $processor->get_attribute('style');
            $processor->set_attribute('style', trim($existing_style . ';' . $style, ';'));
            $processor->set_attribute('data-dbtl-carousel-scroll-mode', $config['scroll_mode']);
            if ($config['prev_icon'] !== '') {
                $processor->set_attribute('data-dbtl-carousel-prev-icon', $config['prev_icon']);
                if ($config['prev_icon_family'] !== '') {
                    $processor->set_attribute('data-dbtl-carousel-prev-icon-family', $config['prev_icon_family']);
                }
                if ($config['prev_icon_weight'] !== '') {
                    $processor->set_attribute('data-dbtl-carousel-prev-icon-weight', $config['prev_icon_weight']);
                }
            }
            if ($config['next_icon'] !== '') {
                $processor->set_attribute('data-dbtl-carousel-next-icon', $config['next_icon']);
                if ($config['next_icon_family'] !== '') {
                    $processor->set_attribute('data-dbtl-carousel-next-icon-family', $config['next_icon_family']);
                }
                if ($config['next_icon_weight'] !== '') {
                    $processor->set_attribute('data-dbtl-carousel-next-icon-weight', $config['next_icon_weight']);
                }
            }
            $processor->set_attribute('data-dbtl-carousel-configs', $encoded_configs);
            return $processor->get_updated_html();
        }
    }

    $tag_pattern = "~<([a-z0-9]+)\\b([^>]*\\bclass=([\"'])(?=[^\"']*\\bet_pb_timeline\\b)([^\"']*)\\3[^>]*)>~i";
    $class_attr_pattern = "~\\bclass=([\"'])([^\"']*)\\1~i";
    $style_attr_pattern = "~\\bstyle=([\"'])([^\"']*)\\1~i";

    return preg_replace_callback(
        $tag_pattern,
        function ($matches) use ($style, $config, $encoded_configs, $class_attr_pattern, $style_attr_pattern) {
            $tag = $matches[0];

            if (!preg_match('/\\bdbtl-horizontal-carousel\\b/', $matches[4])) {
                $tag = preg_replace($class_attr_pattern, 'class=$1$2 dbtl-horizontal-carousel$1', $tag, 1);
            }

            if (preg_match($style_attr_pattern, $tag)) {
                $tag = preg_replace($style_attr_pattern, 'style=$1$2;' . esc_attr($style) . '$1', $tag, 1);
            } else {
                $tag = preg_replace('/>$/', ' style="' . esc_attr($style) . '">', $tag, 1);
            }

            if (!preg_match('/\\sdata-dbtl-carousel-scroll-mode=/', $tag)) {
                $tag = preg_replace('/>$/', ' data-dbtl-carousel-scroll-mode="' . esc_attr($config['scroll_mode']) . '">', $tag, 1);
            }

            if ($config['prev_icon'] !== '' && !preg_match('/\\sdata-dbtl-carousel-prev-icon=/', $tag)) {
                $tag = preg_replace('/>$/', ' data-dbtl-carousel-prev-icon="' . esc_attr($config['prev_icon']) . '">', $tag, 1);
                if ($config['prev_icon_family'] !== '') {
                    $tag = preg_replace('/>$/', ' data-dbtl-carousel-prev-icon-family="' . esc_attr($config['prev_icon_family']) . '">', $tag, 1);
                }
                if ($config['prev_icon_weight'] !== '') {
                    $tag = preg_replace('/>$/', ' data-dbtl-carousel-prev-icon-weight="' . esc_attr($config['prev_icon_weight']) . '">', $tag, 1);
                }
            }

            if ($config['next_icon'] !== '' && !preg_match('/\\sdata-dbtl-carousel-next-icon=/', $tag)) {
                $tag = preg_replace('/>$/', ' data-dbtl-carousel-next-icon="' . esc_attr($config['next_icon']) . '">', $tag, 1);
                if ($config['next_icon_family'] !== '') {
                    $tag = preg_replace('/>$/', ' data-dbtl-carousel-next-icon-family="' . esc_attr($config['next_icon_family']) . '">', $tag, 1);
                }
                if ($config['next_icon_weight'] !== '') {
                    $tag = preg_replace('/>$/', ' data-dbtl-carousel-next-icon-weight="' . esc_attr($config['next_icon_weight']) . '">', $tag, 1);
                }
            }

            if (!preg_match('/\\sdata-dbtl-carousel-configs=/', $tag)) {
                $tag = preg_replace('/>$/', ' data-dbtl-carousel-configs="' . esc_attr($encoded_configs) . '">', $tag, 1);
            }

            return $tag;
        },
        $html,
        1
    );
}

function output_runtime_assets()
{
    if (!is_divi5_active()) {
        return;
    }
    $timeline_slug = wp_json_encode(TIMELINE_SLUG);
    $attr_name = wp_json_encode(ATTR_NAME);
    $scroll_mode_attr_name = wp_json_encode(SCROLL_MODE_ATTR_NAME);
    $arrow_size_attr_name = wp_json_encode(ARROW_SIZE_ATTR_NAME);
    $default_arrow_color = wp_json_encode(DEFAULT_ARROW_COLOR);
    $default_arrow_background_color = wp_json_encode(DEFAULT_ARROW_BACKGROUND_COLOR);
    $default_arrow_size = wp_json_encode(DEFAULT_ARROW_SIZE);
    $breakpoint_settings = wp_json_encode(get_carousel_breakpoint_settings());
    ?>
    <style id="dbtl-horizontal-carousel-style">
        .et_pb_timeline.dbtl-horizontal-carousel {
            --dbtl-carousel-arrow-color: <?php echo esc_html(DEFAULT_ARROW_COLOR); ?>;
            --dbtl-carousel-arrow-background-color: <?php echo esc_html(DEFAULT_ARROW_BACKGROUND_COLOR); ?>;
            --dbtl-carousel-arrow-size: <?php echo esc_html(DEFAULT_ARROW_SIZE); ?>;
            --dbtl-carousel-arrow-button-size: calc(var(--dbtl-carousel-arrow-size) + 18px);
            position: relative;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .et_pb_timeline_track {
            box-sizing: border-box;
            display: flex !important;
            flex-direction: row !important;
            flex-wrap: nowrap !important;
            max-width: 100%;
            overflow-x: hidden !important;
            overflow-y: visible !important;
            scroll-behavior: smooth;
            scrollbar-width: none;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .et_pb_timeline_track::-webkit-scrollbar {
            display: none;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .et_pb_timeline_item {
            flex: 0 0 auto !important;
            max-width: none !important;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .et_pb_timeline_card {
            max-width: none;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow {
            align-items: center;
            background: var(--dbtl-carousel-arrow-background-color);
            border: 0;
            border-radius: 999px;
            box-shadow: 0 3px 14px rgba(0, 0, 0, 0.18);
            color: var(--dbtl-carousel-arrow-color);
            cursor: pointer;
            display: flex;
            height: var(--dbtl-carousel-arrow-button-size);
            justify-content: center;
            line-height: 1;
            padding: 0;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            width: var(--dbtl-carousel-arrow-button-size);
            z-index: 5;
        }
        body.et-fb .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow,
        body.et_fb_preview_active .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow,
        .et-fb-root-ancestor .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow,
        body.et_pb_pagebuilder_layout .et-fb-root-ancestor .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow {
            pointer-events: auto !important;
            z-index: 100000 !important;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow[disabled] {
            cursor: default;
            display: none;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow[hidden],
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow.dbtl-horizontal-carousel-arrow-disabled {
            display: none !important;
            pointer-events: none !important;
            visibility: hidden !important;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow-prev {
            left: 8px;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow-next {
            right: 8px;
        }
        .et_pb_timeline.dbtl-horizontal-carousel .dbtl-horizontal-carousel-arrow-icon {
            align-items: center;
            display: flex;
            font-family: ETmodules;
            font-size: var(--dbtl-carousel-arrow-size);
            height: 1em;
            justify-content: center;
            line-height: 1;
            width: 1em;
        }
    </style>
    <script id="dbtl-horizontal-carousel-script">
        (function() {
            if (window.__dbtlHorizontalCarouselInstalled) {
                return;
            }
            window.__dbtlHorizontalCarouselInstalled = true;

            var scheduled = false;
            var observed = false;
            var timelineSlug = <?php echo $timeline_slug; ?>;
            var attrName = <?php echo $attr_name; ?>;
            var scrollModeAttrName = <?php echo $scroll_mode_attr_name; ?>;
            var arrowSizeAttrName = <?php echo $arrow_size_attr_name; ?>;
            var defaultArrowColor = <?php echo $default_arrow_color; ?>;
            var defaultArrowBackgroundColor = <?php echo $default_arrow_background_color; ?>;
            var defaultArrowSize = <?php echo $default_arrow_size; ?>;
            var breakpointSettings = <?php echo $breakpoint_settings; ?>;

            function getStoreWindow() {
                var candidates = [window];
                try {
                    if (window.parent && window.parent !== window) {
                        candidates.push(window.parent);
                    }
                    if (window.top && window.top !== window && candidates.indexOf(window.top) === -1) {
                        candidates.push(window.top);
                    }
                } catch (e) {}

                for (var i = 0; i < candidates.length; i++) {
                    if (candidates[i] && candidates[i].divi && candidates[i].divi.data && typeof candidates[i].divi.data.select === 'function') {
                        return candidates[i];
                    }
                    if (candidates[i] && candidates[i].wp && candidates[i].wp.data && typeof candidates[i].wp.data.select === 'function') {
                        return candidates[i];
                    }
                }
                return null;
            }

            function selectStore(storeName) {
                var storeWindow = getStoreWindow();
                var data = storeWindow && storeWindow.divi && storeWindow.divi.data ? storeWindow.divi.data : (storeWindow && storeWindow.wp ? storeWindow.wp.data : null);
                if (!data || typeof data.select !== 'function') {
                    return null;
                }
                try {
                    return data.select(storeName);
                } catch (e) {
                    return null;
                }
            }

            function hasBuilderStore() {
                return !!selectStore('divi/edit-post');
            }

            function toPlainObject(value) {
                if (!value) {
                    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 (e) {}
                }
                return value;
            }

            function nestedValue(object, path) {
                var value = object;
                for (var i = 0; i < path.length; i++) {
                    if (!value || typeof value !== 'object' || !Object.prototype.hasOwnProperty.call(value, path[i])) {
                        return null;
                    }
                    value = value[path[i]];
                }
                return value;
            }

            function getModuleName(moduleId) {
                var editPost = selectStore('divi/edit-post');
                var module = null;
                if (!editPost) {
                    return '';
                }
                try {
                    if (typeof editPost.getModuleName === 'function') {
                        return editPost.getModuleName(moduleId) || '';
                    }
                } catch (e) {}
                try {
                    module = typeof editPost.getModule === 'function' ? toPlainObject(editPost.getModule(moduleId)) : null;
                } catch (e) {
                    module = null;
                }
                return module && module.name ? module.name : '';
            }

            function getModuleAttrs(moduleId) {
                var editPost = selectStore('divi/edit-post');
                var module = null;
                if (!editPost) {
                    return {};
                }
                try {
                    if (typeof editPost.getModuleAttrs === 'function') {
                        return toPlainObject(editPost.getModuleAttrs(moduleId)) || {};
                    }
                } catch (e) {}
                try {
                    module = typeof editPost.getModule === 'function' ? toPlainObject(editPost.getModule(moduleId)) : null;
                } catch (e) {
                    module = null;
                }
                return toPlainObject(module && module.props ? module.props.attrs : null) || {};
            }

            function closestTimelineId(timeline) {
                var current = timeline;
                while (current && current.nodeType === 1) {
                    if (current.hasAttribute && current.hasAttribute('data-id')) {
                        var id = current.getAttribute('data-id');
                        if (id && getModuleName(id) === timelineSlug) {
                            return id;
                        }
                    }
                    current = current.parentElement;
                }
                return '';
            }

            function hasResponsiveValue(value) {
                if (value === null || value === undefined) {
                    return false;
                }
                return !(typeof value === 'string' && value.trim() === '');
            }

            function responsiveAttrValue(attr, breakpoint, paths) {
                var orderedPaths = [];
                var i;

                if (breakpoint && breakpoint !== 'desktop') {
                    for (i = 0; i < paths.length; i++) {
                        orderedPaths.push(paths[i](breakpoint));
                    }
                }
                for (i = 0; i < paths.length; i++) {
                    orderedPaths.push(paths[i]('desktop'));
                }

                for (i = 0; i < orderedPaths.length; i++) {
                    var value = nestedValue(attr, orderedPaths[i]);
                    if (hasResponsiveValue(value)) {
                        return value;
                    }
                }

                return '';
            }

            function attrSubValue(attrs, subName, breakpoint) {
                var attr = attrs && attrs[attrName];
                var value = responsiveAttrValue(attr, breakpoint || 'desktop', [
                    function(bp) { return [bp, 'value', subName]; },
                    function(bp) { return [subName, bp, 'value']; }
                ]);

                if (hasResponsiveValue(value)) {
                    return value;
                }

                var legacyPaths = [
                    ['value', subName],
                    [subName, 'value'],
                    [subName]
                ];

                for (var i = 0; i < legacyPaths.length; i++) {
                    value = nestedValue(attr, legacyPaths[i]);
                    if (hasResponsiveValue(value)) {
                        return value;
                    }
                }
                return '';
            }

            function attrValue(attrs, name, fallback, breakpoint) {
                var attr = attrs && attrs[name];
                var value = responsiveAttrValue(attr, breakpoint || 'desktop', [
                    function(bp) { return [bp, 'value']; }
                ]);

                if (hasResponsiveValue(value)) {
                    return value;
                }

                var paths = [
                    ['value']
                ];
                for (var i = 0; i < paths.length; i++) {
                    value = nestedValue(attr, paths[i]);
                    if (hasResponsiveValue(value)) {
                        return value;
                    }
                }
                return fallback || '';
            }

            function parsePixelValue(value) {
                value = typeof value === 'number' ? String(value) : (typeof value === 'string' ? value.trim() : '');
                if (!value) {
                    return null;
                }
                var match = value.match(/^(-?\d+(?:\.\d+)?)px$/i) || value.match(/^(-?\d+(?:\.\d+)?)$/);
                return match ? parseFloat(match[1]) : null;
            }

            function breakpointMatchesWidth(settings, width) {
                if (!settings) {
                    return false;
                }

                var min = parsePixelValue(settings.minWidth && settings.minWidth.value);
                var max = parsePixelValue(settings.maxWidth && settings.maxWidth.value);

                if (min !== null && width < min) {
                    return false;
                }
                if (max !== null && width > max) {
                    return false;
                }

                return true;
            }

            function getCurrentBreakpointName() {
                var names = Object.keys(breakpointSettings || {});
                var width = window.innerWidth || document.documentElement.clientWidth || 0;
                var fallback = breakpointSettings && breakpointSettings.desktop ? 'desktop' : (names[0] || 'desktop');

                names.sort(function(a, b) {
                    return ((breakpointSettings[a] && breakpointSettings[a].order) || 0) - ((breakpointSettings[b] && breakpointSettings[b].order) || 0);
                });

                for (var i = 0; i < names.length; i++) {
                    if (breakpointMatchesWidth(breakpointSettings[names[i]], width)) {
                        return names[i];
                    }
                }

                return fallback;
            }

            function isEnabled(value) {
                return value === true || value === 1 || value === '1' || value === 'on' || value === 'yes' || value === 'true';
            }

            function sanitizeLength(value, fallback) {
                value = typeof value === 'number' ? value + 'px' : (typeof value === 'string' ? value.trim() : '');
                if (!value) {
                    return fallback;
                }
                return /^-?(?:\d+|\d*\.\d+)(?:px|em|rem|vw|vh|%)$/i.test(value) ? value : fallback;
            }

            function sanitizeColor(value, fallback) {
                value = typeof value === 'string' ? value.trim() : '';
                if (!value) {
                    return fallback;
                }
                return /^(#[0-9a-f]{3,8}|rgba?\([0-9.,%\s]+\)|hsla?\([0-9.,%\s]+\)|[a-z]+)$/i.test(value) ? value : fallback;
            }

            function sanitizeScrollMode(value) {
                value = typeof value === 'string' ? value.trim() : '';
                return value === 'item' || value === 'view' ? value : 'view';
            }

            function normalizeIcon(value) {
                if (value === null || value === undefined) {
                    return '';
                }

                if (typeof value === 'number' && isFinite(value)) {
                    try {
                        return String.fromCodePoint(value);
                    } catch (e) {
                        return '';
                    }
                }

                if (value && typeof value === 'object') {
                    var preferredKeys = ['unicode', 'icon', 'char', 'code', 'value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker'];
                    var keys = [];
                    var i;
                    var key;
                    var icon;

                    for (i = 0; i < preferredKeys.length; i++) {
                        key = preferredKeys[i];
                        if (Object.prototype.hasOwnProperty.call(value, key)) {
                            icon = normalizeIcon(value[key]);
                            if (icon) {
                                return icon;
                            }
                        }
                    }

                    keys = Object.keys(value);
                    for (i = 0; i < keys.length; i++) {
                        icon = normalizeIcon(value[keys[i]]);
                        if (icon) {
                            return icon;
                        }
                    }

                    return '';
                }

                value = typeof value === 'string' ? value.trim() : '';
                if (!value || value.indexOf('$' + 'variable(') === 0) {
                    return '';
                }
                value = value.split('||')[0].trim();
                if (/^&#x[0-9a-f]+;?$/i.test(value) || /^&#[0-9]+;?$/.test(value)) {
                    var el = document.createElement('textarea');
                    el.innerHTML = value;
                    return el.value;
                }
                if (value.indexOf('&') !== -1) {
                    var decoded = document.createElement('textarea');
                    decoded.innerHTML = value;
                    value = (decoded.value || value).trim();
                }
                var hexMatch = value.match(/^\\u([0-9a-f]{4,6})$/i) ||
                    value.match(/^U\+([0-9a-f]{4,6})$/i) ||
                    value.match(/^0x([0-9a-f]{4,6})$/i) ||
                    value.match(/^([0-9a-f]{4,6})$/i);
                if (hexMatch) {
                    try {
                        return String.fromCodePoint(parseInt(hexMatch[1], 16));
                    } catch (e) {
                        return '';
                    }
                }
                return value.substring(0, 4);
            }

            function extractIconMetaProp(value, propName) {
                if (!value || typeof value !== 'object') {
                    return '';
                }

                if (Object.prototype.hasOwnProperty.call(value, propName) && value[propName] !== null && value[propName] !== undefined) {
                    var direct = String(value[propName]).trim();
                    if (direct) {
                        return direct;
                    }
                }

                var preferredKeys = ['value', 'desktop', 'tablet', 'phone', 'innerContent', 'iconPicker'];
                var keys;
                var i;
                var key;
                var prop;

                for (i = 0; i < preferredKeys.length; i++) {
                    key = preferredKeys[i];
                    if (Object.prototype.hasOwnProperty.call(value, key)) {
                        prop = extractIconMetaProp(value[key], propName);
                        if (prop) {
                            return prop;
                        }
                    }
                }

                keys = Object.keys(value);
                for (i = 0; i < keys.length; i++) {
                    prop = extractIconMetaProp(value[keys[i]], propName);
                    if (prop) {
                        return prop;
                    }
                }

                return '';
            }

            function isLikelyFontAwesomeGlyph(iconChar) {
                if (typeof iconChar !== 'string' || !iconChar) {
                    return false;
                }
                try {
                    var codepoint = iconChar.codePointAt(0);
                    return isFinite(codepoint) && codepoint >= 0xF000 && codepoint <= 0xF8FF;
                } catch (e) {
                    return false;
                }
            }

            function isLikelyFontAwesomeType(iconType) {
                iconType = typeof iconType === 'string' ? iconType.trim().toLowerCase() : '';
                return iconType.indexOf('fa') === 0 || iconType === 'brands' || iconType === 'solid' || iconType === 'regular' || iconType === 'fontawesome';
            }

            function normalizeFontAwesomeWeight(weight, iconType) {
                weight = weight === null || weight === undefined ? '' : String(weight).trim().toLowerCase();
                if (/^(100|200|300|400|500|600|700|800|900)$/.test(weight)) {
                    return parseInt(weight, 10) >= 600 ? '900' : '400';
                }
                if (weight === 'regular' || weight === 'normal' || weight === 'book' || weight === 'light' || weight === 'thin' || weight === 'hairline' || weight === 'medium') {
                    return '400';
                }
                if (weight === 'solid' || weight === 'bold' || weight === 'semibold' || weight === 'semi-bold' || weight === 'black' || weight === 'heavy') {
                    return '900';
                }

                iconType = typeof iconType === 'string' ? iconType.trim().toLowerCase() : '';
                if (iconType === 'fab' || iconType === 'fa-brands' || iconType === 'brands' || iconType === 'far' || iconType === 'fa-regular' || iconType === 'regular') {
                    return '400';
                }

                return '900';
            }

            function getIconConfig(value) {
                var icon = normalizeIcon(value);
                var iconType = extractIconMetaProp(value, 'type');
                var family = extractIconMetaProp(value, 'fontFamily');
                var isFontAwesome = family === 'FontAwesome' || isLikelyFontAwesomeType(iconType) || isLikelyFontAwesomeGlyph(icon);

                return {
                    icon: icon,
                    family: isFontAwesome ? 'FontAwesome' : '',
                    weight: isFontAwesome ? normalizeFontAwesomeWeight(extractIconMetaProp(value, 'weight'), iconType) : ''
                };
            }

            function directionIsHorizontal(attrs, breakpoint) {
                var timelineAttr = nestedValue(attrs || {}, ['module', 'advanced', 'timeline']);
                var direction = responsiveAttrValue(timelineAttr, breakpoint || 'desktop', [
                    function(bp) { return [bp, 'value', 'direction']; }
                ]);

                return (direction || 'vertical') === 'horizontal';
            }

            function getBuilderConfig(timeline) {
                var id = closestTimelineId(timeline);
                var attrs = id ? getModuleAttrs(id) : {};
                var breakpoint = getCurrentBreakpointName();
                if (!id || !directionIsHorizontal(attrs, breakpoint) || !isEnabled(attrSubValue(attrs, 'enabled', breakpoint))) {
                    return null;
                }
                var legacyIcon = getIconConfig(attrSubValue(attrs, 'icon', breakpoint));
                var prevIcon = getIconConfig(attrSubValue(attrs, 'prevIcon', breakpoint));
                var nextIcon = getIconConfig(attrSubValue(attrs, 'nextIcon', breakpoint));

                if (!prevIcon.icon) {
                    prevIcon = legacyIcon;
                }
                if (!nextIcon.icon) {
                    nextIcon = legacyIcon;
                }

                return {
                    color: sanitizeColor(attrSubValue(attrs, 'arrowColor', breakpoint), defaultArrowColor),
                    backgroundColor: sanitizeColor(attrSubValue(attrs, 'arrowBackgroundColor', breakpoint), defaultArrowBackgroundColor),
                    size: sanitizeLength(attrValue(attrs, arrowSizeAttrName, attrSubValue(attrs, 'arrowSize', breakpoint), breakpoint), defaultArrowSize),
                    scrollMode: sanitizeScrollMode(attrValue(attrs, scrollModeAttrName, attrSubValue(attrs, 'scrollMode', breakpoint), breakpoint)),
                    prevIcon: prevIcon,
                    nextIcon: nextIcon
                };
            }

            function applyTimelineConfig(timeline, config) {
                if (!config) {
                    timeline.classList.remove('dbtl-horizontal-carousel');
                    timeline.classList.remove('dbtl-horizontal-carousel-has-custom-icon');
                    timeline.style.removeProperty('--dbtl-carousel-arrow-color');
                    timeline.style.removeProperty('--dbtl-carousel-arrow-background-color');
                    timeline.style.removeProperty('--dbtl-carousel-arrow-size');
                    timeline.removeAttribute('data-dbtl-carousel-icon');
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon');
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon-family');
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon-weight');
                    timeline.removeAttribute('data-dbtl-carousel-next-icon');
                    timeline.removeAttribute('data-dbtl-carousel-next-icon-family');
                    timeline.removeAttribute('data-dbtl-carousel-next-icon-weight');
                    timeline.removeAttribute('data-dbtl-carousel-scroll-mode');
                    return false;
                }

                timeline.classList.add('dbtl-horizontal-carousel');
                timeline.style.setProperty('--dbtl-carousel-arrow-color', config.color);
                timeline.style.setProperty('--dbtl-carousel-arrow-background-color', config.backgroundColor);
                timeline.style.setProperty('--dbtl-carousel-arrow-size', config.size);
                timeline.setAttribute('data-dbtl-carousel-scroll-mode', config.scrollMode);
                timeline.removeAttribute('data-dbtl-carousel-icon');
                if (config.prevIcon.icon) {
                    timeline.setAttribute('data-dbtl-carousel-prev-icon', config.prevIcon.icon);
                    if (config.prevIcon.family) {
                        timeline.setAttribute('data-dbtl-carousel-prev-icon-family', config.prevIcon.family);
                    } else {
                        timeline.removeAttribute('data-dbtl-carousel-prev-icon-family');
                    }
                    if (config.prevIcon.weight) {
                        timeline.setAttribute('data-dbtl-carousel-prev-icon-weight', config.prevIcon.weight);
                    } else {
                        timeline.removeAttribute('data-dbtl-carousel-prev-icon-weight');
                    }
                } else {
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon');
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon-family');
                    timeline.removeAttribute('data-dbtl-carousel-prev-icon-weight');
                }
                if (config.nextIcon.icon) {
                    timeline.setAttribute('data-dbtl-carousel-next-icon', config.nextIcon.icon);
                    if (config.nextIcon.family) {
                        timeline.setAttribute('data-dbtl-carousel-next-icon-family', config.nextIcon.family);
                    } else {
                        timeline.removeAttribute('data-dbtl-carousel-next-icon-family');
                    }
                    if (config.nextIcon.weight) {
                        timeline.setAttribute('data-dbtl-carousel-next-icon-weight', config.nextIcon.weight);
                    } else {
                        timeline.removeAttribute('data-dbtl-carousel-next-icon-weight');
                    }
                } else {
                    timeline.removeAttribute('data-dbtl-carousel-next-icon');
                    timeline.removeAttribute('data-dbtl-carousel-next-icon-family');
                    timeline.removeAttribute('data-dbtl-carousel-next-icon-weight');
                }
                return true;
            }

            function applyBuilderConfig(timeline) {
                return applyTimelineConfig(timeline, getBuilderConfig(timeline));
            }

            function parseConfigJson(value) {
                if (!value) {
                    return {};
                }
                try {
                    return JSON.parse(value) || {};
                } catch (e) {
                    return {};
                }
            }

            function normalizeSavedIconConfig(config, direction) {
                return {
                    icon: config && config[direction + '_icon'] ? config[direction + '_icon'] : '',
                    family: config && config[direction + '_icon_family'] ? config[direction + '_icon_family'] : '',
                    weight: config && config[direction + '_icon_weight'] ? config[direction + '_icon_weight'] : ''
                };
            }

            function getSavedConfig(timeline) {
                var configs = parseConfigJson(timeline.getAttribute('data-dbtl-carousel-configs'));
                var breakpoint = getCurrentBreakpointName();
                var config = configs[breakpoint] || configs.desktop || null;

                if (!config || !config.active) {
                    return null;
                }

                return {
                    color: sanitizeColor(config.color, defaultArrowColor),
                    backgroundColor: sanitizeColor(config.background_color, defaultArrowBackgroundColor),
                    size: sanitizeLength(config.size, defaultArrowSize),
                    scrollMode: sanitizeScrollMode(config.scroll_mode),
                    prevIcon: normalizeSavedIconConfig(config, 'prev'),
                    nextIcon: normalizeSavedIconConfig(config, 'next')
                };
            }

            function ensureButton(timeline, direction) {
                var className = direction === 'prev' ? 'dbtl-horizontal-carousel-arrow-prev' : 'dbtl-horizontal-carousel-arrow-next';
                var button = timeline.querySelector(':scope > .' + className);
                var icon = null;

                if (!button) {
                    button = document.createElement('button');
                    button.type = 'button';
                    button.className = 'dbtl-horizontal-carousel-arrow ' + className;
                    button.setAttribute('aria-label', direction === 'prev' ? 'Previous timeline item' : 'Next timeline item');
                    icon = document.createElement('span');
                    icon.className = 'dbtl-horizontal-carousel-arrow-icon';
                    button.appendChild(icon);
                    ['pointerdown', 'mousedown', 'touchstart'].forEach(function(eventName) {
                        button.addEventListener(eventName, function(event) {
                            event.stopPropagation();
                            if (event.stopImmediatePropagation) {
                                event.stopImmediatePropagation();
                            }
                        }, true);
                    });
                    button.addEventListener('click', function(event) {
                        event.preventDefault();
                        event.stopPropagation();
                        if (event.stopImmediatePropagation) {
                            event.stopImmediatePropagation();
                        }
                        advanceCarousel(timeline, direction === 'prev' ? -1 : 1);
                    }, true);
                    timeline.appendChild(button);
                }

                icon = button.querySelector('.dbtl-horizontal-carousel-arrow-icon');
                return button;
            }

            function setButtonIcon(icon, customIcon, defaultIcon, family, weight) {
                if (!icon) {
                    return;
                }
                var iconText = customIcon || defaultIcon;
                if (icon.textContent !== iconText) {
                    icon.textContent = iconText;
                }
                if (icon.classList.contains('dbtl-horizontal-carousel-arrow-icon-custom') !== !!customIcon) {
                    icon.classList.toggle('dbtl-horizontal-carousel-arrow-icon-custom', !!customIcon);
                }
                if (family === 'FontAwesome') {
                    if (icon.style.fontFamily !== 'FontAwesome') {
                        icon.style.fontFamily = 'FontAwesome';
                    }
                    if (icon.style.fontWeight !== (weight || '900')) {
                        icon.style.fontWeight = weight || '900';
                    }
                } else {
                    if (icon.style.fontFamily) {
                        icon.style.removeProperty('font-family');
                    }
                    if (icon.style.fontWeight) {
                        icon.style.removeProperty('font-weight');
                    }
                }
            }

            function setButtonDisabled(button, disabled) {
                disabled = !!disabled;
                if (button.disabled !== disabled) {
                    button.disabled = disabled;
                }
                if (button.hidden !== disabled) {
                    button.hidden = disabled;
                }
                if (button.getAttribute('aria-disabled') !== String(disabled)) {
                    button.setAttribute('aria-disabled', String(disabled));
                }
                button.classList.toggle('dbtl-horizontal-carousel-arrow-disabled', disabled);
            }

            function getCarouselItems(timeline) {
                var track = timeline.querySelector('.et_pb_timeline_track');
                return track ? Array.prototype.slice.call(track.querySelectorAll('.et_pb_timeline_item')) : [];
            }

            function getScrollMode(timeline) {
                return timeline.getAttribute('data-dbtl-carousel-scroll-mode') === 'item' ? 'item' : 'view';
            }

            function clampIndex(index, items) {
                var max = Math.max(0, items.length - 1);
                index = parseInt(index, 10);
                if (!isFinite(index)) {
                    index = 0;
                }
                return Math.max(0, Math.min(max, index));
            }

            function getCurrentIndex(timeline, items) {
                if (typeof timeline.__dbtlCarouselCurrentIndex !== 'number') {
                    timeline.__dbtlCarouselCurrentIndex = clampIndex(timeline.getAttribute('data-dbtl-carousel-current-index') || 0, items);
                }
                return clampIndex(timeline.__dbtlCarouselCurrentIndex, items);
            }

            function setItemState(item, stateClass) {
                var currentState = item.classList.contains('dbtl-state-current') ? 'dbtl-state-current' :
                    (item.classList.contains('dbtl-state-previous') ? 'dbtl-state-previous' :
                    (item.classList.contains('dbtl-state-upcoming') ? 'dbtl-state-upcoming' : ''));

                if (currentState === stateClass) {
                    return;
                }

                if (item.classList.contains('dbtl-state-previous')) {
                    item.classList.remove('dbtl-state-previous');
                }
                if (item.classList.contains('dbtl-state-current')) {
                    item.classList.remove('dbtl-state-current');
                }
                if (item.classList.contains('dbtl-state-upcoming')) {
                    item.classList.remove('dbtl-state-upcoming');
                }
                if (stateClass && !item.classList.contains(stateClass)) {
                    item.classList.add(stateClass);
                }
            }

            function updateCarouselStates(timeline, items, currentIndex) {
                items = items || getCarouselItems(timeline);
                currentIndex = clampIndex(currentIndex, items);
                timeline.__dbtlCarouselCurrentIndex = currentIndex;
                if (timeline.getAttribute('data-dbtl-carousel-current-index') !== String(currentIndex)) {
                    timeline.setAttribute('data-dbtl-carousel-current-index', String(currentIndex));
                }

                items.forEach(function(item, index) {
                    if (index < currentIndex) {
                        setItemState(item, 'dbtl-state-previous');
                    } else if (index > currentIndex) {
                        setItemState(item, 'dbtl-state-upcoming');
                    } else {
                        setItemState(item, 'dbtl-state-current');
                    }
                });
            }

            function getItemTargetScrollLeft(track, item) {
                var maxScroll = Math.max(0, track.scrollWidth - track.clientWidth);
                var itemCenter = item.offsetLeft + (item.offsetWidth / 2);
                var target = itemCenter - (track.clientWidth / 2);
                return Math.max(0, Math.min(maxScroll, target));
            }

            function getIndexForScrollLeft(track, items, scrollLeft) {
                var center = scrollLeft + (track.clientWidth / 2);
                var bestIndex = 0;
                var bestDistance = Infinity;

                items.forEach(function(item, index) {
                    var itemCenter = item.offsetLeft + (item.offsetWidth / 2);
                    var distance = Math.abs(itemCenter - center);
                    if (distance < bestDistance) {
                        bestDistance = distance;
                        bestIndex = index;
                    }
                });

                return clampIndex(bestIndex, items);
            }

            function getTrackMaxScroll(track) {
                return Math.max(0, track.scrollWidth - track.clientWidth);
            }

            function clampScrollLeft(track, scrollLeft) {
                return Math.max(0, Math.min(getTrackMaxScroll(track), scrollLeft));
            }

            function animateTrackScroll(track, target) {
                var start = track.scrollLeft;
                var distance = target - start;
                var duration = Math.min(650, Math.max(260, Math.abs(distance) * 1.15));
                var startTime = 0;

                if (track.__dbtlCarouselScrollAnimation && window.cancelAnimationFrame) {
                    window.cancelAnimationFrame(track.__dbtlCarouselScrollAnimation);
                }

                if (Math.abs(distance) < 1 || !window.requestAnimationFrame) {
                    track.scrollLeft = target;
                    track.__dbtlCarouselAnimating = false;
                    return;
                }

                track.__dbtlCarouselAnimating = true;
                function step(timestamp) {
                    if (!startTime) {
                        startTime = timestamp;
                    }
                    var progress = Math.min(1, (timestamp - startTime) / duration);
                    var eased = 1 - Math.pow(1 - progress, 3);
                    track.scrollLeft = start + (distance * eased);

                    if (progress < 1) {
                        track.__dbtlCarouselScrollAnimation = window.requestAnimationFrame(step);
                    } else {
                        track.scrollLeft = target;
                        track.__dbtlCarouselAnimating = false;
                    }
                }

                track.__dbtlCarouselScrollAnimation = window.requestAnimationFrame(step);
            }

            function scrollToCarouselItem(timeline, index) {
                var track = timeline.querySelector('.et_pb_timeline_track');
                var items = getCarouselItems(timeline);
                index = clampIndex(index, items);

                if (!track || !items[index]) {
                    return;
                }

                animateTrackScroll(track, getItemTargetScrollLeft(track, items[index]));
            }

            function advanceCarouselByView(timeline, delta) {
                var track = timeline.querySelector('.et_pb_timeline_track');
                var items = getCarouselItems(timeline);
                var target;
                var nextIndex;

                if (!track || !items.length) {
                    updateButtons(timeline);
                    return;
                }

                target = clampScrollLeft(track, track.scrollLeft + (delta * track.clientWidth * 0.7));
                nextIndex = getIndexForScrollLeft(track, items, target);
                updateCarouselStates(timeline, items, nextIndex);
                animateTrackScroll(track, target);
                updateButtons(timeline, target);
            }

            function advanceCarousel(timeline, delta) {
                var items = getCarouselItems(timeline);
                var currentIndex = getCurrentIndex(timeline, items);
                var nextIndex = clampIndex(currentIndex + delta, items);

                if (getScrollMode(timeline) === 'view') {
                    advanceCarouselByView(timeline, delta);
                    return;
                }

                if (!items.length || nextIndex === currentIndex) {
                    updateButtons(timeline);
                    return;
                }

                updateCarouselStates(timeline, items, nextIndex);
                scrollToCarouselItem(timeline, nextIndex);
                updateButtons(timeline);
            }

            function updateButtons(timeline, pendingScrollLeft) {
                var track = timeline.querySelector('.et_pb_timeline_track');
                var items = getCarouselItems(timeline);
                var currentIndex = getCurrentIndex(timeline, items);
                var scrollMode = getScrollMode(timeline);
                var scrollLeft = typeof pendingScrollLeft === 'number' ? pendingScrollLeft : (track ? track.scrollLeft : 0);
                var legacyIcon = timeline.getAttribute('data-dbtl-carousel-icon') || '';
                var prevCustomIcon = timeline.getAttribute('data-dbtl-carousel-prev-icon') || legacyIcon;
                var nextCustomIcon = timeline.getAttribute('data-dbtl-carousel-next-icon') || legacyIcon;
                var prevFamily = timeline.getAttribute('data-dbtl-carousel-prev-icon-family') || '';
                var nextFamily = timeline.getAttribute('data-dbtl-carousel-next-icon-family') || '';
                var prevWeight = timeline.getAttribute('data-dbtl-carousel-prev-icon-weight') || '';
                var nextWeight = timeline.getAttribute('data-dbtl-carousel-next-icon-weight') || '';
                var prev = ensureButton(timeline, 'prev');
                var next = ensureButton(timeline, 'next');
                var prevIcon = prev.querySelector('.dbtl-horizontal-carousel-arrow-icon');
                var nextIcon = next.querySelector('.dbtl-horizontal-carousel-arrow-icon');
                var canScroll = false;

                timeline.classList.toggle('dbtl-horizontal-carousel-has-custom-icon', !!(prevCustomIcon || nextCustomIcon));
                setButtonIcon(prevIcon, prevCustomIcon, '4', prevFamily, prevWeight);
                setButtonIcon(nextIcon, nextCustomIcon, '5', nextFamily, nextWeight);

                if (track) {
                    canScroll = track.scrollWidth > track.clientWidth + 2;
                    var prevDisabled = scrollMode === 'view' ? (!canScroll || scrollLeft <= 1) : (!canScroll || currentIndex <= 0);
                    var nextDisabled = scrollMode === 'view' ? (!canScroll || scrollLeft >= getTrackMaxScroll(track) - 1) : (!canScroll || currentIndex >= items.length - 1);
                    setButtonDisabled(prev, prevDisabled);
                    setButtonDisabled(next, nextDisabled);
                }
            }

            function removeButtons(timeline) {
                timeline.querySelectorAll(':scope > .dbtl-horizontal-carousel-arrow').forEach(function(button) {
                    button.remove();
                });
            }

            function clearCarouselState(timeline) {
                timeline.__dbtlCarouselCurrentIndex = 0;
                timeline.removeAttribute('data-dbtl-carousel-current-index');
                getCarouselItems(timeline).forEach(function(item) {
                    setItemState(item, '');
                });
            }

            function setupTimeline(timeline) {
                var track = timeline.querySelector('.et_pb_timeline_track');
                var shouldEnable = hasBuilderStore() ? applyBuilderConfig(timeline) : applyTimelineConfig(timeline, getSavedConfig(timeline));

                if (!shouldEnable || !track) {
                    removeButtons(timeline);
                    clearCarouselState(timeline);
                    return;
                }

                updateButtons(timeline);
                var items = getCarouselItems(timeline);
                updateCarouselStates(timeline, items, getCurrentIndex(timeline, items));
            }

            function update() {
                scheduled = false;
                document.querySelectorAll('.et_pb_timeline').forEach(setupTimeline);
            }

            function schedule() {
                if (scheduled) {
                    return;
                }
                scheduled = true;
                if (window.requestAnimationFrame) {
                    window.requestAnimationFrame(update);
                } else {
                    window.setTimeout(update, 16);
                }
            }

            function observe() {
                if (observed || !hasBuilderStore() || !window.MutationObserver || !document.body) {
                    return;
                }
                observed = true;
                new MutationObserver(function(mutations) {
                    for (var i = 0; i < mutations.length; i++) {
                        if (mutations[i].type === 'childList') {
                            schedule();
                            return;
                        }
                    }
                }).observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }

            window.addEventListener('resize', schedule);
            document.addEventListener('DOMContentLoaded', schedule);
            observe();
            schedule();
            window.setTimeout(schedule, 250);
        })();
    </script>
    <?php
}
