*
 *  UPGRADE NOTE:
 *    Do not edit or add to this file if you wish to upgrade AbanteCart to newer
 *    versions in the future. If you wish to customize AbanteCart for your
 *    needs, please refer to http://www.AbanteCart.com for more information.
 */
/** @noinspection PhpMultipleClassDeclarationsInspection */
if (!defined('DIR_CORE')) {
    header('Location: static_pages/');
}
/**
 * Class AHtml
 * @method SelectboxHtmlElement buildSelectBox(array $data)
 * @method MultiSelectboxHtmlElement buildMultiSelectBox(array $data)
 * @method HiddenHtmlElement buildHidden(array $data)
 * @method MultiValueHtmlElement buildMultiValue(array $data)
 * @method MultiValueListHtmlElement buildMultiValueList(array $data)
 * @method SubmitHtmlElement buildSubmit(array $data)
 * @method InputHtmlElement buildInput(array $data)
 * @method PasswordHtmlElement buildPassword(array $data)
 * @method TextareaHtmlElement buildTextarea(array $data)
 * @method TextEditorHtmlElement buildTextEditor(array $data)
 * @method CheckboxHtmlElement buildCheckbox(array $data)
 * @method CheckboxGroupHtmlElement buildCheckboxGroup(array $data)
 * @method FileHtmlElement buildFile(array $data)
 * @method RadioHtmlElement buildRadio(array $data)
 * @method ButtonHtmlElement buildButton(array $data)
 * @method RatingHtmlElement buildRating(array $data)
 * @method CaptchaHtmlElement buildCaptcha(array $data)
 * @method ReCaptchaHtmlElement buildReCaptcha(array $data)
 * @method PasswordSetHtmlElement buildPasswordSet(array $data)
 * @method ResourceHtmlElement buildResource(array $data)
 * @method ResourceImageHtmlElement buildResourceImage(array $data)
 * @method DateHtmlElement buildDate(array $data)
 * @method EmailHtmlElement buildEmail(array $data)
 * @method NumberHtmlElement buildNumber(array $data)
 * @method PhoneHtmlElement buildPhone(array $data)
 * @method IPAddressHtmlElement buildIPAddress(array $data)
 * @method CountriesHtmlElement buildCountries(array $data)
 * @method ZonesHtmlElement buildZones(array $data)
 * @method PaginationHtmlElement buildPagination(array $data)
 * @method ModalHtmlElement buildModal(array $data)
 * @method LabelHtmlElement buildLabel(array $data)
 */
