Your IP : 216.73.217.142


Current Path : /var/www/consult-e-syn/public_html/plugins/system/sliders/src/
Upload File :
Current File : /var/www/consult-e-syn/public_html/plugins/system/sliders/src/Replace.php

<?php
/**
 * @package         Sliders
 * @version         8.4.0PRO
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            https://regularlabs.com
 * @copyright       Copyright © 2023 Regular Labs All Rights Reserved
 * @license         GNU General Public License version 2 or later
 */

namespace RegularLabs\Plugin\System\Sliders;

defined('_JEXEC') or die;

use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Router\Route as JRoute;
use Joomla\CMS\Uri\Uri as JUri;
use RegularLabs\Library\Alias as RL_Alias;
use RegularLabs\Library\Html as RL_Html;
use RegularLabs\Library\HtmlTag as RL_HtmlTag;
use RegularLabs\Library\PluginTag as RL_PluginTag;
use RegularLabs\Library\Protect as RL_Protect;
use RegularLabs\Library\RegEx as RL_RegEx;
use RegularLabs\Library\StringHelper as RL_String;
use RegularLabs\Library\Title as RL_Title;
use RegularLabs\Library\Uri as RL_Uri;

class Replace
{

    static $accesslevels = null;
    static $allitems = [];
    static $context  = '';
    static $ids      = [];
    static $matches  = [];
    static $setcount = 0;
    static $sets     = [];
    static $usergroups = null;

    public static function getSets(&$string, $only_basic_details = false)
    {
        $regex = Params::getRegex();

        RL_RegEx::matchAll($regex, $string, $matches);

        if (empty($matches))
        {
            return [];
        }

        self::$sets = [];
        $set_ids    = [];

        $has_restricted_access = false;

        foreach ($matches as $match)
        {
            if (substr($match['tag'], 0, 1) == '/')
            {
                if (empty($set_ids))
                {
                    continue;
                }

                $set_id = key($set_ids);

                array_pop($set_ids);

                if (empty($set_id))
                {
                    continue;
                }

                self::$sets[$set_id][0]->ending = $match[0];

                continue;
            }

            end($set_ids);

            $item = self::getSetItem($match, $set_ids, $only_basic_details);

            if ($only_basic_details)
            {
                if ( ! isset(self::$sets['basic']))
                {
                    self::$sets['basic'] = [];
                }

                self::$sets['basic'][] = $item;
                continue;
            }

            if ( ! $item->hasaccess)
            {
                $has_restricted_access = true;
            }

            if ( ! isset(self::$sets[$item->set]))
            {
                self::$sets[$item->set] = [];
            }

            self::$sets[$item->set][] = $item;
        }

        if ($has_restricted_access)
        {
            // Remove sliders by access
            self::removeByAccess($string);
        }

        return self::$sets;
    }

    public static function replaceTags(&$string, $area = 'article', $context = '')
    {
        if ( ! is_string($string) || $string == '')
        {
            return false;
        }

        self::$context = $context;

        // Check if tags are in the text snippet used for the search component
        if (strpos($context, 'com_search.') === 0)
        {
            $limit = explode('.', $context, 2);
            $limit = (int) array_pop($limit);

            $string_check = substr($string, 0, $limit);

            if ( ! RL_String::contains($string_check, Params::getTags(true)))
            {
                return false;
            }
        }

        $params = Params::get();

        // allow in component?
        if (RL_Protect::isRestrictedComponent($params->disabled_components ?? [], $area))
        {
            if ( ! $params->disable_components_remove)
            {
                Protect::protectTags($string);

                return true;
            }

            Protect::_($string);

            self::handlePrintPage($string);

            RL_Protect::unprotect($string);

            return true;
        }

        if ( ! RL_String::contains($string, Params::getTags(true)))
        {
            // Links with #slider-name or &slider=slider-name
            self::replaceLinks($string);

            return true;
        }

        Protect::_($string);

        [$start_tags, $end_tags] = Params::getTags();

        [$pre_string, $string, $post_string] = RL_Html::getContentContainingSearches(
            $string,
            $start_tags,
            $end_tags
        );

        if (JFactory::getApplication()->input->getInt('print', 0))
        {
            // Replace syntax with general html on print pages
            self::handlePrintPage($string);

            $string = $pre_string . $string . $post_string;

            RL_Protect::unprotect($string);

            return true;
        }

        $sets = self::getSets($string);
        self::initSets($sets);

        // Tag syntax: {slider ...}
        self::replaceSyntax($string, $sets);

        // Closing tag: {/slider}
        self::replaceClosingTag($string);

        // Links with #slider-name or &slider=slider-name
        self::replaceLinks($string);

        // Link tag {sliderlink ...}
        self::replaceLinkTag($string);

        $string = $pre_string . $string . $post_string;

        RL_Protect::unprotect($string);

        return true;
    }

