//CampaignBundle
/**
 * Setup the campaign view
 *
 * @param container
 */
Mautic.campaignOnLoad = function (container, response) {
    Mautic.lazyLoadGraphStatsOnCampaignDetail();
    Mautic.lazyLoadContactListOnCampaignDetail();
    Mautic.lazyLoadEventStatsOnCampaignDetail();
    const $flashes = mQuery('#flashes');
    const $builder = mQuery('#campaign-builder');
    const isCampaignPreview = $builder.hasClass('preview');
    Mautic.campaignBuilderPostData = [];
    let extraData = {};

    Mautic.reinitializeCampaignEventDeleteHandlers();

    if (mQuery(container + ' #list-search').length) {
        Mautic.activateSearchAutocomplete('list-search', 'campaign');
    }

    if (mQuery('#CampaignEventPanel').length) {
        var tooltipTimeout = null;
        // setup button clicks
        mQuery('#CampaignEventPanelGroups button').on('click', function() {
            var eventType = mQuery(this).data('type');
            Mautic.campaignBuilderUpdateEventList([eventType], false, 'lists', true);
        });

        mQuery('#CampaignEventPanelLists button').on('click', function() {
            Mautic.campaignBuilderUpdateEventList(Mautic.campaignBuilderAnchorClickedAllowedEvents, true, 'groups', true);
        });

        // set hover and double click functions for the event buttons
        if (!isCampaignPreview) {
            mQuery('#CampaignCanvas .list-campaign-event, #CampaignCanvas .list-campaign-source').off('.eventbuttons')
                .on('mouseover.eventbuttons', function() {
                    mQuery(this).find('.campaign-event-buttons').removeClass('hide');
                })
                .on('mouseout.eventbuttons', function() {
                    mQuery(this).find('.campaign-event-buttons').addClass('hide');
                })
                .on('dblclick.eventbuttons', function(event) {
                    event.preventDefault();
                    mQuery(this).find('.btn-edit').first().click();
                });

            // adding delete option modal for events
            mQuery("#CampaignCanvas .list-campaign-event a[data-toggle='ajax-delete']").on("click.ajax", Mautic.handleEventDeleteClick);

            // adding delete option ajax for sources
            mQuery("#CampaignCanvas .list-campaign-source a[data-toggle='ajax-delete']").on("click.ajax", function (event) {
                event.preventDefault();
                mQuery('.btns-builder').find('button').prop('disabled', true);
                extraData = {
                    'modifiedSources': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedSources),
                };
                return Mautic.ajaxifyLink(this, event, extraData);
            });

            // adding ajax model option for sources
            mQuery("#CampaignCanvas .list-campaign-source a[data-toggle='ajaxmodal']").off('click.ajaxmodal');
            mQuery("#CampaignCanvas .list-campaign-source a[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
                event.preventDefault();
                mQuery('.btns-builder').find('button').prop('disabled', true);
                mQuery(this).data('form-data', {
                    'modifiedSources': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedSources),
                });
                Mautic.ajaxifyModal(this, event);
            });

            // remove handlers from event edit option and add ajax modal option for events
            mQuery("#CampaignCanvas .list-campaign-event a[data-toggle='ajaxmodal']").off('click.ajaxmodal');
            mQuery("#CampaignCanvas .list-campaign-event a[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
                event.preventDefault();
                mQuery('.btns-builder').find('button').prop('disabled', true);
                mQuery(this).data('form-data', {
                    "modifiedEvents": JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents),
                    'deletedEvents': Mautic.campaignBuilderCampaignElements.deletedEvents ? JSON.stringify(Mautic.campaignBuilderCampaignElements.deletedEvents) : '',
                });
                Mautic.ajaxifyModal(this, event);
            });
        }

        // setup chosen
        mQuery('.campaign-event-selector').on('chosen:showing_dropdown', function (event) {
            // Disable canvas scrolling
            mQuery('.builder-content').css('overflow', 'hidden');

            var thisSelect = mQuery(event.target).attr('id');
            Mautic.campaignBuilderUpdateEventListTooltips(thisSelect, false);

            mQuery('#'+thisSelect+'_chosen .chosen-search input').on('keydown.tooltip', function () {
                // Destroy tooltips that are filtered out
                Mautic.campaignBuilderUpdateEventListTooltips(thisSelect, true);
            }).on('keyup.tooltip', function() {
                // Recreate tooltips for those left
                if (tooltipTimeout) {
                    clearTimeout(tooltipTimeout);
                }

                // wrap into setTimeout for fast typing users.
                tooltipTimeout = setTimeout(function () {
                    Mautic.campaignBuilderUpdateEventListTooltips(thisSelect, false);
                }, 200);
            });
        });

        mQuery('.campaign-event-selector').on('chosen:hiding_dropdown', function (event) {
            // Re-enable canvas scrolling
            mQuery('.builder-content').css('overflow', 'auto');

            var thisSelect = mQuery(event.target).attr('id');
            Mautic.campaignBuilderUpdateEventListTooltips(thisSelect, true);

            mQuery('#'+thisSelect+'_chosen .chosen-search input').off('keyup.tooltip')
                .off('keydown.tooltip');
        });

        mQuery('.campaign-event-selector').on('change', function() {
            // Hide events list

            if (!mQuery('#CampaignEvent_newsource').length) {
                Mautic.hideCampaignEventPanel();
            }

            // Get the option clicked
            var thisId = mQuery(this).attr('id');
            var option  = mQuery('#'+thisId+' option[value="' + mQuery(this).val() + '"]');

            if (option.attr('data-href') && Mautic.campaignBuilderAnchorNameClicked) {
                var updatedUrl = option.attr('data-href').replace(/anchor=(.*?)$/, "anchor=" + Mautic.campaignBuilderAnchorNameClicked + "&anchorEventType=" + Mautic.campaignBuilderAnchorEventTypeClicked);
                // Replace the anchor in the URL with that clicked
                option.attr('data-href', updatedUrl);
            }

            // Deactivate chosen to kill tooltips
            mQuery('#'+thisId).trigger('chosen:close');

            // Display the modal with form
            Mautic.ajaxifyModal(option);

            // Reset the dropdown
            mQuery(this).val('');
            mQuery(this).trigger('chosen:updated');
        });

        mQuery('#CampaignCanvas').on('click', function(event) {
            if (!mQuery(event.target).parents('#CampaignCanvas').length && !mQuery('#CampaignEvent_newsource').length) {
                Mautic.hideCampaignEventPanel();
            }
        });

        $builder.on('campaign-builder:show', function () {
            $builder.addClass('builder-active').removeClass('hide');
            $flashes.addClass('alert-offset');

        });
        $builder.on('campaign-builder:hide', function () {
            $builder.addClass('hide').removeClass('builder-active');
            $flashes.removeClass('alert-offset');
        });
        if (!isCampaignPreview) {
            Mautic.prepareCampaignCanvas();
        }

        // Open the builder directly when saved from the builder
        if (response && response.inBuilder) {
            Mautic.launchCampaignEditor();
            Mautic.processBuilderErrors(response);
        }

        // update the cloned event info when storage is updated from different tab
        window.addEventListener('storage', function(event) {
            if (event.key === 'mautic_campaign_event_clone') {
                Mautic.campaignBuilderUpdateEventCloneDescription();
            }
        });

        mQuery(document).ajaxError(function(event, jqxhr, settings, thrownError) {
            var path = settings.url.split('?')[0];

            if (path === "/s/campaigns/events/insert") {
                Mautic.campaignEventInsertOnError(event, jqxhr);
            }
        });
    }

    const campaignEmailStats = mQuery(container).find('[data-load="campaign-email-stats"]').first();
    if(campaignEmailStats.length) {
        mQuery(campaignEmailStats).on('click', () => {
            const $campaignWeekdaysContainer = mQuery('[data-campaign-email-stats-weekdays]');
            const $campaignHoursContainer = mQuery('[data-campaign-email-stats-hours]');

            if ($campaignWeekdaysContainer.find('canvas').length === 0) {
                mQuery.ajax({
                    url: $campaignWeekdaysContainer.data('campaign-email-stats-weekdays'),
                    success: function (response) {
                        $campaignWeekdaysContainer.html(response);
                        Mautic.renderCharts($campaignWeekdaysContainer);
                    }
                });
            }

            if ($campaignHoursContainer.find('canvas').length === 0) {
                mQuery.ajax({
                    url: $campaignHoursContainer.data('campaign-email-stats-hours'),
                    success: function (response) {
                        $campaignHoursContainer.html(response);
                        Mautic.renderCharts($campaignHoursContainer);
                    }
                });
            }
        });
    }

    Mautic.campaignAuditlogOnLoad(container, response);
};

Mautic.lazyLoadContactListOnCampaignDetail = function() {
    let containerId = '#leads-container';
    let container = mQuery(containerId);

    // Load the contacts only if the container exists.
    if (!container.length) {
        return;
    }

    let campaignContactUrl = container.data('target-url');
    mQuery.get(campaignContactUrl, function(response) {
        response.target = containerId;
        Mautic.processPageContent(response);
    });
};

Mautic.lazyLoadGraphStatsOnCampaignDetail = function() {
    const containerId = '#campaign-graph-div';
    const container = mQuery(containerId);

    // Load the contacts only if the container exists.
    if (!container.length) {
        return;
    }

    const campaignGraphUrl = container.data('target-url');
    mQuery.get(campaignGraphUrl, function(response) {
        response.target = containerId;
        Mautic.processPageContent(response);
    });
};

Mautic.lazyLoadEventStatsOnCampaignDetail = function()  {
    const containerId = '#campaign-tab-content';
    const container = mQuery(containerId);

    if (!container.length) {
        return;
    }

    const campaignEventStatUrl = container.data('event-target-url');
    if ('undefined' == typeof campaignEventStatUrl) {
        // We don't have to make AJAX requests if the URL is empty
        return;
    }
    mQuery.get(campaignEventStatUrl, function(response) {
        if (response.errors && 'dev' == mauticEnv) {
            alert(response.errors[0].message);
            console.log(response.errors);
        }

        if (typeof response.preview !== 'undefined')
        {
            mQuery('#preview-container').html(response.preview);
            Mautic.prepareCampaignCanvas();
            Mautic.previewCampaignLabels();
        }
        else
        {
            mQuery('#decisions_li').remove();
            mQuery('#decisions-container').remove();
        }

        if (typeof response.decisions !== 'undefined')
        {
            mQuery('#decisions-container').html(response.decisions);
        }
        else
        {
            mQuery('#decisions_li').remove();
            mQuery('#decisions-container').remove();
        }

        if (typeof response.actions !== 'undefined')
        {
            mQuery('#actions-container').html(response.actions);
        }
        else
        {
            mQuery('#actions_li').remove();
            mQuery('#actions-container').remove();
        }

        if (typeof response.conditions !== 'undefined')
        {
            mQuery('#conditions-container').html(response.conditions);
        }
        else
        {
            mQuery('#conditions_li').remove();
            mQuery('#conditions-container').remove();
        }

        mQuery('ul#campaign_nav_header_ul li').removeClass('active');
        mQuery('ul#campaign_nav_header_ul li').eq(0).addClass('active');

        mQuery('div#campaign-tab-content .tab-pane').removeClass('active in');
        mQuery('div#campaign-tab-content .tab-pane').eq(0).addClass('active in');

    });
}


/**
 * Update chosen tooltips
 *
 * @param theSelect
 * @param onlyDestroy
 */