class AHtml extends AController
{
    /**
     * @var Registry
     */
    protected $registry;
    /**
     * @var array
     */
    protected $args = [];
    /**
     * @var ARequest
     */
    protected $request;
    /**
     * @param Registry $registry
     */
    public function __construct($registry)
    {
        $this->registry = $registry;
        $this->request = $this->registry->get('request');
    }
    /**
     * Magic method for html-element classes calls
     *
     * @param string $function_name
     * @param $args
     *
     * @return null|string
     * @throws AException
     */
    public function __call(string $function_name, $args)
    {
        $class_name = ltrim($function_name, 'build') . 'HtmlElement';
        if (class_exists($class_name)) {
            /**
             * @var SelectboxHtmlElement|HiddenHtmlElement| $item
             */
            $item = new $class_name($args[0]);
            if (method_exists($item, 'getHtml')) {
                return $item->getHtml();
            } else {
                unset($item);
            }
        }
        return null;
    }
    /**
     * PR Build sub URL
     *
     * @param string $rt
     * @param string $params
     *
     * @return string
     * @throws AException
     */
    private function buildURL($rt, $params = '')
    {
        $subUrl = '';
        //#PR Add admin path if we are in admin
        if (IS_ADMIN) {
            $subUrl .= '&s=' . ADMIN_PATH;
        }
        //add template if present
        if (!empty($this->request->get['sf'])) {
            $subUrl .= '&sf=' . $this->request->get['sf'];
        }
        //if in embed mode add response prefix
        if ($this->registry->get('config')->get('embed_mode')) {
            $subUrl .= '&embed_mode=1';
            if (substr($rt, 0, 2) != 'r/') {
                $rt = 'r/' . $rt;
            }
        }
        return '?' . ($rt ? 'rt=' . $rt : '') . $params . $subUrl;
    }
    /**
     * Get non-secure home URL.
     *
     * @return string
     *
     * Note: Non-secure URL is base on store_url setting. If this setting is using https URL, all URLs will be secure
     * @throws AException
     */
    public function getHomeURL()
    {
        $seo_prefix = $this->registry->get('config')->get('seo_prefix');
        //for embed mode get home link with getURL
        if ($this->registry->get('config')->get('embed_mode')) {
            return $this->getURL('index/home');
        } else {
            //get config_url first
            $home_url = $this->registry->get('config')->get('config_url') . $seo_prefix;
            if (!$home_url) {
                $home_url = defined('HTTP_SERVER')
                    ? HTTP_SERVER . $seo_prefix
                    : 'http://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']);
            }
            return $home_url;
        }
    }
    /**
     * Get non-secure URL. Read note below.
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     *
     * @return string
     *
     * Note: Non-secure URL is base on store_url setting. If this setting is using https URL, all URLs will be secure
     * @throws AException
     */
    public function getNonSecureURL($rt, $params = '', $encode = '')
    {
        return $this->getURL($rt, $params, $encode, true);
    }
    /**
     * Get URL with auto-detection for protocol
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     * @param bool $nonsecure - force to be non-secure
     *
     * @return string
     * @throws AException
     */
    public function getURL($rt, $params = '', $encode = '', $nonsecure = false)
    {
        $seo_prefix = $this->registry->get('config')->get('seo_prefix');
        //detect if request is using HTTPS
        if ($nonsecure === false && HTTPS === true) {
            $server = defined('HTTPS_SERVER')
                ? HTTPS_SERVER . $seo_prefix
                : 'https://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']);
        } else {
            //to prevent garbage session need to check constant HTTP_SERVER
            $server = defined('HTTP_SERVER')
                ? HTTP_SERVER . $seo_prefix
                : 'http://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']);
        }
        if ($this->registry->get('config')->get('storefront_template_debug')
            && isset($this->request->get['tmpl_debug'])
        ) {
            $params .= '&tmpl_debug=' . $this->request->get['tmpl_debug'];
        }
        // add session id for cross-domain transition in secure mode
        if ($this->registry->get('config')->get('config_shared_session') && HTTPS === true) {
            $params .= '&session_id=' . session_id();
        }
        //add token for embed mode with forbidden 3d-party cookies
        if ($this->registry->get('session')->data['session_mode'] == 'embed_token') {
            $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id();
        }
        return $server . INDEX_FILE . $this->url_encode($this->buildURL($rt, $params), $encode);
    }
    /**
     * Build secure URL with session token
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     *
     * @return string
     * @throws AException
     */
    public function getSecureURL($rt, $params = '', $encode = '')
    {
        $session = $this->registry->get('session');
        $config = $this->registry->get('config');
        $seo_prefix = $config->get('seo_prefix');
        // add session id for cross-domain transition in non-secure mode
        if ($config->get('config_shared_session') && HTTPS !== true) {
            $params .= '&session_id=' . session_id();
        }
        $subUrl = $this->buildURL($rt, $params);
        if (IS_ADMIN === true || (defined('IS_API') && IS_API === true)) {
            //Add session token for admin and API
            if (isset($session->data['token']) && $session->data['token']) {
                $subUrl .= '&token=' . $this->registry->get('session')->data['token'];
            }
        }
        //add token for embed mode with forbidden 3d-party cookies
        if ($session->data['session_mode'] == 'embed_token') {
            $subUrl .= '&' . EMBED_TOKEN_NAME . '=' . session_id();
        }
        if ($config->get('storefront_template_debug') && isset($this->request->get['tmpl_debug'])) {
            $subUrl .= '&tmpl_debug=' . $this->request->get['tmpl_debug'];
        }
        return HTTPS_SERVER . $seo_prefix . INDEX_FILE . $this->url_encode($subUrl, $encode);
    }
    /**
     * Build non-secure SEO URL
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     *
     * @return string
     * @throws AException
     */
    public function getSEOURL($rt, $params = '', $encode = '')
    {
        //skip SEO for embed mode
        if ($this->registry->get('config')->get('embed_mode')) {
            return $this->getURL($rt, $params);
        }
        $this->loadModel('tool/seo_url');
        //#PR Generate SEO URL based on standard URL
        //NOTE: SEO URL is non-secure url
        return $this->url_encode($this->model_tool_seo_url->rewrite($this->getNonSecureURL($rt, $params)), $encode);
    }
    /**
     * Build secure SEO URL
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     *
     * @return string
     * @throws AException
     */
    public function getSecureSEOURL($rt, $params = '', $encode = '')
    {
        //add token for embed mode with forbidden 3d-party cookies
        if ($this->registry->get('session')->data['session_mode'] == 'embed_token') {
            $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id();
        }
        //#PR Generate SEO URL based on standard URL
        $this->loadModel('tool/seo_url');
        return $this->url_encode($this->model_tool_seo_url->rewrite($this->getSecureURL($rt, $params)), $encode);
    }
    /**
     * This builds URL to the catalog to be used in admin
     *
     * @param string $rt
     * @param string $params
     * @param string $encode
     * @param bool $ssl
     *
     * @return string
     * @throws AException
     */
    public function getCatalogURL($rt, $params = '', $encode = '', $ssl = false)
    {
        $seo_prefix = $this->registry->get('config')->get('seo_prefix');
        //add token for embed mode with forbidden 3d-party cookies
        if ($this->registry->get('session')->data['session_mode'] == 'embed_token') {
            $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id();
        }
        $subUrl = '?' . ($rt ? 'rt=' . $rt : '') . $params;
        if ($this->registry->get('config')->get('config_ssl') == 2) {
            $ssl = true;
        }
        if ($ssl && parse_url($this->registry->get('config')->get('config_ssl_url'), PHP_URL_SCHEME) == 'https') {
            $HTTPS_SERVER = $this->registry->get('config')->get('config_ssl_url') . $seo_prefix;
        } else {
            $HTTPS_SERVER = HTTPS_SERVER . $seo_prefix;
        }
        $http = $ssl ? $HTTPS_SERVER : HTTP_SERVER . $seo_prefix;
        return $http . INDEX_FILE . $this->url_encode($subUrl, $encode);
    }
    /**
     * encode URL for & to be &
     *
     * @param string $url
     * @param bool $encode
     *
     * @return string
     */
    public function url_encode($url, $encode = false)
    {
        if ($encode) {
            return str_replace('&', '&', (string)$url);
        } else {
            return $url;
        }
    }
    /**
     * Current URL built based on get params with ability to exclude params
     *
     * @param $filter_params array - array of vars to filter
     *
     * @return string - url without unwanted filter parameters
     * @throws AException
     */
    public function currentURL($filter_params = [])
    {
        $params_arr = $this->request->get;
        //detect if there is RT in the params.
        $rt = 'index/home';
        if (has_value($params_arr['rt'])) {
            $rt = $params_arr['rt'];
            $filter_params[] = 'rt';
        }
        if (has_value($params_arr['s'])) {
            $filter_params[] = 's';
        }
        $URI = '&' . $this->buildURI($params_arr, $filter_params);
        return $this->getURL($rt, $URI);
    }
    /**
     * URI encrypt parameters in URI
     *
     * @param $uri
     *
     * @return string - url without unwanted filter parameters
     * @internal param array $filter_params - array of vars to filter
     */
    public function encryptURI($uri)
    {
        $encrypted = base64_encode($uri);
        if (strlen($encrypted) <= 250) {
            return '__e=' . $encrypted;
        } else {
            return $uri;
        }
    }
    /**
     * Build URI from array provided
     *
     * @param $params_arr    array - data array to process
     * @param $filter_params array - array of vars to filter
     *
     * @return string - url without unwanted filter parameters
     */
    public function buildURI($params_arr, $filter_params = [])
    {
        foreach ($filter_params as $rv) {
            unset($params_arr[$rv]);
        }
        return urldecode(http_build_query($params_arr, '', '&'));
    }
    /**
     * Filter query parameters from url.
     *
     * @param $url           string - url to process
     * @param $filter_params string|array - single var or array of vars
     *
     * @return string - url without unwanted filter query parameters
     */
    public function filterQueryParams($url, $filter_params = [])
    {
        list($url_part, $q_part) = explode('?', $url);
        parse_str($q_part, $q_vars);
        //build array if passed as string
        if (!is_array($filter_params)) {
            $filter_params = [$filter_params];
        }
        foreach ($filter_params as $rv) {
            unset($q_vars[$rv]);
        }
        foreach ($q_vars as $key => $val) {
            $q_vars[$key] = $this->request->clean($val);
        }
        $new_qs = urldecode(http_build_query($q_vars, '', '&'));
        return $url_part . '?' . $new_qs;
    }
    /**
     * function returns text error or empty
     *
     * @param string $query
     * @param string $keyword
     *
     * @return string
     * @throws AException
     */
    public function isSEOKeywordExists($query = '', $keyword = '')
    {
        if (!$keyword) {
            return '';
        }
        $seo_key = SEOEncode($keyword);
        $db = $this->registry->get('db');
        $sql = "SELECT *
				FROM " . $db->table('url_aliases') . "
				WHERE query<>'" . $db->escape($query) . "' AND keyword='" . $db->escape($seo_key) . "'";
        $result = $db->query($sql);
        $kList = array_merge(
            array_column($result->rows, 'keyword'),
            array_map('basename', glob(DIR_ROOT . DS . '*', GLOB_ONLYDIR))
        );
        if (in_array($seo_key, $kList)) {
            $url = HTTP_CATALOG . $seo_key;
            return $this->language->getAndReplace('error_seo_keyword', '', [$url, $seo_key]);
        }
        return '';
    }
    /**
     * create html code based on passed data
     *
     * @param  $data - array with element data
     *               sample
     *               $data = array(
     *               'type' => 'input' //(hidden, textarea, selectbox, file...)
     *               'name' => 'input name'
     *               'value' => 'input value' // can be an array for select
     *               'style' => 'my-form'
     *               'form' => 'form id' // needed for unique element ID     *
     *               );
     *
     * @return object
     * @throws AException
     */
    public function buildElement($data)
    {
        return HtmlElementFactory::create($data);
    }
    /**
     * @return string
     * @throws AException
     */
    public function getStoreSwitcher()
    {
        $registry = $this->registry;
        $view = new AView($this->registry, 0);
        //check if store_id is passed or in the session
        $store_id = $registry->get('config')->get('current_store_id');
        //set store selector
        $stores = [];
        $hidden = [];
        $stores[0] = ['name' => $registry->get('language')->get('text_default')];
        $registry->get('load')->model('setting/store');
        /**
         * @var ModelSettingStore $model
         */
        $model = $registry->get('model_setting_store');
        //if loaded not default store - hide store switcher
        $default_store_settings = $model->getStore(0);
        if ($this->registry->get('config')->get('config_url') != $default_store_settings['config_url']) {
            return '';
        }
        $result_stores = $model->getStores();
        if (sizeof($result_stores) <= 1) {
            //hide store switcher if only one store
            return '';
        } else {
            foreach ($result_stores as $rs) {
                $stores[$rs['store_id']] = [
                    'name'     => $rs['alias'] ?: $rs['name'],
                    'store_id' => $rs['store_id'],
                ];
            }
            foreach ($registry->get('request')->get as $name => $value) {
                if ($name == 'store_id') {
                    continue;
                }
                $hidden[$name] = $value;
            }
            $view->assign('all_stores', $stores);
            $view->assign('current_store', $stores[$store_id]['name']);
            $view->assign('hiddens', $hidden);
            $view->assign('text_select_store', $registry->get('language')->get('text_select_store'));
            return $view->fetch('form/store_switcher.tpl');
        }
    }
    /**
     * @return string
     * @throws AException
     */
    public function getContentLanguageSwitcher()
    {
        $registry = $this->registry;
        $view = new AView($this->registry, 0);
        $registry->get('load')->model('localisation/language');
        $results = $registry->get('model_localisation_language')->getLanguages();
        $template['languages'] = [];
        foreach ($results as $result) {
            if ($result['status']) {
                $template['languages'][] = [
                    'name'  => $result['name'],
                    'code'  => $result['code'],
                    'image' => $result['image'],
                ];
            }
        }
        if (sizeof($template['languages']) > 1) {
            //selected in selectbox
            $template['language_code'] = $registry->get('language')->getContentLanguageCode();
            foreach ($registry->get('request')->get as $name => $value) {
                if ($name == 'content_language_code') {
                    continue;
                }
                $template['hiddens'][$name] = $value;
            }
        } else {
            $template['languages'] = [];
        }
        $view->batchAssign($template);
        return $view->fetch('form/language_switcher.tpl');
    }
    /**
     * @return string
     * @throws AException
     */
    public function getContentLanguageFlags()
    {
        $registry = $this->registry;
        $view = new AView($this->registry, 0);
        $registry->get('load')->model('localisation/language');
        $results = $registry->get('model_localisation_language')->getLanguages();
        $template['languages'] = [];
        foreach ($results as $result) {
            if ($result['status']) {
                $template['languages'][] = [
                    'name'  => $result['name'],
                    'code'  => $result['code'],
                    'image' => $result['image'],
                ];
            }
        }
        if (sizeof($template['languages']) > 1) {
            //selected in selectbox
            $template['language_code'] = $registry->get('language')->getContentLanguageCode();
            foreach ($registry->get('request')->get as $name => $value) {
                if ($name == 'content_language_code') {
                    continue;
                }
                $template['hiddens'][$name] = $value;
            }
        } else {
            $template['languages'] = [];
        }
        $view->batchAssign($template);
        return $view->fetch('form/language_flags.tpl');
    }
    /**
     * @return string
     * @throws AException
     */
    public function installLanguageModal(string $mode = 'render')
    {
        $packCartVersion = '';
        $registry = $this->registry;
        $view = new AView($this->registry, 0);
        $languages = $registry->get('extensions')->getExtensionsList(['filter' => 'language']);
        $loaded = array_column((array)$languages->rows, 'key');
        $remote = [];
        $fp = @fopen('https://github.com/abantecart/abantecart-languages/raw/refs/heads/master/list.txt', 'r');
        if ($fp) {
            while (($line = fgets($fp)) !== false) {
                $remote[] = trim($line);
            }
            fclose($fp);
        }
        //detect last cart version
        $url = 'https://github.com/abantecart/abantecart-languages/releases/tag/v' . VERSION;
        if (isUrlAlive($url)) {
            $packCartVersion = VERSION;
        } elseif (VERSION_BUILT != 0) {
            $versionBuilt = (int)VERSION_BUILT - 1;
            while ($versionBuilt != 0) {
                $url = 'https://github.com/abantecart/abantecart-languages/releases/tag/v' . MASTER_VERSION . '.' . MINOR_VERSION . '.' . $versionBuilt;
                if (isUrlAlive($url)) {
                    $packCartVersion = MASTER_VERSION . '.' . MINOR_VERSION . '.' . $versionBuilt;
                    break;
                }
                $versionBuilt--;
            }
        }
        //show only at least one language can be loaded
        $uninstalled = array_diff($remote, $loaded);
        if ($uninstalled && $packCartVersion) {
            $template = [];
            foreach ($uninstalled as $name) {
                $langName = str_replace('default_', '', $name);
                $template['packages'][$name] =
                    [
                        'name'        => $langName,
                        'image_url'   => 'https://github.com/abantecart/abantecart-languages/raw/refs/heads/master/' . $name . '/image/icon%402x.png',
                        'install_url' => 'https://github.com/abantecart/abantecart-languages/releases/download/v' . $packCartVersion . '/' . $name . '.tar.gz'
                    ];
            }
            $template['text_load_languages'] = $this->language->get('button_load_more_languages', 'extension/extensions');
            if ($mode == 'render') {
                $view->batchAssign($template);
                /** @see form/language_install.tpl */
                return $view->fetch('form/language_install.tpl');
            } else {
                return $template['packages'];
            }
        }
        return '';
    }
    /**
     * @param string $html - text that might contain internal links #admin# or #storefront#
     *                          $mode  - 'href' create complete a tag or default just replace URL
     * @param string $type - can be 'message' to convert url into  tag or empty
     * @param bool $for_admin - force mode for converting links to admin side from storefront scope (see AIM-class etc.)
     *
     * @return string - html code with parsed internal URLs
     * @throws AException
     */
    public function convertLinks($html, $type = '', $for_admin = false)
    {
        $is_admin = (IS_ADMIN === true || $for_admin);
        $route_sections = $is_admin ? ["admin", "storefront"] : ["storefront"];
        foreach ($route_sections as $rt_type) {
            preg_match_all(
                '/(#' . $rt_type . '#rt=)[A-z0-9\/_\-?&=%#]{1,255}(\b|")/',
                $html,
                $matches,
                PREG_OFFSET_CAPTURE
            );
            if ($matches) {
                foreach ($matches[0] as $match) {
                    $href = str_replace('?', '&', (string)$match[0]);
                    if ($rt_type == 'admin') {
                        if ($for_admin && IS_ADMIN !== true) {
                            $href .= '&s=' . ADMIN_PATH;
                        }
                        $new_href = str_replace('#admin#', $this->getSecureURL('') . '&', $href);
                    } else {
                        $new_href = str_replace('#storefront#', $this->getCatalogURL('') . '&', $href);
                    }
                    $new_href = str_replace(['&', '&&', '&?'], '&', $new_href);
                    $new_href = str_replace('?&', '?', $new_href);
                    $new_href = str_replace('&', '&', $new_href);
                    switch ($type) {
                        case 'message':
                            $new_href = '#link-text#';
                            break;
                        default:
                            break;
                    }
                    $html = str_replace($match[0], $new_href, $html);
                }
            }
        }
        return $html;
    }
}
/**
 * Class HtmlElementFactory
 */