    private static function addChildToParent($item)
    {
        if (empty($item->parent))
        {
            return;
        }

        [$parent_set, $parent_item] = $item->parent;

        if (empty(self::$sets[$parent_set]) || empty(self::$sets[$parent_set][$parent_item]))
        {
            return;
        }

        self::$sets[$parent_set][$parent_item]->children[] = $item->set;
    }

    private static function createId($alias)
    {
        $id = $alias;

        $i = 1;

        while (in_array($id, self::$ids))
        {
            $id = $alias . '-' . ++$i;
        }

        self::$ids[] = $id;

        return $id;
    }

    private static function findItemByMatch($id)
    {
        foreach (self::$allitems as $item)
        {
            if ( ! in_array($id, $item->matches))
            {
                continue;
            }

            return $item->id;
        }

        return false;
    }

    private static function getAccessLevels()
    {
        if ( ! is_null(self::$accesslevels))
        {
            return self::$accesslevels;
        }

        $user   = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();
        $levels = $user->getAuthorisedViewLevels();

        $db = JFactory::getDbo();

        $query = $db->getQuery(true)
            ->select('LOWER(REPLACE(a.title, " ", ""))')
            ->from('#__viewlevels as a')
            ->where('a.id IN (\'' . implode('\',\'', $levels) . '\')');
        $db->setQuery($query);

        self::$accesslevels = $db->loadColumn();

        return self::$accesslevels;
    }

    private static function getItemClass($item)
    {
        $class = ['accordion-group panel rl_sliders-group nn_sliders-group'];

        if ($item->open)
        {
            $class[] = 'active';
        }

        if ( ! empty($item->mode))
        {
            $class[] = $item->mode == 'hover' ? 'hover' : 'click';
        }

        $class[] = trim($item->class);

        return trim(implode(' ', $class));
    }

    private static function getLinkAttributes($id)
    {
        return 'href="' . RL_Uri::get($id) . '"'
            . ' class="rl_sliders-link rl_sliders-link-' . $id . ' nn_sliders-link nn_sliders-link-' . $id . '"'
            . ' data-id="' . $id . '"';
    }

    private static function getMainClasses($item)
    {
        $params = Params::get();

        $classes = [
            'rl_sliders nn_sliders accordion panel-group',
            $params->mainclass,
        ];

        if ( ! empty($item->mainclass))
        {
            $classes[] = $item->mainclass;
        }

        if ($params->slide_speed != 350)
        {
            $classes[] = 'has_effects';
        }

        $classes = array_diff($classes, ['']);

        return trim(implode(' ', $classes));
    }

    private static function getParent($set_id, $level)
    {
        if (empty(self::$sets))
        {
            return false;
        }

        if (isset(self::$sets[$set_id]))
        {
            return self::$sets[$set_id][0]->parent;
        }

        reset(self::$sets);

        $previous_set   = current(self::$sets);
        $previous_level = $previous_set[0]->level;

        while ($previous_level >= $level)
        {
            $previous_set = prev(self::$sets);

            if (empty($previous_set))
            {
                end(self::$sets);

                return false;
            }

            $previous_level = $previous_set[0]->level;
        }

        end(self::$sets);
        end($previous_set);

        $parent_item = key($previous_set);

        return [$previous_set[$parent_item]->set, $parent_item];
    }

