<?php 
/**
* @version		$Id: form5.class.php 2602 2025-06-12 17:20:42Z IOS $
* @package		Elxis
* @subpackage	Form builder
* @copyright	Copyright (c) 2006-2025 Elxis CMS (https://www.elxis.org). All rights reserved.
* @license		Elxis Public License ( https://www.elxis.org/elxis-public-license.html )
* @author		Elxis Team ( https://www.elxis.org )
* @description 	Elxis CMS is free software. Read the license for copyright notices and details
*/

defined('_ELXIS_') or die ('Direct access to this location is not allowed');


class elxis5Form {

	private $curlang = 'en';
	private $dir = 'ltr';//text direction based on current language
	private $sitelangs = array();//published languages array
	private $tabidx = 0;
	private $opentab = 0;
	private $php64bit = -1;
	private $options = array(
		'idprefix' => '',//a prefix to add in all automatically generated id attributes (recommended)
		'fieldsetclass' => 'elx5_fieldset',
		'rowclass' => 'elx5_formrow',
		'labelclass' => 'elx5_label', //elx5_label or elx5_labelblock or anything
		'sideclass' => 'elx5_labelside', //contents next to label or elx5_zero or anything
		'tipclass' => 'elx5_tip',
		'tabs_id' => 'elx5_tabs',
		'tabs_class' => 'elx5_tabs',
		'tabs_content_class' => 'elx5_tab_content',
		'tabs_container_class' => 'elx5_tab_container',
		'tabs_open_class' => 'elx5_tab_open',
		'tabs_use_iconclass' => 0,
		'tabs_use_numeric' => 1,
		'date_format' => 'Y-m-d',
		'datetime_format' => 'Y-m-d H:i',
		'time_format' => 'H:i',
		'returnhtml' => false//true: return html, false: echo html
	);
	private $formid = '';


	/***************/
	/* CONSTRUCTOR */
	/***************/
	public function __construct($options=array()) {
		$eLang = eFactory::getLang();

		$this->curlang = $eLang->currentLang();
		$this->dir = $eLang->getinfo('DIR');

		$this->options['date_format'] = $eLang->get('DATE_FORMAT_BOX');
		$this->options['datetime_format'] = preg_replace('@(\:s)$@', '', $eLang->get('DATE_FORMAT_BOX_LONG'));//remove seconds (Y-m-d H:i)

		$this->setOptions($options);
	}


	/***************************/
	/* GET PUBLISHED LANGUAGES */
	/***************************/
	private function getSiteLangs() {
		if (!$this->sitelangs) {
			$this->sitelangs = eFactory::getLang()->getSiteLangs(true);
		}
		return $this->sitelangs;
	}


	/********************/
	/* SET FORM OPTIONS */
	/********************/
	public function setOptions($options=array()) {
		if (!$options) { return; }
		if (!is_array($options)) { return; }
		foreach ($options as $k => $v) { $this->options[$k] = $v; }
	}


	/*******************/
	/* SET FORM OPTION */
	/*******************/
	public function setOption($option, $value) {
		$this->options[$option] = $value;
	}


	/*******************/
	/* GET FORM OPTION */
	/*******************/
	public function getOption($option, $default='') {
		if (($option != '') && isset($this->options[$option])) { return $this->options[$option]; }
		return $default;
	}


	/*************/
	/* OPEN FORM */
	/*************/
	public function openForm($attrs=array()) {
		$attributes = array(
			'name' => 'elxisform',
			'enctype' => 'application/x-www-form-urlencoded',//or multipart/form-data
			'class' => 'elx5_form',
			'action' => 'index.php',
			'method' => 'post'
		);

		if (eFactory::getElxis()->getConfig('TRANSLATOR') != '') {
			if (defined('ELXIS_ADMIN')) {
				$attributes['data-trurl'] = eFactory::getElxis()->makeAURL('cpanel:translate', 'inner.php');
			} else {
				$attributes['data-trurl'] = eFactory::getElxis()->makeURL('content:translate', 'inner.php');
			}
		}

		$this->combinedStarted = false;
		$this->combined = array();

		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}
		if (!isset($attributes['id'])) { $attributes['id'] = $attributes['name'].''.rand(1000, 9999); }
		$this->formid = $attributes['id'];