Mautic.campaignBuilderUpdateEventListTooltips = function(theSelect, onlyDestroy) {
    const $select = mQuery('#'+theSelect);
    const dataAttribute = 'tooltips';

    // create a stack
    if (undefined === $select.data(dataAttribute)) {
        $select.data(dataAttribute, []);
    }

    // remove existing tooltips before we create new ones.
    const tooltips = $select.data(dataAttribute);

    mQuery.each(tooltips, function (index, $tooltip) {
        if (undefined === $tooltip) {
            return;
        }

        $tooltip.tooltip('hide');
        $tooltip.tooltip('destroy');
    });
    $select.data(dataAttribute, []);

    if (true === onlyDestroy) {
        return;
    }

    // create tooltips.
    $select.find('option').each(function () {
        if (mQuery(this).attr('id')) {
            // Initiate a tooltip on each option since chosen doesn't copy over the data attributes
            const chosenOption = '#' + theSelect + '_chosen .option_' + mQuery(this).attr('id');

            const $tooltip = mQuery(chosenOption).tooltip({html: true, container: 'body', placement: 'left'});
            $select.data(dataAttribute).push($tooltip);
        }
    });
}

/**
 * Delete the builder instance so it's regenerated when reopening the campaign event builder
 */
Mautic.campaignOnUnload = function(container) {
    delete Mautic.campaignBuilderInstance;
    delete Mautic.campaignBuilderLabels;
}

Mautic.campaignEventCloneOnLoad = function(container, response) {
    Mautic.setCampaignEventClone({
        'sourceEventName': response['eventName'],
        'sourceEventType': response['eventType'],
        'sourceType': response['type'],
        'sourceCampaignId': response['campaignId'],
        'sourceCampaignName': response['campaignName'],
    });
    const flashMessage = Mautic.addInfoFlashMessage(Mautic.translate('mautic.campaign.event.clone.success'));
    Mautic.setFlashes(flashMessage);
    Mautic.campaignBuilderUpdateEventCloneDescription();
};

Mautic.campaignEventInsertOnError = function (event, jqxhr) {
    Mautic.clearCampaignEventClone();
    Mautic.hideCampaignEventPanel();
    if (jqxhr.responseJSON.error) {
        const flashMessage = Mautic.addErrorFlashMessage(jqxhr.responseJSON.error);
        Mautic.setFlashes(flashMessage);
    }
};

/**
 * Setup the campaign event view
 *
 * @param container
 * @param response
 */
Mautic.campaignEventOnLoad = function (container, response) {
    if (mQuery('#campaignevent_triggerHour').length) {
        Mautic.campaignEventUpdateIntervalHours();
        mQuery('#campaignevent_triggerHour').on('change', Mautic.campaignEventUpdateIntervalHours);
        mQuery('#campaignevent_triggerRestrictedStartHour').on('change', Mautic.campaignEventUpdateIntervalHours);
        mQuery('#campaignevent_triggerRestrictedStopHour').on('change', Mautic.campaignEventUpdateIntervalHours);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_0').on('change', Mautic.campaignEventSelectDOW);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_1').on('change', Mautic.campaignEventSelectDOW);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_2').on('change', Mautic.campaignEventSelectDOW);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_3').on('change', Mautic.campaignEventSelectDOW);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_4').on('change', Mautic.campaignEventSelectDOW);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_7').on('change', Mautic.campaignEventSelectDOW);
    }

    if (!response.hasOwnProperty('eventId')) {
        // There's nothing for us to do, so bail
        return;
    }

    // New action created so append it to the form
    var domEventId = 'CampaignEvent_' + response.eventId;
    var eventId = '#' + domEventId;

    Mautic.campaignBuilderLabels[domEventId] = (response.label) ? response.label : '';

    if (!response.success && Mautic.campaignBuilderConnectionRequiresUpdate) {
        // Modal exited - check to see if a connection needs to be removed
        Mautic.campaignBuilderInstance.deleteConnection(Mautic.campaignBuilderLastConnection);
    }
    Mautic.campaignBuilderConnectionRequiresUpdate = false;
    Mautic.campaignBuilderUpdateLabel(domEventId);
    Mautic.campaignBuilderCanvasEvents[response.event.id] = response.event;

    if (response.deleted) {
        Mautic.campaignBuilderInstance.remove(document.getElementById(domEventId));
        if (response.deletedEvents) {
            // Process each deleted event
            mQuery.each(response.deletedEvents, function(i, eventData) {
                Mautic.addEventToDeletedEvents(eventData.id, eventData.redirectEvent);
            });
        }
        delete Mautic.campaignBuilderCampaignElements.modifiedEvents[response.event.id];
        delete Mautic.campaignBuilderEventPositions[domEventId];
        delete Mautic.campaignBuilderCanvasEvents[response.event.id];
    } else if (response.updateHtml) {
        mQuery(eventId + " .campaign-event-content").replaceWith(response.updateHtml);
    } else if (response.eventHtml) {
        var newHtml = response.eventHtml;

        //append content
        var x = parseInt(mQuery('#droppedX').val());
        var y = parseInt(mQuery('#droppedY').val());

        Mautic.campaignBuilderEventPositions[domEventId] = {
            'left': x,
            'top': y
        };

        mQuery(newHtml).appendTo('#CampaignCanvas');

        mQuery(eventId).css({'left': x + 'px', 'top': y + 'px'});

        Mautic.campaignBuilderRegisterAnchors(Mautic.getAnchorsForEvent(response.event), eventId);
        Mautic.campaignBuilderInstance.draggable(domEventId, Mautic.campaignDragOptions);

        //activate new stuff
        mQuery(eventId + " a[data-toggle='ajax']").click(function (event) {
            event.preventDefault();
            mQuery('.btns-builder').find('button').prop('disabled', true);
            const extraData = {
                'modifiedEvents': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents),
            };
            return Mautic.ajaxifyLink(this, event, extraData);
        });

        //initialize ajax'd modals
        mQuery(eventId + " a[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
            event.preventDefault();
            mQuery('.btns-builder').find('button').prop('disabled', true);
            mQuery(this).data('form-data', {
                'modifiedEvents': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents),
            });

            Mautic.ajaxifyModal(this, event);
        });

        // adding delete option modal for events
        mQuery(eventId + " a[data-toggle='ajax-delete']").off("click.ajax").on("click.ajax", Mautic.handleEventDeleteClick);

        mQuery(eventId).off('.eventbuttons')
            .on('mouseover.eventbuttons', function() {
                mQuery(this).find('.campaign-event-buttons').removeClass('hide');
            })
            .on('mouseout.eventbuttons', function() {
                mQuery(this).find('.campaign-event-buttons').addClass('hide');
            })
            .on('dblclick.eventbuttons', function(event) {
                event.preventDefault();
                mQuery(this).find('.btn-edit').first().click();
            });

        //initialize tooltips
        mQuery(eventId + " *[data-toggle='tooltip']").tooltip({html: true});

        // Connect into last anchor clicked
        Mautic.campaignBuilderInstance.connect({
            uuids: [
                Mautic.campaignBuilderAnchorClicked,
                domEventId+'_top'
            ]
        });
    }

    if (response.hasOwnProperty('clearCloneStorage')) {
        Mautic.hideCampaignEventPanel();
        Mautic.clearCampaignEventClone();
    }

    if(response.modifiedEvents) {
        Mautic.campaignBuilderCampaignElements.modifiedEvents = response.modifiedEvents;
    }
    mQuery('.btns-builder').find('button').prop('disabled', false);
    Mautic.campaignBuilderInstance.repaintEverything();
};

/**
 * Update the trigger hour based on the interval unit selected
 */
Mautic.campaignEventUpdateIntervalHours = function () {
    var hour = mQuery('#campaignevent_triggerHour').val();
    var start = mQuery('#campaignevent_triggerRestrictedStartHour').val();
    var stop = mQuery('#campaignevent_triggerRestrictedStopHour').val();

    if (hour) {
        mQuery('#campaignevent_triggerRestrictedStartHour').val('');
        mQuery('#campaignevent_triggerRestrictedStopHour').val('');
        mQuery('#campaignevent_triggerRestrictedStartHour').prop('disabled', true);
        mQuery('#campaignevent_triggerRestrictedStopHour').prop('disabled', true);
    } else if (start || stop) {
        mQuery('#campaignevent_triggerHour').val('');
        mQuery('#campaignevent_triggerHour').prop('disabled', true);
    } else {
        mQuery('#campaignevent_triggerHour').val('');
        mQuery('#campaignevent_triggerRestrictedStartHour').val('');
        mQuery('#campaignevent_triggerRestrictedStopHour').val('');
        mQuery('#campaignevent_triggerHour').prop('disabled', false);
        mQuery('#campaignevent_triggerRestrictedStartHour').prop('disabled', false);
        mQuery('#campaignevent_triggerRestrictedStopHour').prop('disabled', false);
    }
};

/**
 * Update DOW for weekday selection
 */
Mautic.campaignEventSelectDOW = function() {
    if (mQuery('#campaignevent_triggerRestrictedDaysOfWeek_7').prop('checked')) {
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_0').prop('checked', true);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_1').prop('checked', true);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_2').prop('checked', true);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_3').prop('checked', true);
        mQuery('#campaignevent_triggerRestrictedDaysOfWeek_4').prop('checked', true);
    }

    mQuery('#campaignevent_triggerRestrictedDaysOfWeek_7').prop('checked', false);
};

/**
 * Determine anchors to set up for the given event.
 *
 * This inspects the `connectionRestrictions` property
 * within the event's settings that were passed when
 * registering the event in your bundle's CampaignEventListener.
 *
 * @param event
 */
Mautic.getAnchorsForEvent = function (event) {
    var eventRestrictions = Mautic.campaignBuilderConnectionRestrictions[event.type];

    if (!eventRestrictions || !eventRestrictions.target) {
        // Default fallback if restrictions not found
        console.warn(`No restrictions found for the event type: ${event.type}. Probably the plugin was disabled.`);

        return ['top', 'bottom'];
    }

    var restrictions = eventRestrictions.target;

    // If all connections are restricted, only anchor the top
    if (
        restrictions.decision.length === 1 && restrictions.decision[0] === "none" &&
        restrictions.action.length === 1 && restrictions.action[0] === "none" &&
        restrictions.condition.length === 1 && restrictions.condition[0] === "none"
    ) {
        return ['top'];
    }

    if (event.eventType === 'decision' || event.eventType === 'condition') {
        return ['top', 'yes', 'no'];
    }

    return ['top', 'bottom'];
};

/**
 * Setup the campaign source view
 *
 * @param container
 * @param response
 */