    private static function getPreHtml($items, $first = 0)
    {
        if ( ! $first)
        {
            return '</div></div></div>';
        }

        $class = self::getMainClasses($items[0]);

        return '<div class="' . $class . '" id="set-rl_sliders-' . $items[0]->set . '" role="presentation">'
            . '<a id="rl_sliders-scrollto_' . $items[0]->set . '" class="anchor rl_sliders-scroll nn_sliders-scroll"></a>';
    }

    private static function getSetItem($match, &$set_ids, $only_basic_details = false)
    {
        $item = (object) [];

        // Set the values from the tag
        $tag = RL_Title::clean($match['data'], false, false);
        self::setTagAttributes($item, $tag);

        if ($only_basic_details)
        {
            return $item;
        }

        $item->orig   = $match[0];
        $item->set_id = trim(str_replace('-', '_', $match['set_id']));

        // New set
        if (empty($set_ids) || current($set_ids) != $item->set_id)
        {
            self::$setcount++;
            $set_ids[self::$setcount . '.' . $item->set_id] = $item->set_id;
        }

        $item->set = array_search($item->set_id, array_reverse($set_ids));

        $item->level = self::getSetLevel($item->set, $set_ids);

        $item->id       = count(self::$sets[$item->set] ?? []);
        $item->parent   = self::getParent($item->set, $item->level);
        $item->children = [];

        if ( ! empty($item->parent))
        {
            self::addChildToParent($item);
        }

        $item->hasaccess = self::hasAccess($item);

        [$item->pre, $item->post] = RL_Html::cleanSurroundingTags(
            [$match['pre'], $match['post']],
            ['div', 'p', 'span', 'h[0-6]']
        );

        return $item;
    }

    private static function getSetLevel($set_id, $set_ids)
    {
        // Sets are still empty, so this is the first set
        if (empty(self::$sets))
        {
            return 1;
        }

        // Grab the level from the previous entry of this set
        if (isset(self::$sets[$set_id]))
        {
            return self::$sets[$set_id][0]->level;
        }

        // Look up the level of the previous set
        $previous_set_id = array_search(prev($set_ids), array_reverse($set_ids));

        // Grab the level from the previous entry of this set
        if (isset(self::$sets[$previous_set_id]))
        {
            return self::$sets[$previous_set_id][0]->level + 1;
        }

        return 1;
    }

    private static function getSliderTitle($item, $items)
    {
        $href            = RL_Uri::get($item->id);
        $title           = $item->title_full;
        $link_attributes = ' data-toggle="collapse"'
            . ' id="slider-' . $item->id . '"'
            . ' data-id="' . $item->id . '"'
            . ' data-parent="#set-rl_sliders-' . $items[0]->set . '"'
            . ' aria-expanded="' . ($item->open ? 'true' : 'false') . '"';

        if ( ! empty($item->link_attributes))
        {
            $link_attributes .= ' ' . $item->link_attributes;
        }

        $class = 'accordion-toggle rl_sliders-toggle nn_sliders-toggle';
        $class .= $item->scroll ? ' rl_sliders-item-scroll nn_sliders-item-scroll' : '';

        $onclick = '';
        if ( ! empty($item->onclick))
        {
            $onclick = ' onclick="' . str_replace('"', '&quot;', $item->onclick) . '"';
        }

        if ( ! $item->open)
        {
            $class .= ' collapsed';
        }

        if ($item->haslink)
        {
            if (RL_RegEx::match('<a [^>]*href="(.*?)"', $title, $match))
            {
                $href = $match[1];
            }

            $class = 'accordion-toggle rl_sliders-link';

            if (RL_RegEx::match('<a [^>]*class="(.*?)"', $title, $match))
            {
                $class = trim($class . ' ' . $match[1]);
            }

            $link_attributes = '';

            if (RL_RegEx::match('<a ([^>]*)', $title, $match))
            {
                $link_attributes = $match[1];
                $link_attributes = trim(RL_RegEx::replace('(href|class)=".*?"', '', $link_attributes));
            }

            $title = RL_RegEx::replace('<a .*?>(.*?)</a>', '\1', $title);
        }

        $link_title = $title !== $item->title
            ? ' title="' . htmlspecialchars($item->title) . '"'
            : '';

        return
            '<a href="' . $href . '" class="' . $class . '"' . $link_title . $onclick . $link_attributes . '>'
            . '<span class="rl_sliders-toggle-inner nn_sliders-toggle-inner">'
            . ' ' . $title
            . '</span>'
            . '</a>';
    }