		$html = '<form';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= '>'."\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/**************/
	/* CLOSE FORM */
	/**************/
	public function closeForm() {
		$html = "</form>\n";
		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/************************/
	/* START A NEW FIELDSET */
	/************************/
	public function openFieldset($legend='', $attrs=array()) {
		$attributes = array('class' => $this->options['fieldsetclass']);
		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}

		$html = '<fieldset';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= '>';
		if ($legend != '') { $html .= '<legend>'.$legend.'</legend>'; }
		$html .= "\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/***********************/
	/* CLOSE OPEN FIELDSET */
	/***********************/
	public function closeFieldset() {
		$html = "</fieldset>\n";
		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************/
	/* OPEN TOOLBAR */
	/****************/
	public function openToolbar($attrs=array(), $title='') {
		$attributes = array('class' => 'elx5_toolbar');
		if ($title != '') { $attributes['class'] = 'elx5_toolbar_2cols'; }

		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}

		$html = '<div';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		if ($title != '') {
			$html .= '<div class="elx5_toolbar_tcol"><h1>'.$title."</h1></div>\n";
			$html .= '<div class="elx5_toolbar_bcol">'."\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*****************/
	/* CLOSE TOOLBAR */
	/*****************/
	public function closeToolbar($has_title=false) {
		$html = '';
		if ($has_title) { $html .= "</div>\n"; }//elx5_toolbar_bcol
		$html .= "</div>\n";
		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/************/
	/* ADD NOTE */
	/************/
	public function addNote($text, $class='elx5_info', $attrs=array()) {
		if ($class == '') { $class = 'elx5_info'; }
		$attributes = array('class' => $class);
		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}

		$html = '<div';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= '>'.$text."</div>\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/********************************************/
	/* ADD INFORMATIONAL LINE (NOTE WITH LABEL) */
	/********************************************/
	public function addInfo($label, $text, $attrs=array()) {
		$attributes = array('class' => 'elx5_formtext');
		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}

		$html = '<div class="'.$this->options['rowclass'].'">'."\n";
		$html .= '<label class="'.$this->options['labelclass'].'">'.$label."</label>\n";
		$html .= '<div class="'.$this->options['sideclass'].'">';
		if ($attributes) {
			$html .= '<div';
			foreach ($attributes as $k => $v) {
				//if ($v == '') { continue; }
				$html .= ' '.$k.'="'.$v.'"';
			}
			$html .= '>'.$text.'</div>';
		} else {
			$html .= $text;
		}
		$html .= "</div>\n";
		$html .= "</div>\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/******************/
	/* ADD INPUT ITEM */
	/******************/
	public function addInput($type, $name, $value, $label, $attrs=array()) {
		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_text',
			'dir' => $this->dir,
			'placeholder' => $label
		);
		$tip = '';
		$password_meter = 0;
		$passmatch = '';
		$sidetext = '';
		$sidetextposition = 2;
		$datalistoptions = array();
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'password_meter') {
					if ($type == 'password') { $password_meter = (int)$v; }//1: "front" or 2: "back"
					continue;
				}
				if ($k == 'match') {
					if ($type == 'password') { $passmatch = $v; }//$v : first password element id
					continue;
				}
				if ($k == 'sidetext') { $sidetext = $v; continue; }
				if ($k == 'sidetextposition') { $sidetextposition = (int)$v; continue; }
				if ($k == 'datalistoptions') { $datalistoptions = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}

		if ($password_meter > 0) {
			$attributes['data-passmeterid'] = $attributes['id'];//triggers event instead of inline elx5PasswordMeter
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
		}
		if ($passmatch != '') {
			$attributes['data-passmatchid'] = $attributes['id'];//triggers event instead of inline elx5PasswordMatch
			$attributes['data-passmatch'] = $passmatch;
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
		}
		if (isset($attributes['readonly'])) {
			if (strpos($attributes['class'], 'readonly') === false) { $attributes['class'] .= ' elx5_readonly'; }
		}

		if ($value != '') {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		if ($password_meter > 0) {
			$html .= '<div class="elx5_sideinput_wrap">'."\n";
			if ($password_meter == 1) {
				$html .= '<div class="elx5_sideinput_value_front"><meter id="'.$attributes['id'].'_passmeter" class="elx5_meter" value="0" min="0" max="10"></meter></div>'."\n";
				$html .= '<div class="elx5_sideinput_input_end">'."\n";
			} else {
				$html .= '<div class="elx5_sideinput_value_end"><meter id="'.$attributes['id'].'_passmeter" class="elx5_meter" value="0" min="0" max="10"></meter></div>'."\n";
				$html .= '<div class="elx5_sideinput_input_front">'."\n";
			}
		} else if ($sidetext != '') {//for currency and any other side text
			$html .= '<div class="elx5_sideinput_wrap">'."\n";
			if ($sidetextposition == 2) {
				$html .= '<div class="elx5_sideinput_value_end" id="'.$attributes['id'].'_sidetext">'.$sidetext.'</div>'."\n";
				$html .= '<div class="elx5_sideinput_input_front">'."\n";
			} else {
				$html .= '<div class="elx5_sideinput_value_front" id="'.$attributes['id'].'_sidetext">'.$sidetext.'</div>'."\n";
				$html .= '<div class="elx5_sideinput_input_end">'."\n";
			}
		}

		$html .= '<input type="'.$type.'" name="'.$name.'" value="'.$value.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";

		if (isset($attributes['list'])) {
			if (count($datalistoptions) > 0) {
				$html .= '<datalist id="'.$attributes['list'].'">'."\n";
				foreach ($datalistoptions as $opt) {
					$html .= '<option value="'.$opt['value'].'">'.$opt['label']."</option>\n";
				}
				$html .= "</datalist>\n";
			}
		}

		if (($password_meter > 0) || ($sidetext != '')) { $html .= "</div></div>\n"; }
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/**********************/
	/* ADD TEXTAREA FIELD */
	/**********************/
	public function addTextarea($name, $value, $label, $attrs=array()) {
		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_textarea',
			'dir' => $this->dir,
			'placeholder' => $label
		);

		$editor = '';
		$editoroptions = array();
		$contentslang = '';
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'editor') {
					$editor = trim($v);
					continue;
				}
				if ($k == 'editoroptions') {
					$editoroptions = is_array($v) ? $v : array();
					continue;
				}
				if ($k == 'contentslang') {//TODO: USE IN JODIT?
					$contentslang = (string)$v;
					continue;
				}
				if ($k == 'onlyelement') {
					$onlyelement = (int)$v;
					continue;
				}
				$attributes[$k] = $v;
			}
		}

		$editor_js = '';
		if ($editor == 'html') {
			$tip = ''; //disable tips for rich text editor
			$elxis = eFactory::getElxis();
			$elxeditor = $elxis->obj('editor');
			$elxeditor->prepare($attributes['id'], $editor, $contentslang, $editoroptions);
			$editor_js = $elxeditor->getJS();
			unset($elxeditor);
			$value = htmlspecialchars($value);
		}

		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		$html .= '<textarea name="'.$name.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'forcedir') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">".$value."</textarea>\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}
		$html .= $editor_js;

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/* SHORTCUTS & ELXIS 4.X COMPATIBILITY */  
	public function addUrl($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('url', $name, $value, $label, $attrs);
		} else {
			$this->addInput('url', $name, $value, $label, $attrs);
		}
	}


	public function addText($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addInput('text', $name, $value, $label, $attrs);
		} else {
			$this->addInput('text', $name, $value, $label, $attrs);
		}
	}


	public function addPassword($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('password', $name, $value, $label, $attrs);
		} else {
			$this->addInput('password', $name, $value, $label, $attrs);
		}
	}


	public function addEmail($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('email', $name, $value, $label, $attrs);
		} else {
			$this->addInput('email', $name, $value, $label, $attrs);
		}
	}


	public function addNumber($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('number', $name, $value, $label, $attrs);
		} else {
			$this->addInput('number', $name, $value, $label, $attrs);
		}
	}


	/***********************/
	/* ADD INPUT TEL FIELD */
	/***********************/
	public function addTel($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('tel', $name, $value, $label, $attrs);
		} else {
			$this->addInput('tel', $name, $value, $label, $attrs);
		}
	}


	/*************************/
	/* ADD INPUT COLOR FIELD */
	/*************************/
	public function addColor($name, $value='', $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';
		if (!isset($attrs['class'])) { $attrs['class'] = 'elx5_text elx5_minitext'; }

		if ($this->options['returnhtml']) {
			return $this->addInput('color', $name, $value, $label, $attrs);
		} else {
			$this->addInput('color', $name, $value, $label, $attrs);
		}
	}


	/**************************/
	/* ADD INPUT SEARCH FIELD */
	/**************************/
	public function addSearch($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addInput('search', $name, $value, $label, $attrs);
		} else {
			$this->addInput('search', $name, $value, $label, $attrs);
		}
	}


	/************************/
	/* ADD INPUT DATE FIELD */
	/************************/
	public function addDateNative($name, $value='', $label='', $attrs=array()) { //HTML5 date
		if ($this->options['returnhtml']) {
			return $this->addInput('date', $name, $value, $label, $attrs);
		} else {
			$this->addInput('date', $name, $value, $label, $attrs);
		}
	}


	/****************************/
	/* ADD INPUT DATETIME FIELD */
	/****************************/
	public function addDatetimeNative($name, $value='', $label='', $attrs=array()) { //HTML5 datetime
		if ($this->options['returnhtml']) {
			return $this->addInput('datetime', $name, $value, $label, $attrs);
		} else {
			$this->addInput('datetime', $name, $value, $label, $attrs);
		}
	}


	/**********************************/
	/* ADD INPUT DATETIME-LOCAL FIELD */
	/**********************************/
	public function addDatetimelocal($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addInput('datetime-local', $name, $value, $label, $attrs);
		} else {
			$this->addInput('datetime-local', $name, $value, $label, $attrs);
		}
	}


	/************************/
	/* ADD INPUT TIME FIELD */
	/************************/
	public function addTimeNative($name, $value='', $label='', $attrs=array()) { //HTML5 time
		if ($this->options['returnhtml']) {
			return $this->addInput('time', $name, $value, $label, $attrs);
		} else {
			$this->addInput('time', $name, $value, $label, $attrs);
		}
	}


	/*************************/
	/* ADD INPUT MONTH FIELD */
	/*************************/
	public function addMonthNative($name, $value='', $label='', $attrs=array()) { //HTML5 month
		if ($this->options['returnhtml']) {
			return $this->addInput('month', $name, $value, $label, $attrs);
		} else {
			$this->addInput('month', $name, $value, $label, $attrs);
		}
	}


	/************************/
	/* ADD INPUT LIST FIELD */
	/************************/
	public function addList($name, $value='', $label='', $options=array(), $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		if ($options) {
			if (!isset($attrs['id'])) { $attrs['id'] = $this->options['idprefix'].$name; }
			if (!isset($attrs['list'])) { $attrs['list'] = $attrs['id'].'data'; }
			$attrs['datalistoptions'] = $options;
		}

		if ($this->options['returnhtml']) {
			return $this->addInput('text', $name, $value, $label, $attrs);
		} else {
			$this->addInput('text', $name, $value, $label, $attrs);
		}
	}


	/******************/
	/* ADD FILE FIELD */
	/******************/
	public function addFile($name, $label='', $attrs=array()) {
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		if ($this->options['returnhtml']) {
			return $this->addInput('file', $name, '', $label, $attrs);
		} else {
			$this->addInput('file', $name, '', $label, $attrs);
		}
	}


	/***********************************************/
	/* ADD AJAX FILE UPLOAD (Simple-Ajax-Uploader) */
	/***********************************************/
	public function addAjaxFile($name, $attrs=array()) {
		$eLang = eFactory::getLang();

		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_btn'
		);

		$tip = '';
		$help = '';
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'help') { $help = $v; continue; }
				$attributes[$k] = $v;
			}
		}

		$html = '<div class="'.$this->options['rowclass'].'">'."\n";
		if ($help != '') {
			$html .= '<div class="elx5_2colwrap">'."\n";
			$html .= '<div class="elx5_2colbox">'."\n";
		}
		$html .= '<button type="button" name="'.$name.'" id="'.$attributes['id'].'" class="'.$attributes['class'].'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'id') { continue; }
			if ($k == 'class') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ' data-wait="'.$eLang->get('PLEASE_WAIT').'" data-selfile="'.$eLang->get('SELECT_FILE').'">'.$eLang->get('SELECT_FILE')."</button>\n";
		if ($help != '') {
			$html .= "</div>\n";//elx5_2colbox
			$html .= '<div class="elx5_2colbox">'.$help."</div>\n";
			$html .= "</div>\n";//elx5_2colwrap
		}
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }

		$html .= '<div id="'.$attributes['id'].'outer" class="progress progress-striped active elx5_invisible">'."\n";
		$html .= '<div id="'.$attributes['id'].'bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>'."\n";
		$html .= "</div>\n";//outer
		
		$html .= '<div id="'.$attributes['id'].'msgbox" class="elx5_invisible"></div>'."\n";
		$html .= "</div>\n";//rowclass

		if (!defined('SIMPLE_AJAX_UPLOADER')) {
			$jsfile = eFactory::getElxis()->secureBase(true).'/includes/js/SimpleAjaxUploader.min.js';
			eFactory::getDocument()->addScriptLink($jsfile);
			define('SIMPLE_AJAX_UPLOADER', 1);
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/************************/
	/* ADD PRICE TEXT FIELD */
	/************************/
	public function addPrice($name, $value='0.00', $label='', $decimals=2, $currency='EUR', $attrs=array()) {
		$decimals = (int)$decimals;
		if ($decimals < 1) { $decimals = 2; }
		if ($currency == '') { $currency = 'EUR'; }

		$value = filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
		if (!is_numeric($value)) { $value = '0.00'; }
		$value = number_format($value, $decimals, '.', '');

		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';
		if (!isset($attrs['lang'])) { $attrs['lang'] = 'en'; }
		if (!isset($attrs['step'])) {
			switch ($decimals) {
				case 0: $attrs['step'] = '1'; break;
				case 1: $attrs['step'] = '0.1'; break;
				case 2: $attrs['step'] = '0.01'; break;
				default: $attrs['step'] = 'any'; break;
			} 
		}
		if (!isset($attrs['sidetext'])) { $attrs['sidetext'] = $currency; }
		if (!isset($attrs['sidetextposition'])) { $attrs['sidetextposition'] = 2; }//1:front, 2: end

		if ($this->options['returnhtml']) {
			return $this->addInput('number', $name, $value, $label, $attrs);
		} else {
			$this->addInput('number', $name, $value, $label, $attrs);
		}
	}


	/************************/
	/* ADD HIDDEN INPUT BOX */
	/************************/
	public function addHidden($name, $value='', $attrs=array()) {
		$attributes = array('id' => $this->options['idprefix'].$name, 'dir' => 'ltr');
		if ($attrs) {
			foreach ($attrs as $k => $v) { $attributes[$k] = $v; }
		}

		$html = '<input type="hidden" name="'.$name.'" value="'.$value.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/******************************/
	/* ADD TOKEN HIDDEN INPUT BOX */
	/******************************/
	public function addToken($name='') {
		if (trim($name) == '') { $name = 'token'; }
		$token = md5(uniqid(rand(), true));
		eFactory::getSession()->set('token_'.$name, $token);

		$html = '<input type="hidden" name="token" value="'.$token.'" id="'. $this->options['idprefix'].'token" dir="ltr">'."\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/***************/
	/* MAKE OPTION */
	/***************/
	public function makeOption($value, $label, $attributes=array(), $disabled=0, $optgroup='') {
		$disabled = (int)$disabled;
		$attributes = (is_array($attributes)) ? $attributes : array();
		return array(
			'value' => $value,
			'label' => $label, 
			'attributes' => $attributes,
			'disabled' => $disabled,
			'optgroup' => $optgroup
		);
	}


	/******************************/
	/* ADD DROP DOWN SELECT FIELD */
	/******************************/
	public function addSelect($name, $label='', $selected=null, $options=array(), $attrs=array()) {
		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => $this->dir);
		$multiple = 0;
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'multiple') {
					if (($v == 1) || ($v == 'multiple') || ($v === true)) { $multiple = 1; }
					continue;
				}
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'onlyelement') {
					$onlyelement = (int)$v;
					continue;
				}
				$attributes[$k] = $v;
			}
		}

		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		if (($multiple == 1) && ($attributes['class'] == 'elx5_select')) { $attributes['class'] = 'elx5_select elx5_selectmultiple'; }

		if (isset($attributes['data-showparams']) || isset($attributes['data-hideparams'])) {//elxisParameters class
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		if ($multiple == 1) {
			$html .= '<select name="'.$name.'[]" multiple="multiple"';
			if (($selected === null) || ($selected === false)) { $selected = array(); }
		} else {
			$html .= '<select name="'.$name.'"';
		}
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'multiple') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";

		if (is_array($options)) {
			if (count($options) > 0) {
				$optgroup = '';
				foreach ($options as $option) {
					if ($option['optgroup'] != '') {
						if ($optgroup == '') {
							$html .= '<optgroup label="'.$option['optgroup'].'">'."\n";
						} else {
							if ($option['optgroup'] != $optgroup) {
								$html .= "</optgroup>\n";
								$html .= '<optgroup label="'.$option['optgroup'].'">'."\n";
							}
						}
						$optgroup = $option['optgroup'];
					} else {
						if ($optgroup != '') { $html .= "</optgroup>\n"; }
						$optgroup = '';
					}

					$dis = ($option['disabled'] == 1) ? ' disabled="disabled"' : '';

					if ($multiple == 1) {
						$sel = in_array($option['value'], $selected) ? ' selected="selected"' : '';
					} else {
						$sel = ($option['value'] == $selected) ? ' selected="selected"' : '';
					}

					$attr = '';
					if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
						foreach ($option['attributes'] as $key => $val) { $attr .= ' '.$key.'="'.$val.'"'; }
					}
					$html .= '<option value="'.$option['value'].'"'.$dis.''.$sel.''.$attr.'>'.$option['label']."</option>\n";
				}

				if ($optgroup != '') { $html .= "</optgroup>\n"; }
			}
		}

		$html .= "</select>\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/************************************************************************/
	/* ADD DROP DOWN SELECT FIELD WITH CUSTOM MULTIPLE SELECT FUNCTIONALITY */
	/************************************************************************/
	public function addMultiSelect($name, $label='', $selected=array(), $options=array(), $attrs=array()) {
		$eLang = eFactory::getLang();

		if (!is_array($options)) { $options = array(); }
		if (!is_array($selected)) {
			$v = (string)$selected;
			$selected = array();
			if ($v != '') { $selected = explode(',', $v); }
		}

		$attributes = array(
			'id' => $this->options['idprefix'].$name, 
			'class' => 'elx5_select', 
			'dir' => $this->dir, 
			'onchange' => '', 
			'data-lngremove' => $eLang->get('REMOVE'),
			'flagvalues' => 0,
			'noselected_text' => ''
		);

		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'multiple') { continue; }
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		$attributes['flagvalues'] = (int)$attributes['flagvalues'];

		if ($attributes['onchange'] == '') {
			$attributes['data-multisel'] = $attributes['id'];//triggers event instead of inline elx5MultiSelectAdd
			$attributes['data-flagvalues'] = $attributes['flagvalues'];
			unset($attributes['onchange']);
		}
		if (!defined('ELXIS_FORMELONLOAD')) {//also used on elx5MultiSelectRemove below ( [data-remmsel] )
			eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
			define('ELXIS_FORMELONLOAD', 1);
		}
		if ($attributes['flagvalues'] == 1) { $this->prepareMultiLinguism(); }
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		$selected_vals = array();
		$html .= '<div class="elx5_msel_items" id="'.$attributes['id'].'_items">'."\n";
		if ($selected) {
			if (count($options) > 0) {
				foreach ($selected as $value) {
					foreach ($options as $option) {
						if ($value == $option['value']) {
							$selected_vals[] = $value;
							if ($attributes['flagvalues'] == 1) {
								$class = 'elx5_msel_item elx5_mlflag'.$option['value'];
							} else {
								$class = 'elx5_msel_item';
							}
							$html .= '<a href="#" class="'.$class.'" data-remmsel="'.$attributes['id'].'" data-rval="'.$option['value'].'" data-flagvalues="'.$attributes['flagvalues'].'" title="'.$eLang->get('REMOVE').'">'.$option['label'].' <span>x</span></a>'."\n";
							break;
						}
					}
				}
			}
		}

		if ($attributes['noselected_text'] != '') {
			$class_name = 'elx5_invisible';
			if (!$selected) { $class_name = 'elx5_msel_noselitem'; }
			$html .= '<a href="#" class="'.$class_name.'" id="'.$attributes['id'].'_noselitem">'.$attributes['noselected_text'].'</a>'."\n";
		}
		$html .= "</div>\n";

		$html .= '<select name="'.$name.'_selector" id="'.$attributes['id'].'_selector"';
		foreach ($attributes as $k => $v) {
			if ($k == 'id') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'flagvalues') { continue; }
			if ($k == 'noselected_text') { continue; }
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";

		$html .= '<option value="" selected="selected">- '.$eLang->get('ADD')." -</option>\n";
		$html .= '<option value="addall">- '.$eLang->get('ADD_ALL')." -</option>\n";
		if (count($options) > 0) {
			foreach ($options as $option) {
				if (($option['value'] == '') || ($option['value'] == 'addall')) { continue; }//invalid value
				$dis = ($option['disabled'] == 1) ? ' disabled="disabled"' : '';
				$attr = '';
				if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
					foreach ($option['attributes'] as $key => $val) { $attr .= ' '.$key.'="'.$val.'"'; }
				}
				$html .= '<option value="'.$option['value'].'"'.$dis.''.$attr.'>'.$option['label']."</option>\n";
			}
		}
		$html .= "</select>\n";

		$selected_vals_str = $selected_vals ? implode(',', $selected_vals) : '';
		$html .= '<input type="hidden" name="'.$name.'" id="'.$attributes['id'].'" dir="ltr" value="'.$selected_vals_str.'">'."\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*************************************************************/
	/* ADD DROP DOWN SELECT FIELD WITH "OTHER" TEXT INPUT OPTION */
	/*************************************************************/
	public function addSelectAddOther($name, $label='', $selected='', $options=array(), $attrs=array()) {
		$eLang = eFactory::getLang();

		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => $this->dir);
		$tip = '';
		$noselvalue = '';
		$noseltext = ($label != '') ? ' -'.$label.' -' : '- '.$eLang->get('SELECT').' -';
		$othertext = '--- '.$eLang->get('OTHER').' ---';
		$other_customtitle = '';
		$onlyelement = 0;//Elxis 5.1

		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'name') { continue; }
				if ($k == 'multiple') { continue; }//no multiple support
				if ($k == 'onchange') { continue; }
				if ($k == 'noselvalue') { $noselvalue = $v; continue; }
				if ($k == 'noseltext') { $noseltext = $v; continue; }
				if ($k == 'othertext') {
					$othertext = $v;
					$other_customtitle = eUTF::trim(str_replace('-', '', $v));
					continue;
				}
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		if (!defined('ELXIS_FORMELONLOAD')) {
			eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
			define('ELXIS_FORMELONLOAD', 1);
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		$html .= '<select name="'.$name.'" data-selother="'.$attributes['id'].'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";

		$found = false;
		if ($noselvalue == $selected) {
			$sel = ' selected="selected"';
			$found = true;
		} else {
			$sel = '';
		}

		$html .= '<option value="'.$noselvalue.'"'.$sel.'>'.$noseltext."</option>\n";

		if (is_array($options)) {
			if (count($options) > 0) {
				$optgroup = '';
				foreach ($options as $option) {
					if ($option['optgroup'] != '') {
						if ($optgroup == '') {
							$html .= '<optgroup label="'.$option['optgroup'].'">'."\n";
						} else {
							if ($option['optgroup'] != $optgroup) {
								$html .= "</optgroup>\n";
								$html .= '<optgroup label="'.$option['optgroup'].'">'."\n";
							}
						}
						$optgroup = $option['optgroup'];
					} else {
						if ($optgroup != '') { $html .= "</optgroup>\n"; }
						$optgroup = '';
					}

					$dis = ($option['disabled'] == 1) ? ' disabled="disabled"' : '';
					
					if ($option['value'] == $selected) {
						$sel = ' selected="selected"';
						$found = true;
					} else {
						$sel = '';
					}
					$attr = '';
					if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
						foreach ($option['attributes'] as $key => $val) { $attr .= ' '.$key.'="'.$val.'"'; }
					}
					$html .= '<option value="'.$option['value'].'"'.$dis.''.$sel.''.$attr.'>'.$option['label']."</option>\n";
				}

				if ($optgroup != '') { $html .= "</optgroup>\n"; }
			}
		}

		if (!$found && ($selected != '')) {
			$html .= '<option value="OTHER" selected="selected">'.$othertext."</option>\n";
			$other_class = 'elx5_tsspace';
			$other_value = $selected;
		} else {
			$html .= '<option value="OTHER">'.$othertext."</option>\n";
			$other_class = 'elx5_invisible';
			$other_value = '';
		}
		$html .= "</select>\n";

		if ($other_customtitle != '') {
			$title = $other_customtitle;
		} else {
			$title = ($label != '') ? $label : $eLang->get('OTHER');
		}
		$html .= '<div class="'.$other_class.'" id="'.$attributes['id'].'_other_box">';
		$html .= '<input type="text" name="'.$name.'_other" id="'.$attributes['id'].'_other" value="'.$other_value.'" class="elx5_text" title="'.$title.'" placeholder="'.$title.'">'."\n";
		$html .= "</div>\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*****************************************************/
	/* MAKE IMAGES SELECT LIST WITH PREVIEW (Elxis 5.0+) */
	/*****************************************************/
	public function addSelectImage($name, $label='', $selected='', $options=array(), $attrs=array()) {
		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => 'ltr');
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'name') { continue; }
				if ($k == 'multiple') { continue; }
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if (!isset($attributes['onchange'])) {
			$attributes['data-addselimg'] = $attributes['id'];//triggers event instead of inline elx5SwitchPreviewImage
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$images_dirurl = eFactory::getElxis()->secureBase().'/';
		$empty_image = $images_dirurl.'templates/system/images/nopicture.png';
		$cur_imgurl = ($selected == '') ? $empty_image : $images_dirurl.$selected;

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		$html .= '<div class="elx5_fileimg_wrap">'."\n";
		$html .= '<a href="'.$cur_imgurl.'" target="_blank" id="'.$attributes['id'].'_imagelink">';
		$html .= '<img src="'.$cur_imgurl.'" alt="image" id="'.$attributes['id'].'_image" data-empty="'.$empty_image.'" data-dirurl="'.$images_dirurl.'"></a>'."\n";
		$html .= "</div>\n";//elx5_fileimg_wrap
		$html .= '<div class="elx5_fileimg_inwrap">'."\n";
		$html .= '<select name="'.$name.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'multiple') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		if (is_array($options)) {
			if (count($options) > 0) {
				foreach ($options as $option) {
					$dis = ($option['disabled'] == 1) ? ' disabled="disabled"' : '';
					$sel = ($option['value'] == $selected) ? ' selected="selected"' : '';
					$attr = '';
					if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
						foreach ($option['attributes'] as $key => $val) { $attr .= ' '.$key.'="'.$val.'"'; }
					}
					$html .= '<option value="'.$option['value'].'"'.$dis.''.$sel.''.$attr.'>'.$option['label']."</option>\n";
				}
			}
		}
		$html .= "</select>\n";
		$html .= "</div>\n";//elx5_fileimg_inwrap
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*********************************/
	/* ADD ACCESS LEVEL SELECT FIELD */
	/*********************************/
	public function addAccesslevel($name, $label='', $selected=0, $userlevel=0, $attrs=array()) {
		$eLang = eFactory::getLang();
		$db = eFactory::getDB();

		if (!is_array($attrs)) { $attrs = array(); }
		$attrs['dir'] = 'ltr';

		$stmt = $db->prepare("SELECT * FROM ".$db->quoteId('#__groups').' ORDER BY '.$db->quoteId('level').' DESC');
		$stmt->execute();
		$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

		$options = array();
		if ($rows) {
			$levels = array();
			foreach ($rows as $row) {
				$level = $row['level'];
				if (!isset($levels[$level])) { $levels[$level] = 0; }
				$levels[$level]++;
			}

			$lastlevel = -1;
			$space = '';
			foreach ($rows as $row) {
				$level = $row['level'];
				$lowlevel = $row['level'] * 1000;
				$exactlevel = ($row['level'] * 1000) + $row['gid'];
				if ($row['gid'] == 1) {
					$groupname = $eLang->get('ADMINISTRATOR');
					$exactlevel = 100000;
				} else if ($row['gid'] == 5) {
					$groupname = $eLang->get('USER');
				} else if ($row['gid'] == 6) {
					$groupname = $eLang->get('EXTERNALUSER');
				} elseif ($row['gid'] == 7) {
					$groupname = $eLang->get('GUEST');
					$exactlevel = 0;
				} else {
					$groupname = $row['groupname'];
				}

				$disabled = ($userlevel < $level) ? 1 : 0;
				if ($level != $lastlevel) {
					$space .= ($lastlevel == -1) ? '' : '. &#160;';
					$lastlevel = $level;
					if ($levels[$level] > 1) {
						$leveltext = sprintf($eLang->get('ALL_GROUPS_LEVEL'), $level);
						$options[] = $this->makeOption($lowlevel, $space.$leveltext, array(), $disabled);
					}
				}

				switch ($level) {
					case 0: case 1: case 2: case 100: $optionvalue = $lowlevel; break;
					default:
						$optionvalue = ($levels[$level] > 1) ? $exactlevel : $lowlevel;
					break;
				}

				$options[] = $this->makeOption($optionvalue, $space.$level.' - '.$groupname, array(), $disabled);
			}
		}

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attrs);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attrs);
		}
	}


	/*****************************/
	/* ADD LANGUAGE SELECT FIELD */
	/*****************************/
	public function addLanguage($name, $label, $selected='', $attrs=array(), $ltype=2, $nativeNames=1, $flags=false, $select_text='') {
		$eLang = eFactory::getLang();

		$ltype = (int)$ltype;
		$nativeNames = (int)$nativeNames;
		$xlangs = array();
		switch ($ltype) {
			case 0://all languages even not installed
				if (file_exists(ELXIS_PATH.'/includes/libraries/elxis/language/langdb.php')) {
					include(ELXIS_PATH.'/includes/libraries/elxis/language/langdb.php');
					if (isset($langdb)) {
						$xlangs = $langdb;
						unset($langdb);
					}
				}
			break;
			case 2:	$xlangs = eFactory::getLang()->getSiteLangs(true); break;//site enabled
			case 1: default: $xlangs = eFactory::getLang()->getAllLangs(true); break;//all installed
		}

		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => 'ltr');
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'dir') { continue; }
				$attributes[$k] = $v;
			}
		}

		$options = array();
		if ($select_text != '') { $options[] = $this->makeOption('', $select_text); }

		if ($flags) {
			$attributes['data-addlangelem'] = $attributes['id'];//triggers event instead of inline elx5SwitchAddLanguage
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
			$this->prepareMultiLinguism();
		}

		if (!$xlangs) {
			$selected = '';
			if ($flags) { $attributes['class'] .= ' elx5_mlflagun'; }
			if ($select_text == '') { $options[] = $this->makeOption('', $eLang->get('SELECT')); }
		} else {
			if ($flags) {
				$attributes['class'] .= ($selected == '') ? ' elx5_mlflagun' : ' elx5_mlflag'.$selected;
			}
			if ($select_text == '') {
				if ($selected == '') { $options[] = $this->makeOption('', $eLang->get('SELECT')); }
			}
		}

		if ($xlangs) {
			foreach ($xlangs as $lng => $info) {
				if ($nativeNames == 5) {
					$optlabel = strtoupper($lng).' - '.$info['NAME'];
				} else if ($nativeNames == 4) {
					$optlabel = strtoupper($lng).' - '.$info['NAME_ENG'];
				} else if ($nativeNames == 3) {
					$optlabel = strtoupper($lng);
				} else if ($nativeNames == 2) {
					$optlabel = $info['NAME'].' - '.$info['NAME_ENG'];
				} else if ($nativeNames == 0) {
					$optlabel = $info['NAME_ENG'];
				} else {//1 : default
					$optlabel = $info['NAME'];
				}
				$options[] = $this->makeOption($lng, $optlabel);
			}
		}

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attributes);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attributes);
		}
	}


	/***********************************************/
	/* ADD YES/NO BOX FIELD (ATTENTION: CHECKBOX!) */
	/***********************************************/
	public function addYesNo($name, $label, $checked=0, $attrs=array()) {
		$eLang = eFactory::getLang();

		$checked = (int)$checked;
		$attributes = array('id' => $this->options['idprefix'].$name);
		$tip = '';//not recommended for this type of element
		$enablecolor = 'green';
		$onlyelement = 0;//Elxis 5.1
		$htmlattrs = array();//Elxis 5.6

		if ($attrs) {
			foreach ($attrs as $k => $v) {
				switch ($k) {
					case 'tip': $tip = $v; break;
					case 'id': $attributes['id'] = $v; break;
					case 'enablecolor':
						if ($v != '') { $enablecolor = $v; }
					break;
					case 'onlyelement': $onlyelement = (int)$v; break;
					default: $htmlattrs[] = $k.'="'.$v.'"';	break;
				}
			}
		}

		$str_checked = ($checked == 1) ? ' checked="checked"' : '';
		$switchlabelclass = ($enablecolor == 'red') ? 'elx5_switchlabelred' : 'elx5_switchlabel';
		$str_htmlattrs = '';
		if ($htmlattrs) { $str_htmlattrs = ' '.implode(' ', $htmlattrs); }

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		$html .= '<label class="elx5_switch"><input type="checkbox" name="'.$name.'" id="'.$attributes['id'].'" class="elx5_switchinput" value="1"'.$str_checked.$str_htmlattrs.'>';
		$html .= '<span class="'.$switchlabelclass.'" data-on="'.$eLang->get('YES').'" data-off="'.$eLang->get('NO').'"></span>';
		$html .= '<span class="elx5_switchhandle"></span>';
		$html .= '</label>';
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************************************************************/
	/* ADD ITEM STATUS (ATTENTION: WORKS ONLY WITH INTEGER VALUES!) */
	/****************************************************************/
	public function addItemStatus($name, $label, $value, $status_options, $attrs=array()) {
		if (!$status_options) { return; }
		$value = (int)$value;

		$attributes = array('id' => $this->options['idprefix'].$name);
		$tip = '';//not recommended for this type of element
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			if (isset($attrs['tip'])) { $tip = $attrs['tip']; }
			if (isset($attrs['id'])) { $attributes['id'] = $attrs['id']; }
			if (isset($attrs['onlyelement'])) { $onlyelement = (int)$attrs['onlyelement']; }
		}

		//$status_options = array(
		//array('name' => , 'value' => , 'color' => ), ....
		//);
		if (!defined('ELXIS_ONLOAD')) {
			eFactory::getDocument()->addNativeDocReady('elxOnLoad();');
			define('ELXIS_ONLOAD', 1);
		}

		$cur_color = 'gray';
		$cur_label = '-';
		$values = array();
		$labels = array();
		foreach ($status_options as $option) {
			$v = (int)$option['value'];
			if ($v == $value) {
				$cur_label = $option['name'];
				$cur_color = $option['color'];
			}
			$labels[] = addslashes($option['name']);
			$values[] = (int)$option['value'];
			$colors[] = $option['color'];
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		$html .= '<a href="#" data-attid="'.$attributes['id'].'" class="elx5_itemstatus elx5_itemstatus_'.$cur_color.'" data-values="'.implode('|', $values).'" data-labels="'.implode('|', $labels).'" data-colors="'.implode('|', $colors).'"><span></span>'.$cur_label.'</a>';
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n</div>\n";
		}
		$html .= '<input type="hidden" name="'.$name.'" id="'.$attributes['id'].'" value="'.$value.'">'."\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/***********************/
	/* ADD RADIO BOX FIELD */
	/***********************/
	public function addRadio($name, $label='', $checked='', $options=array(), $attrs=array()) {
		$attributes = array('id' => $this->options['idprefix'].$name);
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}

		if (isset($attributes['data-showparams']) || isset($attributes['data-hideparams'])) {//elxisParameters class
			if (!defined('ELXIS_FORMELONLOAD')) {
				eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
				define('ELXIS_FORMELONLOAD', 1);
			}
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'0">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';			
		}
		if (is_array($options)) {
			if (count($options) > 0) {
				foreach ($options as $q => $option) {
					$chk = ($option['value'] == $checked) ? ' checked="checked"' : '';
					$attr = '';
					$optionclass = 'elx5_radio';
					if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
						foreach ($option['attributes'] as $key => $val) {
							if (in_array($key, array('id', 'name', 'type'))) { continue; }
							if ($key == 'class') { $optionclass = $val; continue; }
							$attr .= ' '.$key.'="'.$val.'"';
						}
					}
					$html .= '<label class="elx5_radiowrap">'.$option['label'].'<input type="radio" name="'.$name.'" id="'.$attributes['id'].$q.'" class="'.$optionclass.'" value="'.$option['value'].'"'.$chk.$attr.'>';
					$html .= '<span class="elx5_radio_checkmark"></span></label>'."\n";
				}
			}
		}

		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*********************************/
	/* ADD SHOW/HIDE RADIO BOX FIELD */
	/*********************************/
	public function addShowHide($name, $label='', $checked=0, $attrs=array()) {
		$eLang = eFactory::getLang();

		$checked = (int)$checked;

		$options = array();
		$options[] = $this->makeOption(1, $eLang->get('SHOW'));
		$options[] = $this->makeOption(0, $eLang->get('HIDE'));

		if ($this->options['returnhtml']) {
			return $this->addRadio($name, $label, $checked, $options, $attrs);
		} else {
			$this->addRadio($name, $label, $checked, $options, $attrs);
		}
	}


	/********************/
	/* ADD BUTTON FIELD */
	/********************/
	public function addButton($name, $title, $button_type='submit', $attrs=array()) {
		$button_type = strtolower($button_type);
		if (($button_type == '') || !in_array($button_type, array('submit', 'reset', 'button'))) { $button_type = 'submit'; }

		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_btn', 'value' => '1', 'title' => $title);

		$tip = '';
		$fontawesome = '';//fontawesome css class
		$sidepad = 0;
		$labelclass = 'elx5_label';//sidepad = 1, Elxis 5.3
		$sideclass = 'elx5_labelside';//sidepad = 1, Elxis 5.3
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'fontawesome') { $fontawesome = $v; continue; }
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'sidepad') { $sidepad = (int)$v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				if ($k == 'labelclass') { $labelclass = $v; continue; }
				if ($k == 'sideclass') { $sideclass = $v; continue; }
				$attributes[$k] = $v;
			}
		}

		$html = '';
		if (($sidepad == 1) && ($onlyelement == 0)) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$labelclass.'" for="'.$attributes['id'].'"></label>'."\n";
			$html .= '<div class="'.$sideclass.'">';
		}
		$html .= '<button type="'.$button_type.'" name="'.$name.'"';
		foreach ($attributes as $k => $v) {
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		if ($fontawesome != '') {
			eFactory::getDocument()->addFontAwesome();
			$html .= '><i class="'.$fontawesome.'" aria-hidden="true"></i><span>'.$title."</span></button>\n";
		} else {
			$html .= '>'.$title."</button>\n";
		}
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if (($sidepad == 1) && ($onlyelement == 0)) { $html .= "</div></div>\n"; }

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/********************************/
	/* ADD LINK LOOKING LIKE BUTTON */
	/********************************/
	public function addLinkButton($title, $link='', $attrs=array()) {
		$attributes = array('class' => 'elx5_btn', 'title' => $title);

		$fontawesome = '';//fontawesome css class
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'fontawesome') { $fontawesome = $v; continue; }
				if ($k == 'href') {
					if ($link == '') { $link = $v; }
					continue;
				}
				$attributes[$k] = $v;
			}
		}

		if ($link == '') { $link = '#'; }

		$html = '<a href="'.$link.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		if ($fontawesome != '') {
			eFactory::getDocument()->addFontAwesome();
			$html .= '><i class="'.$fontawesome.'" aria-hidden="true"></i><span>'.$title."</span></a>\n";
		} else {
			$html .= '>'.$title."</a>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*******************************/
	/* ADD MULTILINGUAL TEXT FIELD */
	/*******************************/
	public function addMLText($name, $trdata, $value='', $label='', $attrs=array()) {
		$elxis = eFactory::getElxis();

		$allowed = ($elxis->acl()->check('component', 'com_etranslator', 'manage') < 1) ? false : true;
		if (!$allowed && !defined('ELXIS_ADMIN')) {
			$allowed = ($elxis->acl()->check('component', 'com_etranslator', 'api') < 1) ? false : true;
		}

		$translations = array();
		$has_translations = false;
		$attributes = array('id' => $this->options['idprefix'].$name, 'placeholder' => $label);
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'id') { continue; }
				if ($k == 'class') { continue; }
				if ($k == 'dir') { continue; }
				if ($k == 'translations') {//multilingual XML parameter, no trdata in this case but provided translations in $attrs
					$has_translations = true;
					$translations = $v;
					continue;
				}
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$sitelangs = $this->getSiteLangs();
		$langnames = $this->getSiteLangs() ? array_keys($sitelangs) : array();
		$clang = $elxis->getConfig('LANG');
		if (!$has_translations) {
			$translations = $this->getTranslations($trdata['category'], $trdata['element'], $trdata['elid']);
		}

		$this->prepareMultiLinguism();

		$distxt = '';
		if (!$allowed) { $distxt = ' disabled="disabled"'; }

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		$html .= '<div class="elx5_mlboxwrap">'."\n";
		$html .= '<div class="elx5_mlboxlang">'."\n";

		$html .= '<select name="'.$name.'_lang" id="'.$attributes['id'].'_lang" class="elx5_select elx5_mlflag'.$clang.'" dir="ltr" data-deflang="'.$clang.'" data-trelement="'.$name.'" data-idprefix="'.$this->options['idprefix'].'" data-editor="0" data-sitelangs="'.implode(',', $langnames).'"'.$distxt.'>'."\n";
		if ($sitelangs) {
			foreach ($sitelangs as $lng => $sitelang) {
				$sel = '';
				$oclass = '';
				if ($lng == $clang) {
					$sel = ' selected="selected"';
					$oclass = ' class="elx5_defoption"';
				} else {
					if (isset($translations[$lng])) { $oclass = ' class="elx5_hloption"'; }
				}
				$html .= '<option value="'.$lng.'"'.$oclass.$sel.'>'.strtoupper($lng)."</option>\n";
			}
		}
		$html .= "</select>\n";
		$html .= "</div>\n";//elx5_mlboxlang

		$hasaccess = $allowed ? 1 : 0;
		$class = 'elx5_mltranslator_off';
		if ($elxis->getConfig('TRANSLATOR') != '') {
			if ($allowed) { $class = 'elx5_mltranslator'; }
		}
		$html .= '<div class="elx5_mlboxtrans">'."\n";
		$html .= '<a href="#" id="'.$attributes['id'].'_translator" data-translatorid="'.$attributes['id'].'" data-formid="'.$this->formid.'" data-hasaccess="'.$hasaccess.'" data-editor="0" title="'.eFactory::getLang()->get('AUTO_TRANS').'" class="'.$class.'"><span class="fas fa-globe"></span></a>';
		$html .= "</div>\n";//elx5_mlboxtrans

		$html .= '<div class="elx5_mlboxtext">'."\n";
		if ($sitelangs) {
			$trname = $name.'_';
			$trname_end = '';
			if (preg_match('@(])$@', $name)) {//XML param or other array style naming like "something[else]" => "something[else_mlLNG]"
				$trname = preg_replace('@(])$@', '', $name).'_ml';
				$trname_end = ']';
			}
			foreach ($sitelangs as $lng => $sitelang) {
				if ($lng == $clang) { continue; }
				$dir = ($sitelang['RTLSFX'] == '-rtl') ? 'rtl' : 'ltr';
				$v = isset($translations[$lng]) ? $translations[$lng] : '';
				$html .= '<input type="text" name="'.$trname.$lng.$trname_end.'" id="'.$attributes['id'].'_'.$lng.'" dir="'.$dir.'" class="elx5_invisible" value="'.$v.'" placeholder="'.$label.' - '.$sitelang['NAME_ENG'].'">'."\n";
			}
		}
		$dir = ($sitelangs[$clang]['RTLSFX'] == '-rtl') ? 'rtl' : 'ltr';
		$html .= '<input type="text" name="'.$name.'" value="'.$value.'" id="'.$attributes['id'].'" dir="'.$dir.'" class="elx5_text elx5_mlflag'.$clang.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'id') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		$html .= "</div>\n";//elx5_mlboxtext
		$html .= "</div>\n";//elx5_mlboxwrap
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) { $html .= "</div>\n</div>\n"; }

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************/
	/* ADD RAW HTML */
	/****************/
	public function addHTML($html) {
		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	public function addCustom($html) {//Elxis 4.x compatibility
		$this->addHTML($html);
	}


	/*****************************/
	/* ADD TIMEZONE SELECT FIELD */
	/*****************************/
	public function addTimezone($name, $label='', $selected='', $attrs=array()) {
		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => 'ltr');
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'dir') { continue; }
				$attributes[$k] = $v;
			}
		}

		$zones = timezone_identifiers_list();
		$options = array();
		foreach ($zones as $zone) { $options[] = $this->makeOption($zone, $zone); }

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attributes);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attributes);
		}
	}


	/****************************/
	/* ADD SLIDER NUMERIC FIELD */
	/****************************/
	public function addSlider($name, $value='', $label='', $attrs=array()) {//Elxis 4.x compatibility
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';
		if (!isset($attrs['min'])) { $attrs['min'] = 0; }
		if (!isset($attrs['max'])) { $attrs['max'] = 100; }
		if (!isset($attrs['step'])) { $attrs['step'] = 1; }
		if (!isset($attrs['showvalue'])) { $attrs['showvalue'] = 0; }

		if ($attrs['showvalue'] == 0) {
			if ($this->options['returnhtml']) {
				return $this->addInput('range', $name, $value, $label, $attrs);
			} else {
				$this->addInput('range', $name, $value, $label, $attrs);
			}
		}

		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_text',
			'dir' => $this->dir,
			'placeholder' => $label
		);
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'showvalue') { continue; }
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}

		if ($value != '') {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		if ($attrs['showvalue'] == 1) {
			$class1 = 'elx5_sideinput_value_front';
			$class2 = 'elx5_sideinput_input_end';
		} else {
			$class1 = 'elx5_sideinput_value_end';
			$class2 = 'elx5_sideinput_input_front';
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';			
		}
		$html .= '<div class="elx5_sideinput_wrap">'."\n";
		$html .= '<div id="'.$attributes['id'].'_value" class="'.$class1.'">'.$value.'</div>'."\n";
		$html .= '<div class="'.$class2.'">';
		$html .= '<input type="range" name="'.$name.'" value="'.$value.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= "></div>\n";
		$html .= "</div>\n";

		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		$html .= '<script nonce="'.eFactory::getDocument()->getNonce().'">'."\n";
		$html .= 'var '.$this->options['idprefix'].'fmslider = document.getElementById(\''.$attributes['id'].'\');'."\n";
		$html .= $this->options['idprefix'].'fmslider.oninput = function() {'."\n";
		$html .= 'document.getElementById(\''.$attributes['id'].'_value\').innerHTML = this.value;'."\n";
		$html .= "}\n";
		$html .= "</script>\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/**************************/
	/* ADD INPUT RANGE FIELD */
	/**************************/
	public function addRangeNative($name, $value='', $label='', $min=1, $max=10, $step=1, $attrs=array()) { //HTML5 range, Elxis 4.x compatibility
		if (!$attrs) { $attrs = array(); }
		$attrs['dir'] = 'ltr';
		$attrs['min'] = $min;
		$attrs['max'] = $max;
		$attrs['step'] = $step;

		if ($this->options['returnhtml']) {
			return $this->addInput('range', $name, $value, $label, $attrs);
		} else {
			$this->addInput('range', $name, $value, $label, $attrs);
		}
	}


	/************************************************/
	/* ADD RANGE OF INTEGERS DROP DOWN SELECT FIELD */
	/************************************************/
	public function addRange($name, $label='', $first=0, $last=1, $selected=-1, $step=1, $attrs=array()) {//Elxis 4.x compatibility
		$first = (int)$first;
		$last = (int)$last;
		$step = (int)$step;
		$selected = (int)$selected;
		if ($step < 1) { $step = 1; }

		if ($first == $last) {
			$values = array($first);
		} else if ($first < $last) {
			$values = range($first, $last, $step);
		} else {
			$values = range($last, $first, $step);
			$values = array_reverse($values);
		}
		$options = array();
		foreach ($values as $value) {
			$options[] = $this->makeOption($value, $value);
		}
		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attrs);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attrs);
		}
	}


	/******************************/
	/* ADD USERGROUP SELECT FIELD */
	/******************************/
	public function addUsergroup($name, $label='', $selected='0', $lowerlevel=0, $upperlevel=100, $attrs=array()) {
		$eLang = eFactory::getLang();
		$db = eFactory::getDB();

		$selected = (int)$selected;
		if (!$attrs) { $attrs = array(); }
		$showgid = 1;
		$showlevel = 1;
		$showgroupname = 1;
		$showalloption = 0;
		$alloptionvalue = 0;
		$alloptiontext = '- '.$eLang->get('ALL').' - ';
		if (isset($attrs['showgid'])) {
			$showgid = (int)$attrs['showgid'];
			unset($attrs['showgid']);
		}
		if (isset($attrs['showlevel'])) {
			$showlevel = (int)$attrs['showlevel'];
			unset($attrs['showlevel']);
		}
		if (isset($attrs['showgroupname'])) {
			$showgroupname = (int)$attrs['showgroupname'];
			unset($attrs['showgroupname']);
		}
		if (isset($attrs['showalloption'])) {
			$showalloption = (int)$attrs['showalloption'];
			unset($attrs['showalloption']);
		}
		if (isset($attrs['alloptionvalue'])) {
			$alloptionvalue = (int)$attrs['alloptionvalue'];
			unset($attrs['alloptionvalue']);
		}
		if (isset($attrs['alloptiontext'])) {
			$alloptiontext = $attrs['alloptiontext'];
			unset($attrs['alloptiontext']);
		}

		$sql = "SELECT * FROM ".$db->quoteId('#__groups');
		$sql .= ' WHERE '.$db->quoteId('level').' >= :llev AND '.$db->quoteId('level').' <= :ulev';
		$sql .= ' ORDER BY '.$db->quoteId('level').' DESC';
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':llev', $lowerlevel, PDO::PARAM_INT);
		$stmt->bindParam(':ulev', $upperlevel, PDO::PARAM_INT);
		$stmt->execute();
		$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

		$options = array();
		if ($showalloption == 1) {
			$options[] = $this->makeOption($alloptionvalue, $alloptiontext);
		}

		if ($rows) {
			foreach ($rows as $row) {
				if ($row['gid'] == 1) {
					$groupname = $eLang->get('ADMINISTRATOR');
				} else if ($row['gid'] == 5) {
					$groupname = $eLang->get('USER');
				} else if ($row['gid'] == 6) {
					$groupname = $eLang->get('EXTERNALUSER');
				} elseif ($row['gid'] == 7) {
					$groupname = $eLang->get('GUEST');
				} else {
					$groupname = $row['groupname'];
				}

				$parts = array();
				if ($showgid == 1) { $parts[] = (string)$row['gid']; }
				if ($showlevel == 1) { $parts[] = sprintf("%03d", $row['level']); }
				if ($showgroupname == 1) { $parts[] = $groupname; }

				$txt = $parts ? implode(' - ', $parts) : $groupname;
				$options[] = $this->makeOption($row['gid'], $txt);
			}
		}

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attrs);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attrs);
		}
	}


	/********************************************/
	/* ADD IMAGE FIELD (FILE WITH IMAGE PREVIEW)*/
	/********************************************/
	public function addImage($name, $value='', $label='', $attrs=array()) {
		$elxis = eFactory::getElxis();
		$eLang = eFactory::getLang();

		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_text',
			'dir' => 'ltr',
			'placeholder' => $label
		);

		$relpath = '';
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'relpath') {
					$relpath = $v;
					continue;
				}
				if ($k == 'onchange') { continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if (!isset($attributes['accept'])) { $attributes['accept'] = '.jpg,.jpeg,.png,.gif,.webp'; }

		$attributes['data-addimage'] = $attributes['id'];//triggers event instead of inline elx5FileImagePreview ans also elx5FileimgDeleteImage
		if (!defined('ELXIS_FORMELONLOAD')) {
			eFactory::getDocument()->addNativeDocReady('elx5FormElementsOnLoad();');
			define('ELXIS_FORMELONLOAD', 1);
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$empty_image = $elxis->secureBase().'/templates/system/images/nopicture.png';
		$cur_imgurl = $empty_image;
		$cur_imgname = $eLang->get('NO_IMAGE_UPLOADED');

		if ($value != '') {
			$parts = preg_split('#\/#', $value, -1, PREG_SPLIT_NO_EMPTY);
			$i = count($parts) - 1;
			$cur_imgname = $parts[$i];
			if (strpos($value, 'http') === 0) {
				$cur_imgurl = $value;
			} else {
				if (file_exists(ELXIS_PATH.'/'.$relpath.$value)) {
					$info = getimagesize(ELXIS_PATH.'/'.$relpath.$value);
					$cur_imgname .= ' ('.$info[0].'x'.$info[1].', '.round((filesize(ELXIS_PATH.'/'.$relpath.$value) / 1024), 2).' KB)';
				}
				$cur_imgurl = $elxis->secureBase().'/'.$relpath.$value;
			}
		}

		if (isset($attributes['readonly'])) {
			if (strpos($attributes['class'], 'readonly') === false) { $attributes['class'] .= ' elx5_readonly'; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';			
		}

		$html .= '<div class="elx5_fileimg_wrap">'."\n";
		$html .= '<a href="'.$cur_imgurl.'" target="_blank" id="'.$attributes['id'].'_imagelink">';
		$html .= '<img src="'.$cur_imgurl.'" alt="image" id="'.$attributes['id'].'_image" data-empty="'.$empty_image.'" data-noimglng="'.addslashes($eLang->get('NO_IMAGE_FILE')).'" data-noimguplng="'.addslashes($eLang->get('NO_IMAGE_UPLOADED')).'"></a>'."\n";
		$html .= "</div>\n";//elx5_fileimg_wrap
		$html .= '<div class="elx5_fileimg_inwrap">'."\n";

		$html .= '<div class="elx5_fileimg_cur_wrap">'."\n";
		$html .= '<div class="elx5_fileimg_cur_file" title="'.$cur_imgname.'" id="'.$attributes['id'].'_imagename">'.$cur_imgname.'</div>'."\n";
		$html .= '<a href="#" data-fimgdelimage="'.$attributes['id'].'" class="elx5_fileimg_del" title="'.$eLang->get('DELETE_CURRENT_IMAGE').'">X</a>'."\n";
		$html .= "</div>\n";//elx5_fileimg_cur_wrap

		$html .= '<input type="file" name="'.$name.'" value=""';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		$html .= '<input type="hidden" name="'.$name.'_deleteold" id="'.$attributes['id'].'_deleteold" value="0" dir="ltr">'."\n";
		$html .= "</div>\n";//elx5_fileimg_inwrap
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************************/
	/* ADD COUNTRY SELECT FIELD */
	/****************************/
	public function addCountry($name, $label='', $selected='', $attrs=array(), $select_text='') {
		$eLang = eFactory::getLang();

		$attributes = array('id' => $this->options['idprefix'].$name, 'class' => 'elx5_select', 'dir' => 'ltr');
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'dir') { continue; }
				$attributes[$k] = $v;
			}
		}

		$lng = $eLang->getinfo('LANGUAGE');
		if (file_exists(ELXIS_PATH.'/includes/libraries/elxis/form/countries.'.$lng.'.php')) {
			include(ELXIS_PATH.'/includes/libraries/elxis/form/countries.'.$lng.'.php');
		} else {
			include(ELXIS_PATH.'/includes/libraries/elxis/form/countries.en.php');
		}

		$options = array();
		if ($select_text != '') {
			$options[] = $this->makeOption('', $select_text);
		} else {
			if ($selected == '') { $options[] = $this->makeOption('', $eLang->get('SELECT')); }
		}
		if (isset($countries)) {
			foreach ($countries as $key => $cname) { $options[] = $this->makeOption($key, $cname); }
		}

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attributes);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attributes);
		}
	}


	public function addDate($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addHmeromhnia($name, $value, $label, $attrs, 'date');
		} else {
			$this->addHmeromhnia($name, $value, $label, $attrs, 'date');
		}
	}


	public function addDatetime($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addHmeromhnia($name, $value, $label, $attrs, 'datetime');
		} else {
			$this->addHmeromhnia($name, $value, $label, $attrs, 'datetime');
		}
	}


	public function addTime($name, $value='', $label='', $attrs=array()) {
		if ($this->options['returnhtml']) {
			return $this->addHmeromhnia($name, $value, $label, $attrs, 'time');
		} else {
			$this->addHmeromhnia($name, $value, $label, $attrs, 'time');
		}
	}


	/************************************/
	/* ADD DATE OR DATETIME TEXT FIELDS */
	/************************************/
	private function addHmeromhnia($name, $value='', $label='', $attrs=array(), $datetype='date') {
		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_text',
			'dir' => 'ltr',
			'readonly' => 'readonly'
		);
		if ($datetype == 'datetime') {
			$formatitem = 'dateTimeFormat';
			$attributes['data-field'] = 'datetime';
			$attributes['data-format'] = $this->options['datetime_format'];
		} else if ($datetype == 'time') {
			$formatitem = 'timeFormat';
			$attributes['data-field'] = 'time';
			$attributes['data-format'] = $this->options['time_format'];
		} else {
			$formatitem = 'dateFormat';
			$attributes['data-field'] = 'date';
			$attributes['data-format'] = $this->options['date_format'];
		}

		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if (($k == 'format') || ($k == 'data-format')) {
					$attributes['data-format'] = $v;
					continue;
				}
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}

		$elxis_date_format = $attributes['data-format'];
		$attributes['data-format'] = $this->elxisTodatePickerFormat($elxis_date_format);
		if (!isset($attributes['title'])) { $attributes['title'] = $label.' ('.$attributes['data-format'].')'; }
		if (!isset($attributes['placeholder'])) { $attributes['placeholder'] = $attributes['data-format']; }
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$dtv = str_replace('/', '-', $value);
		if ($value == '') {
			$datevalue = '';
		} else if ((strpos($dtv, '1970-01-01') !== false) || (strpos($dtv, '01-01-1970') !== false) || (strpos($dtv, '2060-01-01') !== false) || (strpos($dtv, '01-01-2060') !== false)) {
			$datevalue = '';
		} else {
			$dtformat = str_replace('/', '-', $elxis_date_format);
			$datevalue = $this->getDateTimeValue($datetype, $value, $dtformat);
		}
		$lang = (file_exists(ELXIS_PATH.'/includes/js/datetimepicker/i18n/DateTimePicker-i18n-'.$this->curlang.'.js')) ? $this->curlang : 'en';

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'" id="'.$attributes['id'].'_wrap">';			
		}
		$html .= '<input type="text" name="'.$name.'" value="'.$datevalue.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		$html .= '<div id="dtBox'.$attributes['id'].'"></div>';
		$html .= '<script nonce="'.eFactory::getDocument()->getNonce().'">';
		$html .= '$(document).ready(function() { $(\'#dtBox'.$attributes['id'].'\').DateTimePicker( { parentElement: \'#'.$attributes['id'].'_wrap\', language: \''.$lang.'\', '.$formatitem.': \''.$attributes['data-format'].'\'} ); });';
		$html .= "</script>\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		$this->prepareDatePicker();

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	private function elxisTodatePickerFormat($elxisformat) {
		$elxisformat = str_replace('/', '-', $elxisformat);
		switch ($elxisformat) {
			case 'H:i': $format = 'HH:mm'; break;
			case 'H:i:s': $format = 'HH:mm:ss'; break;
			case 'd-m-Y': $format = 'dd-MM-yyyy'; break;
			case 'm-d-Y': $format = 'MM-dd-yyyy'; break;
			case 'Y-m-d': $format = 'yyyy-MM-dd'; break;
			case 'd-m-Y H:i:s': $format = 'dd-MM-yyyy HH:mm:ss'; break;
			case 'Y-m-d H:i:s': $format = 'yyyy-MM-dd HH:mm:ss'; break;
			case 'm-d-Y H:i:s': $format = 'MM-dd-yyyy HH:mm:ss'; break;
			case 'd-m-Y H:i': $format = 'dd-MM-yyyy HH:mm'; break;
			case 'Y-m-d H:i': $format = 'yyyy-MM-dd HH:mm'; break;
			case 'm-d-Y H:i': $format = 'MM-dd-yyyy HH:mm'; break;
			default: $format = $elxisformat; break;
		}
		return $format;
	}


	private function getDateTimeValue($datetype, $value, $format) {
		if ($value == '') { return ''; }
		if ($datetype == 'time') {
			$parts = preg_split('#\:#', $value, -1, PREG_SPLIT_NO_EMPTY);
			if (count($parts) == 3) {
				$h = (int)$parts[0];
				$i = (int)$parts[1];
				$s = (int)$parts[2];
			} else if (count($parts) == 2) {
				$h = (int)$parts[0];
				$i = (int)$parts[1];
				$s = 0;
			} else {
				$h = -1; $i = -1; $s = -1;
			}
			if (($h >= 0) && ($h < 24) && ($i >= 0) && ($i < 60) && ($s >= 0) && ($s < 60)) {
				if ($format == 'H:i') {
					$newvalue = sprintf("%02d", $h).':'.sprintf("%02d", $i);
				} else {//H:i:s
					$newvalue = sprintf("%02d", $h).':'.sprintf("%02d", $i).':'.sprintf("%02d", $s);
				}
			} else {
				$newvalue = '';
			}
			return $newvalue;
		}

		//date - datetime
		if (preg_match('#\/#', $value)) {
			$parts = preg_split('#\/#', $value, -1, PREG_SPLIT_NO_EMPTY);
		} else {
			$parts = preg_split('#\-#', $value, -1, PREG_SPLIT_NO_EMPTY);
		}
		if (!$parts || (count($parts) != 3)) { return ''; }
		$part1 = (int)$parts[0];
		$part2 = (int)$parts[1];
		$lastparts = preg_split('#[\s]#', $parts[2], -1, PREG_SPLIT_NO_EMPTY);
		if ($lastparts && (count($lastparts) == 2)) {
			$part3 = (int)$lastparts[0];
			$part_time = trim($lastparts[1]);
			if (strlen($part_time) == 5) { $part_time .= ':00'; }
		} else {
			$part3 = (int)$parts[2];
			$part_time = '12:00:00';
		}

		$ok = true;
		$h = 12; $i = 0; $s = 0;
		switch ($format) {
			case 'Y-m-d': case 'Y/m/d': $d = $part3; $m = $part2; $y = $part1; break;
			case 'd-m-Y': case 'd/m/Y': $d = $part1; $m = $part2; $y = $part3; break;
			case 'm-d-Y': case 'm/d/Y': $d = $part2; $m = $part1; $y = $part3; break;
			case 'Y-m-d H:i:s': case 'Y/m/d H:i:s': case 'Y-m-d H:i': case 'Y/m/d H:i':
				$y = $part1; $m = $part2; $d = $part3;
				$timeparts = preg_split('#\:#', $part_time, -1, PREG_SPLIT_NO_EMPTY);
				$h = (int)$timeparts[0]; $i = (int)$timeparts[1]; $s = (int)$timeparts[2];
			break;
			case 'd-m-Y H:i:s': case 'd/m/Y H:i:s': case 'd-m-Y H:i': case 'd/m/Y H:i':
				$y = $part3; $m = $part2; $d = $part1;
				$timeparts = preg_split('#\:#', $part_time, -1, PREG_SPLIT_NO_EMPTY);
				$h = (int)$timeparts[0]; $i = (int)$timeparts[1]; $s = (int)$timeparts[2];
			break;
			case 'm-d-Y H:i:s': case 'm/d/Y H:i:s': case 'm-d-Y H:i': case 'm/d/Y H:i':
				$y = $part3; $m = $part1; $d = $part2;
				$timeparts = preg_split('#\:#', $part_time, -1, PREG_SPLIT_NO_EMPTY);
				$h = (int)$timeparts[0]; $i = (int)$timeparts[1]; $s = (int)$timeparts[2];
			break;
			default: $ok = false; break;
		}
		if (!$ok) { return ''; }

		$newvalue = '';
		if (checkdate($m, $d, $y)) {
			$newvalue = $this->safe64bit_gmdate($y, $m, $d, $h, $i, $s, $datetype, $format);
		}

		return $newvalue;
	}


	/************************************/
	/* ADD MONTH DROP DOWN SELECT FIELD */
	/************************************/
	public function addMonth($name, $label='', $selected=1, $short=false, $attrs=array()) {
		$eLang = eFactory::getLang();
		$eDate = eFactory::getDate();

		$selected = (int)$selected;
		if (($selected < 1) || ($selected > 12)) { $selected = 1; }

		$short = (bool)$short;
		$options = array();
		for ($i=1; $i<13; $i++) {
			$mname = $eDate->monthName($i, $short);
			$options[] = $this->makeOption($i, $mname);
		}

		if ($this->options['returnhtml']) {
			return $this->addSelect($name, $label, $selected, $options, $attrs);
		} else {
			$this->addSelect($name, $label, $selected, $options, $attrs);
		}
	}


	/******************/
	/* START NEW TABS */
	/******************/
	public function startTabs($tabs, $opentab=-1, $attrs=array()) {
		$this->tabidx = 0;
		$this->opentab = (int)$opentab;
		if ($this->opentab == -1) {
			$this->opentab = (isset($_GET['tabopen'])) ? (int)$_GET['tabopen'] : 0;//auto get desired tab to open (Elxis 4.x style)
		}

		if (!$attrs) { $attrs = array(); }
		if (!isset($attrs['id'])) { $attrs['id'] = 'elx5_tabs'.rand(1000, 9999); }
		$this->options['tabs_id'] = $attrs['id'];

		eFactory::getDocument()->addNativeDocReady('elx5Tabs(\''.$attrs['id'].'\', \''.$this->options['tabs_open_class'].'\', \''.$this->options['tabs_content_class'].'\');');
		$html = '<ul class="'.$this->options['tabs_class'].'" id="'.$attrs['id'].'">'."\n";

		$k = 0;
		foreach ($tabs as $tab) {
			$icon = '';
			if (is_array($tab)) {
				$title = (isset($tab['title'])) ? $tab['title'] : 'Tab '.($k + 1);
				if ($this->options['tabs_use_iconclass'] == 1) {
					if (isset($tab['iconclass'])) {
						if ($tab['iconclass'] != '') { $icon = '<i class="'.$tab['iconclass'].'" aria-hidden="false"></i>'; }
					}
				} else if ($this->options['tabs_use_numeric'] == 1) {
					$icon = '<i class="elx5_tab_num" aria-hidden="false">'.($k + 1).'</i>';
				}
			} else {//string
				$title = (string)$tab;
				if ($this->options['tabs_use_numeric'] == 1) { $icon = '<i class="elx5_tab_num" aria-hidden="false">'.($k + 1).'</i>'; }
			}

			$class_str = ($k == $this->opentab) ? ' class="'.$this->options['tabs_open_class'].'"' : '';
			$html .= "\t".'<li><a href="#" data-tab="tab_elx5_'.$k.'"'.$class_str.'>';
			$html .= ($icon != '') ? $icon.'<span class="elx5_lmobhide">'.$title.'</span>' : $title;
			$html .= "</a></li>\n";
			$k++;
		}

		$html .= "</ul>\n";
		$html .= '<div class="'.$this->options['tabs_container_class'].'">'."\n";

		if ($this->options['returnhtml']) {
			return $html;
		} else {
			echo $html;
		}
	}


	/************/
	/* END TABS */
	/************/
	public function endTabs() {
		$html = '<input type="hidden" name="tabopen" id="tabopen'.$this->options['tabs_id'].'" value="'.$this->opentab.'">'."\n";
		$html .= "</div>\n";//tabs_container_class
		$this->tabidx = 0;
		$this->opentab = 0;

		if ($this->options['returnhtml']) {
			return $html;
		} else {
			echo $html;
		}
	}


	/*************/
	/* OPEN TAB */
	/*************/
	public function openTab() {
		$class = ($this->tabidx == $this->opentab) ? $this->options['tabs_content_class'] : 'elx5_invisible';
		$html = '<div id="tab_elx5_'.$this->tabidx.'" class="'.$class.'">'."\n";
		$this->tabidx++;
		if ($this->options['returnhtml']) {
			return $html;
		} else {
			echo $html;
		}
	}


	/*************/
	/* CLOSE TAB */
	/*************/
	public function closeTab() {
		if ($this->options['returnhtml']) {
			return "</div>\n";//tabs_content_class
		} else {
			echo "</div>\n";//tabs_content_class
		}
	}


	/**************************************/
	/* OPEN ROW (ELXIS 4.x compatibility) */
	/**************************************/
	public function openRow() {
		if ($this->options['returnhtml']) {
			return '';
		} else {
			echo '';
		}
	}


	/********************************************/
	/* CLOSE OPEN ROW (ELXIS 4.x compatibility) */
	/********************************************/
	public function closeRow() {
		if ($this->options['returnhtml']) {
			return '';
		} else {
			echo '';
		}
	}


	/****************************/
	/* GET ELEMENT TRANSLATIONS */
	/****************************/
	private function getTranslations($ctg, $elem, $elid) {
		$db = eFactory::getDB();

		$sql = "SELECT ".$db->quoteId('language').", ".$db->quoteId('translation')." FROM ".$db->quoteId('#__translations')
		."\n WHERE ".$db->quoteId('category')." = :xcat AND ".$db->quoteId('element')." = :xelem AND ".$db->quoteId('elid')." = :xid";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xcat', $ctg, PDO::PARAM_STR);
		$stmt->bindParam(':xelem', $elem, PDO::PARAM_STR);
		$stmt->bindParam(':xid', $elid, PDO::PARAM_INT);
		$stmt->execute();
		$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

		$translations = array();
		if ($rows) {
			foreach ($rows as $row) {
				$lng = $row['language'];
				$translations[$lng] = $row['translation'];
			}
		}

		return $translations;
	}


	/****************************************/
	/* PREPARE MULTILINGUAL CONTENT SUPPORT */
	/****************************************/
	private function prepareMultiLinguism() {
		if (defined('ELXIS_MULTILINGUISM_OK')) { return; }

		$elxis = eFactory::getElxis();
		$eDoc = eFactory::getDocument();
		$eLang = eFactory::getLang();

		$eDoc->addStyleLink($elxis->secureBase().'/includes/libraries/elxis/language/mlflags'.$eLang->getinfo('RTLSFX').'.css');
		$eDoc->addNativeDocReady('elx5MLOnLoad();');

		define('ELXIS_MULTILINGUISM_OK', 1);
	}


	/******************************/
	/* PREPARE DATEPICKER SUPPORT */
	/******************************/
	private function prepareDatePicker() {
		if (defined('ELXIS_DTPICKER')) { return; }

		$eDoc = eFactory::getDocument();
		$caldir = eFactory::getElxis()->secureBase(true).'/includes/js/datetimepicker';
		$eDoc->addJQuery();
		$eDoc->addLibrary('datetimepicker', $caldir.'/DateTimePicker.min.js', '0.1.29');
		$eDoc->addScriptLink($caldir.'/i18n/DateTimePicker-i18n.js');
		$eDoc->addStyleLink($caldir.'/DateTimePicker.min.css', 'text/css', 'all');
		define('ELXIS_DTPICKER', 1);
	}


	/*******************************************/
	/* 64BIT SAFE FORMAT GMDATE FOR DATE FIELD */
	/*******************************************/
	private function safe64bit_gmdate($y, $m, $d, $h=12, $i=0, $s=0, $datetype='date', $format='') {
		if ($format == '') {
			if ($datetype == 'datetime') {
				$format = $this->datetime_format;
			} elseif ($datetype == 'time') {
				$format = $this->time_format;
			} else {//date
				$format = $this->date_format;
			}
		}

		if ($y < 2038) {
			return gmdate($format, gmmktime($h, $i, $s, $m, $d, $y));
		}
		if ($this->php64bit === -1) {
			$int = "9223372036854775807";
			$int = intval($int);
			$this->php64bit = ($int == 9223372036854775807) ? 1 : 0;
		}

		if ($this->php64bit === 1) {
			return gmdate($format, gmmktime($h, $i, $s, $m, $d, $y));
		}

		$d = sprintf("%02d", $d);
		$m = sprintf("%02d", $m);
		$h = sprintf("%02d", $h);
		$i = sprintf("%02d", $i);
		$s = sprintf("%02d", $s);
		switch ($format) {
			case 'H:i': return $h.':'.$i; break;
			case 'H:i:s': return $h.':'.$i.':'.$s; break;
			case 'm/d/Y': return $m.'/'.$d.'/'.$y; break;
			case 'm-d-Y': return $m.'-'.$d.'-'.$y; break;
			case 'Y/m/d': return $y.'/'.$m.'/'.$d; break;
			case 'Y-m-d': return $y.'-'.$m.'-'.$d; break;
			case 'd/m/Y': return $d.'/'.$m.'/'.$y; break;
			case 'd-m-Y': return $d.'-'.$m.'-'.$y; break;
			case 'm/d/Y H:i:s': return $m.'/'.$d.'/'.$y.' '.$h.':'.$i.':'.$s; break;
			case 'm-d-Y H:i:s': return $m.'-'.$d.'-'.$y.' '.$h.':'.$i.':'.$s; break;
			case 'Y/m/d H:i:s': return $y.'/'.$m.'/'.$d.' '.$h.':'.$i.':'.$s; break;
			case 'Y-m-d H:i:s': return $y.'-'.$m.'-'.$d.' '.$h.':'.$i.':'.$s; break;
			case 'd/m/Y H:i:s': return $d.'/'.$m.'/'.$y.' '.$h.':'.$i.':'.$s; break;
			case 'd-m-Y H:i:s': return $d.'-'.$m.'-'.$y.' '.$h.':'.$i.':'.$s; break;
			case 'm/d/Y H:i': return $m.'/'.$d.'/'.$y.' '.$h.':'.$i; break;
			case 'm-d-Y H:i': return $m.'-'.$d.'-'.$y.' '.$h.':'.$i; break;
			case 'Y/m/d H:i': return $y.'/'.$m.'/'.$d.' '.$h.':'.$i; break;
			case 'Y-m-d H:i': return $y.'-'.$m.'-'.$d.' '.$h.':'.$i; break;
			case 'd/m/Y H:i': return $d.'/'.$m.'/'.$y.' '.$h.':'.$i; break;
			case 'd-m-Y H:i': return $d.'-'.$m.'-'.$y.' '.$h.':'.$i; break;
			default: return $y.'-'.$m.'-'.$d.' '.$h.':'.$i.':'.$s; break;
		}
	}


	/*********************/
	/* ADD CAPTCHA FIELD */
	/*********************/
	public function addCaptcha($name, $label='', $attrs=array()) {
		$v1 = rand(4, 30);
		$v2 = rand(3, 29);
		if ($v1 % 2) {
			$operator = '+';
			$number1 = $v1;
			$number2 = $v2;
			$sum = $number1 + $number2;
		} else {
			$operator = '-';
			if ($v1 == $v2) {
				$number1 = $v1 + rand(6, 21);
				$number2 = $v2;
			} else if ($v1 > $v2) {
				if (($v1 - $v2) < 6) { $v1 = $v1 + rand(5, 20); }
				$number1 = $v1;
				$number2 = $v2;
			} else {
				$number1 = $v1 + rand(5, 20);
				$number2 = $v1;
			}
			$sum = $number1 - $number2;
		}

		eFactory::getSession()->set('captcha_'.$name, $sum);

		$sidetext = $number1.' '.$operator.' '.$number2.' =';

		if (trim($label == '')) { $label = eFactory::getLang()->get('SECURITY_CODE'); }

		$attributes = array(
			'id' => $this->options['idprefix'].$name,
			'class' => 'elx5_text',
			'dir' => 'ltr',
			'placeholder' => $label,
			'maxlength' => 5,
			'required' => 'required'
		);

		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';			
		}

		$html .= '<div class="elx5_sideinput_wrap">'."\n";
		$html .= '<div class="elx5_sideinput_text">'.$sidetext.'</div>'."\n";
		$html .= '<div class="elx5_sideinput_input">'."\n";

		$html .= '<input type="text" name="'.$name.'" value=""';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'type') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'value') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">\n";
		$html .= "</div></div>\n";
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************************************/
	/* ADD "I AM NOT A ROBOT" CAPTCHA FIELD */
	/****************************************/
	public function addNoRobot($name='', $emptylabel=true) {
		if ($name == '') { $name = 'norobot'; }
		$attributes = array('id' => $this->options['idprefix'].$name);
		if (defined('ELXIS_ADMIN')) {
			$linkbase = eFactory::getElxis()->makeAURL('cpanel:/', 'inner.php', true);
		} else {
			$linkbase = eFactory::getElxis()->makeURL('content:/', 'inner.php', true);
		}
		$eDoc = eFactory::getDocument();
		$eDoc->addFontAwesome();

		if (!defined('ELXIS_ONLOAD')) {
			$eDoc->addNativeDocReady('elxOnLoad();');
			define('ELXIS_ONLOAD', 1);
		}

		$html = '';
		if ($emptylabel) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'">&#160;'."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}
		$html .= '<div class="elxnorobot"><a href="#" data-attid="'.$attributes['id'].'" class="elxanorobot"><span id="'.$attributes['id'].'box" class="elxnorobotbox">&#160;</span> '.eFactory::getLang()->get('IAMNOTA_ROBOT').'</a></div>'."\n";
		$html .= '<input type="hidden" name="'.$name.'" id="'.$attributes['id'].'" value="" data-genbase="'.$linkbase.'" dir="ltr">'."\n";
		if ($emptylabel) {
			$html .= "</div></div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/***********************/
	/* ADD CHECK BOX FIELD */
	/***********************/
	public function addCheckbox($name, $label='', $checked=null, $options=array(), $attrs=array()) {
		$checked = (is_array($checked)) ? $checked : array((string)$checked);
		$vertical_options = 0;
		$attributes = array('id' => $this->options['idprefix'].$name);
		$tip = '';
		$onlyelement = 0;//Elxis 5.1

		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'vertical_options') { $vertical_options = (int)$v; continue; }
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'1">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		if (is_array($options)) {
			if (count($options) > 0) {
				foreach ($options as $q => $option) {
					$i = $q + 1;
					$chk = (in_array($option['value'], $checked)) ? ' checked="checked"' : '';
					$attr = '';
					$optionclass = 'elx5_checkbox';
					if (is_array($option['attributes']) && (count($option['attributes']) > 0)) {
						foreach ($option['attributes'] as $key => $val) {
							if (in_array($key, array('id', 'name', 'type'))) { continue; }
							if ($key == 'class') { $optionclass = $val; continue; }
							$attr .= ' '.$key.'="'.$val.'"';
						}
					}
					$html .= '<label class="elx5_checkboxwrap">'.$option['label'].'<input type="checkbox" name="'.$name.'[]" id="'.$attributes['id'].$i.'" class="'.$optionclass.'" value="'.$option['value'].'"'.$chk.$attr.'>';
					$html .= '<span class="elx5_checkbox_checkmark"></span></label>'."\n";
				}
			}
		}
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) {
			$html .= "</div>\n";
			$html .= "</div>\n";
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/*****************************************************************/
	/* ADD SEO SUGGEST/VALIDATE LINKS (ELXIS 4.X STYLE - DEPRECATED) */
	/*****************************************************************/
	public function addSEO($name, $seoname, $sugfunc='', $valfunc='', $sargs=array(), $vargs=array()) {
		$eLang = eFactory::getLang();

		if ($sugfunc == '') { $sugfunc = 'suggestSEO'; }
		if ($valfunc == '') { $valfunc = 'validateSEO'; }
		$updateid = $this->options['idprefix'].'valseo'.rand(100, 999);
		if ($name == '') { $name = 'title'; }
		if ($seoname == '') { $seoname = 'seotitle'; }
		$titleid =  $this->options['idprefix'].$name;
		$seotitleid = $this->options['idprefix'].$seoname;

		$onsuggest = $sugfunc.'(\''.$titleid.'\', \''.$seotitleid.'\', \''.$updateid.'\'';
		if (is_array($sargs) && (count($sargs) > 0)) { $onsuggest .= ', \''.implode('\', \'', $sargs).'\''; }
		$onsuggest .= ')';
		$onvalidate = $valfunc.'(\''.$seotitleid.'\', \''.$updateid.'\'';
		if (is_array($vargs) && (count($vargs) > 0)) { $onvalidate .= ', \''.implode('\', \'', $vargs).'\''; }
		$onvalidate .= ')';

		eFactory::getDocument()->addFontAwesome();

		$html = '<div class="'.$this->options['rowclass'].'">'."\n";
		$html .= '<label class="'.$this->options['labelclass'].'">&#160;'."</label>\n";
		$html .= '<div class="'.$this->options['sideclass'].'">';
		$html .= '<a href="javascript:void(null);" onclick="'.$onsuggest.'" class="elx5_suggest"><i class="fas fa-cog" id="'.$updateid.'sug"></i> '.$eLang->get('SUGGESTED')."</a>\n";
		$html .= '<a href="javascript:void(null);" onclick="'.$onvalidate.'" class="elx5_validate"><i class="fas fa-check" id="'.$updateid.'val"></i> '.$eLang->get('VALIDATE')."</a>\n";
		$html .= '<div id="'.$updateid.'" class="elx5_invisible"></div>'."\n";
		$html .= "</div></div>\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/****************************************************/
	/* ADD SEO SUGGEST/VALIDATE LINKS (ELXIS 5.X STYLE) */
	/****************************************************/
	public function add5SEO($name, $seoname, $idname, $svbaseurl) {
		$eLang = eFactory::getLang();

		$id =  $this->options['idprefix'].$idname;
		$titleid =  $this->options['idprefix'].$name;
		$seotitleid = $this->options['idprefix'].$seoname;

		$updateid = $this->options['idprefix'].'valseo'.rand(100, 999);

		eFactory::getDocument()->addFontAwesome();

		$html = '<div class="'.$this->options['rowclass'].'">'."\n";
		$html .= '<label class="'.$this->options['labelclass'].'">&#160;'."</label>\n";
		$html .= '<div class="'.$this->options['sideclass'].'">';
		$html .= '<a href="#" data-sugtid="'.$titleid.'" data-sugsid="'.$seotitleid.'" data-sugid="'.$id.'" data-suguid="'.$updateid.'" data-sugurl="'.$svbaseurl.'" class="elx5_suggest"><i class="fas fa-cog" id="'.$updateid.'sug"></i> '.$eLang->get('SUGGESTED')."</a>\n";
		$html .= '<a href="#" data-valsid="'.$seotitleid.'" data-valid="'.$id.'" data-valuid="'.$updateid.'" data-valurl="'.$svbaseurl.'" class="elx5_validate" id="'.$updateid.'vallink"><i class="fas fa-check" id="'.$updateid.'val"></i> '.$eLang->get('VALIDATE')."</a>\n";
		$html .= '<div id="'.$updateid.'" class="elx5_invisible"></div>'."\n";
		$html .= "</div></div>\n";

		if (!defined('ELXIS_SEOSUGVAL_OK')) {
			eFactory::getDocument()->addNativeDocReady('elx5SEOOnLoad();');
			define('ELXIS_SEOSUGVAL_OK', 1);
		}

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/***********************************/
	/* ADD MULTILINGUAL TEXTAREA FIELD */
	/***********************************/
	public function addMLTextarea($name, $trdata, $value='', $label='', $attrs=array()) {
		$elxis = eFactory::getElxis();

		$allowed = ($elxis->acl()->check('component', 'com_etranslator', 'manage') < 1) ? false : true;
		if (!$allowed && !defined('ELXIS_ADMIN')) {
			$allowed = ($elxis->acl()->check('component', 'com_etranslator', 'api') < 1) ? false : true;
		}

		$editor = '';
		$contentslang = '';
		$editoroptions = array();
		$has_translations = false;
		$translations = array();
		$attributes = array('id' => $this->options['idprefix'].$name, 'placeholder' => $label);
		$tip = '';
		$onlyelement = 0;//Elxis 5.1
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') {
					$tip = $v;
					continue;
				}
				if ($k == 'id') { continue; }
				if ($k == 'class') { continue; }
				if ($k == 'dir') { continue; }
				if ($k == 'translations') {//multilingual XML parameter, no trdata in this case but provided translations in $attrs
					$has_translations = true;
					$translations = $v;
					continue;
				}
				if ($k == 'editor') {
					$editor = trim($v);//html
					continue;
				}
				if ($k == 'contentslang') {//TODO: USE IN JODIT?
					$contentslang = (string)$v;
					continue;
				}
				if ($k == 'editoroptions') {
					$editoroptions = is_array($v) ? $v : array();
					continue;
				}
				if ($k == 'onlyelement') { $onlyelement = (int)$v; continue; }
				$attributes[$k] = $v;
			}
		}
		if ($onlyelement == 1) {
			if (!isset($attributes['title'])) { $attributes['title'] = $label; }
		}

		$sitelangs = $this->getSiteLangs();
		$langnames = $this->getSiteLangs() ? array_keys($sitelangs) : array();
		$clang = $elxis->getConfig('LANG');
		if (!$has_translations) {
			$translations = $this->getTranslations($trdata['category'], $trdata['element'], $trdata['elid']);
		}

		$editor_js = '';
		if ($editor == 'html') {
			$tip = ''; //disable tips for rich text editor
			$elxeditor = $elxis->obj('editor');
			$elxeditor->prepare($attributes['id'], $editor, $contentslang, $editoroptions);
			$editor_js = $elxeditor->getJS();
			$value = htmlspecialchars($value);
			unset($elxeditor);
		}

		$this->prepareMultiLinguism();

		$distxt = '';
		if (!$allowed) { $distxt = ' disabled="disabled"'; }

		$hasaccess = $allowed ? 1 : 0;
		$class = 'elx5_mltranslator_off';
		if ($elxis->getConfig('TRANSLATOR') != '') {
			if ($allowed) { $class = 'elx5_mltranslator'; }
		}

		$html = '';
		if ($onlyelement == 0) {
			$html .= '<div class="'.$this->options['rowclass'].'">'."\n";
			$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
			$html .= '<div class="'.$this->options['sideclass'].'">';
		}

		if ($editor == 'html') {
			$html .= '<div class="elx5_mlboxwrap_editor">'."\n";
			$html .= '<div class="elx5_mlboxtrans_editor">'."\n";
			$html .= '<a href="#" id="'.$attributes['id'].'_translator" data-translatorid="'.$attributes['id'].'" data-formid="'.$this->formid.'" data-hasaccess="'.$hasaccess.'" data-editor="1" title="'.eFactory::getLang()->get('AUTO_TRANS').'" class="'.$class.'"><span class="fas fa-globe"></span></a>';
			$html .= "</div>\n";//elx5_mlboxtrans_editor
			$html .= '<div class="elx5_mlboxlang_editor">'."\n";
		} else {
			$html .= '<div class="elx5_mlboxwrap">'."\n";
			$html .= '<div class="elx5_mlboxlang">'."\n";
		}

		$is_editor = ($editor == 'html') ? 1 : 0;
		$html .= '<select name="'.$name.'_lang" id="'.$attributes['id'].'_lang" class="elx5_select elx5_mlflag'.$clang.'" dir="ltr" data-deflang="'.$clang.'" data-trelement="'.$name.'" data-idprefix="'.$this->options['idprefix'].'" data-editor="'.$is_editor.'" data-sitelangs="'.implode(',', $langnames).'"'.$distxt.'>'."\n";
		if ($sitelangs) {
			foreach ($sitelangs as $lng => $sitelang) {
				$sel = '';
				$oclass = '';
				if ($lng == $clang) {
					$sel = ' selected="selected"';
					$oclass = ' class="elx5_defoption"';
				} else {
					if (isset($translations[$lng])) { $oclass = ' class="elx5_hloption"'; }
				}
				$html .= '<option value="'.$lng.'"'.$oclass.$sel.'>'.strtoupper($lng)."</option>\n";
			}
		}
		$html .= "</select>\n";
		$html .= "</div>\n";//elx5_mlboxlang / elx5_mlboxlang_editor

		if ($editor != 'html') {
			$html .= '<div class="elx5_mlboxtrans">'."\n";
			$html .= '<a href="#" id="'.$attributes['id'].'_translator" data-translatorid="'.$attributes['id'].'" data-formid="'.$this->formid.'" data-hasaccess="'.$hasaccess.'" data-editor="0" title="'.eFactory::getLang()->get('AUTO_TRANS').'" class="'.$class.'"><span class="fas fa-globe"></span></a>';
			$html .= "</div>\n";//elx5_mlboxtrans
			$html .= '<div class="elx5_mlboxtext">'."\n";
		} else {
			$html .= "</div>\n";//elx5_mlboxwrap_editor
		}
		if ($sitelangs) {
			$trname = $name.'_';
			$trname_end = '';
			if (preg_match('@(])$@', $name)) {//XML param or other array style naming like "something[else]" => "something[else_mlLNG]"
				$trname = preg_replace('@(])$@', '', $name).'_ml';
				$trname_end = ']';
			}
			foreach ($sitelangs as $lng => $sitelang) {
				if ($lng == $clang) { continue; }
				$dir = ($sitelang['RTLSFX'] == '-rtl') ? 'rtl' : 'ltr';
				$v = isset($translations[$lng]) ? $translations[$lng] : '';
				$html .= '<textarea name="'.$trname.$lng.$trname_end.'" id="'.$attributes['id'].'_'.$lng.'" dir="'.$dir.'" class="elx5_invisible" placeholder="'.$label.' - '.$sitelang['NAME_ENG'].'">'.$v.'</textarea>'."\n";
			}
		}
		$dir = ($sitelangs[$clang]['RTLSFX'] == '-rtl') ? 'rtl' : 'ltr';

		$html .= '<textarea name="'.$name.'" id="'.$attributes['id'].'" dir="'.$dir.'" class="elx5_textarea elx5_mlflag'.$clang.'"';
		foreach ($attributes as $k => $v) {
			//if ($v == '') { continue; }
			if ($k == 'id') { continue; }
			if ($k == 'name') { continue; }
			if ($k == 'dir') { continue; }
			if ($k == 'class') { continue; }
			if ($k == 'forcedir') { continue; }
			$html .= ' '.$k.'="'.$v.'"';
		}
		$html .= ">".$value."</textarea>\n";
		if ($editor != 'html') {
			$html .= "</div>\n";//elx5_mlboxtext
			$html .= "</div>\n";//elx5_mlboxwrap
		}
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		if ($onlyelement == 0) { $html .= "</div>\n</div>\n"; }
		$html .= $editor_js;

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}


	/********************************************************************************/
	/* ADD COMBINED FORM ELEMENTS (GENERATED WITH "onlyelement" option) - Elxis 5.1 */
	/********************************************************************************/
	public function addCombined($name, $label, $combined_html, $attrs=array()) {
		$attributes = array(
			'id' => $this->options['idprefix'].$name,
		);
		$tip = '';
		if ($attrs) {
			foreach ($attrs as $k => $v) {
				if ($k == 'tip') { $tip = $v; continue; }
				if ($k == 'id') { $attributes['id'] = $v; continue; }
			}
		}

		$html = '<div class="'.$this->options['rowclass'].'">'."\n";
		$html .= '<label class="'.$this->options['labelclass'].'" for="'.$attributes['id'].'">'.$label."</label>\n";
		$html .= '<div class="'.$this->options['sideclass'].'">';
		$html .= $combined_html;
		if ($tip != '') { $html .= '<div class="'.$this->options['tipclass'].'">'.$tip."</div>\n"; }
		$html .= "</div>\n";
		$html .= "</div>\n";

		if ($this->options['returnhtml']) { return $html; }
		echo $html;
	}

}

?>