Mautic.campaignSourceOnLoad = function (container, response) {
    //new action created so append it to the form
    var domEventId = 'CampaignEvent_' + response.sourceType;
    var eventId = '#' + domEventId;

    if (response.deleted) {
        Mautic.campaignBuilderInstance.remove(document.getElementById(domEventId));
        delete Mautic.campaignBuilderEventPositions[domEventId];

        mQuery('#campaignLeadSource_' + response.sourceType).prop('disabled', false);
        mQuery('#SourceList').trigger('chosen:updated');

        // Check to see if all sources have been deleted
        if (!mQuery('.list-campaign-source:not(#CampaignEvent_newsource_hide)').length) {
            mQuery('#CampaignEvent_newsource_hide').attr('id', 'CampaignEvent_newsource');
            Mautic.campaignBuilderPrepareNewSource();
        }

    } else if (response.updateHtml) {

        mQuery(eventId + " .campaign-event-content").html(response.updateHtml);
    } else if (response.sourceHtml) {
        mQuery('#campaignLeadSource_' + response.sourceType).prop('disabled', true);
        mQuery('#SourceList').trigger('chosen:updated');

        var newHtml = response.sourceHtml;

        if (mQuery('#CampaignEvent_newsource').length) {
            var x = mQuery('#CampaignEvent_newsource').position().left;
            var y = mQuery('#CampaignEvent_newsource').position().top;

            mQuery('#CampaignEvent_newsource').attr('id', 'CampaignEvent_newsource_hide');
            Mautic.hideCampaignEventPanel();
            var autoConnect = false;
        } else {
            //append content
            var x = parseInt(mQuery('#droppedX').val());
            var y = parseInt(mQuery('#droppedY').val());
            var autoConnect = true;
        }

        mQuery(newHtml).appendTo('#CampaignCanvas');
        Mautic.campaignBuilderEventPositions[domEventId] = {
            'left': x,
            'top': y
        };
        mQuery(eventId).css({'left': x + 'px', 'top': y + 'px'});

        Mautic.campaignBuilderRegisterAnchors(['leadSource', 'leadSourceLeft', 'leadSourceRight'], eventId);
        Mautic.campaignBuilderInstance.draggable(domEventId, Mautic.campaignDragOptions);

        //activate new stuff
        mQuery(eventId + " a[data-toggle='ajax']").click(function (event) {
            event.preventDefault();
            const extraData = {
                'modifiedSources': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedSources),
            };
            return Mautic.ajaxifyLink(this, event, extraData);
        });

        //initialize ajax'd modals
        mQuery(eventId + " a[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
            event.preventDefault();

            mQuery(this).data('form-data', {
                'modifiedSources': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedSources),
            });
            Mautic.ajaxifyModal(this, event);
        });

        mQuery(eventId).off('.eventbuttons')
            .on('mouseover.eventbuttons', function() {
                mQuery(this).find('.campaign-event-buttons').removeClass('hide');
            })
            .on('mouseout.eventbuttons', function() {
                mQuery(this).find('.campaign-event-buttons').addClass('hide');
            })
            .on('dblclick.eventbuttons', function(event) {
                event.preventDefault();
                mQuery(this).find('.btn-edit').first().click();
            });

        //initialize tooltipslist-campaign-event
        mQuery(eventId + " *[data-toggle='tooltip']").tooltip({html: true});
        if (autoConnect) {
            // Connect into last anchor clicked
            if (Mautic.campaignBuilderAnchorClicked.search('left') !== -1) {
                var source = domEventId + '_leadsourceright';
                var target = Mautic.campaignBuilderAnchorClicked;
            } else {
                var source = Mautic.campaignBuilderAnchorClicked;
                var target = domEventId + '_leadsourceleft';
            }

            Mautic.campaignBuilderInstance.connect({
                uuids: [
                    source,
                    target
                ]
            });
        }

        // If there are no other events, auto click add action
        if (!mQuery('.list-campaign-event').length) {
            mQuery('.jtk-endpoint_anchor_leadsource.'+domEventId).trigger('click');
        }
    }

    if(response.modifiedSources) {
        Mautic.campaignBuilderCampaignElements.modifiedSources = response.modifiedSources;
    }
    mQuery('.btns-builder').find('button').prop('disabled', false);
    Mautic.campaignBuilderInstance.repaintEverything();
};

/**
 * Update a connectors label
 *
 * @param domEventId
 */
Mautic.campaignBuilderUpdateLabel = function (domEventId) {
    var theLabel = typeof Mautic.campaignBuilderLabels[domEventId] == 'undefined' ? '' : Mautic.campaignBuilderLabels[domEventId];
    var currentConnections = Mautic.campaignBuilderInstance.select({
        target: domEventId
    });

    if (currentConnections.length > 0) {
        currentConnections.each(function(conn) {

            //remove current label
            var overlays = conn.getOverlays();
            if (overlays.length > 0) {
                for (var i = 0; i <= overlays.length; i++ ) {
                    if ( typeof overlays[i] != 'undefined' && overlays[i].type == 'Label') {
                        conn.removeOverlay(overlays[i].id);
                    }
                }
            }

            if (theLabel) {
                conn.addOverlay(["Label", {
                    label: theLabel,
                    location: 0.65,
                    cssClass: "jtk-label",
                    id: conn.sourceId + "_" + conn.targetId + "_connectionLabel"
                }]);
            }
        });
    }
};

/**
 * Launch campaign builder modal
 */
Mautic.launchCampaignEditor = function() {
    Mautic.stopIconSpinPostEvent();
    mQuery('body').css('overflow-y', 'hidden');

    mQuery('#campaign-builder').trigger('campaign-builder:show');

    // Center new source
    if (mQuery('#CampaignEvent_newsource').length) {
        Mautic.campaignBuilderPrepareNewSource();
    }

    if (Mautic.campaignBuilderCanvasSettings) {
        Mautic.campaignBuilderInstance.setSuspendDrawing(true);
        Mautic.campaignBuilderReconnectEndpoints();
        Mautic.campaignBuilderInstance.setSuspendDrawing(false, true);
    }
    Mautic.campaignBuilderInstance.repaintEverything();
};

/**
 * Launch campaign preview view
 */
Mautic.launchCampaignPreview = function() {
    Mautic.stopIconSpinPostEvent();

    if (Mautic.campaignBuilderCanvasSettings) {
        Mautic.campaignBuilderInstance.setSuspendDrawing(true);
        Mautic.campaignBuilderReconnectEndpoints();
        Mautic.campaignBuilderInstance.setSuspendDrawing(false, true);
    }
    Mautic.campaignBuilderInstance.repaintEverything();
};

/**
 *
 * @type {{source: {leadsource: {source: Array, action: [*], condition: [*], decision: [*]}, leadsourceleft: {source: [*], action: Array, condition: Array, decision: Array}, leadsourceright: {source: [*], action: Array, condition: Array, decision: Array}}, action: {top: {source: [*], action: Array, condition: [*], decision: [*]}, bottom: {source: Array, action: Array, condition: [*], decision: [*]}}, condition: {top: {source: [*], action: [*], condition: [*], decision: [*]}, yes: {source: Array, action: [*], condition: [*], decision: [*]}, no: {source: Array, action: [*], condition: [*], decision: [*]}}, decision: {top: {action: [*], source: [*], condition: [*], decision: Array}, yes: {source: Array, action: [*], condition: [*], decision: Array}, no: {source: Array, action: [*], condition: [*], decision: Array}}}}
 */
Mautic.campaignBuilderConnectionsMap = {
    // source
    'source': {
        // source anchors
        'leadsource': {
            // target
            'source': [],
            'action': ['top'], // target anchors
            'condition': ['top'],
            'decision': ['top'],
        },
        'leadsourceleft': {
            'source': ['leadsourceright'],
            'action': [],
            'condition': [],
            'decision': []
        },
        'leadsourceright': {
            'source': ['leadsourceleft'],
            'action': [],
            'condition': [],
            'decision': []
        }
    },
    'action': {
        'top': {
            'source': ['leadsource'],
            'action': ['bottom'],
            'condition': ['yes', 'no'],
            'decision': ['yes', 'no']
        },
        'bottom': {
            'source': [],
            'action': ['top'],
            'condition': ['top'],
            'decision': ['top']
        }
    },
    'condition': {
        'top': {
            'source': ['leadsource'],
            'action': ['bottom'],
            'condition': ['yes', 'no'],
            'decision': ['yes', 'no']
        },
        'yes': {
            'source': [],
            'action': ['top'],
            'condition': ['top'],
            'decision': ['top']
        },
        'no': {
            'source': [],
            'action': ['top'],
            'condition': ['top'],
            'decision': ['top']
        }
    },
    'decision': {
        'top': {
            'action': ['bottom'],
            'source': ['leadsource'],
            'condition': ['yes', 'no'],
            'decision': [],
        },
        'yes': {
            'source': [],
            'action': ['top'],
            'condition': ['top'],
            'decision': [],
        },
        'no': {
            'source': [],
            'action': ['top'],
            'condition': ['top'],
            'decision': [],
        }
    }
};

Mautic.campaignBuilderAnchorDefaultColor = 'var(--border-subtle)';

Mautic.campaignEndpointDefinitions = {
    'top': {
        anchors: [0.5, 0, 0, -1, 0, 0],
        isTarget: true
    },
    'bottom': {
        anchors: [0.5, 1, 0, 1, 0, 0],
        isTarget: false
    },
    'yes': {
        anchors: [0, 1, 0, 1, 30, 0],
        connectorColor: 'var(--support-success-inverse)',
        isTarget: false
    },
    'no': {
        anchors: [1, 1, 0, 1, -30, 0],
        connectorColor: 'var(--support-error-inverse)',
        isTarget: false
    },
    'leadSource': {
        anchors: [0.5, 1, 0, 1, 0, 0],
        isTarget: false
    },
    'leadSourceLeft': {
        anchors: [0, 0.5, -1, 0, -1, 0],
        connectorColor: '#fdb933',
        isTarget: true,
        connectorStyle: 'Straight'
    },
    'leadSourceRight': {
        anchors: [1, 0.5, 1, 0, 1, 0],
        connectorColor: '#fdb933',
        isTarget: false,
        connectorStyle: 'Straight'
    }
};

/**
 * Push callbacks to these events
 *
 * @type {{connection: Array, connectionDetached: Array, connectionMoved: Array, beforeDrop: Array}}
 */
Mautic.campaignConnectionCallbacks = {
    // sourceEndpoint, targetEndpoint, connection
    'beforeDetach': [],
    // sourceEndpoint, targetEndpoint, endpoint, source, sourceId
    'beforeDrag': [],
    // sourceEndpoint, targetEndpoint, endpoint, source, sourceId
    'beforeStartDetach': [],
    // sourceEndpoint, targetEndpoint, endpoint, source, sourceId, connection
    'beforeDrop': [],
    //sourceEndpoint, endpoint, event
    'onHover': [],
    // no arguments
    'beforeAnchorsRegistered': [],
    'afterAnchorsRegistered': [],
    'beforeEndpointsRegistered': [],
    'beforeEndpointsReconnected': [],
    'afterEndpointsReconnected': []
};

Mautic.campaignBuilderAnchorClicked = false;
Mautic.campaignBuilderEventPositions = {};