    private static function getTagAttributes($string)
    {
        RL_PluginTag::protectSpecialChars($string);

        $is_old_syntax = (strpos($string, '|') !== false);

        if ($is_old_syntax)
        {
            // Fix some different old syntaxes
            $string = str_replace(
                [
                    '|alias:',
                ],
                [
                    '|alias=',
                ],
                $string
            );
        }

        RL_PluginTag::unprotectSpecialChars($string, true);

        $known_boolean_keys = [
            'open', 'active', 'opened', 'default',
            'close', 'inactive', 'closed',
            'scroll', 'noscroll',
            'nooutline', 'outline_handles', 'outline_content', 'color_inactive_handles',
        ];

        // Get the values from the tag
        $attributes = RL_PluginTag::getAttributesFromString($string, 'title', $known_boolean_keys);

        $key_aliases = [
            'title'              => ['name'],
            'title-opened'       => ['title-open', 'title-active'],
            'title-closed'       => ['title-close', 'title-inactive'],
            'open'               => ['active', 'opened', 'default'],
            'close'              => ['inactive', 'closed'],
            'access'             => ['accesslevels', 'accesslevel'],
            'usergroup'          => ['usergroups', 'group', 'groups'],
            'heading_attributes' => ['li_attributes'],
            'link_attributes'    => ['a_attributes'],
            'body_attributes'    => ['content_attributes'],
        ];

        RL_PluginTag::replaceKeyAliases($attributes, $key_aliases);

        if ( ! empty($attributes->close))
        {
            $attributes->open = false;
            unset($attributes->close);
        }

        return $attributes;
    }

    private static function getUserGroups()
    {
        if ( ! is_null(self::$usergroups))
        {
            return self::$usergroups;
        }

        $user   = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();
        $levels = $user->getAuthorisedGroups();

        $db = JFactory::getDbo();

        $query = $db->getQuery(true)
            ->select('LOWER(REPLACE(u.title, " ", ""))')
            ->from('#__usergroups as u')
            ->where('u.id IN (\'' . implode('\',\'', $levels) . '\')');
        $db->setQuery($query);

        self::$usergroups = $db->loadColumn();

        return self::$usergroups;
    }

    private static function handlePrintPage(&$string)
    {
        $sets = self::getSets($string);
        self::initSets($sets);

        $prefix = '';

        foreach ($sets as $items)
        {
            foreach ($items as $item)
            {

                $class = 'rl_sliders-print';

                if ($item->open)
                {
                    $class .= ' active';
                }

                $replace = $prefix . '<div id="' . $item->id . '" class="' . $class . '">'
                    . '<' . $item->title_tag . ' class="rl_sliders-title nn_sliders-title">'
                    . '<a id="anchor-' . $item->id . '" class="anchor"></a>'
                    . $item->title_full
                    . '</' . $item->title_tag . '>';

                $string = RL_String::replaceOnce($item->orig, $replace, $string);
            }
        }

        $regex = Params::getRegex('end');

        RL_RegEx::matchAll($regex, $string, $matches);

        $replace = '</div>';

        foreach ($matches as $match)
        {
            $string  = RL_String::replaceOnce($match[0], $replace, $string);
            $replace = '';
        }

        $regex = Params::getRegex('link');

        RL_RegEx::matchAll($regex, $string, $matches);

        foreach ($matches as $match)
        {
            $href   = RL_Uri::get($match['id']);
            $link   = '<a href="' . $href . '">' . $match['text'] . '</a>';
            $string = RL_String::replaceOnce($match[0], $link, $string);
        }
    }