class HtmlElementFactory
{
    static private $available_elements = [
        'I' => [
            'type'   => 'input',
            'method' => 'buildInput',
            'class'  => 'InputHtmlElement',
        ],
        'T' => [
            'type'   => 'textarea',
            'method' => 'buildTextarea',
            'class'  => 'TextareaHtmlElement',
        ],
        'S' => [
            'type'   => 'selectbox',
            'method' => 'buildSelectbox',
            'class'  => 'SelectboxHtmlElement',
        ],
        'M' => [
            'type'   => 'multiselectbox',
            'method' => 'buildMultiselectbox',
            'class'  => 'MultiSelectboxHtmlElement',
        ],
        'R' => [
            'type'   => 'radio',
            'method' => 'buildRadio',
            'class'  => 'RadioHtmlElement',
        ],
        'C' => [
            'type'   => 'checkbox',
            'method' => 'buildCheckbox',
            'class'  => 'CheckboxHtmlElement',
        ],
        'G' => [
            'type'   => 'checkboxgroup',
            'method' => 'buildCheckboxgroup',
            'class'  => 'CheckboxgroupHtmlElement',
        ],
        'U' => [
            'type'   => 'file',
            'method' => 'buildFile',
            'class'  => 'FileHtmlElement',
        ],
        'K' => [
            'type'   => 'captcha',
            'method' => 'buildCaptcha',
            'class'  => 'CaptchaHtmlElement',
        ],
        'J' => [
            'type'   => 'recaptcha',
            'method' => 'buildReCaptcha',
            'class'  => 'ReCaptchaHtmlElement',
        ],
        'H' => [
            'type'   => 'hidden',
            'method' => 'buildHidden',
            'class'  => 'HiddenHtmlElement',
        ],
        'P' => [
            'type'   => 'multivalue',
            'method' => 'buildMultivalue',
            'class'  => 'MultiValueHtmlElement',
        ],
        'L' => [
            'type'   => 'multivaluelist',
            'method' => 'buildMultivalueList',
            'class'  => 'MultivalueListHtmlElement',
        ],
        'D' => [
            'type'   => 'date',
            'method' => 'buildDateInput',
            'class'  => 'DateInputHtmlElement',
        ],
        'E' => [
            'type'   => 'email',
            'method' => 'buildEmail',
            'class'  => 'EmailHtmlElement',
        ],
        'N' => [
            'type'   => 'number',
            'method' => 'buildNumber',
            'class'  => 'NumberHtmlElement',
        ],
        'F' => [
            'type'   => 'phone',
            'method' => 'buildPhone',
            'class'  => 'PhoneHtmlElement',
        ],
        'A' => [
            'type'   => 'IPaddress',
            'method' => 'buildIPaddress',
            'class'  => 'IPaddressHtmlElement',
        ],
        'O' => [
            'type'   => 'countries',
            'method' => 'buildCountries',
            'class'  => 'CountriesHtmlElement',
        ],
        'Z' => [
            'type'   => 'zones',
            'method' => 'buildZones',
            'class'  => 'ZonesHtmlElement',
        ],
        'B' => [
            'type'   => 'label',
            'method' => 'buildLabel',
            'class'  => 'LabelHtmlElement',
        ],
    ];
    static private $elements_with_options = [
        'S',
        'M',
        'R',
        'G',
        'O',
        'Z',
    ];
    static private $multivalue_elements = [
        'M',
        'R',
        'G',
    ];
    static private $elements_with_placeholder = [
        'S',
        'I',
        'M',
        'O',
        'Z',
        'F',
        'N',
        'E',
        'D',
        'U',
        'T',
    ];
    /**
     *  return array of HTML elements supported
     *  array key - code of element
     *  [
     *   type - element type
     *   method - method in html class to get element html
     *   class - element class
     *  ]
     *
     * @static
     * @return array
     */
    static function getAvailableElements()
    {
        return self::$available_elements;
    }
    /**
     * return array of elements indexes for elements which has options
     *
     * @static
     * @return array
     */
    static function getElementsWithOptions()
    {
        return self::$elements_with_options;
    }
    /**
     * return array of elements indexes for elements which has options
     *
     * @static
     * @return array
     */
    static function getElementsWithPlaceholder()
    {
        return self::$elements_with_placeholder;
    }
    /**
     * return array of elements indexes for elements which has options
     *
     * @static
     * @return array
     */
    static function getMultivalueElements()
    {
        return self::$multivalue_elements;
    }
    /**
     * return element type
     *
     * @static
     *
     * @param $code - element code ( from $available_elements )
     *
     * @return null | string
     */
    static function getElementType($code)
    {
        if (!array_key_exists($code, self::$available_elements)) {
            return null;
        }
        return self::$available_elements[$code]['type'];
    }
    /**
     * @param $data
     *
     * @return HiddenHtmlElement | MultivalueListHtmlElement | MultivalueHtmlElement | SubmitHtmlElement
     * @return InputHtmlElement | PasswordHtmlElement | PaginationHtmlElement | TextareaHtmlElement
     * @return SelectboxHtmlElement | MultiSelectboxHtmlElement | CheckboxHtmlElement
     * @return CheckboxGroupHtmlElement | FileHtmlElement | RadioHtmlElement | ButtonHtmlElement
     * @return FormHtmlElement | RatingHtmlElement | CaptchaHtmlElement | ReCaptchaHtmlElement
     * @return PasswordSetHtmlElement | ResourceHtmlElement | ResourceImageHtmlElement | DateHtmlElement
     * @return EmailHtmlElement | NumberHtmlElement | PhoneHtmlElement | IPaddressHtmlElement
     * @return CountriesHtmlElement | ZonesHtmlElement | ModalHtmlElement
     *
     * @throws AException
     */
    static function create($data)
    {
        $class = ucfirst($data['type'] . 'HtmlElement');
        if (!class_exists($class)) {
            throw new AException(AC_ERR_LOAD, 'Error: Could not load HTML element ' . $data['type'] . '!');
        }
        return new $class($data);
    }
}
/**
 * @abstract
 * Class HtmlElement
 * @property mixed $value
 * @property array $options
 * @property array $disabled_options
 * @property bool $required
 * @property bool $multilingual
 * @property string $template
 * @property array $history
 * @property AMessage $messages
 *
 * @property ARequest $request
 * @property AConfig $config
 * @property ALoader $load
 * @property ALanguageManager|ALanguage $language
 * @property AHtml $html
 * @property ADocument $document
 * @property CSRFToken $csrftoken
 * @property string $icon
 */