Mautic.prepareCampaignCanvas = function() {
    if (typeof Mautic.campaignBuilderInstance == 'undefined') {
        Mautic.campaignBuilderInstance = jsPlumb.getInstance({
            Container: document.querySelector("#CampaignCanvas")
        });

        Mautic.campaignEndpoints = {};

        var startingPosition;
        Mautic.campaignDragOptions = {
            start: function (params) {
                //double clicking activates the stop function so add a catch to prevent unnecessary ajax calls
                startingPosition =
                    {
                        top: params.el.offsetTop,
                        left: params.el.offsetLeft,
                    };

            },
            stop: function (params) {
                var endingPosition =
                    {
                        top: params.finalPos[0],
                        left: params.finalPos[1]
                    };

                if (startingPosition.left !== endingPosition.left || startingPosition.top !== endingPosition.top) {
                    //update coordinates
                    Mautic.campaignBuilderEventPositions[mQuery(params.el).attr('id')] = {
                        'left': parseInt(endingPosition.left),
                        'top': parseInt(endingPosition.top)
                    };

                    var campaignId = mQuery('#campaignId').val();
                    var query = "action=campaign:updateCoordinates&campaignId=" + campaignId + "&droppedX=" + endingPosition.top + "&droppedY=" + endingPosition.left + "&eventId=" + mQuery(params.el).attr('id');
                    mQuery.ajax({
                        url: mauticAjaxUrl,
                        type: "POST",
                        data: query,
                        dataType: "json",
                        error: function (request, textStatus, errorThrown) {
                            Mautic.processAjaxError(request, textStatus, errorThrown);
                        }
                    });
                }
            },
            containment:true
        };

        Mautic.campaignBuilderEventDimensions = {
            'width': 200,
            'height': 45,
            'anchor': 10,
            'wiggleWidth': 30,
            'wiggleHeight': 50
        };

        // Store labels
        Mautic.campaignBuilderLabels = {};

        // Update the labels on connection/disconnection
        Mautic.campaignBuilderInstance.bind("connection", function (info, originalEvent) {
            // Mark the connection so it can be removed if the form is cancelled
            Mautic.campaignBuilderConnectionRequiresUpdate = false;
            Mautic.campaignBuilderLastConnection           = info.connection;

            // If there is a switch between active/inactive anchors, reload the form
            var epDetails          = Mautic.campaignBuilderGetEndpointDetails(info.sourceEndpoint);
            var targetElementId    = info.targetEndpoint.elementId;

            var previousConnection = mQuery('#'+targetElementId).attr('data-connected');
            var editButton         = mQuery('#'+targetElementId).find('a.btn-edit');
            var editUrl            = editButton.attr('href');

            if (editUrl) {
                var anchorQueryParams = 'anchor=' + epDetails.anchorName + "&anchorEventType=" + epDetails.eventType;
                if (editUrl.search('anchor=') !== -1) {
                    editUrl.replace(/anchor=(.*?)$/, anchorQueryParams);
                } else {
                    var delimiter = (editUrl.indexOf('?') === -1) ? '?' : '&';
                    editUrl = editUrl + delimiter + anchorQueryParams;
                }
                editButton.attr('data-href', editUrl);

                if (previousConnection && previousConnection != epDetails.anchorName && (previousConnection == 'no' || epDetails.anchorName == 'no')) {
                    editButton.attr('data-prevent-dismiss', true);
                    Mautic.campaignBuilderConnectionRequiresUpdate = true;

                    editButton.trigger('click');
                }
            }

            mQuery('#'+targetElementId).attr('data-connected', epDetails.anchorName);

            Mautic.campaignBuilderUpdateLabel(info.connection.targetId);
            info.targetEndpoint.setPaintStyle(
                {
                    fill: info.connection.getPaintStyle().stroke
                }
            );
            info.sourceEndpoint.setPaintStyle(
                {
                    fill: info.connection.getPaintStyle().stroke
                }
            );
        });

        Mautic.campaignBuilderInstance.bind("connectionDetached", function (info, originalEvent) {
            Mautic.campaignBuilderUpdateLabel(info.connection.targetId);

            info.targetEndpoint.setPaintStyle(
                {
                    fill: "#d5d4d4"
                }
            );

            var currentConnections = info.sourceEndpoint.connections.length;
            // JavaScript counts index which still accounts for old connection
            currentConnections -= 1;
            if (!currentConnections) {
                info.sourceEndpoint.setPaintStyle(
                    {
                        fill: "#d5d4d4"
                    }
                );
            }
        });

        Mautic.campaignBuilderInstance.bind("connectionMoved", function (info, originalEvent) {
            Mautic.campaignBuilderUpdateLabel(info.connection.originalTargetId);

            info.originalTargetEndpoint.setPaintStyle(
                {
                    fill: "#d5d4d4"
                }
            );

            Mautic.campaignBuilderUpdateLabel(info.connection.newTargetId);

            info.newTargetEndpoint.setPaintStyle(
                {
                    fill: info.newSourceEndpoint.getPaintStyle().fill
                }
            );
        });

        mQuery('.builder-content').scroll(function () {
            Mautic.campaignBuilderInstance.repaintEverything();
        });

        mQuery.each(Mautic.campaignConnectionCallbacks.beforeEndpointsRegistered, function (index, callback) {
            callback();
        });
        mQuery.each(Mautic.campaignEndpointDefinitions, function (ep, definition) {
            Mautic.campaignBuilderRegisterEndpoint(ep, definition);
        });

        //manually loop through each so a UUID can be set for reconnecting connections
        mQuery.each(Mautic.campaignConnectionCallbacks.beforeAnchorsRegistered, function (index, callback) {
            callback();
        });

        mQuery("#CampaignCanvas div[data-event-id]").each(function () {
            var event = Mautic.campaignBuilderCanvasEvents[mQuery(this).data('eventId')];

            Mautic.campaignBuilderRegisterAnchors(Mautic.getAnchorsForEvent(event), this);
        });

        mQuery("#CampaignCanvas div.list-campaign-event.list-campaign-source").not('#CampaignEvent_newsource').not('#CampaignEvent_newsource_hide').each(function () {
            Mautic.campaignBuilderRegisterAnchors(['bottom'], this);
        });

        mQuery("#CampaignCanvas div.list-campaign-leadsource").not('#CampaignEvent_newsource').not('#CampaignEvent_newsource_hide').each(function () {
            Mautic.campaignBuilderRegisterAnchors(['leadSource', 'leadSourceLeft', 'leadSourceRight'], this);
        });

        mQuery.each(Mautic.campaignConnectionCallbacks.afterAnchorsRegistered, function (index, callback) {
            callback();
        });

        if (mQuery('.preview').length) {
            Mautic.launchCampaignPreview();
        } else {
            //enable drag and drop
            Mautic.campaignBuilderInstance.draggable(
                document.querySelectorAll("#CampaignCanvas .draggable"),
                Mautic.campaignDragOptions
            );
        }
    }
};

/**
 * Validate a connection before it can be made
 *
 * @param params
 * @returns {boolean}
 */
Mautic.campaignBeforeDropCallback = function(params) {
    var sourceEndpoint = Mautic.campaignBuilderGetEndpointDetails(params.connection.endpoints[0]);
    var targetEndpoint = Mautic.campaignBuilderGetEndpointDetails(params.dropEndpoint);

    var callbackAllowed = null;
    mQuery.each(Mautic.campaignConnectionCallbacks.beforeDrop, function(index, callback) {
        var result = callback(sourceEndpoint, targetEndpoint, params);
        if (null !== result) {
            callbackAllowed = result;

            return false;
        }
    });
    if (null !== callbackAllowed) {
        return callbackAllowed;
    }

    if (!Mautic.campaignBuilderValidateConnection(sourceEndpoint, targetEndpoint.eventType, targetEndpoint.event)){

        return false;
    }

    if (mQuery.inArray(targetEndpoint.anchorName, ['top', 'leadsourceleft', 'leadsourceright'])) {
        //ensure two events aren't looping
        var sourceConnections = Mautic.campaignBuilderInstance.select({
            source: params.targetId
        });

        var loopDetected = false;
        sourceConnections.each(function (conn) {

            if (conn.sourceId == targetEndpoint.elementId && conn.targetId == sourceEndpoint.elementId) {
                loopDetected = true;

                return false;
            }
        });
    }

    //ensure that the connections are not looping back into the same event
    if (params.sourceId == params.targetId) {

        return false;
    }

    // ensure the map allows this connection
    var allowedConnections = Mautic.campaignBuilderConnectionsMap[sourceEndpoint.eventType][sourceEndpoint.anchorName][targetEndpoint.eventType];

    var allowed = mQuery.inArray(targetEndpoint.anchorName, allowedConnections) !== -1;

    if (allowed) {
        //ensure that a top only has one connection at a time unless connected from a source
        if (params.dropEndpoint.connections.length > 0) {

            // Replace the connection
            mQuery.each(params.dropEndpoint.connections, function(key, conn) {
                Mautic.campaignBuilderInstance.deleteConnection(conn);
            });
        }
    }

    return allowed;

};

/**
 * Process beforeDetach event callbacks
 *
 * @param connection
 * @returns {*}
 */
Mautic.campaignBeforeDetachCallback = function(connection) {
    var sourceEndpoint = Mautic.campaignBuilderGetEndpointDetails(connection.sourceId);
    var targetEndpoint = Mautic.campaignBuilderGetEndpointDetails(connection.targetId);

    var callbackAllowed = null;
    mQuery.each(Mautic.campaignConnectionCallbacks.beforeDetach, function (index, callback) {
        var result = callback(sourceEndpoint, targetEndpoint, connection);
        if (null !== result) {
            callbackAllowed = result;

            return false;
        }
    });

    if (null !== callbackAllowed) {
        return callbackAllowed;
    }

    return true;
};

/**
 * Process beforeDetach event callbacks
 *
 * @param connection
 */
Mautic.campaignBeforeDragCallback = function(endpoint, source, sourceId) {
    var sourceEndpoint = Mautic.campaignBuilderGetEndpointDetails(sourceId);
    var targetEndpoint = Mautic.campaignBuilderGetEndpointDetails(endpoint);

    var callbackAllowed = null;
    mQuery.each(Mautic.campaignConnectionCallbacks.beforeDrag, function (index, callback) {
        var result = callback(sourceEndpoint, targetEndpoint, endpoint, source, sourceId);
        if (null !== result) {
            callbackAllowed = result;

            return false;
        }
    });

    if (null !== callbackAllowed) {
        return callbackAllowed;
    }

    return true;
};

/**
 * Process beforeDetach event callbacks
 *
 * @param endpoint
 * @param source
 * @param sourceId
 * @param connection
 * @returns {*}
 */
Mautic.campaignBeforeStartDetachCallback = function(endpoint, source, sourceId, connection) {
    var sourceEndpoint = Mautic.campaignBuilderGetEndpointDetails(sourceId);
    var targetEndpoint = Mautic.campaignBuilderGetEndpointDetails(endpoint);

    var callbackAllowed = null;
    mQuery.each(Mautic.campaignConnectionCallbacks.beforeStartDetach, function (index, callback) {
        var result = callback(sourceEndpoint, targetEndpoint, endpoint, source, sourceId, connection);
        if (null !== result) {
            callbackAllowed = result;

            return false;
        }
    });

    if (null !== callbackAllowed) {
        return callbackAllowed;
    }

    return true;
};

/**
 * Adds an event to the deletedEvents array in campaignBuilderCampaignElements
 *
 * @param {string} eventId - The ID of the event to delete
 * @param {string|null} redirectEventId - Optional ID of the event to redirect to
 */
Mautic.addEventToDeletedEvents = function(eventId, redirectEventId) {
    if (!Mautic.campaignBuilderCampaignElements.deletedEvents) {
        Mautic.campaignBuilderCampaignElements.deletedEvents = [];
    }
    // Check if this event is already in the deletedEvents array
    let exists = false;
    for (const event of Mautic.campaignBuilderCampaignElements.deletedEvents) {
        if (event.id === eventId) {
            exists = true;
            break;
        }
    }
    // Add to deletedEvents if it doesn't exist yet
    if (!exists) {
        Mautic.campaignBuilderCampaignElements.deletedEvents.push({
            id: eventId,
            redirectEvent: redirectEventId
        });
    }
};

/**
 * Process beforeDetach event callbacks
 *
 * @param connection
 */
Mautic.campaignHoverCallback = function(sourceEndpoint, endpoint, event) {
    var callbackAllowed = null;
    mQuery.each(Mautic.campaignConnectionCallbacks.onHover, function (index, callback) {
        var result = callback(sourceEndpoint, endpoint, event);
        if (null !== result) {
            callbackAllowed = result;

            return false;
        }
    });

    if (null !== callbackAllowed) {
        return callbackAllowed;
    }

    return true;
};

/**
 * Enable/Disable timeframe settings if the toggle for immediate trigger is changed
 */
Mautic.campaignToggleTimeframes = function() {
    const triggerModes = {
        immediate: mQuery('#campaignevent_triggerMode_0').prop('checked'),
        interval: mQuery('#campaignevent_triggerMode_1').prop('checked'),
        date: mQuery('#campaignevent_triggerMode_2').prop('checked'),
        optimal: mQuery('#campaignevent_triggerMode_3').prop('checked')
    };

    if (!mQuery('#campaignevent_triggerMode_2').length) {
        triggerModes.date = triggerModes.interval;
        triggerModes.interval = triggerModes.immediate;
        triggerModes.immediate = false;
    }

    if (mQuery('#campaignevent_triggerInterval').length) {
        mQuery('#triggerInterval').toggleClass('hide', !triggerModes.interval);
        mQuery('#triggerDate').toggleClass('hide', !triggerModes.date);
        mQuery('#triggerOptimized').toggleClass('hide', !triggerModes.optimal);
    }
};

/**
 * Close campaign builder
 */