    private static function hasAccess($item)
    {
        if ( ! isset($item->access) && ! isset($item->usergroup))
        {
            return true;
        }

        if (isset($item->access))
        {
            $has_access = self::hasAccessByList($item->access, self::getAccessLevels());

            if ( ! is_null($has_access))
            {
                return $has_access;
            }
        }

        if (isset($item->usergroup))
        {
            $has_access = self::hasAccessByList($item->usergroup, self::getUserGroups());

            if ( ! is_null($has_access))
            {
                return $has_access;
            }
        }

        return false;
    }

    private static function hasAccessByList($levels, $list)
    {
        $levels = explode(',', str_replace(' ', '', strtolower($levels)));

        $pass = null;

        foreach ($levels as $level)
        {
            if (empty($level))
            {
                continue;
            }

            if (substr($level, 0, 1) == '!')
            {
                $level = substr($level, 1);

                if (in_array($level, $list))
                {
                    return false;
                }

                $pass = true;
                continue;
            }

            if (in_array($level, $list))
            {
                $pass = true;
            }
        }

        return $pass;
    }

    private static function initSets(&$sets)
    {
        $params = Params::get();

        $urlitem   = JFactory::getApplication()->input->get('slider');
        $itemcount = 0;

        foreach ($sets as $set_id => $items)
        {
            $opened_by_default = '';

            if (isset($items[0]->open) && $items[0]->open === 'random')
            {
                unset($items[0]->open);
                $random_item               = rand(0, count($items) - 1);
                $items[$random_item]->open = true;
            }

            foreach ($items as $i => $item)
            {
                $item->title      = trim($item->title ?? 'Slider');
                $item->title_full = $item->title;

                if (isset($item->{'title-opened'}) || isset($item->{'title-closed'}))
                {
                    $title_closed = $item->{'title-closed'} ?? $item->title;
                    $title_opened = $item->{'title-opened'} ?? $item->title;

                    // Set main title to the title-opened, otherwise to title-closed
                    $item->title = $title_opened ?: ($title_closed ?: $item->title);

                    // place the title-opened and title-closed in css controlled spans
                    $item->title_full = '<span class="rl_sliders-title-inactive nn_sliders-title-inactive">' . $title_closed . '</span>'
                        . '<span class="rl_sliders-title-active nn_sliders-title-active">' . $title_opened . '</span>';
                }

                $item->haslink = RL_RegEx::match('<a [^>]*>.*?</a>', $item->title);

                $item->title = RL_Title::clean($item->title, true);
                $item->title = $item->title ?: RL_HtmlTag::getAttributeValue('title', $item->title_full);
                $item->title = $item->title ?: RL_HtmlTag::getAttributeValue('alt', $item->title_full);

                $item->alias = RL_Alias::get($item->alias ?? $item->title);
                $item->alias = $item->alias ?: 'slider';

                $item->id    = self::createId($item->alias);
                $item->set   = (int) $set_id;
                $item->count = $i + 1;

                $item->scroll = ( ! empty($item->scroll) || ( ! isset($item->scroll) && $params->scroll && empty($item->noscroll)));

                $set_keys = [
                    'class', 'output_title_tag', 'title_tag', 'onclick',
                    'slideshow',
                ];

                foreach ($set_keys as $key)
                {
                    $item->{$key} = isset($item->{$key})
                        ? $item->{$key}
                        : ($params->{$key} ?? '');
                }

                $item->matches   = RL_Title::getUrlMatches([$item->id, $item->title]);
                $item->matches[] = ++$itemcount . '';
                $item->matches[] = $item->set . '.' . ($i + 1);
                $item->matches[] = $item->set . '-' . ($i + 1);

                $item->matches = array_unique($item->matches);
                $item->matches = array_diff($item->matches, self::$matches);
                self::$matches = array_merge(self::$matches, $item->matches);

                if (self::itemIsOpen($item, $urlitem, $i == 0))
                {
                    $opened_by_default = $i;
                }

                // Will be set after all items are checked based on the $opened_by_default id
                $item->open = false;

                $sets[$set_id][$i] = $item;
                self::$allitems[]  = $item;
            }

            self::setOpenItem($sets[$set_id], $opened_by_default);
        }
    }