abstract class HtmlElement
{
    /** @var array */
    protected $data = [];
    /** @var string */
    public $element_id;
    /** @var AView */
    protected $view;
    /**
     * @param array $data
     *
     * @throws AException
     */
    function __construct($data)
    {
        if (!isset($data['value'])) {
            $data['value'] = '';
        }
        if (isset($data['required']) && $data['required'] == 1) {
            $data['required'] = 'Y';
        }
        $this->data = $data;
        $this->element_id = ($data['id'] ?? '')
            ? preformatTextID($data['id'])
            : preformatTextID($data['name'] ?? '');
        if ($data['form'] ?? '') {
            $this->element_id = $data['form'] . '_' . $this->element_id;
        }
        $this->view = new AView(Registry::getInstance(), 0);
    }
    /**
     * @param string $name
     *
     * @return null|string
     */
    public function __get($name)
    {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        return Registry::getInstance()->get($name);
    }
    /**
     * @param string $name
     * @param mixed $value
     *
     * @return void
     */
    public function __set(string $name, $value)
    {
        $this->data[$name] = $value;
    }
    /**
     * @param string $name
     *
     * @return bool
     */
    public function __isset($name)
    {
        return isset($this->data[$name]);
    }
    /**
     * @return string
     */
    public function __toString()
    {
        $javascript = '';
        $this->data['javascript'] = $this->data['javascript'] ?? '';
        if ($this->data['javascript']) {
            $javascript = $this->data['javascript'];
        }
        return $javascript . $this->getHtml();
    }
    public function __serialize(): array
    {
        $viewData = $this->view->getData();
        $this->data['view_data'] = $viewData;
        $this->view = null;
        return (array)$this;
    }
    public function __unserialize($data)
    {
        $this->view = new AView(Registry::getInstance(), 0);
        $this->view->batchAssign((array)$this->data['view_data']);
        unset($this->data['view_data']);
        if ($data) {
            foreach ($data as $key => $val) {
                $this->{$key} = $val;
            }
        }
    }
    public function addHookVars(array $vars)
    {
        if (!$vars) {
            return;
        }
        //add hook variables into tpl. can be used in *.post.tpl or *.pre.tpl
        foreach ($vars as $hkvName => $hkvValue) {
            $this->view->addHookVar($hkvName, $hkvValue);
        }
    }
    protected function extendAndBatchAssign(array $array)
    {
        $this->view->batchAssign(
            array_merge($this->data, $array)
        );
    }
    protected function getMultiLingual()
    {
        if (is_object($this->language) && sizeof($this->language->getActiveLanguages()) > 1) {
            $multilingual = $this->multilingual;
        } else {
            $multilingual = false;
        }
        return $multilingual;
    }
    /**
     * @return null
     */
    public function getHtml()
    {
        return null;
    }
    protected function _validate_options()
    {
        $this->disabled_options = (array)$this->disabled_options;
        //check case when all options are disabled
        $all_disabled = true;
        foreach ((array)$this->options as $id => $text) {
            if (!in_array($id, $this->disabled_options)) {
                $all_disabled = false;
                break;
            }
        }
        //if all disabled and options presents (for select-chosen element or empty)
        if ($all_disabled && $this->options) {
            if (in_array($this->data['type'], ['selectbox', 'multiselectbox'])) {
                $this->options = ['' => '------'] + $this->options;
            }
            $this->value = [0];
            if ($this->required) {
                $seo_prefix = $this->config->get('seo_prefix');
                $url = HTTPS_SERVER . $seo_prefix;
                $query_string = $this->request->server['QUERY_STRING'];
                if (!str_contains((string)$query_string, '_route_=')) {
                    $url .= '?';
                } else {
                    $query_string = str_replace('_route_=', '', $query_string);
                }
                $url .= $query_string;
                $this->messages->saveWarning(
                    'Form Field #' . $this->element_id . ' Issue',
                    'Abnormal situation. All options of required field "' . $this->data['name']
                    . '" are disabled. URL: ' . $url . ""
                );
            }
        }
    }
}
/**
 * Class HiddenHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property string $attr
 */
class HiddenHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'id'    => $this->element_id,
                'name'  => $this->name,
                'value' => $this->value,
                'attr'  => $this->attr,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/hidden.tpl');
    }
}
/**
 * Class MultiValueListHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property array $values
 * @property array $postvars
 * @property string $content_url
 * @property string $edit_url
 * @property string $form
 * @property string $multivalue_hidden_id
 * @property string $return_to
 * @property string $with_sorting
 * @property array $text
 */
class MultiValueListHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $data = [
            'id'                   => $this->element_id,
            'name'                 => $this->name,
            'values'               => $this->values,
            'content_url'          => $this->content_url,
            'edit_url'             => $this->edit_url,
            'postvars'             => $this->postvars,
            'form_name'            => $this->form,
            'multivalue_hidden_id' => $this->multivalue_hidden_id,
            'return_to'            => ($this->return_to ?: $this->form . '_' . $this->multivalue_hidden_id . '_item_count'),
            'with_sorting'         => $this->with_sorting,
        ];
        $data['text']['delete'] = $this->text['delete'] ?: 'delete';
        $data['text']['delete_confirm'] = $this->text['delete_confirm'] ?: 'Confirm to delete?';
        $data['text']['column_action'] = $this->language->get('column_action');
        $data['text']['column_sort_order'] = $this->language->get('text_sort_order');
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($this->template ?: 'form/multivalue_list.tpl');
    }
}
/**
 * Class MultiValueHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $selected_name
 * @property string $title
 * @property string $selected
 * @property string $content_url
 * @property string $form
 * @property string $return_to
 * @property string $no_save
 * @property array $text
 * @property int $popup_height
 * @property int $popup_width
 * @property array $js
 * @property array $postvars
 */
class MultiValueHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $data = [
            'id'            => $this->element_id,
            'name'          => $this->name,
            'selected_name' => ($this->selected_name ?: 'selected[]'),
            'title'         => $this->title,
            'selected'      => $this->selected,
            'content_url'   => $this->content_url,
            'postvars'      => ($this->postvars ? json_encode($this->postvars) : ''),
            'form_name'     => $this->form,
            'return_to'     => ($this->return_to ?: $this->element_id . '_item_count'),
            'no_save'       => (isset($this->no_save) && $this->no_save),
            'popup_height'  => ((int)$this->popup_height ?: 620),
            'popup_width'   => ((int)$this->popup_width ?: 800),
            // custom triggers for dialog events (custom functions calls)
            'js'            => [
                'apply'  => $this->js['apply'],
                'cancel' => $this->js['cancel'],
            ],
            'text_selected' => $this->text['selected'],
            'text_edit'     => $this->text['edit'] ?: 'Add / Edit',
            'text_apply'    => $this->text['apply'] ?: 'apply',
            'text_save'     => $this->text['save'] ?: 'save',
            'text_reset'    => $this->text['reset'] ?: 'reset',
        ];
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($this->template ?: 'form/multivalue_hidden.tpl');
    }
}
/**
 * Class SubmitHtmlElement
 *
 * @property string $form,
 * @property string $name
 * @property string $value
 * @property string $attr
 * @property string $style
 */
class SubmitHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'form'  => $this->form,
                'name'  => $this->name,
                'value' => $this->value,
                'attr'  => $this->attr,
                'style' => $this->style,
                'icon'  => $this->icon,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/submit.tpl');
    }
}
/**
 * Class InputHtmlElement
 *
 * @property string $element_id
 * @property string $default
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 * @property string $help_url
 * @property bool $multilingual
 * @property array $list
 */
class InputHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $history_url = '';
        if ($this->history) {
            // Check if a field name contains square brackets or block_ prefix
            if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else {
                $fieldName = $this->name;
            }
            $history_url = $this->html->getSecureURL(
                'r/common/common/getDescriptionHistory',
                '&' . http_build_query(
                    [
                        'field'      => $fieldName,
                        'table_name' => $this->history['table'],
                        'record_id'  => $this->history['record_id'],
                        //element ID (need to paste selected value back to the form field)
                        'elm_id'     => $this->element_id
                    ]
                )
            );
            if (is_object($this->language)) {
                $button_field_history = $this->language->get('button_field_history');
            }
        }
        $this->extendAndBatchAssign(
            [
                'name'                 => $this->name,
                'id'                   => $this->element_id,
                'type'                 => 'text',
                'value'                => str_replace('"', '"', (string)$this->value),
                'default'              => $this->default,
                'attr'                 => $this->attr,
                'required'             => $this->required,
                'style'                => $this->style,
                'placeholder'          => $this->placeholder,
                'regexp_pattern'       => trim((string)$this->regexp_pattern, '/'),
                'error_text'           => $this->error_text,
                'multilingual'         => $this->getMultiLingual(),
                'help_url'             => $this->help_url,
                'list'                 => $this->list,
                'history_url'          => $history_url,
                'button_field_history' => $button_field_history ?? '',
            ]
        );
        return $this->view->fetch($this->template ?: 'form/input.tpl');
    }
}
/**
 * Class InputHtmlElement
 *
 * @property string $element_id
 * @property string $default
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 * @property string $help_url
 * @property bool $multilingual
 */
class ColorHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $this->extendAndBatchAssign(
            [
                'name'         => $this->name,
                'id'           => $this->element_id,
                'type'         => 'color',
                'value'        => str_replace('"', '"', (string)$this->value),
                'default'      => $this->default,
                'attr'         => $this->attr,
                'required'     => $this->required,
                'style'        => $this->style,
                'error_text'   => $this->error_text,
                'multilingual' => $this->getMultiLingual(),
                'help_url'     => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/input.tpl');
    }
}
/**
 * Class PasswordHtmlElement
 *
 * @property string $element_id
 * @property string $default
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 */
class PasswordHtmlElement extends HtmlElement
{
    static $starPassword = '**********';
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        if (!$this->required && $this->value) {
            $value = self::$starPassword;
        } else {
            $value = '';
        }
        $this->extendAndBatchAssign(
            [
                'name'           => $this->name,
                'id'             => $this->element_id,
                'type'           => 'password',
                'value'          => $value,
                'has_value'      => ($this->value) ? 'Y' : 'N',
                'attr'           => $this->attr,
                'required'       => $this->required,
                'style'          => $this->style,
                'placeholder'    => $this->placeholder,
                'regexp_pattern' => trim((string)$this->regexp_pattern, '/'),
                'error_text'     => $this->error_text,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/input.tpl');
    }
}
/**
 * Class TextareaHtmlElement
 *
 * @property string $element_id
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property string $label_text
 * @property bool $multilingual
 * @property string $help_url
 */
class TextareaHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $history_url = '';
        if ($this->history) {
            // Check if a field name contains square brackets or block_ prefix
            if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else {
                $fieldName = $this->name;
            }
            $history_url = $this->html->getSecureURL(
                'r/common/common/getDescriptionHistory',
                '&' . http_build_query(
                    [
                        'field'      => $fieldName,
                        'table_name' => $this->history['table'],
                        'record_id'  => $this->history['record_id'],
                        //element ID (need to paste selected value back to the form field)
                        'elm_id'     => $this->element_id
                    ]
                )
            );
            if (is_object($this->language)) {
                $button_field_history = $this->language->get('button_field_history');
            }
        }
        $this->extendAndBatchAssign(
            [
                'name'                 => $this->name,
                'id'                   => $this->element_id,
                'value'                => $this->value,
                'ovalue'               => htmlentities($this->value, ENT_QUOTES, 'UTF-8'),
                'attr'                 => $this->attr,
                'required'             => $this->required,
                'style'                => $this->style,
                'placeholder'          => $this->placeholder,
                'label_text'           => $this->label_text,
                'multilingual'         => $this->getMultiLingual(),
                'help_url'             => $this->help_url,
                'history_url'          => $history_url,
                'button_field_history' => $button_field_history ?? '',
            ]
        );
        return $this->view->fetch($this->template ?: 'form/textarea.tpl');
    }
}
/**
 * Class TextEditorHtmlElement
 *
 * @property ALanguage $language
 * @property string $element_id
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property string $base_url - need for inserting pictures into html for emails
 * @property bool $preview - enable/disable visual mode of tinymce. default true
 * @property string $preview_url - custom preview url
 * @property string $js_onload - custom js-code will be run on doc ready
 * @property bool $multilingual
 * @property array $history - array with table, record_id
 */
class TextEditorHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $this->multilingual = $this->multilingual ?? '';
        $data = [
            'name'        => $this->name,
            'id'          => $this->element_id,
            'value'       => $this->value,
            'ovalue'      => htmlentities($this->value, ENT_QUOTES, 'UTF-8'),
            'attr'        => $this->attr ?? '',
            'required'    => $this->required ?? false,
            'style'       => $this->style ?? '',
            'placeholder' => $this->placeholder ?? '',
            'base_url'    => $this->base_url ?? '',
            'preview'     => $this->preview ?? true,
            'preview_url' => $this->preview_url ?? '',
            'js_onload'   => $this->js_onload ?? ''
        ];
        if ($this->history) {
            // Check if a field name contains square brackets or
            if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) {
                $fieldName = $matches[1];
            } else {
                $fieldName = $this->name;
            }
            $data['history_url'] = $this->html->getSecureURL(
                'r/common/common/getDescriptionHistory',
                '&' . http_build_query(
                    [
                        'field'      => $fieldName,
                        'table_name' => $this->history['table'],
                        'record_id'  => $this->history['record_id'],
                        //element ID (need to paste selected value back to the form field)
                        'elm_id'     => $this->element_id
                    ]
                )
            );
        }
        if (is_object($this->language)) {
            if (sizeof($this->language->getActiveLanguages()) > 1) {
                $data['multilingual'] = $this->multilingual;
            }
            $data['language_code'] = $this->language->getContentLanguageCode();
            $data['tab_text'] = $this->language->get('tab_text');
            $data['tab_visual'] = $this->language->get('tab_visual');
            $data['button_add_media'] = $this->language->get('button_add_media');
            $data['button_preview'] = $this->language->get('button_preview');
            $data['button_field_history'] = $this->language->get('button_field_history');
        }
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($this->template ?: 'form/text_editor.tpl');
    }
}
/**
 * Class SelectboxHtmlElement
 *
 * @property string $element_id
 * @property string $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property array $options
 * @property array $disabled_options
 * @property bool $disabled
 * @property string $ajax_url
 * @property string $search_mode
 * @property string $help_url
 */
class SelectboxHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!is_array($this->value)) {
            $this->value = [$this->value => (string)$this->value];
        }
        $this->options = !$this->options ? [] : (array)$this->options;
        foreach ($this->options as &$opt) {
            $opt = (string)$opt;
        }
        unset($opt);
        $text_continue_typing = $text_looking_for = '';
        if (is_object($this->language)) {
            $text_continue_typing = $this->language->get('text_continue_typing', '', true);
            $text_looking_for = $this->language->get('text_looking_for', '', true);
        }
        $text_continue_typing = !$text_continue_typing
        || $text_continue_typing == 'text_continue_typing' ? 'Continue typing ...' : $text_continue_typing;
        $text_looking_for =
            !$text_looking_for || $text_looking_for == 'text_looking_for' ? 'Looking for' : $text_looking_for;
        $this->_validate_options();
        $data = [
            'name'                 => $this->name,
            'id'                   => $this->element_id,
            'value'                => $this->value,
            'ovalue'               => $this->value,
            'options'              => $this->options,
            'disabled'             => $this->disabled,
            'disabled_options'     => $this->disabled_options,
            'attr'                 => $this->attr,
            'required'             => $this->required,
            'style'                => $this->style,
            'placeholder'          => $this->placeholder,
            'ajax_url'             => $this->ajax_url, //if mode of data load is ajax based
            'search_mode'          => $this->search_mode,
            'text_continue_typing' => $text_continue_typing,
            'text_looking_for'     => $text_looking_for,
            'help_url'             => $this->help_url,
        ];
        if ($this->template) {
            $template = $this->template;
        } elseif (str_contains((string)$this->style, 'chosen')) {
            $data['ajax_url'] = $this->ajax_url; //if mode of data load is ajax based
            $data['text_continue_typing'] = $text_continue_typing;
            $data['text_looking_for'] = $text_looking_for;
            $template = 'form/chosen_select.tpl';
        } else {
            $template = 'form/selectbox.tpl';
        }
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($template);
    }
}
/**
 * Class MultiSelectBoxHtmlElement
 *
 * @property string $element_id
 * @property array|mixed $value
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $placeholder
 * @property array $options
 * @property array $disabled_options
 * @property string $filter_params - some additional parameters
 * @property string $ajax_url
 * @property string|array $option_attr
 * @property string $help_url
 * @property bool $disabled
 */
class MultiSelectBoxHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!is_array($this->value)) {
            $this->value = [$this->value => $this->value];
        }
        $this->_validate_options();
        $data = [
            'name'             => $this->name,
            'id'               => $this->element_id,
            'value'            => $this->value,
            'options'          => $this->options,
            'disabled_options' => $this->disabled_options,
            'disabled'         => $this->disabled,
            'attr'             => $this->attr . ' multiple="multiple" ',
            'required'         => $this->required,
            'style'            => $this->style,
            'placeholder'      => $this->placeholder,
            'filter_params'    => $this->filter_params,
            'help_url'         => $this->help_url,
        ];
        if ($this->template) {
            $template = $this->template;
        } elseif (str_contains((string)$this->style, 'chosen')) {
            $option_attr = $this->option_attr && !is_array($this->option_attr)
                ? [$this->option_attr]
                : $this->option_attr;
            $option_attr = $option_attr ?: [];
            $data['ajax_url'] = $this->ajax_url; //if mode of data load is ajax based
            $data['option_attr'] = $option_attr; //list of custom html5 attributes for options of selectbox
            $data['text_continue_typing'] = $this->language->get('text_continue_typing', '', true);
            $data['text_looking_for'] = $this->language->get('text_looking_for', '', true);
            $template = 'form/chosen_select.tpl';
        } else {
            $template = 'form/selectbox.tpl';
        }
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($template);
    }
}
/**
 * Class CheckboxHtmlElement
 *
 * @property string|int $value
 * @property bool $checked
 * @property string $element_id
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $label_text
 * @property string $help_url
 */
class CheckboxHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $checked = false;
        $this->data['value'] = $this->value = $this->value ?? 1;
        if ($this->template) {
            $tpl = $this->template;
        } elseif (str_contains((string)$this->style, 'btn_switch')) { //for switch button NOTE: value is binary (1 or 0)!!!
            $checked = is_null($this->checked) && $this->value || (bool)$this->checked;
            if ($checked) {
                $this->value = 1;
            } else {
                $this->value = 0;
            }
            $tpl = 'form/switch.tpl';
        } else {//for generic checkbox NOTE: in this case value must be any and goes to tpl as-is
            $checked = !is_null($this->checked) ? $this->checked : false;
            $tpl = 'form/checkbox.tpl';
        }
        $text_on = $text_off = '';
        if (is_object($this->language)) {
            $text_on = $this->language->get('text_on', '', true);
            $text_off = $this->language->get('text_off', '', true);
        }
        $text_on = !$text_on || $text_on == 'text_on' ? 'ON' : $text_on;
        $text_off = !$text_off || $text_off == 'text_off' ? 'OFF' : $text_off;
        $this->extendAndBatchAssign(
            [
                'name'       => $this->name,
                'id'         => $this->element_id,
                'value'      => $this->value,
                'attr'       => $this->attr,
                'required'   => $this->required,
                'label_text' => $this->label_text ?: $this->display_name,
                'checked'    => $checked,
                'style'      => $this->style,
                'text_on'    => $text_on,
                'text_off'   => $text_off,
                'help_url'   => $this->help_url,
            ]
        );
        return $this->view->fetch($tpl);
    }
}
/**
 * Class CheckboxGroupHtmlElement
 *
 * @property string|int $value
 * @property array $options
 * @property array $disabled_options
 * @property string $element_id
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property bool $scrollbox
 * @property string $help_url
 * @property string $placeholder
 */
class CheckboxGroupHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->value = !is_array($this->value) ? [$this->value => $this->value] : $this->value;
        $this->_validate_options();
        if ($this->options && is_array($this->options)) {
            $option_keys = array_keys($this->options);
            foreach ($this->value as $value) {
                if ($value && !in_array($value, $option_keys)) {
                    $this->options += [$value => 'unknown'];
                }
            }
        }
        $this->extendAndBatchAssign(
            [
                'name'             => $this->name,
                'id'               => $this->element_id,
                'value'            => $this->value,
                'options'          => $this->options,
                'disabled_options' => $this->disabled_options,
                'attr'             => $this->attr . ' multiple="multiple" ',
                'required'         => $this->required,
                'scrollbox'        => $this->scrollbox,
                'style'            => $this->style,
                'placeholder'      => $this->placeholder,
                'help_url'         => $this->help_url,
            ]
        );
        if ($this->template) {
            $template = $this->template;
        } elseif (str_contains((string)$this->style, 'chosen')) {
            $template = 'form/chosen_select.tpl';
        } else {
            $template = 'form/checkboxgroup.tpl';
        }
        return $this->view->fetch($template);
    }
}
/**
 * Class FileHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property string $help_url
 * @property string $placeholder
 */
class FileHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'         => $this->name,
                'id'           => $this->element_id,
                'attr'         => $this->attr,
                'required'     => $this->required,
                'style'        => $this->style,
                'default_text' => $this->language->get('text_click_browse_file'),
                'text_browse'  => $this->language->get('text_browse'),
                'placeholder'  => $this->placeholder,
                'help_url'     => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/file.tpl');
    }
}
/**
 * Class RadioHtmlElement
 *
 * @property string $value
 * @property string $element_id
 * @property string $name
 * @property string $attr
 * @property string $required
 * @property string $style
 * @property array $options
 * @property array $disabled_options
 * @property array $disabled
 * @property string $help_url
 */
class RadioHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        //if no option provided, default to value
        if (empty($this->options) && has_value($this->value)) {
            $this->options = [$this->value => $this->value];
        }
        $this->_validate_options();
        $this->extendAndBatchAssign(
            [
                'name'             => $this->name,
                'id'               => $this->element_id,
                'value'            => $this->value,
                'options'          => $this->options,
                'disabled_options' => $this->disabled_options,
                'disabled'         => $this->disabled,
                'attr'             => $this->attr,
                'required'         => $this->required,
                'style'            => $this->style,
                'help_url'         => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/radio.tpl');
    }
}
/**
 * Class ButtonHtmlElement
 *
 * @property string $element_id
 * @property string $text
 * @property string $title
 * @property string $attr
 * @property string $href
 * @property string $style
 * @property string $href_class
 * @property string $target
 */
class ButtonHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'text'       => $this->text,
                'title'      => $this->title,
                'id'         => $this->element_id,
                'attr'       => $this->attr,
                'style'      => $this->style,
                'href'       => $this->href,
                'href_class' => $this->href_class,
                'icon'       => $this->icon,
                'target'     => $this->target,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/button.tpl');
    }
}
/**
 * Class FormHtmlElement
 *
 * @property string $name
 * @property string $action
 * @property string $attr
 * @property string $method
 * @property string $style
 * @property string $enctype
 * @property bool $csrf
 */
class FormHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->method = $this->method ?: 'post';
        $this->enctype = $this->enctype ?: 'application/x-www-form-urlencoded';
        $data = [
            'id'      => $this->name,
            'name'    => $this->name,
            'action'  => $this->action,
            'method'  => $this->method,
            'attr'    => $this->attr,
            'style'   => $this->style,
            'enctype' => $this->enctype,
        ];
        //add CSRF token
        if ($this->csrf === true) {
            $csrftoken = $this->csrftoken;
            $data['csrfinstance'] = $csrftoken->setInstance();
            $data['csrftoken'] = $csrftoken->setToken();
        }
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($this->template ?: 'form/form_open.tpl') . $this->view->fetch('form/form_csrf.tpl');
    }
}
/**
 * Class RatingHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property array $options
 * @property bool $required
 */
class RatingHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'     => $this->name,
                'id'       => $this->element_id,
                'value'    => $this->value,
                'options'  => $this->options,
                'style'    => 'star',
                'required' => $this->required,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/rating.tpl');
    }
}
/**
 * Class CaptchaHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property Registry $registry
 */
class CaptchaHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'        => $this->name,
                'id'          => $this->element_id,
                //TODO: remove deprecated attribute aform_field_type
                'attr'        => 'aform_field_type="captcha" ' . $this->attr . ' data-aform-field-type="captcha"',
                'style'       => $this->style,
                'required'    => $this->required,
                'captcha_url' => $this->html->getURL('common/captcha'),
                'placeholder' => $this->placeholder,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/captcha.tpl');
    }
}
/**
 * Class ReCaptchaHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $attr
 * @property string $language_code
 * @property string $recaptcha_site_key
 */
class ReCaptchaHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'               => $this->name ?: 'g-recaptcha-response',
                'id'                 => $this->element_id,
                'attr'               => $this->attr . ' data-aform-field-type="captcha"',
                'language_code'      => $this->language_code,
                'recaptcha_site_key' => trim((string)$this->recaptcha_site_key),
                'recaptcha_v3'       => $this->config->get('account_recaptcha_v3') ?: 0,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/recaptcha.tpl');
    }
}
/**
 * Class PasswordsetHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 */
class PasswordsetHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'                  => $this->name,
                'id'                    => $this->element_id,
                'value'                 => $this->value,
                'attr'                  => $this->attr,
                'style'                 => $this->style,
                'required'              => $this->required,
                'text_confirm_password' => $this->language->get('text_confirm_password'),
                'placeholder'           => $this->placeholder,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/passwordset.tpl');
    }
}
/**
 * Class ResourceHtmlElement
 *
 * @property string $rl_type - image or audio or pdf etc
 * @property string $element_id
 * @property string $name
 * @property string $resource_path
 * @property int $resource_id
 * @property string $object_name
 * @property string $object_id
 * @property bool $hide    - sign to hide image preview
 * @property string $placeholder
 */
class ResourceHtmlElement extends HtmlElement
{
    function __construct($data)
    {
        parent::__construct($data);
    }
    public function getHtml()
    {
        if (empty($this->rl_type)) {
            throw new AException(
                AC_ERR_LOAD,
                'Error: Could not load HTML element of resource library. Resource type not given!'
            );
        }
        $data = [
            'id'            => $this->element_id,
            'wrapper_id'    => $this->element_id . '_wrapper',
            'name'          => $this->name,
            'resource_path' => $this->resource_path,
            'resource_id'   => $this->resource_id ?: null,
            'object_name'   => $this->object_name,
            'object_id'     => $this->object_id,
            'rl_type'       => $this->rl_type,
            'hide'          => (bool)$this->hide,
        ];
        if (!$data['resource_id'] && $data['resource_path']) {
            $path = ltrim($data['resource_path'], $data['rl_type'] . '/');
            $r = new AResource($data['rl_type']);
            $data['resource_id'] = $r->getIdFromHexPath($path);
        }
        if ($data['resource_id'] && !$data['resource_path']) {
            $r = new AResource($data['rl_type']);
            $info = $r->getResource($data['resource_id']);
            if ($info['resource_path']) {
                $data['resource_path'] = $data['rl_type'] . '/' . $info['resource_path'];
            } else {
                //for code-resources
                $data['resource_path'] = $data['resource_id'];
            }
        }
        $this->extendAndBatchAssign($data);
        return $this->view->fetch($this->template ?: 'form/resource.tpl');
    }
}
/**
 * Class ResourceImageHtmlElement
 *
 * @property int $width
 * @property int $height
 * @property string $attr
 * @property string $url
 */
class ResourceImageHtmlElement extends HtmlElement
{
    function __construct($data)
    {
        parent::__construct($data);
    }
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'url'    => $this->url,
                'width'  => $this->width,
                'height' => $this->height,
                'attr'   => $this->attr,
            ]
        );
        return $this->view->fetch($this->template ?: 'common/resource_image.tpl');
    }
}
/**
 * Class DateHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $end_date_name
 * @property string $start_date_name
 * @property string $value
 * @property string $default
 * @property string $style
 * @property string $attr
 * @property array $js_custom_config
 * @property string $mode
 * @property bool $required
 * @property string $dateformat
 * @property string $highlight
 * @property string $help_url
 */
class DateHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $this->element_id = preg_replace('/[\[+\]]/', '_', $this->element_id);
        if ($this->end_date_name || $this->start_date_name) {
            $this->mode = 'range';
        }
        $this->extendAndBatchAssign(
            [
                'name'             => $this->name,
                'id'               => $this->element_id,
                'type'             => 'text',
                'value'            => $this->value,
                'default'          => $this->default,
                'required'         => $this->required,
                'attr'             => $this->attr,
                //js config of initialization of date field
                'js_custom_config' => (object)$this->js_custom_config,
                'mode'             => in_array($this->mode, ["single", "multiple", "range"]) ? $this->mode : "single",
                'style'            => $this->style,
                'dateformat'       => $this->dateformat ?: format4Datepicker($this->language->get('date_format_short')),
                'help_url'         => $this->help_url,
                'end_date_name'    => $this->end_date_name,
                'start_date_name'  => $this->start_date_name,
                'text_reset'       => $this->language->get('button_reset'),
            ]
        );
        /** @see public_html/admin/view/default/template/form/date.tpl */
        return $this->view->fetch($this->template ?: 'form/date.tpl');
    }
}
/**
 * Class EmailHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property string $default
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 * @property string $help_url
 */
class EmailHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $this->extendAndBatchAssign(
            [
                'name'           => $this->name,
                'id'             => $this->element_id,
                'type'           => 'email',
                'value'          => str_replace('"', '"', (string)$this->value),
                'default'        => $this->default,
                //TODO: remove deprecated attribute aform_field_type
                'attr'           => 'aform_field_type="email" ' . $this->attr . ' data-aform-field-type="captcha"',
                'required'       => $this->required,
                'style'          => $this->style,
                'placeholder'    => $this->placeholder,
                'regexp_pattern' => trim((string)$this->regexp_pattern, '/'),
                'error_text'     => $this->error_text,
                'help_url'       => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/input.tpl');
    }
}
/**
 * Class NumberHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property int $min
 * @property int $max
 * @property string $default
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 * @property string $help_url
 */
class NumberHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $this->extendAndBatchAssign(
            [
                'name'           => $this->name,
                'id'             => $this->element_id,
                'type'           => 'number',
                'value'          => str_replace('"', '"', (string)$this->value),
                'min'            => $this->min,
                'max'            => $this->max,
                'default'        => $this->default,
                //TODO: remove deprecated attribute aform_field_type
                'attr'           => 'aform_field_type="number" ' . $this->attr
                    . ' data-aform-field-type="captcha"',
                'required'       => $this->required,
                'style'          => $this->style,
                'placeholder'    => $this->placeholder,
                'regexp_pattern' => trim((string)$this->regexp_pattern, '/'),
                'error_text'     => $this->error_text,
                'help_url'       => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/input.tpl');
    }
}
/**
 * Class PhoneHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property string $default
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property string $regexp_pattern
 * @property string $error_text
 * @property string $help_url
 */
class PhoneHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        if (!isset($this->default)) {
            $this->default = '';
        }
        if ($this->value == '' && !empty($this->default)) {
            $this->value = $this->default;
        }
        $telJs = '/javascript/intl-tel-input/js/intlTelInput.min.js';
        if (is_file($this->view->templateResource($telJs, 'file'))) {
            $this->document->addScript($this->view->templateResource($telJs));
            $this->document->addStyle(
                [
                    'href' => $this->view->templateResource('/javascript/intl-tel-input/css/intlTelInput.css'),
                    'rel'  => 'stylesheet',
                ]
            );
        }
        $this->extendAndBatchAssign(
            [
                'name'           => $this->name,
                'id'             => $this->element_id,
                'type'           => 'tel',
                'value'          => str_replace('"', '"', (string)$this->value),
                'default'        => $this->default,
                'attr'           => $this->attr,
                'required'       => $this->required,
                'style'          => $this->style,
                'placeholder'    => $this->placeholder,
                'regexp_pattern' => trim((string)$this->regexp_pattern, '/'),
                'error_text'     => $this->error_text,
                'help_url'       => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/phone.tpl');
    }
}
/**
 * Class IPaddressHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $attr
 */
class IPAddressHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'id'    => $this->element_id,
                'name'  => $this->name,
                'value' => $this->request->getRemoteIP(),
                //TODO: remove deprecated attribute aform_field_type
                'attr'  => 'aform_field_type="ipaddress" ' . $this->attr . ' data-aform-field-type="captcha"',
            ]
        );
        return $this->view->fetch($this->template ?: 'form/hidden.tpl');
    }
}
/**
 * Class CountriesHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property array $options
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property string $help_url
 * @property string $submit_mode
 */
class CountriesHtmlElement extends HtmlElement
{
    public function __construct($data)
    {
        parent::__construct($data);
        /** @var ModelLocalisationCountry $mdl */
        $mdl = $this->load->model('localisation/country');
        $results = $mdl->getCountries();
        if ($this->submit_mode == 'id') {
            $this->options = array_column($results, 'name', 'country_id');
        } else {
            $this->options = array_column($results, 'name', 'name');
        }
    }
    public function getHtml()
    {
        if (!is_array($this->value)) {
            $this->value = [$this->value => (string)$this->value];
        }
        $this->options = !$this->options ? [] : $this->options;
        $this->extendAndBatchAssign(
            [
                'name'        => $this->name,
                'id'          => $this->element_id,
                'value'       => $this->value,
                'options'     => $this->options,
                'attr'        => $this->attr,
                'required'    => $this->required,
                'style'       => $this->style,
                'placeholder' => $this->placeholder,
                'help_url'    => $this->help_url,
                'icon'        => $this->icon
            ]
        );
        return $this->view->fetch($this->template ?: 'form/selectbox.tpl');
    }
}
/**
 * Class ZonesHtmlElement
 *
 * @property string $element_id
 * @property string $name
 * @property string $value
 * @property string $submit_mode
 * @property int|array $default_value
 * @property string $zone_field_name
 * @property string $default_zone_field_name
 * @property string $default_zone_name
 * @property array $default_zone_value
 * @property string $zone_name
 * @property array $options
 * @property array $zone_options
 * @property array|string|int $zone_value
 * @property string $style
 * @property string $attr
 * @property bool $required
 * @property string $placeholder
 * @property string $help_url
 * @property string $zone_only
 */