Mautic.closeCampaignBuilder = function() {
    // Disable buttons
    mQuery('.btns-builder').find('button').prop('disabled', true);
    var builderCss = {
        margin: "0",
        padding: "0",
        border: "none",
        width: "100%",
        height: "100%"
    };

    var panelHeight = (mQuery('.builder-content').css('right') == '0px') ? mQuery('.builder-panel').height() : 0,
        panelWidth = (mQuery('.builder-content').css('right') == '0px') ? 0 : mQuery('.builder-panel').width(),
        spinnerLeft = (mQuery(window).width() - panelWidth - 60) / 2,
        spinnerTop = (mQuery(window).height() - panelHeight - 60) / 2;

    var overlay = mQuery('<div id="builder-overlay" class="modal-backdrop fade in"><div style="position: absolute; top:' + spinnerTop + 'px; left:' + spinnerLeft + 'px" class=".builder-spinner"><i class="ri-loader-3-line ri-spin ri-5x"></i></div></div>').css(builderCss).appendTo('.builder-content');

    mQuery('#builder-errors span').text('');
    mQuery('#builder-errors').hide('fast');

    Mautic.updateConnections(function(err, response) {
        mQuery('body').css('overflow-y', '');

        if (!err) {
            mQuery('#builder-overlay').remove();
            mQuery('body').css('overflow-y', '');
            if (response.success) {
                mQuery('#campaign-builder').trigger('campaign-builder:hide');
                // Enable buttons
                mQuery('.btns-builder').find('button').prop('disabled', false);
            }
        }
    });
    mQuery('#campaign_campaignElements').val(JSON.stringify(Mautic.campaignBuilderCampaignElements));
};

Mautic.saveCampaignFromBuilder = function() {
    // Disable buttons
    mQuery('.btns-builder').find('button').prop('disabled', true);
    Mautic.activateButtonLoadingIndicator(mQuery('.btn-apply-builder'));
    Mautic.updateConnections(function(err) {
        if (!err) {
            var applyBtn = mQuery('.btn-apply');
            mQuery('#campaign_campaignElements').val(JSON.stringify(Mautic.campaignBuilderCampaignElements));
            Mautic.inBuilderSubmissionOn(applyBtn.closest('form'));
            applyBtn.trigger('click');
            Mautic.inBuilderSubmissionOff();

            // Trigger an event for components to listen to
            mQuery(document).trigger('mauticCampaignBuilderCanvasLoaded');

            // Call our handler initialization function
            Mautic.ensureCampaignEventHandlers();
        }
    });
};

Mautic.updateConnections = function(callback) {
    var nodes = [];

    mQuery("#CampaignCanvas .list-campaign-event").each(function (idx, elem) {
        nodes.push({
            id:        mQuery(elem).attr('id').replace('CampaignEvent_', ''),
            positionX: parseInt(mQuery(elem).css('left'), 10),
            positionY: parseInt(mQuery(elem).css('top'), 10)
        });
    });

    mQuery("#CampaignCanvas .list-campaign-source").not('#CampaignEvent_newsource').not('#CampaignEvent_newsource_hide').each(function (idx, elem) {
        nodes.push({
            id:        mQuery(elem).attr('id').replace('CampaignEvent_', ''),
            positionX: parseInt(mQuery(elem).css('left'), 10),
            positionY: parseInt(mQuery(elem).css('top'), 10)
        });
    });

    var connections = [];
    mQuery.each(Mautic.campaignBuilderInstance.getConnections(), function (idx, connection) {
        connections.push({
            sourceId:     connection.sourceId.replace('CampaignEvent_', ''),
            targetId:     connection.targetId.replace('CampaignEvent_', ''),
            anchors:      mQuery.map(connection.endpoints, function (endpoint) {
                var anchor = Mautic.campaignBuilderGetEndpointDetails(endpoint);
                return {
                    'endpoint': anchor.anchorName,
                    'eventId':  anchor.eventId
                };
            })
        });
    });

    var chart          = {};
    chart.nodes        = nodes;
    chart.connections  = connections;
    var canvasSettings = {canvasSettings: chart};

    Mautic.campaignBuilderCampaignElements.canvasSettings = chart;
    var campaignId     = mQuery('#campaignId').val();
    var query          = "action=campaign:updateConnections&campaignId=" + campaignId;

    mQuery.ajax({
        url: mauticAjaxUrl + '?' + query,
        type: "POST",
        data: canvasSettings,
        dataType: "json",
        success: function (response) {
            if (typeof callback === 'function') callback(false, response);
        },
        error: function (response, textStatus, errorThrown) {
            Mautic.processAjaxError(response, textStatus, errorThrown);
            if (typeof callback === 'function') callback(true, response);
        }
    });
};

/**
 * Cancel the campaign event form
 */
Mautic.cancelCampaignEvent = function(e) {
    e.preventDefault();

    const extraData = {
        'modifiedEvents': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents),
    };
    Mautic.postForm(mQuery('form[name="campaignevent"]'), function (response) {
        Mautic.processModalContent(response, '#' + response.modalId);
    }, extraData);
};

/**
 * Submit the campaign event form
 */
Mautic.submitCampaignEvent = function(e) {
    e.preventDefault();

    mQuery('#campaignevent_canvasSettings_droppedX').val(mQuery('#droppedX').val());
    mQuery('#campaignevent_canvasSettings_droppedY').val(mQuery('#droppedY').val());

    // Disable save and apply buttton
    mQuery('.btns-builder').find('button').prop('disabled', true);
    // Get number of running ajax
    const runningAjax = mQuery.active;

    Mautic.refreshModifiedEvents(mQuery('form[name="campaignevent"]'), '#CampaignEventModal');

    const waitForElement = function (){
        if(mQuery.active <= runningAjax){
            mQuery('.btns-builder').find('button').prop('disabled', false);
        }
        else{
            setTimeout(waitForElement, 100);
        }
    }
    // When Ajax finish enable buttons
    waitForElement();
};

Mautic.refreshModifiedEvents = function(form, target) {
    const extraData = {
        'modifiedEvents': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents),
    };
    Mautic.postForm(form, function (response) {
        if(response.modifiedEvents) {
            Mautic.campaignBuilderCampaignElements.modifiedEvents = { ...Mautic.campaignBuilderCampaignElements.modifiedEvents, ...response.modifiedEvents };
        }
        Mautic.processModalContent(response, target);
    }, extraData);
};

/**
 * Submit source form
 * @param e
 */
Mautic.submitCampaignSource = function(e) {
    e.preventDefault();

    mQuery('#campaign_leadsource_droppedX').val(mQuery('#droppedX').val());
    mQuery('#campaign_leadsource_droppedY').val(mQuery('#droppedY').val());

    Mautic.refreshModifiedSources(mQuery('form[name="campaign_leadsource"]'), '#CampaignEventModal');
};

Mautic.refreshModifiedSources = function(form, target) {
    const extraData = {
        'modifiedSources': JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedSources),
    };
    Mautic.postForm(form, function (response) {
        if(response.modifiedSources) {
            Mautic.campaignBuilderCampaignElements.modifiedSources = { ...Mautic.campaignBuilderCampaignElements.modifiedSources, ...response.modifiedSources };
        }
        Mautic.processModalContent(response, target);
    }, extraData);
};

/**
 * Reconnect jsplumb connections
 */
Mautic.campaignBuilderReconnectEndpoints = function () {

    mQuery.each(Mautic.campaignConnectionCallbacks.beforeEndpointsReconnected, function (index, callback) {
        callback();
    });

    if (typeof Mautic.campaignBuilderCanvasSettings == 'undefined') {
        return;
    }

    if (typeof Mautic.campaignBuilderCanvasSettings.nodes !== 'undefined') {
        // Reposition events
        var sourceFound = false;
        mQuery.each(Mautic.campaignBuilderCanvasSettings.nodes, function (key, node) {
            if (typeof Mautic.campaignBuilderCanvasSources[node.id] !== 'undefined') {
                sourceFound = true;
            }

            mQuery('#CampaignEvent_' + node.id).css({
                position: 'absolute',
                left: node.positionX + 'px',
                top: node.positionY + 'px'
            });

            Mautic.campaignBuilderEventPositions['CampaignEvent_' + node.id] = {
                left: parseInt(node.positionX),
                top: parseInt(node.positionY)
            };
        });
    }

    if (typeof Mautic.campaignBuilderCanvasSettings.connections !== 'undefined') {

        // Recreate jsPlumb connections and labels
        mQuery.each(Mautic.campaignBuilderCanvasSettings.connections, function (key, connection) {
            if (typeof Mautic.campaignBuilderCanvasEvents[connection.targetId] !== 'undefined') {
                var targetEvent = Mautic.campaignBuilderCanvasEvents[connection.targetId];
            } else if (typeof Mautic.campaignBuilderCanvasSources[connection.targetId] !== 'undefined') {
                var targetEvent = Mautic.campaignBuilderCanvasSources[connection.targetId];
            }

            if (targetEvent && targetEvent.label) {
                Mautic.campaignBuilderLabels["CampaignEvent_" + connection.targetId] = targetEvent.label;
            }

            Mautic.campaignBuilderInstance.connect({
                uuids: [
                    "CampaignEvent_" + connection.sourceId + '_' + connection.anchors.source,
                    "CampaignEvent_" + connection.targetId + '_' + connection.anchors.target
                ]
            });
        });
    }

    if (!sourceFound) {
        var topOffset = 25;
        mQuery.each(Mautic.campaignBuilderCanvasSources, function (type, source) {
            mQuery('#CampaignEvent_' + type).css({
                position: 'absolute',
                left: '20px',
                top: topOffset + 'px'
            });
        });

        topOffset += 45;
    }

    mQuery.each(Mautic.campaignConnectionCallbacks.afterEndpointsReconnected, function (index, callback) {
        callback();
    });

    delete Mautic.campaignBuilderCanvasSettings;
};

/**
 * Register an endpoint with JsPlumb
 *
 * @param name
 * @param params
 */
Mautic.campaignBuilderRegisterEndpoint = function (name, params) {
    var isTarget, isSource, color, connectorColor, connectorStyle;
    if (params.color) {
        color = params.color;
    } else {
        color = Mautic.campaignBuilderAnchorDefaultColor;
    }

    if (params.connectorColor) {
        connectorColor = params.connectorColor;
    } else {
        connectorColor = color;
    }

    if (params.connectorStyle) {
        connectorStyle = params.connectorStyle;
    } else {
        connectorStyle = ["Bezier", {curviness: 25}];
    }

    isTarget = params.isTarget;
    isSource = true;
    if (isTarget === null) {
        // Both are allowed
        isTarget = true;
    } else {
        if (typeof isTarget == 'undefined') {
            isTarget = false;
        }
        if (isTarget) {
            isSource = false;
        }
    }

    Mautic.campaignEndpoints[name] = {
        endpoint: ["Dot", { radius: 10 }],
        paintStyle: {
            fill: color
        },
        endpointStyle: {
          fill: color
        },
        connectorStyle: {
            stroke: connectorColor,
            strokeWidth: 2
        },
        connector: connectorStyle,
        connectorOverlays: [],
        maxConnections: -1,
        isTarget: isTarget,
        isSource: isSource,
        beforeDrop: Mautic.campaignBeforeDropCallback,
        beforeDetach: Mautic.campaignBeforeDetachCallback,
        beforeStartDetach: Mautic.campaignBeforeStartDetachCallback,
        beforeDrag: Mautic.campaignBeforeDragCallback
    }
};

/**
 * Register an anchor with JsPlumb
 *
 * @param names
 * @param el
 */