    private static function itemIsOpen($item, $urlitem, $is_first = false)
    {
        if ($item->haslink)
        {
            return false;
        }

        if ( ! empty($item->close))
        {
            return false;
        }

        if (isset($item->open))
        {
            return $item->open;
        }

        if ($urlitem && in_array($urlitem, $item->matches))
        {
            return true;
        }

        if ( ! $is_first)
        {
            return false;
        }

        $params = Params::get();

        return $params->state_first == 'open';
    }

    private static function removeByAccess(&$string)
    {
        $remove_sets  = [];
        $remove_items = [];

        $reverse_sets = array_reverse(self::$sets, true);

        foreach ($reverse_sets as $set_id => $set)
        {
            $remove_set       = true;
            $remove_set_items = [];

            foreach ($set as $item)
            {
                if ($item->hasaccess)
                {
                    $remove_set = false;

                    continue;
                }

                $remove_set_items[] = $item;

                $remove_sets = array_merge($remove_sets, $item->children);
            }

            if ($remove_set)
            {
                $remove_sets[] = $set_id;

                continue;
            }

            $remove_items = [...$remove_items, ...$remove_set_items];
        }

        foreach ($remove_sets as $set_id)
        {
            if ( ! isset($set_id) || ! isset(self::$sets[$set_id]))
            {
                continue;
            }

            $set            = self::$sets[$set_id];
            $first_set_item = $set[0];

            if ( ! isset($first_set_item->ending))
            {
                continue;
            }

            $regex =
                RL_RegEx::quote($first_set_item->orig)
                . '.*?'
                . RL_RegEx::quote($first_set_item->ending);

            $string = RL_RegEx::replaceOnce($regex, '', $string);

            unset(self::$sets[$set_id]);
        }

        $removed_items = [];

        foreach ($remove_items as $item)
        {
            if ( ! isset($item->set) || ! isset(self::$sets[$item->set][0]->ending))
            {
                continue;
            }

            $next_tag = isset(self::$sets[$item->set][$item->id + 1])
                ? self::$sets[$item->set][$item->id + 1]->orig
                : self::$sets[$item->set][0]->ending;

            $regex =
                RL_RegEx::quote($item->orig)
                . '.*?'
                . '(?=' . RL_RegEx::quote($next_tag) . ')';

            $string = RL_RegEx::replaceOnce($regex, '', $string);

            $removed_items[] = $item;
        }

        foreach ($removed_items as $item)
        {
            unset(self::$sets[$item->set][$item->id]);
        }

        foreach (self::$sets as $id => $set)
        {
            self::$sets[$id] = array_values($set);
        }
    }

    private static function replaceAnchorLinks(&$string)
    {
        RL_RegEx::matchAll(
            '(?<link><a\s[^>]*href="(?<url>([^"]*)?)\#(?<id>[^"]*)"[^>]*>)(?<text>.*?)</a>',
            $string,
            $matches
        );

        if (empty($matches))
        {
            return;
        }

        self::replaceLinksMatches($string, $matches);
    }