class ZonesHtmlElement extends HtmlElement
{
    public function __construct($data)
    {
        parent::__construct($data);
        if ($this->zone_only) {
            return;
        }
        /** @var ModelLocalisationCountry $mdl */
        $mdl = $this->load?->model('localisation/country');
        $results = $mdl->getCountries();
        $this->zone_options = [];
        $this->default_zone_field_name = 'zone_id';
        $config_country_id = $this->config->get('config_country_id');
        $options = [];
        foreach ($results as $c) {
            if ($c['country_id'] == $config_country_id) {
                $this->default_value = $this->submit_mode == 'id'
                    ? [$config_country_id]
                    : [$c['name'] => $c['name']];
            }
            if ($this->submit_mode == 'id') {
                $options[$c['country_id']] = $c['name'];
            } else {
                $options[$c['name']] = $c['name'];
            }
        }
        $this->options = $options;
    }
    public function getHtml()
    {
        if (!is_array($this->value)) {
            if (!$this->value) {
                $this->value = [];
            } else {
                $this->value = [$this->value => (string)$this->value];
            }
        }
        $this->zone_name = $this->zone_name ? urlencode($this->zone_name) : '';
        $this->default_zone_value = [];
        $this->options = !$this->options ? [] : $this->options;
        $this->element_id = preg_replace('/[\[+\]+]/', '_', $this->element_id);
        $html = new AHtml(Registry::getInstance());
        if ($this->submit_mode == 'id') {
            $url = $html->getSecureURL('common/zone');
        } else {
            $url = $html->getSecureURL('common/zone/names');
        }
        /** @var ModelLocalisationZone $model_zone */
        $model_zone = $this->load->model('localisation/zone');
        $config_country_id = $this->config->get('config_country_id');
        if ($this->submit_mode == 'id') {
            $id = $this->value ? key($this->value) : $config_country_id;
            $results = $model_zone->getZonesByCountryId($id);
        } else {
            if ($this->value) {
                $name = current($this->value);
            } else {
                /** @var ModelLocalisationCountry $mdl */
                $mdl = $this->load->model('localisation/country');
                $temp = $mdl->getCountry($config_country_id);
                $name = $temp['name'];
            }
            $results = $model_zone->getZonesByCountryName($name);
        }
        if (!is_array($this->zone_value)) {
            $this->zone_value =
                $this->zone_value ? [(string)$this->zone_value => (string)$this->zone_value] : [];
        }
        $config_zone_id = $this->config->get('config_zone_id');
        $zone_options = [];
        foreach ($results as $result) {
            // default zone_id is zone of shop
            if ($result['zone_id'] == $config_zone_id) {
                $this->default_zone_value = $this->submit_mode == 'id'
                    ? [$config_zone_id]
                    : [$result['name'] => $result['name']];
                $this->default_zone_name = $result['name'];
            }
            if ($this->submit_mode == 'id') {
                $zone_options[$result['zone_id']] = $result['name'];
            } else {
                $zone_options[$result['name']] = $result['name'];
            }
            $this->zone_options = $zone_options;
        }
        $this->extendAndBatchAssign(
            [
                'name'            => $this->name ?: 'country_id',
                'id'              => $this->element_id,
                'value'           => $this->value ?: $this->default_value,
                'options'         => $this->options,
                'attr'            => $this->attr,
                'required'        => $this->required,
                'style'           => $this->style,
                'url'             => $url,
                'zone_field_name' => $this->zone_field_name ?: $this->default_zone_field_name ?: 'zone_id',
                'zone_name'       => $this->zone_name ?: $this->default_zone_name,
                'zone_value'      => (array)($this->zone_value ?: $this->default_zone_value),
                'zone_options'    => $this->zone_options ?: ['-1' => $this->language->get('text_none')],
                'submit_mode'     => $this->submit_mode,
                'placeholder'     => $this->placeholder,
                'help_url'        => $this->help_url,
                //sign to show only zone selector, without countries
                'zone_only'       => $this->zone_only,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/countries_zones.tpl');
    }
}
/*
* Build pagination HTML element based on the template.
* @since 1.1.5
*/
class PaginationHtmlElement extends HtmlElement
{
    public $sts = [];
    /**
     * @param array $data
     *
     * @throws AException
     */
    public function __construct($data)
    {
        parent::__construct($data);
        //default settings
        $this->sts['total'] = 0;
        $this->sts['page'] = 1;
        $this->sts['limit'] = (int)$this->config?->get('config_catalog_limit') ?: 20;
        $this->sts['split'] = $data['split'] ?: 10;
        $this->sts['limits'] = [];
        //max pages to show in pagination
        $this->sts['num_links'] = $data['num_links'] ?: 10;
        $this->sts['url'] = '';
        $this->sts['text'] = $this->language->get('text_pagination_pages_info', '', true);
        $this->sts['text'] = $this->sts['text'] == 'text_pagination_pages_info'
            ? 'Showing {start} to {end} of {total} ({pages} Pages)'
            : $this->sts['text'];
        $this->sts['text_limit'] = $this->language->get('text_per_page', '', true);
        $this->sts['text_limit'] = $this->sts['text_limit'] == 'text_per_page' ? 'Per Page' : $this->sts['text_limit'];
        $this->sts['text_first'] = $this->language->get('text_first', '', true);
        $this->sts['text_first'] = $this->sts['text_first'] == 'text_first' ? '|<<' : $this->sts['text_first'];
        $this->sts['text_last'] = $this->language->get('text_last', '', true);
        $this->sts['text_last'] = $this->sts['text_last'] == 'text_last' ? '>>|' : $this->sts['text_last'];
        $this->sts['text_next'] = $this->language->get('text_next', '', true);
        $this->sts['text_next'] = $this->sts['text_next'] == 'text_next' ? '>' : $this->sts['text_next'];
        $this->sts['text_prev'] = $this->language->get('text_previous', '', true);
        $this->sts['text_prev'] = $this->sts['text_prev'] == 'text_previous' ? '<' : $this->sts['text_prev'];
        $this->sts['style_links'] = 'links';
        $this->sts['style_results'] = 'results';
        $this->sts['style_limits'] = 'limits';
        //override default
        foreach ($this->data as $key => $val) {
            if (isset($val)) {
                $this->sts[$key] = $val;
            }
        }
    }
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        //Build pagination data and display
        $registry = Registry::getInstance();
        $html = new AHtml($registry);
        $s = $this->sts;
        $s['no_perpage'] = $s['no_perpage'] ?? 0;
        //some more defaults
        if ($s['page'] < 1 || !is_numeric($s['page'])) {
            $s['page'] = 1;
        }
        if (!$s['limit'] || !is_numeric($s['limit'])) {
            $s['limit'] = 10;
        }
        //count limits if needed
        if (!$s['no_perpage'] && !$s['limits']) {
            $s['limits'][0] = $x = ($s['split'] ?: $registry->get('config')->get('config_catalog_limit'));
            while ($x <= 50) {
                $s['limits'][] = $x;
                $x += $s['limits'][0];
            }
        }
        $s['url'] = str_replace('--limit--', $s['limit'], (string)$s['url']);
        $s['direct_url'] = str_replace('--limit--', $s['limit'], (string)$s['direct_url']);
        $s['total_pages'] = ceil($s['total'] / $s['limit']);
        if ($s['page'] > 1) {
            //not first page
            $this->view->assign('first_url', str_replace('--page--', 1, (string)$s['url']));
            $this->view->assign('prev_url', str_replace('--page--', $s['page'] - 1, (string)$s['url']));
        }
        if ($s['total_pages'] > 1) {
            if ($s['total_pages'] <= $s['num_links']) {
                $s['start'] = 1;
                $s['end'] = $s['total_pages'];
            } else {
                $s['start'] = $s['page'] - floor($s['num_links'] / 2);
                $s['end'] = $s['page'] + floor($s['num_links'] / 2);
                if ($s['start'] < 1) {
                    $s['end'] += abs($s['start']) + 1;
                    $s['start'] = 1;
                }
                if ($s['end'] > $s['total_pages']) {
                    $s['start'] -= ($s['end'] - $s['total_pages']);
                    $s['end'] = $s['total_pages'];
                }
            }
        } else {
            $s['start'] = $s['end'] = 1;
        }
        if ($s['page'] < $s['total_pages']) {
            $this->view->assign('next_url', str_replace('--page--', $s['page'] + 1, (string)$s['url']));
            $this->view->assign('last_url', str_replace('--page--', $s['total_pages'], (string)$s['url']));
        }
        $replace = [
            ($s['total']) ? (($s['page'] - 1) * $s['limit']) + 1 : 0,
            ((($s['page'] - 1) * $s['limit']) > ($s['total'] - $s['limit'])) ? $s['total']
                : ((($s['page'] - 1) * $s['limit']) + $s['limit']),
            $s['total'],
            $s['total_pages'],
        ];
        if (!$s['no_perpage']) {
            if (!in_array($s['limit'], $s['limits'])) {
                $s['limits'][] = $s['limit'];
                sort($s['limits']);
            }
            $options = [];
            foreach ($s['limits'] as $item) {
                $options[$item] = $item;
            }
            $limit_url = str_replace('--page--', 1, (string)$s['url']);
            $limit_url = str_replace('&limit=' . $s['limit'], '', $limit_url);
            $limit_select = $html->buildSelectbox(
                [
                    'name'    => 'limit',
                    'value'   => $s['limit'],
                    'options' => $options,
                    'style'   => 'input-mini',
                    'attr'    => ' onchange="location=\'' . $limit_url . '&limit=\'+this.value;"',
                ]
            );
            $limit_select = str_replace('&', '&', (string)$limit_select);
            $this->view->assign('limit_select', $limit_select);
        }
        $find = [
            '{start}',
            '{end}',
            '{total}',
            '{pages}',
            '--limit--',
        ];
        $s['text'] = str_replace($find, $replace, (string)$s['text']);
        $this->extendAndBatchAssign($s);
        return $this->view->fetch($this->template ?: 'form/pagination.tpl');
    }
}
/**
 * Class ModalHtmlElement
 *
 * @property string $id
 * @property string $title
 * @property string $modal_type
 * @property string $content
 * @property string $footer
 * @property string $data_source
 * @property string $js_onshow
 * @property string $js_onload
 * @property string $js_onclose
 */
class ModalHtmlElement extends HtmlElement
{
    /**
     * @param array $data
     *
     * @throws AException
     */
    public function __construct($data)
    {
        parent::__construct($data);
    }
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $modal_type = $this->modal_type ?: 'lg';
        $this->extendAndBatchAssign(
            [
                'id'          => $this->id,
                'title'       => $this->title,
                'content'     => $this->content,
                'footer'      => $this->footer,
                'modal_type'  => $modal_type,
                // if 'ajax' we clean up modal content after it close
                'data_source' => (string)$this->data_source,
                // js-triggers for modal events
                'js_onshow'   => (string)$this->js_onshow,
                'js_onload'   => ($this->data_source == 'ajax' ? (string)$this->js_onload : ';'),  //if content
                'js_onclose'  => (string)$this->js_onclose,
            ]
        );
        /** @see public_html/admin/view/default/template/form/modal.tpl */
        return $this->view->fetch($this->template ?: 'form/modal.tpl');
    }
}
/**
 * Class LabelHtmlElement
 * NOTE: only for storefront
 *
 * @property string $element_id
 * @property string $name
 * @property string $text
 * @property string $value
 * @property string $style
 * @property string $help_url
 * @property string $attr
 */
class LabelHtmlElement extends HtmlElement
{
    /**
     * @return string
     * @throws AException
     */
    public function getHtml()
    {
        $this->extendAndBatchAssign(
            [
                'name'     => $this->name,
                'id'       => $this->element_id,
                'text'     => html_entity_decode(($this->text ?: $this->value)),
                'attr'     => $this->attr,
                'style'    => $this->style,
                'help_url' => $this->help_url,
            ]
        );
        return $this->view->fetch($this->template ?: 'form/label.tpl');
    }
}
/**
 * @property string $name
 * @property float|int $min
 * @property float|int $max
 * @property float|int $step
 * @property float|int $from
 * @property float|int $to
 * @property boolean $disabled
 */
class RangeHtmlElement extends HtmlElement
{
    public function getHtml()
    {
        $data = [
            'id'       => $this->element_id,
            'name'     => $this->name,
            'min'      => $this->min,
            'max'      => $this->max,
            'step'     => $this->step,
            'from'     => $this->from,
            'to'       => $this->to,
            'disabled' => $this->disabled,
        ];
        $this->view->batchAssign($data);
        return $this->view->fetch($this->template ?: 'form/range.tpl');
    }
}