Mautic.campaignBuilderRegisterAnchors = function(names, el) {
    var id = mQuery(el).attr('id');

    mQuery(names).each(function(key, anchorName) {
        var theAnchor = Mautic.campaignEndpointDefinitions[anchorName]['anchors'];
        theAnchor[6] = anchorName.toLowerCase() + ' ' + id;
        var ep = Mautic.campaignBuilderInstance.addEndpoint(
            id,
            {
                anchor: theAnchor,
                uuid: id + "_" + anchorName.toLowerCase()
            },
            Mautic.campaignEndpoints[anchorName]
        );

        ep.bind("mouseover", function (endpoint, event) {
            var epDetails = Mautic.campaignBuilderGetEndpointDetails(endpoint);

            if (!Mautic.campaignHoverCallback(epDetails, endpoint, event)) {
                return;
            }

            if (epDetails.anchorName == 'top') {
                // Don't add a plus sign to a top anchor
                return;
            }

            // If this is a leadsourceleft or leadsourceright anchor - ensure there are source options available before allowing to add a new
            if (epDetails.anchorName == 'leadsourceleft' || epDetails.anchorName == 'leadsourceright') {
                if (mQuery('#SourceList option:enabled').length === 1) {
                    // Accounts for empty option

                    return;
                }
            }

            // Set color style
            endpoint.setPaintStyle(
                {
                    fill: endpoint.connectorStyle.stroke
                }
            );

            var dot = mQuery(endpoint.canvas);
            dot.addClass('jtk-clickable_anchor');

            if (!dot.find('svg text').length) {
                // Add a plus sign to SVG
                var svg = dot.find('svg')[0];

                var textElement = document.createElementNS("http://www.w3.org/2000/svg", 'text');
                textElement.setAttributeNS(null, 'x', '50%');
                textElement.setAttributeNS(null, 'y', '50%');
                textElement.setAttributeNS(null, 'text-anchor', 'middle');
                textElement.setAttributeNS(null, 'stroke-width', '1px');
                textElement.setAttributeNS(null, 'stroke', '#ffffff');
                textElement.setAttributeNS(null, 'dy', '.3em');

                var textNode = document.createTextNode('+');
                textElement.appendChild(textNode);
                svg.appendChild(textElement);
            }
        });

        ep.bind("mouseout", function (endpoint) {
            var dot = mQuery(endpoint.canvas);
            dot.removeClass('jtk-clickable_anchor');

            if (!endpoint.connections.length) {
                // Set color style
                endpoint.setPaintStyle(
                    {
                        fill: Mautic.campaignBuilderAnchorDefaultColor
                    }
                );

            }
        });

        ep.bind("click", function (endpoint, event) {
            if (mQuery('#CampaignEvent_newsource').length) {
                // Can't do anything until a new source is added

                return;
            }

            var epDetails = Mautic.campaignBuilderGetEndpointDetails(endpoint);
            if (epDetails.anchorName == 'top') {
                // Don't do anything for top anchors

                return;
            }

            // If this is a leadsourceleft or leadsourceright anchor - ensure there are source options available before allowing to add a new
            if (epDetails.anchorName == 'leadsourceleft' || epDetails.anchorName == 'leadsourceright') {
                if (mQuery('#SourceList option:enabled').length === 1) {
                    // Accounts for empty option

                    return;
                }
            }

            // Note the anchor so it can be auto attached after the event is created
            var epDetails = Mautic.campaignBuilderGetEndpointDetails(endpoint);
            var clickedAnchorName = epDetails.anchorName;
            Mautic.campaignBuilderAnchorClicked = endpoint.elementId+'_'+clickedAnchorName;
            Mautic.campaignBuilderAnchorNameClicked = clickedAnchorName;
            Mautic.campaignBuilderAnchorEventTypeClicked = epDetails.eventType;

            // Get the position of the event
            var elPos = Mautic.campaignBuilderGetEventPosition(endpoint.element);
            var spotFound = false,
                putLeft = elPos.left,
                putTop = elPos.top,
                direction = '', // xl, xr, yu, yd
                fullWidth = Mautic.campaignBuilderEventDimensions.width + Mautic.campaignBuilderEventDimensions.anchor,
                wiggleWidth = fullWidth + Mautic.campaignBuilderEventDimensions.wiggleWidth,
                fullHeight = Mautic.campaignBuilderEventDimensions.height + Mautic.campaignBuilderEventDimensions.anchor,
                wiggleHeight = fullHeight + Mautic.campaignBuilderEventDimensions.wiggleHeight,
                debug = false;

            if (debug) {
                console.log(Mautic.campaignBuilderEventPositions);
                console.log(clickedAnchorName+' - starting with: x = '+ putLeft+', y = '+putTop);
            }

            switch (clickedAnchorName) {
                case 'leadsourceleft':
                    direction = 'xl';
                    putLeft -= wiggleWidth;
                    break;
                case 'leadsourceright':
                    direction = 'xr';
                    putLeft += wiggleWidth;
                    break;
                case 'bottom':
                    direction = 'yd';
                    putTop += wiggleHeight;
                    break;
                case 'yes':
                case 'leadsource':
                    // Place slightly to the left of the anchor
                    putLeft -= Mautic.campaignBuilderEventDimensions.width/2;
                    putTop  += wiggleHeight;
                    direction = 'xl';
                    break;
                case 'no':
                    // Place slightly to the left of the anchor
                    putLeft += Mautic.campaignBuilderEventDimensions.width/2;
                    putTop  += wiggleHeight;
                    direction = 'xr';
                    break;
                case 'top':
                    directon = 'yu';
                    putTop -= wiggleHeight;
                    break;
            }

            if (debug) {
                console.log('Going direction: '+direction);
                console.log('Start test with: x = '+ putLeft+', y = '+putTop);

            }

            var counter = 0;
            var windowWidth = mQuery(window).width();
            while (!spotFound) {
                // Find out if spot is taken
                var isOccupied = false;
                mQuery.each(Mautic.campaignBuilderEventPositions, function (id, pos) {
                    var l = Math.max(putLeft, pos.left);
                    var r = Math.min(putLeft + fullWidth, pos.left + fullWidth);
                    var b = Math.max(putTop, pos.top);
                    var t = Math.min(putTop + fullHeight, pos.top + fullHeight);
                    var h = t-b;
                    var w = r-l;
                    if (debug) {
                        console.log('Checking ' + id);
                        console.log(putLeft, putTop, l,r,b,t,h,w);
                    }

                    if (h > 0 && w > 0) {
                        if (debug) {
                            console.log('Slot occupied by '+id);
                        }

                        isOccupied = true;

                        // xl, xr, yu, yd
                        switch (direction) {
                            case 'xl':
                                putLeft -= (w + Mautic.campaignBuilderEventDimensions.wiggleWidth);
                                if (putLeft <= 0) {
                                    putLeft = 0;
                                    // Ran out of room so go down
                                    direction = 'yd';
                                    putTop += fullHeight + Mautic.campaignBuilderEventDimensions.wiggleHeight;
                                }
                                break;
                            case 'xr':
                                if (putLeft + w + Mautic.campaignBuilderEventDimensions.wiggleWidth > windowWidth) {
                                    // Hit right canvas so start going down by default
                                    direction = 'yd';
                                    putLeft -= Mautic.campaignBuilderEventDimensions.wiggleWidth;
                                    putTop += fullHeight + Mautic.campaignBuilderEventDimensions.wiggleHeight;
                                } else {
                                    putLeft += (w + Mautic.campaignBuilderEventDimensions.wiggleWidth);
                                }
                                break;
                            case 'yu':
                                putTop -= (h - Mautic.campaignBuilderEventDimensions.wiggleHeight);
                                if (putTop <= 0) {
                                    putTop = 0;
                                    // Ran out of room going up so try the right
                                    direction = 'xr';
                                }
                                break;
                            case 'yd':
                                putTop += (h + Mautic.campaignBuilderEventDimensions.wiggleHeight);
                                break;
                        }

                        return false
                    }
                });

                if (!isOccupied) {
                    if (debug) {
                       console.log('It fits!');
                    }

                    spotFound = true;
                }

                counter++;
                if (counter >= 100) {
                    putTop = 10;
                    putLeft = 10;
                    if (debug) {
                        console.log('Too many loops');
                    }
                    spotFound = true;
                }
            }

            if (debug) {
                console.log('To be placed at: x = '+ putLeft+', y = '+putTop);
            }

            if (putLeft <= 0) {
                putLeft = 10;
            }
            if (putTop <= 0) {
                putTop = 10;
            }

            // Mark the spot
            mQuery('#droppedX').val(putLeft);
            mQuery('#droppedY').val(putTop);

            // Update the event selector
            var allowedEvents = [];
            mQuery.each(Mautic.campaignBuilderConnectionsMap[epDetails.eventType][epDetails.anchorName], function (group, eventTypes) {
                if (eventTypes.length) {
                    allowedEvents[allowedEvents.length] = group.charAt(0).toUpperCase() + group.substr(1);
                }
            });
            Mautic.campaignBuilderAnchorClickedAllowedEvents = allowedEvents;
            Mautic.campaignBuilderIsEventCloneAllowed = Mautic.isCampaignCloneEventAllowedForEndpoint(epDetails);

            if (!(mQuery('.preview').length)) {
                var el = (mQuery(event.target).hasClass('jtk-endpoint')) ? event.target : mQuery(event.target).parents('.jtk-endpoint')[0];
                Mautic.campaignBuilderAnchorClickedPosition = Mautic.campaignBuilderGetEventPosition(el);
                Mautic.campaignBuilderUpdateEventList(allowedEvents, false, 'groups');
                Mautic.campaignBuilderUpdateEventCloneButton(allowedEvents, epDetails.eventType, epDetails.anchorName);
                Mautic.campaignBuilderUpdateEventCloneDescription();
            }

            // Disable the list items not allowed
            mQuery('.campaign-event-selector:not(#SourceList) option').prop('disabled', false);
            if ('source' == epDetails.eventType) {
                var checkSelects = ['action', 'decision', 'condition'];
            } else {
                var primaryType       = (epDetails.eventType === 'decision') ? 'action': 'decision';
                var checkSelects = [primaryType, 'condition'];
            }

            mQuery.each(checkSelects, function(key, targetType) {
                var selectId = '#' + targetType.charAt(0).toUpperCase() + targetType.slice(1) + 'List';
                mQuery(selectId + ' option').each(function () {
                    var optionVal = mQuery(this).val();
                    if (optionVal) {
                        if (!Mautic.campaignBuilderValidateConnection(epDetails, targetType, optionVal)) {
                            mQuery(this).prop('disabled', true);
                        }
                    }
                });
                mQuery(selectId).trigger('chosen:updated');
            });
        });
    });
};

/**
 * Extract information about an event/endpoint from element
 *
 * @param el
 * @returns {{left: Number, top: Number}}
 */
Mautic.campaignBuilderGetEventPosition = function(el) {
    return {
        'left': parseInt(mQuery(el).css('left')),
        'top': parseInt(mQuery(el).css('top'))
    }
};

/**
 * Update the event select list
 *
 * @param groups
 * @param hidden
 * @param view
 * @param active
 * @param forcePosition
 */