    private static function replaceClosingTag(&$string)
    {
        $params = Params::get();
        $regex  = Params::getRegex('end');

        RL_RegEx::matchAll($regex, $string, $matches);

        if (empty($matches))
        {
            return;
        }

        foreach ($matches as $match)
        {
            $html = '</div></div></div></div>';

            if ($params->place_comments)
            {
                $html .= Protect::getCommentEndTag();
            }

            [$pre, $post] = RL_Html::cleanSurroundingTags([$match['pre'], $match['post']]);

            $html = $pre . $html . $post;

            $string = RL_String::replaceOnce($match[0], $html, $string);
        }
    }

    private static function replaceLinkTag(&$string)
    {
        $regex = Params::getRegex('link');

        RL_RegEx::matchAll($regex, $string, $matches);

        if (empty($matches))
        {
            return;
        }

        foreach ($matches as $match)
        {
            self::replaceLinkTagMatch($string, $match);
        }
    }

    private static function replaceLinkTagMatch(&$string, $match)
    {
        $params = Params::get();

        $id = RL_Alias::get($match['id']);

        if ( ! self::stringHasItem($string, $id))
        {
            $id_by_name = self::findItemByMatch($match['id']);
            $id_by_id   = self::findItemByMatch($id);
            $id         = $id_by_name ?: ($id_by_id ?: $id);
        }

        if ( ! self::stringHasItem($string, $id))
        {
            $html = '<a href="' . RL_Uri::get($id) . '">' . $match['text'] . '</a>';

            if ($params->place_comments)
            {
                $html = Protect::wrapInCommentTags($html);
            }

            $string = RL_String::replaceOnce($match[0], $html, $string);

            return;
        }

        $html = '<a ' . self::getLinkAttributes($id) . '>'
            . '<span class="rl_sliders-link-inner nn_sliders-link-inner">' . $match['text'] . '</span>'
            . '</a>';

        if ($params->place_comments)
        {
            $html = Protect::wrapInCommentTags($html);
        }

        $string = RL_String::replaceOnce($match[0], $html, $string);
    }

    private static function replaceLinks(&$string)
    {
        // Links with #slider-name
        self::replaceAnchorLinks($string);
        // Links with &slider=slider-name
        self::replaceUrlLinks($string);
    }

    private static function replaceLinksMatches(&$string, $matches)
    {
        $uri            = JUri::getInstance();
        $current_urls   = [];
        $current_urls[] = $uri->toString(['path']);
        $current_urls[] = $uri->toString(['scheme', 'host', 'path']);
        $current_urls[] = $uri->toString(['scheme', 'host', 'port', 'path']);

        foreach ($matches as $match)
        {
            $link = $match['link'];

            if (
                strpos($link, 'data-toggle=') !== false
                || strpos($link, 'onclick=') !== false
                || strpos($link, 'rl_sliders-link') !== false
                || strpos($link, 'rl_tabs-toggle-sm') !== false
                || strpos($link, 'rl_tabs-link') !== false
            )
            {
                continue;
            }

            $url = $match['url'];

            if (strpos($url, 'index.php/') === 0)
            {
                $url = '/' . $url;
            }

            if (strpos($url, 'index.php') === 0)
            {
                $url = JRoute::_($url);
            }

            if ($url != '' && ! in_array($url, $current_urls))
            {
                continue;
            }

            $id = $match['id'];

            if ( ! self::stringHasItem($string, $id))
            {
                // This is a link to a normal anchor or other element on the page
                // Remove the prepending obsolete url and leave the hash
                // $string = str_replace('href="' . $match['url'] . '#' . $id . '"', 'href="#' . $id . '"', $string);

                continue;
            }

            $attributes = self::getLinkAttributes($id);

            // Combine attributes with original
            $attributes = RL_HtmlTag::combineAttributes($link, $attributes);

            $html = '<a ' . $attributes . '><span class="rl_sliders-link-inner nn_sliders-link-inner">' . $match['text'] . '</span></a>';

            $string = str_replace($match[0], $html, $string);
        }
    }