Mautic.campaignBuilderUpdateEventList = function (groups, hidden, view, active, forcePosition) {
    var groupsEnabled = 0;
    var inGroupsView = ('groups' == view);

    if (groups.length === 1 && mQuery.inArray('Source', groups) !== -1 && !hidden) {
        // Force groups mode
        inGroupsView = false;
    }
    mQuery.each(['Source', 'Action', 'Decision', 'Condition'], function (key, theGroup) {
        if (mQuery.inArray(theGroup, groups) !== -1) {
            if (inGroupsView) {
                mQuery('#' + theGroup + 'GroupSelector').removeClass('hide');

                if ('source' != theGroup) {
                    groupsEnabled++;
                }
            } else {
                mQuery('#' + theGroup + 'GroupList').removeClass('hide');
            }
        } else {
            if (inGroupsView) {
                mQuery('#' + theGroup + 'GroupSelector').addClass('hide');
            } else {
                mQuery('#' + theGroup + 'GroupList').addClass('hide');
            }
        }
    });

    mQuery('#CampaignEventPanelGroups').removeClass('groups-enabled-1 groups-enabled-2 groups-enabled-3')
    mQuery('#CampaignEventPanelGroups').addClass('groups-enabled-' + groups.length)

    if (inGroupsView) {
        mQuery.each(groups, function (key, theGroup) {
            mQuery('#'+theGroup+'GroupSelector').removeClass(
                function (index, css) {
                    return (css.match(/col-(\S+)/g) || []).join(' ');
                }
            ).addClass('col-md-' + (12 / groupsEnabled));
        });

        var newWidth = (500 / 3) * groupsEnabled;
        if (newWidth >= mQuery(window).width()) {
            newWidth = mQuery(window).width() - 10;
        }

        var leftPos = (forcePosition) ? forcePosition.left : Mautic.campaignBuilderAnchorClickedPosition.left - (newWidth / 2 - 10);
        var topPos  = (forcePosition) ? forcePosition.top : Mautic.campaignBuilderAnchorClickedPosition.top + 25;
        mQuery('#CampaignEventPanel').css({
                left: (leftPos >=0 ) ? leftPos : 10,
                top: topPos,
                width: newWidth,

            });

        mQuery('#CampaignEventPanel').removeClass('hide');
        mQuery('#CampaignEventPanelGroups').removeClass('hide');
        mQuery('#CampaignEventPanelLists').addClass('hide');

        if (Mautic.campaignBuilderIsEventCloneAllowed) {
            mQuery('#CampaignPasteContainer').removeClass('hide');
        } else {
            mQuery('#CampaignPasteContainer').addClass('hide');
        }

    } else {
        var leftPos = (forcePosition) ? forcePosition.left : Mautic.campaignBuilderAnchorClickedPosition.left - 125;
        var topPos  = (forcePosition) ? forcePosition.top : Mautic.campaignBuilderAnchorClickedPosition.top + 25;
        mQuery('#CampaignEventPanel').css({
            left: (leftPos >= 0) ? leftPos : 10,
            top: topPos,
            width: 300,
        });

        mQuery('#CampaignEventPanelGroups').addClass('hide');
        mQuery('#CampaignEventPanelLists').removeClass('hide');
        mQuery('#CampaignEventPanel').removeClass('hide');

        if (groups.length === 1) {
            setTimeout(function () {
                // Activate chosen
                mQuery('#CampaignEventPanelLists #' + groups[0] + 'List').trigger('chosen:open');
            }, 10);
        }
    }
};

Mautic.campaignBuilderUpdateEventCloneButton = function (groups, eventType, anchorName) {
    var $insertButton = mQuery('#EventInsertButton');
    var updatedUrl = $insertButton.attr('href').replace(/anchor=(.*?)$/, "anchor=" + anchorName + "&anchorEventType=" + eventType);
    $insertButton.attr('href', updatedUrl);
};

Mautic.campaignBuilderUpdateEventCloneDescription = function () {
    var cloneDetails = Mautic.getCampaignEventClone();
    if (cloneDetails) {
        mQuery('[data-campaign-event-clone="sourceEventName"]').html(cloneDetails['sourceEventName']);
        mQuery('[data-campaign-event-clone="sourceCampaignName"]').html(cloneDetails['sourceCampaignName']);
    }
};

/**
 *
 * @param endpoint
 * @param nameOnly
 * @returns {{endpointName: *, elementId: *}}
 */
Mautic.campaignBuilderGetEndpointDetails = function(endpoint) {
    var anchorName, eventId;

    if (typeof endpoint === 'string') {
        eventId = endpoint;
    } else {
        var parts = endpoint.anchor.cssClass.split(' ');
        if (parts.length > 1) {
            anchorName = parts[0];
            eventId = parts[1];
        } else {
            anchorName = parts[0];
            eventId = endpoint.elementId
        }
    }

    return {
        'anchorName': anchorName,
        'eventId': eventId.replace('CampaignEvent_', ''),
        'elementId' : eventId,
        'eventType': mQuery('#'+eventId).data('type'),
        'event': mQuery('#'+eventId).data('event')
    };
};

/**
 * Display new source when required
 */
Mautic.campaignBuilderPrepareNewSource = function () {
    var newSourcePos = {
        left: mQuery(window).width()/2 - 100,
        top: 50
    };

    mQuery('#CampaignEvent_newsource').css(newSourcePos);

    Mautic.campaignBuilderUpdateEventList(['Source'], false, 'list', false, {
        left: newSourcePos.left - 50,
        top: newSourcePos.top + 35
    });
    mQuery('#SourceList').trigger('chosen:open');
};

/**
 *
 * @param epDetails
 * @param targetType action|decision|condition
 * @param targetEvent
 * @returns {boolean}
 */
Mautic.campaignBuilderValidateConnection = function (epDetails, targetType, targetEvent) {
    var valid = true;
    var sourceType  = epDetails.eventType;
    var sourceEvent = 'source' === sourceType ? sourceType : epDetails.event;

    if (typeof Mautic.campaignBuilderConnectionRestrictions[targetEvent] !== 'undefined') {
        if ('source' === sourceEvent) {
            // If there are any restrictions, then don't allow it to be the target of the campaign source
            mQuery.each(Mautic.campaignBuilderConnectionRestrictions[targetEvent]['source'], function(eventType, events) {
                if (events.length) {
                    valid = false;

                    // break the loop
                    return false;
                }
            });

            return valid;
        }

        if (
            typeof Mautic.campaignBuilderConnectionRestrictions[targetEvent]['source'][sourceType] !== 'undefined' &&
            Mautic.campaignBuilderConnectionRestrictions[targetEvent]['source'][sourceType].length &&
            mQuery.inArray(sourceEvent, Mautic.campaignBuilderConnectionRestrictions[targetEvent]['source'][sourceType]) === -1
        ) {
            // If the source event is not included in the source list of the target event, then don't allow it
            valid = false;
        }
    }

    if (
        typeof Mautic.campaignBuilderConnectionRestrictions[sourceEvent] !== 'undefined' &&
        typeof Mautic.campaignBuilderConnectionRestrictions[sourceEvent]['target'][targetType] !== 'undefined' &&
        Mautic.campaignBuilderConnectionRestrictions[sourceEvent]['target'][targetType].length
    ) {
        // If the target event is defined in the target list of the source event, then allow it; otherwise don't allow it
        valid = (mQuery.inArray(targetEvent, Mautic.campaignBuilderConnectionRestrictions[sourceEvent]['target'][targetType]) !== -1);
    }

    if (
        typeof Mautic.campaignBuilderConnectionRestrictions['anchor'][sourceType] !== 'undefined' &&
        typeof Mautic.campaignBuilderConnectionRestrictions['anchor'][sourceType][targetEvent] !== 'undefined'
    ) {
        mQuery(Mautic.campaignBuilderConnectionRestrictions['anchor'][sourceType][targetEvent]).each(
            function(key, anchor) {
                switch (anchor) {
                    case 'inaction':
                        anchor = 'no';
                        break;
                    case 'action':
                        anchor = 'yes';
                        break;
                }

                if (anchor == epDetails.anchorName) {
                    valid = false;

                    // Break form the loop
                    return false;
                }
            }
        );
    }

    return valid;
};

/**
 *
 * @param eventId
 * @param contactId
 */
Mautic.updateScheduledCampaignEvent = function(eventId, contactId) {
    // Convert scheduled date/time to an input
    mQuery('#timeline-campaign-event-'+eventId+' .btn-reschedule').addClass('disabled');

    var converting = false;
    var eventWrapper = '#timeline-campaign-event-'+eventId;
    var eventSpan = '.timeline-campaign-event-date-'+eventId;
    var eventText = '#timeline-campaign-event-text-'+eventId;
    var saveButton = '#timeline-campaign-event-save-' + eventId;
    var originalDate = mQuery(eventWrapper+' '+eventSpan).first().text();
    var revertInput = function(input) {
        converting = true;
        mQuery(input).datetimepicker('destroy');
        mQuery(eventSpan).text(originalDate);
        mQuery(eventWrapper+' .btn-reschedule').removeClass('disabled');
    };

    var date = mQuery(eventSpan).attr('data-date');
    mQuery(saveButton).show();
    var input = mQuery('<input type="text" id="timeline-reschedule"/>')
        .css('height', '20px')
        .css('color', '#000000')
        .val(date)
        .on('keyup', function(e) {
            var code = e.keyCode || e.which;
            if (code == 13) {
                e.preventDefault();
                converting = true
                mQuery(input).prop('readonly', true);
                mQuery(input).datetimepicker('destroy');
                Mautic.ajaxActionRequest('campaign:updateScheduledCampaignEvent',
                    {
                        eventId: eventId,
                        contactId: contactId,
                        date: mQuery(this).val(),
                        originalDate: date
                    }, function (response) {
                        mQuery(eventSpan).text(response.formattedDate);
                        mQuery(eventSpan).attr('data-date', response.date);
                        mQuery(eventWrapper+' .btn-reschedule').removeClass('disabled');

                        if (response.success) {
                            mQuery(eventText).removeClass('text-warning').addClass('text-info');
                            mQuery(eventSpan).css('textDecoration', 'inherit');
                            mQuery('.fa.timeline-campaign-event-cancelled-'+eventId).remove();
                            mQuery('.timeline-campaign-event-scheduled-'+eventId).removeClass('hide');
                            mQuery('.timeline-campaign-event-cancelled-'+eventId).addClass('hide');
                            mQuery(saveButton).hide();
                        }
                    }, false
                );
            } else if (code == 27) {
                e.preventDefault();
                revertInput(input);
                mQuery(saveButton).hide();
            }
        })
        .on('blur', function (e) {
            if (!converting) {
                revertInput(input);
            }
            mQuery(saveButton).hide();
        });
    mQuery('#timeline-campaign-event-'+eventId+' '+eventSpan).html(input);
    Mautic.activateDateTimeInputs('#timeline-reschedule');
    mQuery('#timeline-reschedule').focus();
};

/**
 *
 * @param eventId
 * @param contactId
 */
Mautic.saveScheduledCampaignEvent = function (eventId, contactId) {
    var saveButton = '#timeline-campaign-event-save-' + eventId;
    mQuery(saveButton).addClass('disabled');

    // Convert scheduled date/time to an input
    var eventWrapper = '#timeline-campaign-event-' + eventId;
    var eventSpan = '.timeline-campaign-event-date-' + eventId;
    var eventText = '#timeline-campaign-event-text-' + eventId;

    var date = mQuery(eventSpan).attr('data-date');
    Mautic.ajaxActionRequest('campaign:updateScheduledCampaignEvent',
        {
            eventId: eventId,
            contactId: contactId,
            date: mQuery('#timeline-reschedule').val(),
            originalDate: date
        }, function (response) {
            mQuery(eventSpan).text(response.formattedDate);
            mQuery(eventSpan).attr('data-date', response.date);

            if (response.success) {
                mQuery(eventText).removeClass('text-warning').addClass('text-info');
                mQuery(eventSpan).css('textDecoration', 'inherit');
                mQuery('.fa.timeline-campaign-event-cancelled-' + eventId).remove();
                mQuery('.timeline-campaign-event-scheduled-' + eventId).removeClass('hide');
                mQuery('.timeline-campaign-event-cancelled-' + eventId).addClass('hide');
            }

            mQuery(saveButton).removeClass('disabled').hide();
            mQuery(eventWrapper + ' .btn-reschedule').removeClass('disabled');
        }, false);
};

/**
 *
 * @param eventId
 * @param contactId
 */