    private static function replaceSyntax(&$string, $sets)
    {
        $regex = Params::getRegex('end');

        if ( ! RL_RegEx::match($regex, $string))
        {
            return;
        }

        foreach ($sets as $items)
        {
            self::replaceSyntaxItemList($string, $items);
        }
    }

    private static function replaceSyntaxItem(&$string, $item, $items, $first = 0)
    {
        if (strpos($string, $item->orig) === false)
        {
            return;
        }

        $params = Params::get();

        $html   = [];
        $html[] = $item->post;
        $html[] = $item->pre;

        if ($first && $params->place_comments)
        {
            $html[] = Protect::getCommentStartTag();
        }

        $html[] = self::getPreHtml($items, $first);

        if ( ! in_array(self::$context, ['com_search.search', 'com_finder.indexer']))
        {
            $heading_attributes = ' aria-controls="' . $item->id . '"';

            if ( ! empty($item->heading_attributes))
            {
                $heading_attributes .= ' ' . $item->heading_attributes;
            }

            $html[] = '<div class="' . self::getItemClass($item) . '">';
            $html[] = '<a id="rl_sliders-scrollto_' . $item->id . '" class="anchor rl_sliders-scroll nn_sliders-scroll"></a>';
            $html[] = '<div class="accordion-heading panel-heading"' . $heading_attributes . '>';
            $html[] = self::getSliderTitle($item, $items);
            $html[] = '</div>';

            $body_class = 'accordion-body rl_sliders-body nn_sliders-body collapse';

            if ($item->open)
            {
                $body_class .= ' in';
            }

            $body_attributes  = 'class="' . $body_class . '" '
                . ' role="region"'
                . ' aria-labelledby="slider-' . $item->id . '"'
                . ' id="' . $item->id . '"';
            $panel_attributes = 'class="accordion-inner panel-body"';

            if ( ! $item->open)
            {
                $panel_attributes .= ' hidden="hidden"';
            }

            if ( ! empty($item->body_attributes))
            {
                $body_attributes .= ' ' . $item->body_attributes;
            }

            $html[] = '<div ' . $body_attributes . '>';
            $html[] = '<div ' . $panel_attributes . '>';
        }

        if ($item->output_title_tag)
        {
            $html[] = '<' . $item->title_tag . ' class="rl_sliders-title nn_sliders-title">'
                . RL_RegEx::replace('<\?h[0-9](\s[^>]* )?>', '', $item->title_full)
                . '</' . $item->title_tag . '>';
        }

        $html = implode("\n", $html);

        $string = RL_String::replaceOnce($item->orig, $html, $string);
    }

    private static function replaceSyntaxItemList(&$string, $items)
    {
        $first = key($items);
        end($items);

        foreach ($items as $i => &$item)
        {
            self::replaceSyntaxItem($string, $item, $items, ($i == $first));
        }
    }

    private static function replaceUrlLinks(&$string)
    {
        RL_RegEx::matchAll(
            '(?<link><a\s[^>]*href="(?<url>[^"]*)(?:\?|&(?:amp;)?)tab=(?<id>[^"\#&]*)(?:\#[^"]*)?"[^>]*>)(?<text>.*?)</a>',
            $string,
            $matches
        );

        if (empty($matches))
        {
            return;
        }

        self::replaceLinksMatches($string, $matches);
    }

    private static function setOpenItem(&$items, $opened_by_default = '')
    {
        if ($opened_by_default === '')
        {
            return;
        }

        $opened_by_default = (int) $opened_by_default;

        while ($items[$opened_by_default]->haslink)
        {
            $opened_by_default++;
        }

        if ( ! isset($items[$opened_by_default]))
        {
            return;
        }

        $items[$opened_by_default]->open = true;
    }

    private static function setTagAttributes(&$item, $string)
    {
        $values = self::getTagAttributes($string);

        $item = (object) array_merge((array) $item, (array) $values);
    }

    private static function stringHasItem(&$string, $id)
    {
        return (strpos($string, 'data-toggle="collapse" data-id="' . $id . '"') !== false);
    }
}