Mautic.cancelScheduledCampaignEvent = function(eventId, contactId) {
    mQuery('#timeline-campaign-event-'+eventId+' .btn').prop('disabled', true).addClass('disabled');
    var eventWrapper = '#timeline-campaign-event-'+eventId;
    var eventSpan = '.timeline-campaign-event-date-' + eventId;
    var eventText = '#timeline-campaign-event-text-' + eventId;
    Mautic.ajaxActionRequest('campaign:cancelScheduledCampaignEvent',
        {
            eventId: eventId,
            contactId: contactId,
        }, function (response) {
            if (response.success) {
                mQuery(eventText).removeClass('text-info').addClass('text-warning');
                mQuery(eventWrapper+' .btn-edit').prop('disabled', false).removeClass('disabled');
                mQuery('.timeline-campaign-event-scheduled-'+eventId).addClass('hide');
                mQuery('.timeline-campaign-event-cancelled-'+eventId).removeClass('hide');
            } else {
                mQuery(eventWrapper+' .btn').prop('disabled', false).removeClass('disabled');
            }
        }, false
    );
};

/**
 * Update the "Jump to Event" select list to be available events.
 */
Mautic.updateJumpToEventOptions = function() {
    var jumpToEventSelectNode = mQuery("#campaignevent_properties_jumpToEvent");

    jumpToEventSelectNode.children().remove();

    for (var eventId in Mautic.campaignBuilderCanvasEvents) {
        var event = Mautic.campaignBuilderCanvasEvents[eventId];

        if (event.type !== 'campaign.jump_to_event' && event.eventType !== 'decision') {
            var opt = mQuery("<option />")
                .attr("value", event.id)
                .text(event.name)

            if (event.id == jumpToEventSelectNode.data("selected")) {
                opt.attr("selected", "selected");
            }

            jumpToEventSelectNode.append(opt);
        }
    }

    jumpToEventSelectNode.trigger("chosen:updated");
};

/**
 * Handles the deletion of a campaign event
 *
 * @param {string} eventId - The ID of the event to delete
 * @param {string} redirectEventId - Optional ID of another event to redirect contacts to
 * @param {string} deleteUrl - The URL to send the delete request to
 * @param {function} successCallback - Function to call on successful deletion
 * @param {function} errorCallback - Function to call on error
 */
Mautic.campaignBuilderDeleteEvent = function(eventId, redirectEventId, deleteUrl, successCallback, errorCallback) {
    // Prepare the URL
    let ajaxUrl = deleteUrl || '';
    if (redirectEventId && ajaxUrl) {
        ajaxUrl += (ajaxUrl.indexOf('?') !== -1 ? '&' : '?') + 'redirectTo=' + redirectEventId;
    }

    // Make sure deletedEvents is initialized properly
    if (!Mautic.campaignBuilderCampaignElements.deletedEvents) {
        Mautic.campaignBuilderCampaignElements.deletedEvents = [];
    }

    // Prepare data for the AJAX call
    const data = {
        eventId: eventId,
        campaignId: mQuery('#campaignId').val(),
        redirectTo: redirectEventId,
        modifiedEvents: JSON.stringify(Mautic.campaignBuilderCampaignElements.modifiedEvents || {}),
        deletedEvents: JSON.stringify(Mautic.campaignBuilderCampaignElements.deletedEvents)
    };

    // Execute the delete request
    mQuery.ajax({
        url: ajaxUrl,
        type: 'POST',
        data: data,
        success: function(response) {
            if (response.success) {
                // Remove the event from the canvas
                if (typeof Mautic.campaignBuilderInstance !== 'undefined') {
                    Mautic.campaignBuilderInstance.remove(document.getElementById('CampaignEvent_' + eventId));
                }

                // Update all campaign builder data structures to mark the event as deleted
                if (Mautic.campaignBuilderCanvasEvents[eventId]) {
                    // Mark the event as deleted
                    Mautic.campaignBuilderCanvasEvents[eventId].deleted = true;
                }

                // Add to the deletedEvents array using the helper function
                Mautic.addEventToDeletedEvents(eventId, redirectEventId);

                // Update the campaignElements for when the campaign is saved
                mQuery('#campaign_campaignEvents').val(JSON.stringify(Mautic.campaignBuilderCampaignElements));

                // Store connected events as deleted events as well if provided in the response
                if (typeof response.deletedEvents !== 'undefined' && response.deletedEvents.length) {
                    mQuery.each(response.deletedEvents, function(i, eventData) {
                        if (Mautic.campaignBuilderCanvasEvents[eventData.id]) {
                            Mautic.campaignBuilderCanvasEvents[eventData.id].deleted = true;
                        }

                        Mautic.addEventToDeletedEvents(eventData.id, eventData.redirectEvent);
                    });
                }

                // Call the success callback if provided
                if (typeof successCallback === 'function') {
                    successCallback(response);
                }
            } else if (typeof errorCallback === 'function') {
                errorCallback(response);
            }
        },
        error: function(response) {
            // Call the error callback if provided
            if (typeof errorCallback === 'function') {
                errorCallback(response);
            }
        }
    });
};

Mautic.highlightJumpTarget = function(event, el) {
    var element = mQuery(el);
    var parentEventElement = element.parent().parent();
    var highlightedAlready = parentEventElement.data('highlighted');
    var jumpTargetID = '#CampaignEvent_' + element.data('jumpTarget');
    var jumpTarget = mQuery(jumpTargetID);
    var overlay = mQuery('#EventJumpOverlay');

    if (highlightedAlready) {
        parentEventElement.data('highlighted', false);
        overlay.hide();
        overlay.off('click.closejump');
        parentEventElement.css("z-index", 1010);
        jumpTarget.css("z-index", 1010);
    } else {
        if (mQuery('[data-highlighted="true"]').length) {
            mQuery('[data-highlighted="true"]').find('.highlight-jump-target').click();
        }

        parentEventElement.data('highlighted', true);
        overlay.show();
        overlay.on('click.closejump', function() {
            element.click();
        });
        parentEventElement.css("z-index", 2010);
        jumpTarget.css("z-index", 2010);
    }
};

/**
 * Display confirmation modal if user wishes to unpublish the campaign.
 */
Mautic.showCampaignConfirmation = function (el) {
    let element = mQuery(el);
    if (element.prop('checked') && element.val() !== "1") {
        Mautic.showConfirmation(element, element.data('message-unpublish'));
    } else {
        Mautic.showConfirmation(element, element.data('message-publish'));
    }
};

/**
 * Cancel Callback to trigger the yes button and dismiss the confirmation modal.
 */
Mautic.setPublishedButtonToYes = function (el) {
    // Dismiss the confirmation
    Mautic.dismissConfirmation();

    // Find the yes button id and trigger click event
    var yesButton  = mQuery(el).parent('.btn-no').siblings('.btn-yes').children('input');
    var yesButtonId = mQuery(yesButton).attr('id');
    if (yesButtonId !== undefined) {
        mQuery('#' + yesButtonId).trigger('click');
        mQuery(el).parent('.btn-no').removeClass('active');
        mQuery(el).parent('.btn-no').siblings('.btn-yes').addClass('active');
    }
};

/**
 * Onclick Callback to show the confirmation modal during toggling campaign status.
 */
Mautic.confirmationCampaignPublishStatus = function (el) {
    let element = mQuery(el);

    if (element.data('status') === 'published') {
        Mautic.showConfirmation(element, element.data('message-unpublish'));
    } else {
        Mautic.showConfirmation(element, element.data('message-publish'));
    }
}

/**
 * Confirm Callback to toggling campaign status if user chooses Yes.
 */
Mautic.confirmCallbackCampaignPublishStatus = function (action, el) {
    let element = mQuery(el);

    let idClass = element.data('id-class');
    let model = element.data('model');
    let itemId = element.data('item-id');
    let query = element.data('query');
    let backdrop = element.data('backdrop');

    // Toggles published status of an campaign
    Mautic.togglePublishStatus(event, idClass, model, itemId, query, backdrop);

    // Dismiss the confirmation
    Mautic.dismissConfirmation();
}

Mautic.isCampaignCloneEventAllowedForEndpoint = function(endpointDetails) {
    const eventClone = Mautic.getCampaignEventClone();
    if (!eventClone) {
        return false;
    }
    // uppercase first letter for string comparison
    const eventType = eventClone['sourceEventType'].charAt(0).toUpperCase() + eventClone['sourceEventType'].slice(1);
    const allowedEvents = Mautic.campaignBuilderAnchorClickedAllowedEvents || [];
    const isValidConnection = Mautic.campaignBuilderValidateConnection(endpointDetails, eventClone['sourceEventType'], eventClone['sourceType']);
    return allowedEvents.includes(eventType) && isValidConnection;
}

Mautic.getCampaignEventClone = function() {
    const eventClone = localStorage.getItem("mautic_campaign_event_clone");
    return eventClone === null ? null : JSON.parse(eventClone);
}

Mautic.setCampaignEventClone = function(data) {
    localStorage.setItem("mautic_campaign_event_clone", JSON.stringify(data));
}

Mautic.clearCampaignEventClone = function() {
    localStorage.removeItem("mautic_campaign_event_clone");
}

Mautic.hideCampaignEventPanel = function() {
    mQuery('#CampaignEventPanel').addClass('hide');
}

Mautic.previewCampaignLabels = function() {
    const campaignBuilder = Mautic.campaignBuilderInstance;
    const managedElements = Mautic.campaignBuilderInstance.getManagedElements();
    const allElements = Object.values(managedElements).map(el => el.el);

    allElements.forEach(function(element) {
        const id = element.id;
        const connections = campaignBuilder.getConnections({source: id});

        connections.forEach(function(connection) {
            const connectionAnchor = connection.target.dataset.connected ?? null;
            if (connectionAnchor === 'yes') {
                connection.addOverlay(["Label", {
                    label: element.dataset.eventYesPercent + '%',
                    location: 0.44,
                    cssClass: 'jtk-label jtk-label--success',
                    id: element.id + 'yes-path-label'
                }]);
            }
            if (connectionAnchor === 'no') {
                connection.addOverlay(["Label", {
                    label: element.dataset.eventNoPercent + '%',
                    location: 0.44,
                    cssClass: 'jtk-label jtk-label--error',
                    id: element.id + 'no-path-label'
                }]);
            }
        });
    });
}

Mautic.campaignAuditlogOnLoad = function (container, response) {
    document.querySelector("#campaign-auditlog a[data-activate-details='all']")?.addEventListener("click", function () {
        let icon = this.querySelector("span:first-child");
        let isExpanded = icon.classList.contains("ri-arrow-down-s-line");

        document.querySelectorAll("#campaign-auditlog a[data-activate-details]:not([data-activate-details='all'])").forEach(element => {
            const detailsId = element.getAttribute("data-activate-details");
            const detailsElem = document.querySelector(`#auditlog-details-${detailsId}`);
            const elementIcon = element.querySelector("span:first-child");

            if (detailsElem) {
                detailsElem.classList.toggle("hide", !isExpanded);
                element.classList.toggle("active", isExpanded);
                elementIcon.classList.replace(
                    isExpanded ? "ri-arrow-down-s-line" : "ri-arrow-up-s-line",
                    isExpanded ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line"
                );
            }
        });

        icon.classList.replace(
            isExpanded ? "ri-arrow-down-s-line" : "ri-arrow-up-s-line",
            isExpanded ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line"
        );
    });

    document.querySelectorAll("#campaign-auditlog a[data-activate-details]:not([data-activate-details='all'])").forEach(anchor => {
        anchor.addEventListener("click", function () {
            const detailsId = this.dataset.activateDetails;
            const icon = this.querySelector("span");
            const detailsElement = document.getElementById(`auditlog-details-${detailsId}`);

            if (detailsId && detailsElement) {
                const isActive = this.classList.contains("active");

                if (isActive) {
                    detailsElement.classList.add("hide");
                    this.classList.remove("active");
                    icon.classList.replace("ri-arrow-up-s-line", "ri-arrow-down-s-line");
                } else {
                    detailsElement.classList.remove("hide");
                    this.classList.add("active");
                    icon.classList.replace("ri-arrow-down-s-line", "ri-arrow-up-s-line");
                }
            }
        });
    });
};
